harfbuzz 的用法
HarfBuzz 整形 API 的核心是函数。此函数采用一种字体,即 包含一串 Unicode 代码点的缓冲区和 (可选)字体功能列表作为其输入。它取代了 缓冲区中的代码点,其中包含来自 字体,正确排序和定位,以及任何 应用的可选字体功能。hb_shape()
除了保存预整形输入(Unicode 构成输入字符串的代码点)和后整形 输出(字形和位置),HarfBuzz缓冲区有几个 影响成形的属性。最重要的是 文本流方向(例如,从左到右、从右到左、 从上到下或从下到上)、脚本标记和 语言标记。
对于输入字符串缓冲区,可以使用标志来表示何时 缓冲区表示段落的开头或结尾,到 指示是否以可见方式呈现 Unicode 代码点,以及修改群集合并 缓冲区的行为。对于整形输出缓冲区, 每个字形的单个 X 和 Y 偏移量和(逻辑尺寸)为 访问。HarfBuzz 还会标记字形,就好像在 该字形(例如,在换行或连字过程中) 将需要重新塑造案文。Default IgnorableadvancesUNSAFE_TO_BREAK
HarfBuzz还提供了比较以下内容的方法 缓冲区、联接缓冲区、规范化缓冲区内容和句柄 无效的代码点,以及确定状态 缓冲区(例如,输入代码点或输出字形)。缓冲区 管理生命周期,并对所有缓冲区进行引用计数。
虽然默认函数是 足以满足大多数用例,还提供了变体 允许您指定在缓冲区上使用哪些 HarfBuzz 的整形器。hb_shape()
HarfBuzz可以读取TrueType字体,TrueType集合,OpenType 字体和 OpenType 集合。提供查询函数 有关指标、Unicode 覆盖率、可用表和 功能和变体选择器。单个字形也可以是 查询指标、变体和字形名称。开放类型 支持可变字体,HarfBuzz 允许您设置 字体对象上的变体轴坐标。
HarfBuzz 提供胶水代码以与其他各种代码集成 库,包括FreeType,GObject和CoreText。支持 与Uniscribe和DirectWrite集成是实验性的 现在。
1.创建一个缓冲区并将文本放入其中。
#include <hb.h> hb_buffer_t *buf; buf = hb_buffer_create(); hb_buffer_add_utf8(buf, text, -1, 0, -1);
2.设置缓冲区的脚本、语言和方向。
hb_buffer_set_direction(buf, HB_DIRECTION_LTR); hb_buffer_set_script(buf, HB_SCRIPT_LATIN); hb_buffer_set_language(buf, hb_language_from_string("en", -1));
3.从字体文件创建字体和字体。
hb_blob_t *blob = hb_blob_create_from_file(filename); /* or hb_blob_create_from_file_or_fail() */ hb_face_t *face = hb_face_create(blob, 0); hb_font_t *font = hb_font_create(face);
4.形状!
hb_shape(font, buf, NULL, 0);
5.获取字形和位置信息。
unsigned int glyph_count; hb_glyph_info_t *glyph_info = hb_buffer_get_glyph_infos(buf, &glyph_count); hb_glyph_position_t *glyph_pos = hb_buffer_get_glyph_positions(buf, &glyph_count);
6.遍历每个字形。
hb_position_t cursor_x = 0; hb_position_t cursor_y = 0; for (unsigned int i = 0; i < glyph_count; i++) { hb_codepoint_t glyphid = glyph_info[i].codepoint; hb_position_t x_offset = glyph_pos[i].x_offset; hb_position_t y_offset = glyph_pos[i].y_offset; hb_position_t x_advance = glyph_pos[i].x_advance; hb_position_t y_advance = glyph_pos[i].y_advance; /* draw_glyph(glyphid, cursor_x + x_offset, cursor_y + y_offset); */ cursor_x += x_advance; cursor_y += y_advance; }
7.整理。
hb_buffer_destroy(buf); hb_font_destroy(font); hb_face_destroy(face); hb_blob_destroy(blob);
HarfBuzz 中的数据类型概述
HarfBuzz 具有两种数据类型:非不透明、 按值传递类型和不透明的堆分配类型。这种 的分离在必须提供 API/ABI 兼容性(几乎)无限期。
*值类型:*不透明、按值传递 类型包括整数类型、枚举和小结构。暴露 公共 API 中的结构使得无法扩展 结构在未来。因此,公开结构保留用于 否则效率极低的情况。
在 HarfBuzz 中,几个结构,比如 和 ,都属于效率敏感型。 类别,并且是不透明的。hb_glyph_info_thb_glyph_position_t
对于未来扩展性可能为 包括必要的保留成员以保留空间 可能的未来成员。因此,为此类结构提供 和方法非常重要,允许 API 的用户 有效处理类型,而无需 调整他们的代码以适应未来的变化。equal()``hash()
HarfBuzz 提供的重要值类型包括结构 用于处理 Unicode 代码点、字形和字体标记 表和功能,以及许多 Unicode 和 开放类型属性。
一个可用的例子:
需要额外使用stb库,该库可以在github上找到对应的,只需要将stb_image_write 拷贝过来就可以了
//#include <iostream> //#include <QApplication> //#include <turboopenglwidget.h> // //int main(int argc, char **argv) //{ // QApplication app(argc, argv); // TurboOpenGLWidget tt; // tt.show(); // return app.exec(); //} #include <iostream> #include <stdlib.h> #include <stdio.h> #include <assert.h> #include <stdint.h> #include <hb.h> #include <hb-ft.h> #define STB_IMAGE_WRITE_IMPLEMENTATION #include "stb_image_write.h" struct Pixel32 { uint8_t r, g, b, a; }; struct Image32 { size_t width; size_t height; Pixel32 *pixels; bool is_null() const{ return pixels == nullptr; } }; void save_image(Image32 image, const char *filePath) { int ret = stbi_write_png(filePath, image.width, image.height, 4, image.pixels, image.width * sizeof(Pixel32)); if(!ret) { fprintf(stderr, "could not save file\n"); } } Pixel32 mix_pixels(Pixel32 dst, Pixel32 src) { uint8_t rev_src_a = 255 - src.a; Pixel32 result; result.r = ((uint16_t) src.r * (uint16_t)src.a + (uint16_t)dst.r * rev_src_a) >> 8; result.g = ((uint16_t) src.g * (uint16_t)src.a + (uint16_t)dst.g * rev_src_a) >> 8; result.b = ((uint16_t) src.b * (uint16_t)src.a + (uint16_t)dst.b * rev_src_a) >> 8; result.a = dst.a; return result; } void slap_ftbitmap_onto_image32(Image32 dest, FT_Bitmap *src, Pixel32 color, int x, int y) { assert(src->pixel_mode == FT_PIXEL_MODE_GRAY); assert(src->num_grays == 256); for(size_t row = 0; row < src->rows; ++row) { if(row + y < dest.height) { for(size_t col = 0; col < src->width; ++col) { if(col + x < dest.width) { color.a = src->buffer[row*src->pitch + col]; dest.pixels[(row+y)*dest.width +col +x] = mix_pixels(dest.pixels[(row+y)*dest.width+col+x], color); } } } } } void draw_glyph(Image32 surface, FT_Face face, hb_codepoint_t glyphId, double x, double y) { // FT_UInt glyphIndex = FT_Get_Char_Index(face, glyphId); FT_Error error = FT_Load_Glyph(face, glyphId, FT_LOAD_DEFAULT); if(error) { fprintf(stderr, "could not load glyph: %d", glyphId); return; } error = FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL); if(error) { fprintf(stderr, "could not render glyph: %d", glyphId); return; } const Pixel32 FONT_COLOR{200, 200, 200, 255}; slap_ftbitmap_onto_image32(surface, &face->glyph->bitmap, FONT_COLOR, (int) floor(x) + face->glyph->bitmap_left, (int) floor(y) + face->glyph->bitmap_top); } void destroy_or_whatever(void *userData) { printf("destroy_or_whatever(%p)\n", userData); } int main(int argc, char **argv) { // 创建一个缓冲区并将文本放入其中。 hb_buffer_t *buf = hb_buffer_create(); hb_buffer_add_utf8(buf, u8"我是傻逼", -1, 0, -1); // 设置缓冲区的脚本、语言和方向。 hb_buffer_set_direction(buf, HB_DIRECTION_LTR); hb_buffer_set_script(buf, HB_SCRIPT_HAN); hb_buffer_set_language(buf, hb_language_from_string("zh", -1)); // 从字体文件创建字体和字体。 FT_Library ftLib; FT_Error error = FT_Init_FreeType(&ftLib); if(error) { fprintf(stderr, "could not initialize Freetype\n"); return -1; } FT_Face face; const char *fontPath = "E:\\QHarfbuzz3\\fonts\\NotoSerifSC-Regular.otf"; size_t index = 0; error = FT_New_Face(ftLib, fontPath, index, &face); if(error) { fprintf(stderr, "could not initialize load the font face %s\n", fontPath); return -1; } error = FT_Set_Char_Size(face, 0, 5000, 0, 0); if(error) { fprintf(stderr, "could not set char size %s\n"); return -1; } hb_font_t *font = hb_ft_font_create(face, destroy_or_whatever); // 形状! hb_shape(font, buf, NULL, 0); // 获取字形和位置信息。 unsigned int glyph_count = 0; printf("glyph_count initial = %d\n", glyph_count); hb_glyph_info_t *glyph_info = hb_buffer_get_glyph_infos(buf, &glyph_count); printf("glyph_count infos = %d\n", glyph_count); hb_glyph_position_t *glyph_pos = hb_buffer_get_glyph_positions(buf, &glyph_count); printf("glyph_count position = %d\n", glyph_count); // 遍历每个字形。 hb_position_t cursor_x = 0; hb_position_t cursor_y = 0; Image32 surface; surface.width = 1000; surface.height = 1000; surface.pixels = (Pixel32 *)malloc(sizeof(Pixel32) * surface.width * surface.height); for(int i = 0; i < surface.height; i++) { for(int j = 0; j < surface.width; j++) { surface.pixels[i*surface.width+j] = {30, 30, 30, 255}; } } for (unsigned int i = 0; i < glyph_count; i++) { hb_codepoint_t glyph_id = glyph_info[i].codepoint; hb_position_t x_offset = glyph_pos[i].x_offset / 64.0; hb_position_t y_offset = glyph_pos[i].y_offset / 64.0; hb_position_t x_advance = glyph_pos[i].x_advance / 64.0; hb_position_t y_advance = glyph_pos[i].y_advance / 64.0; draw_glyph(surface, face, glyph_id, cursor_x + x_offset, cursor_y + y_offset); cursor_x += x_advance; cursor_y += y_advance; } save_image(surface, "out.png"); hb_buffer_destroy(buf); hb_font_destroy(font); return 0; }