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

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

この記事を読むのに必要な時間:およそ 11 分

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のウィジェットを混在して利用する方法を説明します。

著者プロフィール

杉田研治(すぎたけんじ)

1955年生まれ。東京都出身。株式会社SRAに勤務。プログラマ。

仕事のほとんどをMac OS XとKubuntu KDE 4でQtと供に過ごす。