Back to home

解决Python Image Library无法渲染小号字体问题

问题描述

拿Python Image Library (下简称“PIL”)实现渲染文字到图片的功能,挑选好字体后,发现渲染小于一定字号(随不同的字体而不同),字就会显示乱码似的结果。 最后修改PIL调用FreeType2字体渲染参数解决问题。

问题解决

首先是定位问题,根据 PIL.ImageFontgetmask 方法取得字模时就发现乱码现象,下面是测试的实现

    def test_mask(self):
        case = u"TITLE测试时候的再别康桥测试"
        im = Image.new("RGBA", (500, 500), IMG_COLOR_BACKGROUND)
        dr = ImageDraw.Draw(im, "RGBA")
        im_font = ImageFont.truetype(resource_file("Zpix_PC_09-12-10.ttc"), 12)
        fm = im_font.getmask(case)
        inf , fill = dr._getink((0,0,0))
        dr.draw.draw_bitmap((0, 0), fm, inf)
        im.show()

问题出现在getmask方法,于是进去查看,发现最终调用的是PIL C 模块 _imagingft.c 中实现的static PyObject* font_render(FontObject* self, PyObject* args)方法,最终将问题定位到了343 行的 error = FT_Load_Glyph(self->face, index, load_flags); 调用上。
FT_Load_Glyph 是PIL调用Freetype2,获取字体轮廓(字模)的方法,下面是调用部分。

    im = (Imaging) id;
    load_flags = FT_LOAD_RENDER;
    if (mask)
        load_flags |= FT_LOAD_TARGET_MONO;

        ….

        error = FT_Load_Glyph(self->face, index, load_flags);
        if (error)
            return geterror(error);
        glyph = self->face->glyph;

作为调用参数,load_flags很明显是控制了获取字模时的参数,于是跑去查看文档。找到了一些关于参数的说明,作出如下修改:

load_flags = FT_LOAD_RENDER | FT_LOAD_NO_BITMAP | FT_LOAD_FORCE_AUTOHINT;

FT_LOAD_NO_BITMAP是指不加载字体的点阵数据(因为很多字体都是矢量数据,没有点阵结构),FT_LOAD_FORCE_AUTOHINT是由于过小的矢量渲染会造成字体发虚,需要强制锐化。
然后重新编译PIL,安装,解决问题。

事后发现Freetype的Tutorial已经给出了解释:

Loading a glyph image into the slot is performed by calling FT_Load_Glyph as in

    error = FT_Load_Glyph(
              face,          /* handle to face object */
              glyph_index,   /* glyph index           */
              load_flags );  /* load flags, see below */

The load_flags value is a set of bit flags used to indicate some special operations. The default value FT_LOAD_DEFAULT is 0. This function will try to load the corresponding glyph image from the face:
If a bitmap is found for the corresponding glyph and pixel size, it will be loaded into the slot. Embedded bitmaps are always favored over native image formats, because we assume that they are higher-quality versions of the same glyph. This can be changed by using the FT_LOAD_NO_BITMAP flag. Otherwise, a native image for the glyph will be loaded. It will also be scaled to the current pixel size, as well as hinted for certain formats like TrueType and Type 1.

总结

由于字体资源很难控制,在使用这些字体进行渲染的时候需要对他们存储内容进行考量,如果是有效的点阵数据,那当然不错,不过这很少(收费字体居多),且点阵字体可选字号有限,不一定能够满足使用。而采用矢量内容渲染的时候,又不得不对渲染结果进行评估。
另外,一款字体的制作本身就是一个极其复杂的过程,可能最终只是一个ttf文件,其中的过程,也只有冷暖自知,所以尽可能的使用正版字体,维护这个行业吧。(现在很多收费字体提供了限制字号的试用版,能够解决一些需求)