伝統的なXのテキスト描画 (Xft)

[2020.12] 古い方法でも動くか確認。

Xでは伝統的に, フォントを XLFD (X Logical Font Description) で選択するようになっていた。フォントはビットマップフォント。この方法は廃れた。

Xに含まれるもう一つの方法は, Xft。ただ、これも開発は低迷している。

Core X11 Font

この方法は、コンパイルは通るが、完全に廃れた。

XLFD の最後の2フィールドで文字セット (Coded Character Set; CCS) を表す。XDrawString*() に与えるテキストデータは、CCS に応じた値。

xfontsel でマッチするフォントを確認できる。

Fedora 33 だと、今さらビットマップフォントでもないので、インストールされているフォントは非常に限られる。iso8859-1, jisx0208.1983-0, iso10646-1 で, 大きさを揃えることもできない。

C++
[RAW]
  1. #include <stdio.h>
  2. #include <assert.h>
  3. #include <string.h>
  4. #include <X11/Xlib.h>
  5. #include <stdint.h>
  6. // このフォントの指定方法では限界。ビットマップフォントがインストールされておら
  7. // ず, 下の例では大きさを揃えることができない。
  8. // XLFDフォント名 で, XLoadFont() または XLoadQueryFont() を呼び出したりする。
  9. // XCreateFontSet() でもダメ。
  10. const char* FONT_NAME_UB =
  11. "-*-helvetica-bold-o-normal--*-240-*-*-p-*-iso8859-1";
  12. const char* FONT_NAME_MB =
  13. "-*-fixed-*-r-normal--*-230-*-*-c-*-jisx0208.1983-0";
  14. const char* FONT_NAME_UNI =
  15. "-*-*-medium-r-normal-ja-*-120-*-*-*-*-iso10646-1";
  16. XFontStruct* font_ub = NULL;
  17. XFontStruct* font_mb = NULL;
  18. XFontStruct* font_uni = NULL;
  19. GC gc_ub, gc_mb, gc_uni;
  20. static const char* text_ub = "English";
  21. // 多バイト文字 (JIS) -- フォントで選ぶ
  22. static const XChar2b text_mb[] = {
  23. {.byte1 = 0x34, .byte2 = 0x41},
  24. {.byte1 = 0x3b, .byte2 = 0x7a} }; // "漢字"
  25. // Unicode (UTF-16)
  26. static const XChar2b text_uni[] = {
  27. {.byte1 = 0x00, .byte2 = 0x41},
  28. {.byte1 = 0x30, .byte2 = 0x42} }; // "あ"
  29. void onExposed(const XExposeEvent* e)
  30. {
  31. printf("onExposed() enter\n");
  32. int x, y;
  33. int i;
  34. x = 10; y = 100;
  35. for (i = 0; i < 2; i++) {
  36. // 1バイト文字
  37. XDrawString(e->display, e->window, gc_ub, x, y, text_ub,
  38. strlen(text_ub));
  39. x += XTextWidth(font_ub, text_ub, strlen(text_ub));
  40. XDrawString16(e->display, e->window, gc_mb, x, y, text_mb, 2);
  41. x += XTextWidth16(font_mb, text_mb, 2);
  42. XDrawString16(e->display, e->window, gc_uni, x, y, text_uni, 2);
  43. x += XTextWidth16(font_uni, text_uni, 2);
  44. }
  45. }
  46. int main()
  47. {
  48. // ウィンドウを準備する
  49. Display* disp = XOpenDisplay(NULL);
  50. Window toplevel = XCreateSimpleWindow(disp, RootWindow(disp, 0),
  51. 400, 200, // pos
  52. 500, 150, 2,
  53. BlackPixel(disp, 0),
  54. WhitePixel(disp, 0));
  55. XSelectInput(disp, toplevel, ExposureMask | ButtonPressMask);
  56. XMapWindow(disp, toplevel);
  57. // フォントを生成する
  58. font_ub = XLoadQueryFont(disp, FONT_NAME_UB);
  59. assert(font_ub);
  60. font_mb = XLoadQueryFont(disp, FONT_NAME_MB);
  61. assert(font_mb);
  62. printf("min_char_or_byte2 = %d\n", font_mb->min_char_or_byte2);
  63. printf("max_char_or_byte2 = %d\n", font_mb->max_char_or_byte2);
  64. printf("min_byte1 = %d\n", font_mb->min_byte1);
  65. printf("max_byte1 = %d\n", font_mb->max_byte1);
  66. font_uni = XLoadQueryFont(disp, FONT_NAME_UNI);
  67. assert(font_uni);
  68. // GCを用意する
  69. gc_ub = XCreateGC(disp, toplevel, 0, NULL);
  70. XSetForeground(disp, gc_ub, BlackPixel(disp, 0));
  71. XSetFont(disp, gc_ub, font_ub->fid);
  72. gc_mb = XCreateGC(disp, toplevel, 0, NULL);
  73. XSetForeground(disp, gc_mb, BlackPixel(disp, 0));
  74. XSetFont(disp, gc_mb, font_mb->fid);
  75. gc_uni = XCreateGC(disp, toplevel, 0, NULL);
  76. XSetForeground(disp, gc_uni, BlackPixel(disp, 0));
  77. XSetFont(disp, gc_uni, font_uni->fid);
  78. // イベントループ
  79. while (true) {
  80. XEvent ev;
  81. XNextEvent(disp, &ev);
  82. if (ev.type == Expose)
  83. onExposed(&ev.xexpose);
  84. else if (ev.type == ButtonPress)
  85. break;
  86. }
  87. XCloseDisplay(disp);
  88. return 0;
  89. }

実行結果:

字が小さい。とほほ。

Xft

この方法はまだ現役だが、バグが放置されており、将来は暗い。

特に, 絵文字 Emoji に対応しておらず、逆に絵文字フォントを指定すると落ちる。pango-view コマンドのバックエンドとしてXft を指定した場合も同様に落ちるので、Xft の問題。

このバグ BadLength X error in Xft when trying to render emoji だが、2018年に報告されたバグがまだ修正されていない。

IVS にも未対応. FreeType を直接使えば扱えるようだ (未確認).

C++
[RAW]
  1. #include <stdio.h>
  2. #include <assert.h>
  3. #include <string.h>
  4. #include <stdint.h>
  5. #include <X11/Xlib.h>
  6. #include <X11/Xft/Xft.h>
  7. XftFont* xftFont = nullptr;
  8. XftDraw* xftDraw = nullptr;
  9. XftColor xftColor;
  10. static const char* text_ub = "English";
  11. // Unicode (UCS-4 または UTF-16)
  12. // 関数名が 〜32 のに渡すときは UCS-4.
  13. static const uint32_t text_uni[] = {
  14. 0x0041,
  15. 0x3042, // "あ"
  16. 0x845b, 0xe0100, // 葛󠄀 IVS Xftは未対応.
  17. 0x845b, 0xe0101, // 葛󠄁 IVS
  18. 0x1f601, // 😁 emoji は未対応.
  19. 0 };

描画は XftDrawString*(). UCS-4 または UTF-16 を渡す。

フォントについては, FreeType を利用しており, fc-list コマンドや fc-match コマンドで確認できる。

C++
[RAW]
  1. void onExposed(const XExposeEvent* e)
  2. {
  3. printf("onExposed() enter\n");
  4. int x, y;
  5. int i;
  6. x = 10; y = 150;
  7. for (i = 0; i < 2; i++) {
  8. // 1バイト文字
  9. XftDrawString8(xftDraw, &xftColor, xftFont, x, y,
  10. (unsigned char*) text_ub, strlen(text_ub));
  11. XGlyphInfo extents;
  12. XftTextExtents8(e->display, xftFont,
  13. (unsigned char*) text_ub, strlen(text_ub), &extents);
  14. x += extents.width;
  15. // Unicode (UCS-4). XftDrawString16() だと UTF-16.
  16. XftDrawString32(xftDraw, &xftColor, xftFont, x, y, text_uni, 7);
  17. XftTextExtents32(e->display, xftFont, text_uni, 7, &extents);
  18. x += extents.width;
  19. }
  20. }
  21. int main()
  22. {
  23. // ウィンドウを準備する
  24. Display* disp = XOpenDisplay(NULL);
  25. Window toplevel = XCreateSimpleWindow(disp, RootWindow(disp, 0),
  26. 400, 200, // pos
  27. 800, 300, 2,
  28. BlackPixel(disp, 0),
  29. WhitePixel(disp, 0));
  30. XSelectInput(disp, toplevel, ExposureMask | ButtonPressMask);
  31. XMapWindow(disp, toplevel);
  32. // インストールされたフォント一覧は fc-list コマンドで確認できる.
  33. // 略称が何にマッチするかは fc-match コマンド
  34. // $ fc-match 'Arial-36'
  35. // LiberationSans-Regular.ttf: "Liberation Sans" "Regular"
  36. xftFont = XftFontOpenName(disp, // display
  37. 0, // screen
  38. "Noto Sans CJK JP:pixelsize=36:style=DemiLight" );
  39. assert(xftFont);
  40. Colormap cmap = DefaultColormap(disp, 0);
  41. Bool r = XftColorAllocName(disp, DefaultVisual(disp, 0), cmap, "blue",
  42. &xftColor);
  43. assert(r);
  44. xftDraw = XftDrawCreate(disp, // display
  45. toplevel, // drawable
  46. DefaultVisual(disp, 0), // visual
  47. cmap ); // colormap
  48. assert(xftDraw);
  49. // イベントループ
  50. while (true) {
  51. XEvent ev;
  52. XNextEvent(disp, &ev);
  53. if (ev.type == Expose)
  54. onExposed(&ev.xexpose);
  55. else if (ev.type == ButtonPress)
  56. break;
  57. }
  58. XftFontClose(disp, xftFont); // これを忘れるとリークする.
  59. XftDrawDestroy(xftDraw);
  60. //これはなくてもリークしない. (あってもよい)
  61. //XftColorFree(disp, DefaultVisual(disp, 0), cmap, &xftColor);
  62. XCloseDisplay(disp);
  63. return 0;
  64. }

実行結果:

豆腐になる。落ちるよりはよい。