Pango でテキスト描画

(2005.5.15 新規作成)
[2017.5] gtk3に更新. コンパイルエラーになった箇所を修正。
[2020.12] 最新の環境で再コンパイル。

Pango は、GNOMEで使われているテキストのレイアウト・レンダリングモジュール。内部ではテキストをUnicodeで表現する。

日本語が表示できるのはアプリケーションを見ていれば分かるので、合字のある言語でも表示できるか試してみた。

タイ語で、น้ำ と表示できるか。Windows版のMozilla 1.7.7, Internet Explorer 6.0では表示できる。Linux版のMozilla, Firefoxでは表示できなかった。

この文字列はUnicodeでは次のコードからなる。2番目と3番目のコードの両方が1文字目のグリフに影響する。また、2番目のコードが表す字形の表示位置が上のほうへ回っているのが分かる。

  • U+0E19 THAI CHARACTER NO NU
  • U+0E49 THAI CHARACTER MAI THO
  • U+0E33 THAI CHARACTER SARA AM

[2017-05] Fedora 25, gtk3 3.22.12, pango 1.40.5 という環境で表示できるか試してみた。

ソースコード

まずは、ヘッダをincludeして、ウィンドウを閉じたときの処理を書く。Pango単体のときはともかく、通常はgtk+から使うので、gtk+のヘッダをincludeする。

C++
[RAW]
  1. #include <stdio.h>
  2. #include <gtk/gtk.h>
  3. int on_destroy(GtkWidget* w, GdkEvent* e, void* d)
  4. {
  5. gtk_main_quit();
  6. return 0;
  7. }

今回テストしたのは、次の文字列;

C++
[RAW]
  1. const char* str = u8"<span font_desc='IPAexMincho' size='40000'><u>日本語</u> "
  2. "<span>&#x0E19;&#x0E49;&#x0E33;</span>葛󠄀葛󠄁😁🇯🇵🥷🏽\U0001F441\U0000FE0F\U0000200D\U0001F5E8\U0000FE0F</span>";
  • IVS (Ideographic Variation Sequence) への対応。よくある例で, 葛 U+845B に U+E0100 または U+E0101を続けます。
  • 絵文字 Emoji. 吹出しの目 [ U+1F441 片目, U+FE0F バリエイションセレクタ16, U+200D ゼロ幅ジョイナ, U+1F5E8 吹出し左, U+FE0F バリエイションセレクタ16]
  • 国旗 Regional Indicator Symbols. U+1F1E6 〜 U+1F1FF を2文字続ける。Unicode 6.2 (2012年) から 2文字ペアで分割しないことが明確になった。See UAX #14: Unicode Line Breaking Algorithm. また, このために, データベースに Regional_Indicator という新しいフラグを導入するハメに。
  • Skin-tone (Unicode 8.0; 2015年). Leftist (差別主義者) は何でも区別したがる。人間というただ一つの種があるのに, 細分化し、区別し、対立させる。この例では, U+1F3FD EMOJI MODIFIER FITZPATRICK TYPE-4

Pangoでは、テキストの文字の大きさ、色などを指定するのに、HTMLに似たタグでテキストをマークアップする; <span>, <b>, <i>, <s>, <sub>, <sup>, <u>.

特に, <span> の属性値で視覚効果を指定できます。font_desc 属性値でフォント名・大きさなどを与えます。

これらでマークアップした文字列を pango_parse_markup()に渡して、プレーンテキストの文字列と、属性に分解する。下の例では, textattrs に分離しています。

gtk+では、ラベルなど, ただの文字列も与えることができるようになっています。まず文字列を設定してから属性を別に追加するようになっている。属性は *_set_attributes() で与える。

C++
[RAW]
  1. int main(int argc, char* argv[])
  2. {
  3. GtkWidget* window;
  4. GtkWidget* label;
  5. char* text = NULL;
  6. PangoAttrList* attrs = NULL;
  7. GError* error = NULL;
  8. gtk_init(&argc, &argv);
  9. // 呼出側で attrs, text の解放必要.
  10. if (!pango_parse_markup(str, -1, 0, &attrs, &text, NULL, &error)) {
  11. printf("error:%s\n", error->message);
  12. return 1;
  13. }
  14. label = gtk_label_new(text);
  15. // テキスト属性を追加
  16. gtk_label_set_attributes(GTK_LABEL(label), attrs);

あとは、ウィンドウを生成するだけ。

C++
[RAW]
  1. window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  2. gtk_window_set_title(GTK_WINDOW(window), "Pangoでの表示");
  3. gtk_container_add(GTK_CONTAINER(window), label);
  4. gtk_container_set_border_width(GTK_CONTAINER(window), 30);
  5. gtk_widget_show_all(window);
  6. g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(on_destroy), NULL);
  7. gtk_main();
  8. pango_attr_list_unref(attrs);
  9. free(text);
  10. if (error) g_error_free(error);
  11. return 0;
  12. }

コンパイル、実行結果

次のようにしてコンパイルする。

$ gcc -Wall `pkg-config --cflags --libs pango gtk+-3.0` pango.cc -lstdc++

実行結果は、次のようになる。[2020.12] Fedora 33 で実行。IVS は未対応。HarfBuzz (hb-view コマンド) では対応しているので, Pango の問題か?

タイ語も表示できている。