Harfbuzz-ng API Basic Usage

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的结构。

  1. 从line 30 ~ line 61这31行,可以看到所做的事情就是读取字库文件中的数据,然后创建一个hb_blot_t。
  2. 从line 66 ~ line 70这几行,可以看到所做的事情是,利用前面创建的那个含有字库文件数据的blob,创建一个face。
  3. 从line 72 ~ line 86这几行,可以看到所做的事情主要是,利用前面创建的face,创建一个font。然后把字体大小的信息(ppem)及字体设计空间向用户空间转换的系数(scale)设置给font。计算ppem及scale 的那段code是借用了android 4.2 TextLayoutCache.cpp的一些code。
  4. 从line 93 ~ line 111这几行,所做的事情是,创建一个buffer,然后把text添加进去。这个地方用UTF-16编码,是因为就手动编码Unicode而言,对于许多复杂语系的Unicode范围,UTF-16比UTF-8要方便的多,因而也使得我们可以更为方便的来修改这个test。
  5. line 113是调用Harfbuzz-ng 的主shape接口来做shape动作。
  6. line 115 ~ line 131则是从shape之后的buffer中,取出glyph 和position的相关信息。

通常情况下对于Harfbuzz-ng API 的使用,大体上也会如上面的几个步骤。用一张图来说命上面的那个过程:

Harfbuzz-ng API Basic Usage_第1张图片

这样的用法,之所以称为basic usage,最起码有如下的这样一些原因:

  1. 前面的第2、3步,在创建face和font时,是直接通过字库文件。通常情况每个系统中都会有自己的字库文件管理系统和Glyph管理系统,这种做法就完全没有考虑与现有系统的这些模块衔接的问题。在实际系统中,着两个object应该要通过相应有callback参数的那些接口来创建。
  2. 在Harfbuzz API Design中,我们看到有提到Unicode callback及Script、Language和Direction这些text 属性等,这些都是需要正确的设置给buffer的,因而前面第4步所对应的这个test app的做法,所创建的buffer是不足够完整的
  3. 在print position信息时,我们看到有通过HBFixedToFloat() 这个函数来对harfbuzz-ng 输出的position信息做一个转换,转换为float格式的像素个数值。可以看到这个地方是除了一个2048。这个系数在这个test app里面用的是一个猜想的值。字体大小为36,所以猜想返回的advance值应该要处于这一数量级。所以取用了2048这个系数。


你可能感兴趣的:(Harfbuzz-ng API Basic Usage)