Harfbuzz是一个OpenType 的shaping engine (http://www.freedesktop.org/wiki/Software/HarfBuzz)。目前Harfbuzz有两个Code trees,早期的那个code tree称之为Harfbuzz,或者old harfbuzz。而目前还处于比较活跃的开发状态的这个code tree,在整个代码的结构上面,与前面的那一版,有着非常大的区别,为了能够区分这两者,新的这个称之为harfbuzz-ng。前面Behdad Esfahbod所发的那个Title为Harfbuzz API 设计(Harfbuzz API design)的mail,所描述的是Harfbuzz-ng API的设计。
下面这段code来源于harfbuzz-0.9.10代码库(harfbuzz-0.9.10/src/test.cc):
#ifdef HAVE_CONFIG_H #include "config.h" #endif #include "hb.h" #ifdef HAVE_GLIB #include <glib.h> #endif #include <stdlib.h> #include <stdio.h> #ifdef HAVE_FREETYPE #include "hb-ft.h" #endif static inline float HBFixedToFloat(int v) { // Harfbuzz uses 26.6 fixed point values for pixel offsets return v * (1.0f / 2048); } int main(int argc, char **argv) { hb_blob_t *blob = NULL; 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, NULL); 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 = NULL; 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); printf("x_scale = %d, y_scale = %d, x_ppem = %d, y_ppem = %d\n", x_scale, y_scale, 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 = thaiChars; // 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_shape(font, buffer, NULL, 0); unsigned int count = hb_buffer_get_length(buffer); hb_glyph_info_t *infos = hb_buffer_get_glyph_infos(buffer, NULL); hb_glyph_position_t *positions = hb_buffer_get_glyph_positions(buffer, NULL); 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 offset(%f,%f)+position(%f,%f)\n", info->cluster, info->codepoint, HBFixedToFloat(pos->x_offset), HBFixedToFloat(pos->x_offset), HBFixedToFloat(pos->x_advance), HBFixedToFloat(pos->y_advance)); } hb_buffer_destroy(buffer); hb_font_destroy(font); hb_face_destroy(face); return 0; }
Harfbuzz-ng的编译,与网络上那些基于源码发布的项目的编译相比,也没有什么特别的地方,也都是configure->make这种。编译Harfbuzz-ng时,这个test app会一并被编译。可以像下面这样来执行这个test app:
./test ~/Data/font_language/font_files/android_4.2_fonts/DroidSansThai.ttf
这个地方的这个字库文件取自于android 4.2的codebase。android 4.2 codebase的下载可以参考Google官网上的说明。下面是执行的输出:
Opened font file ~/Data/font_language/font_files/android_4.2_fonts/DroidSansThai.ttf: 35584 bytes long x_scale = 73728, y_scale = 73728, x_ppem = 36, y_ppem = 36 cluster 0 glyph 0x4 at offset(0.000000,0.000000)+position(22.218750,0.000000) cluster 0 glyph 0x4e at offset(0.123047,0.123047)+position(0.000000,0.000000) cluster 0 glyph 0x5e at offset(2.232422,2.232422)+position(0.000000,0.000000) cluster 0 glyph 0x37 at offset(0.000000,0.000000)+position(17.472656,0.000000) cluster 3 glyph 0x25 at offset(0.000000,0.000000)+position(23.115234,0.000000) cluster 4 glyph 0x26 at offset(0.000000,0.000000)+position(21.533203,0.000000) cluster 5 glyph 0x27 at offset(0.000000,0.000000)+position(22.869141,0.000000) cluster 6 glyph 0x30 at offset(0.000000,0.000000)+position(23.203125,0.000000) cluster 7 glyph 0x31 at offset(0.000000,0.000000)+position(24.240234,0.000000) cluster 8 glyph 0x33 at offset(0.000000,0.000000)+position(21.005859,0.000000)
接着再来看前面那段code的结构。
通常情况下对于Harfbuzz-ng API 的使用,大体上也会如上面的几个步骤。用一张图来说命上面的那个过程:
这样的用法,之所以称为basic usage,最起码有如下的这样一些原因: