Harfbuzz API 基本用法

[Harfbuzz](http://harfbuzz.org/ 是一個 OpenType 文本整形引擎陷遮。當前的 Harfbuzz 代碼庫寝蹈,之前被稱為 harfbuzz-ng慧域,版本號為 1.x.x罗售,它是穩(wěn)定的且處于活躍的維護之中辜窑。Harfbuzz 的使用非常廣泛,在最新版本的 Firefox寨躁,GNOME穆碎,ChromeOS,Chrome职恳,LibreOffice所禀,XeTeX方面,Android,和 KDE 等項目中都有應用色徘。Harfbuzz 的代碼可以在 這里 下載恭金,也可以通過 GitHub 訪問。

老的 HarfBuzz 代碼庫褂策,現(xiàn)在被稱為 harfbuzz-old横腿,它從 FreeTypePango斤寂,和 Qt 派上而來耿焊,可以在 這里 下載。老的 HarfBuzz 代碼庫目前已經(jīng)不再維護了遍搞。

Harfbuzz 在代碼結構上罗侯,與 harfbuzz-old 的差別非常大。前面 Behdad Esfahbod 發(fā)的那篇名為 Harfbuzz API 設計(Harfbuzz API design) 的郵件溪猿,所描述的是新 Harfbuzz API 的設計钩杰。

本文來看一下 Harfbuzz API 的基本用法。學習一個開源庫的 API 的用法最方便的途徑诊县,常常正是庫本身包含的一些示例程序或這測試程序讲弄。下載當前最新的發(fā)布版本 1.7.5 的源碼。將源碼解壓縮之后依痊,通過如下命令編譯它:

harfbuzz-1.7.5$ ./configure
harfbuzz-1.7.5$ make

編譯過程將產(chǎn)生 Harfbuzz 的二進制庫文件垂睬,和一些測試程序的可執(zhí)行文件,位于 harfbuzz-1.7.5/src/test.cc 的即是其中一個測試程序抗悍。這個測試程序編譯之后產(chǎn)生的可執(zhí)行文件為 harfbuzz-1.7.5/src/test驹饺,通過如下方式執(zhí)行:

harfbuzz-1.7.5$ src/test font-file.ttf

即需要唯一的參數(shù),字庫文件的路徑缴渊。我們對這個測試程序做一點簡單的修改赏壹,讓它處理泰語和緬甸語,字庫文件我們使用 Android 7.1.1 代碼庫中衔沼,external/noto-fonts/other/ 下的 NotoSansThai-Regular.ttfNotoSansMyanmar-Regular.ttf

harfbuzz-1.7.5$ src/test ~/Androids/android_7.1.1/external/noto-fonts/other/NotoSansThai-Regular.ttf
Opened font file ~/Androids/android_7.1.1/external/noto-fonts/other/NotoSansThai-Regular.ttf: 21380 bytes long
cluster 0   glyph 0x4 at    (0,0)+(1264,0)
cluster 0   glyph 0x4e at   (7,0)+(0,0)
cluster 0   glyph 0x5e at   (127,0)+(0,0)
cluster 0   glyph 0x37 at   (0,0)+(994,0)
cluster 3   glyph 0x25 at   (0,0)+(1315,0)
cluster 4   glyph 0x26 at   (0,0)+(1225,0)
cluster 5   glyph 0x27 at   (0,0)+(1301,0)
cluster 6   glyph 0x30 at   (0,0)+(1320,0)
cluster 7   glyph 0x31 at   (0,0)+(1379,0)
cluster 8   glyph 0x33 at   (0,0)+(1195,0)
harfbuzz-1.7.5$ src/test ~/Androids/android_7.1.1/external/noto-fonts/other/NotoSansMyanmar-Regular.ttf
Opened font file ~/Androids/android_7.1.1/external/noto-fonts/other/NotoSansMyanmar-Regular.ttf: 108160 bytes long
cluster 0   glyph 0x9 at    (0,0)+(1381,0)
cluster 0   glyph 0x196 at  (0,0)+(993,0)
cluster 2   glyph 0x5 at    (0,0)+(1384,0)
cluster 2   glyph 0x195 at  (0,0)+(547,0)
cluster 4   glyph 0x22 at   (0,0)+(2308,0)
cluster 4   glyph 0x197 at  (-42,0)+(0,0)
cluster 6   glyph 0x4 at    (0,0)+(2302,0)
cluster 6   glyph 0xd5 at   (-19,-50)+(0,0)
cluster 6   glyph 0x196 at  (0,0)+(993,0)
cluster 10  glyph 0x22 at   (0,0)+(2308,0)
cluster 11  glyph 0x16 at   (0,0)+(1311,0)
cluster 11  glyph 0x104 at  (-4,-50)+(0,0)
cluster 11  glyph 0x195 at  (0,0)+(547,0)
cluster 15  glyph 0xe at    (0,0)+(2271,0)
cluster 15  glyph 0x197 at  (-14,0)+(0,0)
cluster 15  glyph 0xd2 at   (0,0)+(534,0)
cluster 18  glyph 0x15 at   (0,0)+(2304,0)
cluster 18  glyph 0x19f at  (-57,0)+(0,0)
cluster 18  glyph 0xd1 at   (-31,0)+(0,0)
cluster 18  glyph 0x1a1 at  (0,0)+(709,0)

harfbuzz-1.7.5/src/test.cc 的完整源碼如下:

#include "hb-private.hh"

#include "hb.h"

#ifdef HAVE_GLIB
# include <glib.h>
# if !GLIB_CHECK_VERSION (2, 22, 0)
#  define g_mapped_file_unref g_mapped_file_free
# endif
#endif
#include <stdlib.h>
#include <stdio.h>

#ifdef HAVE_FREETYPE
#include "hb-ft.h"
#endif

int
main (int argc, char **argv)
{
  hb_blob_t *blob = nullptr;

  if (argc != 2) {
    fprintf (stderr, "usage: %s font-file.ttf\n", argv[0]);
    exit (1);
  }

  /* Create the blob */
  {
    const char *font_data;
    unsigned int len;
    hb_destroy_func_t destroy;
    void *user_data;
    hb_memory_mode_t mm;

#ifdef HAVE_GLIB
    GMappedFile *mf = g_mapped_file_new (argv[1], false, nullptr);
    font_data = g_mapped_file_get_contents (mf);
    len = g_mapped_file_get_length (mf);
    destroy = (hb_destroy_func_t) g_mapped_file_unref;
    user_data = (void *) mf;
    mm = HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE;
#else
    FILE *f = fopen (argv[1], "rb");
    fseek (f, 0, SEEK_END);
    len = ftell (f);
    fseek (f, 0, SEEK_SET);
    font_data = (const char *) malloc (len);
    if (!font_data) len = 0;
    len = fread ((char *) font_data, 1, len, f);
    destroy = free;
    user_data = (void *) font_data;
    fclose (f);
    mm = HB_MEMORY_MODE_WRITABLE;
#endif

    blob = hb_blob_create (font_data, len, mm, user_data, destroy);
  }

  printf ("Opened font file %s: %u bytes long\n", argv[1], hb_blob_get_length (blob));

  /* Create the face */
  hb_face_t *face = hb_face_create (blob, 0 /* first face */);
  hb_blob_destroy (blob);
  blob = nullptr;
  unsigned int upem = hb_face_get_upem (face);

  int textSize = 36;
  uint16_t x_ppem, y_ppem;
  int x_scale, y_scale;

  x_ppem = y_ppem = textSize;
  const int kDevicePixelFraction = 64;
  const int kMultiplyFor16Dot16 = 1 << 16;
  float emScale = kDevicePixelFraction * kMultiplyFor16Dot16 / (float)upem;
  x_scale = emScale * textSize;
  y_scale = emScale * textSize;

  hb_font_t *font = hb_font_create (face);
  hb_font_set_scale(font, x_scale, y_scale);
  hb_font_set_ppem(font, x_ppem, y_ppem);

#ifdef HAVE_FREETYPE
  hb_ft_font_set_funcs (font);
#endif

  hb_buffer_t *buffer = hb_buffer_create ();

  uint16_t myanmarChars[] = {0x1005, 0x102C, 0x1001, 0x102B,
          0x101E, 0x102D, 0x1000, 0x1039, 0x1001, 0x102C,
          0x101E, 0x1012, 0x1039, 0x1013, 0x102B,
          0x100A, 0x102D, 0x102F, 0x1011, 0x102F, 0x1036, 0x1038
  };

  uint16_t thaiChars[] = {
          0xE01, 0xE49, 0xE33, 0xE20, 0xE21, 0xE22, 0xE2B, 0xE2C, 0xE2E
  };

  uint16_t *chars = myanmarChars;

//  hb_buffer_add_utf8 (buffer, "\xe0\xa4\x95\xe0\xa5\x8d\xe0\xa4\xb0\xe0\xa5\x8d\xe0\xa4\x95", -1, 0, -1);
  hb_buffer_add_utf16(buffer, chars, -1, 0, -1);
  hb_buffer_guess_segment_properties (buffer);

  hb_shape (font, buffer, nullptr, 0);

  unsigned int count = hb_buffer_get_length (buffer);
  hb_glyph_info_t *infos = hb_buffer_get_glyph_infos (buffer, nullptr);
  hb_glyph_position_t *positions = hb_buffer_get_glyph_positions (buffer, nullptr);

  for (unsigned int i = 0; i < count; i++)
  {
    hb_glyph_info_t *info = &infos[i];
    hb_glyph_position_t *pos = &positions[i];

    printf ("cluster %d glyph 0x%x at   (%d,%d)+(%d,%d)\n",
        info->cluster,
        info->codepoint,
        pos->x_offset,
        pos->y_offset,
        pos->x_advance,
        pos->y_advance);

  }

  hb_buffer_destroy (buffer);
  hb_font_destroy (font);
  hb_face_destroy (face);

  return 0;
}

接著再來看前面那段code的結構蝌借,上面這段代碼執(zhí)行的步驟如下:

    1. 讀取字庫文件中的數(shù)據(jù),然后創(chuàng)建 hb_blot_t指蚁。
    1. 利用前面創(chuàng)建的那個含有字庫文件數(shù)據(jù)的 blob菩佑,創(chuàng)建一個 face
    1. 利用前面創(chuàng)建的 face凝化,創(chuàng)建一個 font稍坯。然后把字體大小的信息(ppem)及字體設計空間向用戶空間轉(zhuǎn)換的系數(shù)(scale)設置給 font。計算 ppemscale 的那段代碼借用了android 4.2 TextLayoutCache.cpp 的一些做法。
    1. 創(chuàng)建一個 buffer瞧哟,把文本添加進去混巧。這個地方用 UTF-16 編碼,是因為就手動編碼 Unicode 而言勤揩,對于許多復雜語系的 Unicode 范圍咧党,UTF-16 比 UTF-8 要方便的多,因而也使我們可以更方便地修改它陨亡。
    1. 調(diào)用 Harfbuzz 的主 shape 接口執(zhí)行 shape 動作傍衡。
    1. 最后從 shape 之后的 buffer 中,取出 glyphposition 相關信息负蠕。

通常情況下對于 Harfbuzz API 的使用聪舒,大體上如上面所述。用一張圖來簡單說明上面的過程:

這樣的用法虐急,之所以稱為基本用法,有如下這樣一些原因:

  • 前面的第 2滔迈、3 步中止吁,在創(chuàng)建 facefont 時,是直接通過字庫文件的路徑進行的燎悍。通常情況每個系統(tǒng)都會有自己的字庫文件管理系統(tǒng)和 Glyph 管理系統(tǒng)敬惦,這種做法就完全沒有考慮與現(xiàn)有系統(tǒng)的這些模塊銜接的問題。在實際系統(tǒng)中谈山,這兩個對象應該通過相應有 callback 參數(shù)的那些接口來創(chuàng)建俄删。
  • 在 Harfbuzz API Design 中,我們看到有提到 Unicode callback 及 Script奏路、Language 和 Direction 這些文本屬性等畴椰,這些都是需要正確的設置給 buffer 的,因而前面第 4 步所對應的這個測試程序的做法鸽粉,所創(chuàng)建的 buffer 是不夠完整的斜脂。
  • 在打印位置信息時,我們看到有通過 HBFixedToFloat() 這個函數(shù)來對 Harfbuzz 輸出的位置信息做一個轉(zhuǎn)換触机,轉(zhuǎn)換為 float 格式的像素個數(shù)值帚戳。可以看到這個地方除了一個 2048儡首。這個系數(shù)在這個測試程序里用的是一個猜想的值片任。字體大小為 36,所以猜想返回的 advance 值應該處于這一數(shù)量級蔬胯。所以取了 2048 這個系數(shù)对供。

Done.

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市氛濒,隨后出現(xiàn)的幾起案子犁钟,更是在濱河造成了極大的恐慌棱诱,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,826評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件涝动,死亡現(xiàn)場離奇詭異迈勋,居然都是意外死亡,警方通過查閱死者的電腦和手機醋粟,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,968評論 3 395
  • 文/潘曉璐 我一進店門靡菇,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人米愿,你說我怎么就攤上這事厦凤。” “怎么了育苟?”我有些...
    開封第一講書人閱讀 164,234評論 0 354
  • 文/不壞的土叔 我叫張陵较鼓,是天一觀的道長。 經(jīng)常有香客問我违柏,道長博烂,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,562評論 1 293
  • 正文 為了忘掉前任漱竖,我火速辦了婚禮禽篱,結果婚禮上,老公的妹妹穿的比我還像新娘馍惹。我一直安慰自己躺率,他們只是感情好,可當我...
    茶點故事閱讀 67,611評論 6 392
  • 文/花漫 我一把揭開白布万矾。 她就那樣靜靜地躺著悼吱,像睡著了一般。 火紅的嫁衣襯著肌膚如雪良狈。 梳的紋絲不亂的頭發(fā)上舆绎,一...
    開封第一講書人閱讀 51,482評論 1 302
  • 那天,我揣著相機與錄音们颜,去河邊找鬼吕朵。 笑死,一個胖子當著我的面吹牛窥突,可吹牛的內(nèi)容都是我干的努溃。 我是一名探鬼主播,決...
    沈念sama閱讀 40,271評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼阻问,長吁一口氣:“原來是場噩夢啊……” “哼梧税!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,166評論 0 276
  • 序言:老撾萬榮一對情侶失蹤第队,失蹤者是張志新(化名)和其女友劉穎哮塞,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體凳谦,經(jīng)...
    沈念sama閱讀 45,608評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡忆畅,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,814評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了尸执。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片家凯。...
    茶點故事閱讀 39,926評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖如失,靈堂內(nèi)的尸體忽然破棺而出绊诲,到底是詐尸還是另有隱情,我是刑警寧澤褪贵,帶...
    沈念sama閱讀 35,644評論 5 346
  • 正文 年R本政府宣布掂之,位于F島的核電站,受9級特大地震影響脆丁,放射性物質(zhì)發(fā)生泄漏世舰。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,249評論 3 329
  • 文/蒙蒙 一偎快、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧洽胶,春花似錦晒夹、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,866評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至翔横,卻和暖如春读跷,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背禾唁。 一陣腳步聲響...
    開封第一講書人閱讀 32,991評論 1 269
  • 我被黑心中介騙來泰國打工效览, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人荡短。 一個月前我還...
    沈念sama閱讀 48,063評論 3 370
  • 正文 我出身青樓丐枉,卻偏偏與公主長得像,于是被迫代替她去往敵國和親掘托。 傳聞我的和親對象是個殘疾皇子瘦锹,可洞房花燭夜當晚...
    茶點故事閱讀 44,871評論 2 354

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務發(fā)現(xiàn),斷路器弯院,智...
    卡卡羅2017閱讀 134,656評論 18 139
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,126評論 25 707
  • 姓名:殷晨陽 轉(zhuǎn)載自:http://mp.weixin.qq.com/s/VBbltIIcx9qkMVw9Dgk0...
    風雨無阻59閱讀 1,546評論 0 1
  • 這幾日在青島出差辱士,是自我和小神廚在一起后最長時間的分開,每天聊的不多听绳,打電話也是小神廚在叨叨叨颂碘,吃的好不好,睡的好...
    禪司大人閱讀 414評論 1 0
  • 推己及人,是生活最好的法則 文 小河七七 仲夏周末贴妻,微風拂暖切油。我早早起了床,直奔菜市場名惩,給家人買些牛肉和排骨澎胡,補充...
    小河七七閱讀 300評論 0 4