Truetype&Harfbuzz&FreeType联合应用完全解析(三)

本章主要介绍harfbuzz,并结合freetype、truetype来写一个代码示例,该示例可以在qt的窗口上显示出来,你将学习到的知识点有:

  1. harfbuzz是什么,用来做什么的
  2. harfbuzz如何配合freetype、truetype使用

harfbuzz的重要的一个功能是根据某种语言的字符组合规则来进行字形变换。下图是高棉语 “បាទ”(中文意思:是的),在是否使用harfbuzz整形的情况下的两种字符显示情况。
未使用harfbuzz
在这里插入图片描述
使用harfbuzz
在这里插入图片描述
高棉语中还有很多这样的变换规则,是它本身这种语言觉得的,harfbuzz能够解析这种规则,然后是字符正确显示

一、 什么是harfbuzz,作用是什么

相关网站
(https://www.freedesktop.org/wiki/Software/HarfBuzz/)
https://zh.wikipedia.org/wiki/HarfBuzz
harfbuzz是一个开源的文本整形引擎,网上说它是属于OpenType的一部分。由于OpenType是FreeType的一个升级版本,所以它也可以和FreeType一起使用。

harfbuzz广泛使用在Firefox,GNOME,ChromeOS,Chrome,LibreOffice中,Qt底层的文字引擎也使用了harfbuzz

什么是整形引擎?
笔者的理解就是:当两个或多个单独的字符组合到一起的时候,可能就变成了另外的字符。这在某些语言和艺术字方面经常用到,列如:“f"和"i"这两个字符组合成"fi”,这在有些语言或者艺术字里面可能要写成:
在这里插入图片描述

这应该是它的一个最基本的作用。

二、harfbuzz代码示例

下面笔者将以高棉语truetype字体文件来介绍harfbuzz的基本用法。
准备材料

  • truetype字体文件KhmerUI.ttf,这是高棉语的字体文件
  • freetype库,这里使用的是freetype-2.10.1
  • harfbuzz库,这里使用的是harfbuzz-2.6.4
  • ubuntu16.04,我是虚拟机里面运行的
  • 安装Qt Creator
    以上材料都可以在 这里下载
    开始写代码
    使用Qt Creator作为IDE
    工程目录结构如下:
.
├── contrib  
│   ├── freetype2       ----- freetype2    头文件
│   └── harfbuzz       ------harfbuzz头文件
├── encoding_conv.c     -----utf8转换成ucs2算法
├── encoding_conv.h
├── font.qrc
├── fonts
│   └── KhmerUI.ttf          ------高棉语字体文件
├── lib
│   ├── freetype-2.10.1         -------freetype共享库
│   └── harfbuzz-2.6.4	 	-------harfbuzz共享库
├── main.cpp
├── mainwin.cpp
├── mainwin.h
├── qt_font_freetype.pro       Qt工程文件
└── qt_font_freetype.pro.user

主要代码分析

//MainWin构造函数
MainWin::MainWin(QWidget *parent)
    : QWidget(parent)
{

    simulator_screen_width = 800;
    simulator_screen_height = 300;
    resize(QSize(simulator_screen_width, simulator_screen_height));
    pixel_mode = 4;
    simulator_screen = (unsigned int *)qMallocAligned(simulator_screen_width*simulator_screen_height*pixel_mode, 1);

    if (simulator_screen == Q_NULLPTR)
    {
       qDebug("simulator_screen malloc failed\n");
    }
    set_simulator_screen_color(WHITE_COLOR);
	// "បាទ"在高棉语中表示"yes"
    draw_text(40, 40, "Khmer use harfbuzz:", BLUE_COLOR, 27);
    draw_text(450, 40, "បាទ", BLUE_COLOR, 27);
}

draw_text函数,最重要的函数

qint32 MainWin::draw_text(int x, int y, char *utf8_text, unsigned int color, int font_size)
{
    FILE *fpttf;
    unsigned int fileLen = 0;
    char *fontData = NULL;
    void *userData = NULL;
    unsigned int upem = 0;
    unsigned int count = 0;
    int pen_x = x;
    int pen_y = y;
    int abjust_x = 0;
    int abjust_y = 0;
    int a,b,i,j;
    int width;
    unsigned int draw_color;
    int m_font_size;

    int loop = 0;
    int error = 0;
    hb_glyph_info_t *info;
    hb_glyph_info_t *infos = NULL;
    hb_blob_t *blob = NULL;
    hb_destroy_func_t destroy;
    hb_face_t *face;
    hb_font_t *font;
    hb_memory_mode_t mm = HB_MEMORY_MODE_WRITABLE;
    FT_Face FTFace;
    hb_buffer_t *buffer;

    fpttf = fopen(ttf_file, "rb");
    if (NULL == fpttf)
    {
        qDebug("open ttf file faild!, [%s, %d]\r\n", __FUNCTION__, __LINE__);
        return 0;
    }

    fseek(fpttf, 0, SEEK_END);
    fileLen = ftell(fpttf);
    fseek(fpttf, 0, SEEK_SET);
    fontData = (char *)malloc(fileLen);
    destroy = free;
    fileLen = fread(fontData, 1, fileLen, fpttf);

    fclose(fpttf);
    userData = (void *)fontData;
    blob = hb_blob_create((const char *)fontData, fileLen, mm, userData, destroy);
    face = hb_face_create(blob, 0);
    hb_blob_destroy(blob);
    blob = NULL;
    upem = hb_face_get_upem(face);
    font = hb_font_create(face);
    hb_font_set_scale(font, upem, upem);
    hb_ft_font_set_funcs(font);

    // init freetype
    FT_Library library;
    if (font_size < 1 || font_size > 72)
    {
        m_font_size = 18;
    }
    if (FT_Init_FreeType(&library) != 0)
    {
        return -2;
    }
    if (FT_New_Face(library, ttf_file, 0, &FTFace) != 0)
    {
        qDebug("func:%s line:%d \r\n",__FUNCTION__,__LINE__);
        return -3;
    }
    FT_Set_Char_Size(FTFace, 0, font_size * 64, 96, 96);


    buffer = hb_buffer_create();
    hb_buffer_add_utf8(buffer, (const char *)utf8_text, -1, 0, -1);
    hb_buffer_guess_segment_properties(buffer);
    if(font!=NULL)
    {
        hb_shape(font, buffer, NULL, 0);
        count = hb_buffer_get_length(buffer);
        infos = hb_buffer_get_glyph_infos(buffer, NULL);
    }

    for (loop = 0; loop < count; loop++)
    {
        info = &infos[loop];

        error = FT_Load_Glyph(FTFace, info->codepoint, FT_LOAD_DEFAULT);
        if (FTFace->glyph->format != FT_GLYPH_FORMAT_BITMAP)
        {
            error = FT_Render_Glyph(FTFace->glyph, ft_render_mode_normal);
        }
        FT_GlyphSlot slot = FTFace->glyph;
        FT_Bitmap bitmap = slot->bitmap;
        if(error==0)
        {
            abjust_x = slot->bitmap_left;
            abjust_y = (FTFace->size->metrics.ascender >> 6) - slot->bitmap_top;

            for (a = 0, j = pen_y + abjust_y; j < simulator_screen_height && a < bitmap.rows; j++, a++)
            {
                for (b = 0, i = pen_x + abjust_x; i < simulator_screen_width && b < bitmap.width; i++, b++)
                {
                    draw_color = (color & 0x00ffffff) | (bitmap.buffer[ a*bitmap.width + b ] << 24);
                    simulator_screen[j*simulator_screen_width + i] = draw_color;
                }
            }

            width += slot->advance.x >> 6;
            pen_x += slot->advance.x >> 6;
        }
    }
    this->repaint();
    return width;
}

draw_text函数中,freetype是使用方式和前一篇博客基本一样,只不过在只用freetype渲染字符前,先使用harfbuzz整一下形。进一步来说,高棉语中的truetype文件包含了全部的高棉语字符,也就是包含了整形后的字符,在使用freetype渲染字符前,先用harfbuzz获取相邻两个或者多个字符组合成新字符的信息,这个过程好像重定位,最后把获取的重定位信息传给freetype就可以正确渲染出字符了。
高棉语truetype文件-----》harfbuzz整形------》freetype渲染

三、总结

  1. 不使用harfbuzz,单单用freetype+truetype文件也可以渲染出高棉语的字符串,但是可能字符串的意思不正确。harfbuzz可以根据某种语言或者某种艺术字的特性,重新整形,得到正确的字符串。在此过程中,harfbuzz就像一个中间件。
  2. 到此为止,关于truetype、freetype和harfbuzz的介绍已经应用示例都写完了,这都是笔者在工作过程中的一些技术总结。笔者在博客中贴出的是主要代码,如果真的想要对这部分知识有一个更深入了解,还是建议下载笔者上传的资源文件。

陌上人如玉,君子世无双

  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值