Qt最新事情-QtでWebKitを使ってみよう

第5回Qt WebKit~ブラウザ機能の基本

はじめに

これまで4回に渡ってQtの基本部分について説明して来ました。今回と次回は、Qt WebKitでのブラウザ機能の利用方法、そして、ブラウザ機能とQtのウィジェットを混在して利用する方法について説明します。前回の後に来年1月にリリース予定のQt 4.5のTechnology Previewが2008/10/21にリリースされているので、これについても触れておきます。

※⁠Qt 4.5 Technology Previewの前日にSymbian OSへの移植もアナウンスされています。また、Nokiaへの買収に伴い、社名がTrolltechからQt Software, Nokiaに変更されWebページも一新されました。

Qt 4.5 Technology Preview

Technology Previewには、Qt 4.5で予定されている改善や機能がすべて盛り込まれてはいませんが、いくつかを実際に確認できます。ただし、改善点や新しい機能の評価用というのがTechnology Previewのリリース目的なため、落ちてしまったり今まで通りには動かないことがあります。

パフォーマンス改善

Qt 4.4まででは、2D描画機能のバックエンドの描画エンジンとして、それぞれのプラットフォームの描画機能とOpenGL を利用できます。何もしなければ各プラットフォームの描画機能が使われ、以下のようにすればOpenGLを2D描画に使って描画速度を改善できるようになっています。

  1. Graphics View
    setViewpor(QGLWidget(QGLFormat(QGL::SampleBuffers))) を呼び出す。
  2. ウィジェット
    QGLWidget(QGLFormat(QGL::SampleBuffers)) を親ウィジェットにする。

描画精度や描画速度を上げようとすると3つの問題があります。

  1. プラットフォーム固有の描画機能には、描画上で細かな差異があります。そこで、プラットフォームに依存せずに正確な描画をしたい場合には、QImageに描画をしてからQImageを表示するという方法を取ることとなります。しかし、一般にQImageがクライアントアプリケーション中に存在するデータということから、プラットフォームによっては描画コストが高くなります。
  2. OpenGLを描画エンジンにすると速度は改善されますが描画精度が落ちます。正確な描画のためには各プラットフォームの描画エンジンを用いるか、1)の方法を取ることとなります。
  3. OpenGLを描画エンジンに使用するには、僅かな修正ですがすべてのウィジェットのベースクラスを変更しなければなりません。アプリケーション全体で簡単にOpenGLを描画エンジンに切換える方法はありません。

Qt 4.5 Technology Previewではこれらの問題への解決が試みられています。ひとつは、ラスター(つまりQImageを使用した描画と表示)とOpenGLをアプリケーションの描画エンジンとして使用できるようにする機能です。

$ app -graphicssystem raster
$ app -graphicssystem opengl

使い方は簡単で、上記のようにアプリケーション起動時のパラメータに指定するだけで、アプリケーションのソースコードの変更は要りません。Qt Labs Blogsに掲載されている描画エンジンのベンチマーク結果は以下のようになっています。

表1 2D描画バックエンドのベンチマーク(単位:Fps)
描画エンジンWindowsX11Mac OS X
プラットフォームの描画機能60209
ラスター603630
OpenGL24592215

※出典:Qt Labs Blogs So Long and Thanks for the Blit!

一般に、QPixmapの実装には描画速度に適した画像リソースを使い、QImageはアプリケーション空間のデータに持ってラスター操作に適したデータとして扱うようにしていますが、ラスター描画エンジンのベンチマーク結果に違いがあります。

Windowsでラスター描画エンジンの効果がないのは、Windowsでは元々ラスター描画をしているためです。実際、WindowsでのQPixmapの実装はラスターとなっていて、QImageを内部に抱えているような構造になっています。

X11では、QPixmapはX Window Systemのサーバ側のリソースPixmapで、QImageはラスターデータXImageとなっているので、単にラスター描画をするだけではQImageをサーバ側に転送するためかなりのボトルネックになります。Qt 4.5 Technology Preview では、X Shared Memory Extension(MIT-SHM)を使い、同一ホスト内のアプリケーションであれば、サーバへの転送を避けるようにしています。MIT-SHMを利用するようになったので、QPixmapとQPixmapの相互変換も改善されるはずで、計測してみると次のようになりました。

表2 X11でのQPixmapとQImageの変換処理のベンチマーク(単位 ms)
1024x1024
2048x2048
4.4.34.5 TP14.4.34.5 TP1
QPixmap::toImage()92860266850
QPixmap::fromImage()9185814311853450

かなりの改善結果です。画像のスケールをスライダで変えて表示するコードを書いて、サイズが3000×3000の画像で、X11でラスター描画エンジンを試してみると、OpenGLを描画エンジンにしたのと同じように滑らかにスケールが変わるようになっています。

描画速度は改善されるのですが、問題はあります。ラスター描画エンジンを使用して、イメージを表示しようとして読み込むと、ボタンのテキストが文字化けをしたりします。また、OpenGLを描画エンジンにする機能は、4.5ではまだ実験的なレベルとなるそうです。OpenGLを使用しているプログラムに対して、グラフィックスシステムにOpenGLを指定すると、OpenGLで描画する図形がずれて表示されたりするなどの問題がありました。一方、ラスター描画エンジンは全プラットフォームでフルサポートされます。

※⁠Windowsでの2D描画には、Direct Xも実験的に描画エンジンとして用いられるようになっています。ただし、ウィジェットが表示されない場合があるなどの問題があります。
※⁠MIT-SHMを使うコードは、Qt 1から書かれてはいます。

WebKitの改善

第1回で挙げた機能やキャッシュが実装されていて、YouTubeなどで動画の再生がデスクトップ版のQtだけではなくWindows CEでもできるようになっています。

その他

ざっと見ただけですが、以下のような追加がされていました。

  • Qt/Mac OS XのCocoaブランチの取り込み。
  • QtTestモジュールへのベンチマーク機能の追加。
  • QtXmlPatternsモジュールでのXSL-Tサポート。
  • ODF (Open Document Format) のサポート。
  • Qt Designerの操作性改善
  • Qt/Mac OS Xでのバンドル作成支援ツールmacdeployの追加。
  • リファレンスマニュアルへのQt for Windows CEについての記述の追加。

Qt WebKitの動かし方

Qt WebKitを動かすには3通りの方法があります。

(1) Qt 4.4.3のQt WebKit

現時点での正式リリース版です。Qtにはリリース時のWebKitのソースコードが含まれています。Qt 4.4.3では、Qt WebKitでYouTubeなどの動画再生はまだできません。

(2) Qt 4.4.3とオープンソースWebKit

Qt 4.5向けのQt WebKitの開発中のソースコードは、開発中のWebKitに含まれています。Phononをバックエンドとして、Qt WebKitでFlashビデオの再生ができます。Qt本体は4.4.3なので、最新のQt WebKitを安定して動くQtで試せます。

(3) Qt 4.5 Technology Preview

Qt本体がQt 4.5の開発版となる他は(2)と同様です。

Qt WebKitのビルド方法

Qt 4.4.3やQt 4.5 Technology Previewには、各Qtで使えるWebKitが同梱されているので、Qtをインストールすれば、Qt WebKitが使えます。

Qt 4.4.3とオープンソースWebKitを用いる場合には、まず、Qt 4.4.3をインストールしておき、次にWebKitをビルドします。

1.SVNリポジトリからWebKitを引出します。1Gバイト以上あります。

$ svn checkout http://svn.webkit.org/repository/webkit/trunk WebKit

2.ビルド環境の設定

オプション--releaseまたは--debugを指定してビルドモードを設定し、環境変数QTDIRを設定します。

$ cd WebKit/WebKitTools/Scripts
$ ./set-webkit-configuration --release
$ export QTDIR <Qt 4.4.3のインストールディレクトリ>

3.ビルド

ビルド方法は以下のようにします。WebKit/WebKitBuild/Releaseのlibやbinにライブラリやサンプルブラウザが作成されます。

$ cd WebKit/WebKitTools/Scripts
$ ./build-webkit

4.実行

最小限のブラウザ機能を実装したWebKit/qt/QtLaucherもビルドされ、WebKit/WebKitBuild/Release/binの下にでき、以下のようにすれば動かせます。

$ cd WebKit/WebKitTools/Scripts
$ ./run-launcher
図1 WebKitのQtLauncher
図1 WebKitのQtLauncher

Qt WebKitの構成

Qt WebKitは、表3に挙げたクラスから構成されていて、QtからWebKitの種々の機能をアクセスできるようにしています。

表3 Qt WebKitのクラス一覧
クラス機能
QWebFrameWebページ内のフレーム
QWebHistoryQWebPageのヒストリー
QWebHistoryInterfaceリンクヒストリーの実装とのインターフェース
QWebHistoryItemQWebPageのヒストリー項目
QWebHitTestResultWebページのヒットテスト結果
QWebPageWebドキュメントオブジェクト
QWebPluginFactoryWebページに組込むプラグイン生成
QWebSettingsQWebPageとQWebFrameの設定内容の格納
QWebViewWebドキュメントの表示と編集をするウィジェット

図2に、これらのクラスの関連を示します。QWebPageはWebのページに対応し、ページ内のフレームはQWebFrameで構成されています。実際に目に見える表示をするのはQWebViewで、QWebPage内のページがQWebViewに表示されます。

図2 Qt WebKitのクラス関連図
図2 Qt WebKitのクラス関連図

Webページの表示

リスト1は、コマンドラインで指定したURLを開いて表示するプログラムです。実行は以下のようにします。

$ step1 url

QWebViewのインスタンスを生成し、URLを指定してロードしているだけです。図3は、その実行結果です。右ボタンをクリックして表示されるコンテキストメニューによって、ページを行き来するナビゲーションもできます。

リスト1 step1.cpp
01: #include <QApplication>
02: #include <QtWebKit>
03: 
04: int main( int argc, char** argv )
05: {
06:     QApplication app( argc, argv );
07: 
08:     QUrl url;
09:     if ( argc == 1 )
10:         url = QUrl( "http://trolltech.com" );
11:     else
12:         url = QUrl( argv[1] );
13: 
14:     QWebView* webView = new QWebView( 0 );
15:     webView->load( url );
16:     webView->show();
17: 
18:     return app.exec();
19: }

図3 step1の実行結果
図3 step1の実行結果

QWebView::load() の実装コードでは、表示対象のQWebPageオブジェクトのインスタンスのメインフレームのQWebFrame::load() にURLを指定して呼出しています。

Qt 4.5 Technology PreviewかWebKitを用いれば、リスト2の行番号14~15のコードを付け加えるだけで、http://jp.youtube.comをコマンドラインで指定すれば、YouTubeの動画も再生できるようになります。

リスト2 step2.cpp
12:          url = QUrl( argv[1] );
13: 
14:     QWebSettings* webSettings = QWebSettings::globalSettings();
15:     webSettings->setAttribute( QWebSettings::PluginsEnabled, true );
16: 
17:     QWebView* webView = new QWebView( 0 );

QWebSettingsにはフォントのファミリーやサイズ設定、イメージ読み込みの可否やJavaScriptの有効無効などの設定が格納され、QWebPageとQWebFrameでその設定値が使われます。

Webブラウザらしくする

いくつか機能を追加してWebブラウザらしくすると、図4のようになります。本格的なブラウザ機能を実装するには、Qt Demo Browser(ソースコードはQt配布コード中のdemos/browser/)のように1万行以上のコード記述が必要です。この簡単なブラウザには辿れないリンクがあるなど不備はありますが、3ファイルで合わせて120行程度でブラウザらしいプログラムができてしまいますリスト3~5⁠。

図4 簡単なブラウザ
図4 簡単なブラウザ
リスト3 mainwindow.h
01: #ifndef MAINWINDOW_H
01: #define MAINWINDOW_H
02: 
03: #include <QMainWindow>
04: class QWebView;
05: class QLineEdit;
06: class QProgressBar;
07: 
08: class MainWindow : public QMainWindow

QMainWindowを用いて、メニューバー、ツールバー、ステータスバーを持つ一般的なアプリケーションを作ります。

10: {
11:      Q_OBJECT
12: 
13: public:
14:     MainWindow();
15: 
16: protected slots:
17:     void changeLocation();
18:     void loadFinished();
19:     void showLinkHover( const QString& link );
20: 
21: private:
22:     QWebView* webView;
23:     QLineEdit* urlEdit;
24:     QProgressBar* progress;
25: };
26: 
27: #endif
リスト4 mainwindow.cpp
01: #include <QtWebKit>
02: #include <QWebView>
03: #include <QLineEdit>
04: #include <QProgressBar>
05: #include <QMenuBar>
06: #include <QToolBar>
07: #include <QStatusBar>
08: #include "mainwindow.h"
09: 
10: MainWindow::MainWindow()
11:   : QMainWindow( 0 ) 
12: {
13:     webView = new QWebView( this ) ;
14:     setCentralWidget( webView );

QWebViewのインスタンスを処理の中心となるウィジェットとして設定しています。

15: 
16:     progress = new QProgressBar( this );
17:     progress->setRange( 0, 100 );
18:     progress->setMinimumSize( 100, 20 );
19:     progress->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
20:     progress->hide();
21:     statusBar()->addPermanentWidget( progress );
22: 
23:     connect( webView, SIGNAL( loadProgress( int ) ), progress, SLOT( show() ) );
24:     connect( webView, SIGNAL( loadProgress( int ) ), progress, SLOT( setValue( int ) ) );
25:     connect( webView, SIGNAL( loadFinished( bool ) ), progress, SLOT( hide() ) );

ステータスバーにロード状況を表示するためにプログレスバーを埋め込みます。この他に、ステータスバーには、ポインタの下にリンクがあればそのURLも表示しします。

26: 
27:     urlEdit = new QLineEdit( this );
28:     urlEdit->setSizePolicy( QSizePolicy::Expanding, urlEdit->sizePolicy().verticalPolicy() )
29:     connect( urlEdit, SIGNAL( returnPressed() ), SLOT( changeLocation() ) );

URLの入力欄です。横に広がりやすくするようにサイズポリシーを設定し、リターンキーが押されたらスロットchangeLocation()を呼出します。

30: 
31:     QToolBar* bar = addToolBar( "Navigation" );
32:     bar->setMovable( false );
33:     bar->addAction( webView->pageAction( QWebPage::Back ) );
34:     bar->addAction( webView->pageAction( QWebPage::Forward ) );
35:     bar->addAction( webView->pageAction( QWebPage::Reload ) );
36:     bar->addAction( webView->pageAction( QWebPage::Stop ) );
37:     bar->addWidget( urlEdit );

ナビゲーションのためのツールバーを作成しています。QActionのインスタンスは、アイコンも含めてQWebViewが用意しているのでとても簡単です。このようなコードだけで、ナビゲーションアイコンが付いたツールバーが作れてしまいます。

38: 
39:     QMenu* fileMenu = menuBar()->addMenu( "&File" );
40:     fileMenu->addAction( "Quit", qApp, SLOT( quit() ) );
41: 
42:     connect( webView, SIGNAL( loadFinished( bool ) ),
43:              this, SLOT( loadFinished() ) );
44:     connect( webView, SIGNAL( titleChanged( const QString& ) ),
45:              this, SLOT( setWindowTitle( const QString& ) ) );
46:     connect( webView->page(), SIGNAL( linkHovered( const QString&, const QString&, const QSt& ) ),
47:              this, SLOT( showLinkHover( const QString& ) ) );

QWebViewは、操作やWebページの表示状況に対応して、適切なシグナルを数多く用意しているので、必要な処理をするスロットをシグナルと接続します。setWindowTitle()はQWidgetのスロットで、QWebViewのタイトルが変わったならば、それを表示するようにしています。

48: }
49: 
50: void MainWindow::changeLocation() 
51: {
52:     QUrl url = urlEdit->text().simplified();
53:     urlEdit->setText( url.toString() );
54:     webView->load( url );
55:     webView->setFocus( Qt::OtherFocusReason );
56: }

URLが入力されてリターンキーが押されると呼出されるスロットです。入力値の頭と末尾の空白を取り除くようにしています。ロード後にQWebViewにフォーカスを設定して、表示領域にキー入力を移しています。

57: 
58: void MainWindow::loadFinished() 
59: {
60:     urlEdit->setText( webView->url().toString() );
61: }

リンクを辿って表示した場合に、表示されたページのURLをURL入力欄に表示しています。

62: 
63: void MainWindow::showLinkHover( const QString& link ) 
64: {
65:     if ( link.isEmpty() )
66:         statusBar()->clearMessage();
67:     else
68:         statusBar()->showMessage( QString( tr( "Open %1" ).arg( link ) ) );
69: }

ポインタがリンクの上にあるときにそのURLをステータスバーに表示し、ポインタがリンクの外に出たらステータスバーを消去しています。

リスト5 main.cpp
01: #include <QApplication>
02: #include <QWebSettings>
03: 
04: #include "mainwindow.h"
05: 
06: int main( int argc, char** argv )
07: {
08:     QApplication app( argc, argv );
09: 
10:     QWebSettings* webSettings = QWebSettings::globalSettings();
11:     webSettings->setAttribute( QWebSettings::PluginsEnabled, true );
12: 
13:     MainWindow mainWindow;
14:     mainWindow.show();
15: 
16:     return app.exec();
17: }

QMainWindowを継承してカスタマイズしたMainWindowのインスタンスを生成し表示しています。

Webページのレンダリング結果を画像で保存する

GUI機能を用いずQt WebKitの機能のみでWebベースのユーティリティプログラムを作成してみましょう。表3に挙げたQt WebKit関連のクラスの中で、ウィジェットを継承するクラスはQWebViewのみとなっています。Webページを構成するWebフレームのQFrame::load(const QUrl&)でURLを読み込めるようになっていて、QFrame::render()は引数にQPainterを取るようになっていることに注目しましょう。つまり、ウィジェットがなくともWebアクセスをしてHTMLをレンダリングできるのです。

QWebPageのリファレンスマニュアル に、URLを指定してレンダリングをし、その結果を画像ファイルとして保存するコードが載っています。このコードを元にして次のようにして使うコマンドを作りましょう。

$ render url [スケール]

このコマンドを実行すると指定したURLのWebページをレンダリングし、その結果が画像ファイルとしてthumbnail.pngに保存されます。デフォルトのスケールは1.0で、0.5ならば1/2に縮小され、2.0ならば倍に拡大されます。

リスト6が実装コードです。行を追って説明します。

リスト6 render.cpp
01: #include <QtGui>
02: #include <QtWebKit>
03: 
04: class Thumbnailer : public QObject

Webページへのアクセスは、ネットワークを含めてQtのイベントループの中で行われ、シグナルによって処理状況が通知されるので、QObjectを継承したクラスを用いています。

05: {
06:     Q_OBJECT
07: 
08: public:
09:     Thumbnailer( const QUrl& url, double scale = 1.0 );
10: 
11: signals:
12:     void finished();

新たに追加したシグナルでWebページをロードし、画像ファイルとして保存が完了したときに送信されます。

13: 
14: private slots:
15:     void render();

QWebPageでのロードが完了したときに呼び出す処理スロットです。

16: 
17: private:
18:     QWebPage page;
19:     double scale;
20: };
21: 
22: Thumbnailer::Thumbnailer( const QUrl& url, double scale )
23:     : scale( scale )
24: {
25:     page.mainFrame()->load( url );
26:     connect( &page, SIGNAL( loadFinished( bool ) ), this, SLOT( render() ) );

Webページpageのメインフレームに指定されたURLを渡してロードしています。レンダリング終了はQWebPageのシグナルloadFinished(bool) でわかるので、このシグナルをスロットrender()に接続してすれば、ロードしたWebページを扱えます。

27: }
28: 
29: void Thumbnailer::render()
30: {
31:     page.setViewportSize( page.mainFrame()->contentsSize() );

QWebPageのビューポート(表示領域)のサイズを、QWebPageのメインフレーム(最外部のフレーム)のサイズと同じにします。

32: 
33:     QImage image( page.viewportSize(), QImage::Format_ARGB32 );

ラスターイメージデータQImageをQWebPageのビューポートと同じサイズで用意します。

34: 
35:     QPainter painter( &image );
36:     page.mainFrame()->render( &painter );
37:     painter.end();

Qtの2D描画機能を使うには、描画対象オブジェクトに対してQPainterのインスタンスを作成して、その描画機能を呼び出して使います。ここでは、そのインスタンスをQWebFrame::render() に渡して、QWebFrame内で描画をさせています。

38: 
39:     QImage thumbnail;
40:     if ( scale == 1.0 ) {
41:         thumbnail = image;
42:     } else {
43:         QSize size = page.viewportSize() * scale;
44:         thumbnail = image.scaled( size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation );
45:     }
46: 
47:     thumbnail.save( "thumbnail.png" );
48: 
49:     emit finished();

スケーリングした画像を格納するためにQImageのデータthumbnailを用いています。スケールが1.0のときにthumbnailにimageを代入していますが、QImageは暗黙の共有クラスですから画像データ全体がコピーされず、imageと画像データが共有されます。スケールが1.0でないときには、QImage::scaled()を用い、Qt::SmoothTransformationを指定してスケーリング結果が滑らかになるようにしています。そして、QImage::save()で画像ファイルを保存し、処理が完了したことをシグナルfinished()で送信します。

50: }
51: 
52: int main( int argc, char** argv )
53: {
54:     QApplication app( argc, argv );
55: 
56:     QUrl url = QUrl( "http://trolltech.com" );
57:     double scale = 1.0;
58: 
59:     switch ( argc ) {
60:     case 2:
61:         url = argv[1];
62:         break;
63:     case 3:
64:         url = argv[1];
65:         scale = QString( argv[2] ).toDouble();
66:         break;
67:     }
68: 
69:     Thumbnailer thumbnailer( url, scale );
70: 
71:     QObject::connect( &thumbnailer, SIGNAL( finished() ), &app, SLOT( quit() ) );

コマンドラインで指定したURLのレンダリングが完了したならば、イベントループから抜けてプログラムを終了させるために、Thumbnailerのシグナルfinished()をQApplicationのスロットquit()に接続しています。

72: 
73:     return app.exec();
74: }
75: 
76: #include "render.moc"

SVGファイルのレンダリング結果を画像で保存する

Qt Labsには、Qtの開発元の開発者が実験中の機能やQtの突っ込んだ使い方が掲載されています。その中から、Qt WebKitを使ってSVG(Scalable Vector Graphics)を描画し、PNG形式の画像ファイルとして保存するプログラムを紹介します。

Qt Labs Blogs - WebKit-based SVG rasterizer
http://labs.trolltech.com/blogs/2008/08/06/webkit-based-svg-rasterizer/

上記ページに書かれている説明から要点のみ説明しましょう。

$ svn2png tiger.svg tiger.png 300 300

コマンドをこのように実行すると300x300の大きさにSVGをレンダリングし、PNGファイルに保存されます。図5はそこで生成されたPNG画像です。

図5 SVGからPNGへの変換結果
図5 SVGからPNGへの変換結果

リスト7リスト8が上記のコマンドの実装コードです。このコードは、

  • svn://labs.trolltech.com/svn/graphics/dojo/svg2png

から入手できます。行を追って説明します。

リスト7 svg2png.h
01: #ifndef WEBKIT_SVG_RENDERER
02: #define WEBKIT_SVG_RENDERER
03: 
04: #include <QWebView>
05: 
06: class WebKitSVGRenderer : public QWebView
07: {
08:     Q_OBJECT
09: 
10: public:
11:     QString fileName;
12:     QSize targetSize;
13:     WebKitSVGRenderer(QWidget *parent = 0);
14: 
15: private slots:
16:     void saveResult(bool ok);
17: };
18: 
19: #endif // WEBKIT_SVG_RENDERER
20: 
リスト8 svg2png.cpp
11: WebKitSVGRenderer::WebKitSVGRenderer(QWidget *parent) : QWebView(parent)
12: {
13:     connect(this, SIGNAL(loadFinished(bool)), this, SLOT(saveResult(bool)));

先に説明したサンプルと同様に、SVGファイルのロードがされたときに送信されるシグナルloadFinished(bool)をスロットsaveResult(bool)に接続して、saveResult()で画像を保存するようにしています。

21: #define EVAL_JS(x) page()->mainFrame()->evaluateJavaScript((x))

Qt 4.4には、まだDOM APIが用意されていないので、JavaScriptを実行し、その結果を得ることで、ロードしたSVGファイルの情報にアクセスします。このマクロはそのための定義です。

23: void WebKitSVGRenderer::saveResult(bool ok)
24: {
25:     // crude error-checking
26:     if (!ok) {
27:         std::cerr << "Failed loading " << qPrintable(url().toString()) << std::endl;
28:         QApplication::instance()->exit(1);
29:         return;

ちょっとわかり難いコードかもしれません。QApplication::exec() で開始されたイベントループを終了コードを指定して抜けるようにしています。イベントループを実際に抜けるのは、このメンバー関数を抜けてイベントループに戻った後なので、return で戻る必要があることに注意してください。なお、QApplication::quit() は exit(0) と同じです。

32:     // ensure it is an SVG
33:     QString root = EVAL_JS("document.rootElement.nodeName").toString();
34:     if (root.isEmpty() || root.compare("svg", Qt::CaseInsensitive)) {
35:         std::cerr << "Not an SVG! " << qPrintable(url().toString()) << std::endl;
36:         close();
37:         return;
38:     }

JavaScriptでドキュメントのノード名を取出し、SVGファイルどうかをチェックしています。

40:     // get the dimension, i.e. the width and height attributes
41:     // Note: if an attribute is not defined WebKit would return the view's dimension
42:     // hence the hack of checking for the MAGIC_SIZE
43:     double ww = EVAL_JS("document.rootElement.width.baseVal.value").toDouble();
44:     double hh = EVAL_JS("document.rootElement.height.baseVal.value").toDouble();
45:     if (ww == 0.0 || hh == 0.0 || ww == MAGIC_SIZE || hh == MAGIC_SIZE) {
46:         std::cerr << "SVG does not specify proper width and height! " << std::endl;
47:         close();
48:         return;
49:     }

こちらも同様にしてJavaScriptを使って、SVGの画像サイズのチェックをしています。

このサンプルの説明があるWebページから入手できるSVGファイルは、昔からPostScriptでよく引用されている虎の画像ファイルです。そのままだと、このプログラムで使うとエラーになってしまうので、SVGファイルをエディタで編集してwidthとheightの属性を両方600に指定して使っています。

59:     // create the target surface
60:     QSize t = targetSize.isValid() ? targetSize : QSize(ww, hh);
61:     QImage img(t, QImage::Format_ARGB32_Premultiplied);
62:     img.fill(Qt::transparent);
63: 
64:     // prepare the painter
65:     QPainter p(&img);
66:     qreal xs = targetSize.isValid() ? targetSize.width() / ww : 1.0;
67:     qreal ys = targetSize.isValid() ? targetSize.height() / hh : 1.0;
68:     p.scale(xs, ys);
69: 
70:     // the best quality
71:     p.setRenderHint(QPainter::Antialiasing);
72:     p.setRenderHint(QPainter::TextAntialiasing);
73:     p.setRenderHint(QPainter::SmoothPixmapTransform);

先に説明したサンプルとの描画方法と違い、QPainterにアフィン変換を適用してスケーリングしています。

75:     page()->mainFrame()->render(&p);
76:     p.end();
77: 
78:     if (img.save(fileName, "png"))
79:         std::cout << "Result saved to " << qPrintable(fileName) << std::endl;
80:     else
81:         std::cout << "Failed to save to " << qPrintable(fileName) << std::endl;
82: 
83:     close();
84: }

SVGを変換しつつ描画をしてから、ファイルに保存しています。

SVGのレンダリングをしているWebKitSVGRenderでは、QWebViewを継承したクラスはウィジェットであって、インスタンスを生成するとウィンドウとなっています。このためclose() を何ヵ所かで呼び、ウィンドウを閉じてプログラムを終了させています。あまり見慣れないコードですが、close()の呼び出しをしないとウィンドウが存在し続けるので、SVGからPNGが生成されてもプログラムは終了しません。

まとめと次回の予告

今回は、Qtでの簡単なブラウザの作成方法とブラウザ機能の利用方法について説明しました。WebKitはブラウザエンジンであって、ブラウザそのものではありません。同じようにQt WebKitもブラウザではなく、WebKitというブラウザエンジンをQtから利用して、いろいろなアプリケーションを作成するための機能です。

次回は、ブラウザ機能とQtのウィジェットを混在して利用する方法を説明します。

おすすめ記事

記事・ニュース一覧