harfbuzz 的用法

简介: HarfBuzz 整形 API 的核心是函数。此函数采用一种字体,即 包含一串 Unicode 代码点的缓冲区和 (可选)字体功能列表作为其输入。它取代了 缓冲区中的代码点,其中包含来自 字体,正确排序和定位,以及任何 应用的可选字体功能。hb_shape()

hb-blob: HarfBuzz Manual


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;
}


目录
相关文章
|
5月前
|
安全 API Windows
CreateMutex用法
CreateMutex用法
|
6月前
|
监控 前端开发 数据库
RACSignal的八种常用用法
RACSignal的八种常用用法
54 0
/与%,%与/的用法
/与%,%与/的用法
166 0
|
Java Spring
@ConditionalOnProperty的用法
@ConditionalOnProperty的用法
342 0
|
JSON Java fastjson
JackJSON的用法
网上大多数都是FastJSON的用法,很少有JackJSON的用法。我总结了几个自己经常用到的,供大家参考。
|
存储 SQL Oracle
DatabaseMetaData的用法(转)
DatabaseMetaData的用法(转)
589 0
${}用法
[el表达式],它会从page,request,session,application中取值。比如:{name}它的意思就从以上4个对象中去名为name的值。
1356 0