使用 Pillow 绘制 TrueType 小型大写字母

我正在使用 Pillow 在图像上绘制文本:


from PIL import Image, ImageDraw, ImageFont


im = Image.new("L", (400, 50), color="black")

draw = ImageDraw.Draw(im)

font = ImageFont.truetype("Roboto-Medium.ttf", 28)

draw.text((0, 0), "How To Get Small Caps?", font=font, fill="white")

im.save('small_caps.png')

但我不知道如何用小写字母而不是常规字符绘制该字符串。


千万里不及你
浏览 102回答 1
1回答

Qyouu

最新版本的 Roboto-Medium确实包含按大小绘制的小写字母,但它们并未映射到 Unicode 代码点。这意味着在简单字符串中指定字符的常规方法行不通。OpenType 感知软件可以通过 OpenType 功能访问字形c2sc(将大写字母替换为等效的小型大写字母)和smcp(其作用相同,但仅适用于小写字母)。这些翻译表位于 TrueTypeGSUB表中的字体内——它们以一种特别不正当的格式编码。这可能是 PIL 程序员从不费心去实现它的原因(以及其他可能有用的 OpenType 功能,例如上标和下标、字距调整和每种语言的本地化字母形式)。Python 模块freetype 可以访问原始字形。但是,同样地,FreeType 开发人员曾短暂考虑将 OpenType 功能解析添加到他们的库中,但最终决定放弃,让 FreeType 只专注于绘图。尽管如此,您仍然可以使用freetype来获取和栅格化所有字体中的任何字形。唯一的事情是,您将对字形索引进行硬编码。“字形索引”只不过是“什么编码值对应于哪个图像”,从 0 开始,一直到最后一个字形。字体的可能编码位于字体数据中的其他位置;它可能包含确定的 Windows 编码(旧的“代码页”东西——但更大)、完整的 Unicode 编码、MacRoman 以及其他一些。您只需要选择一种编码并告诉您想要该编码中的freetype什么字符代码,就会为您查找正确的字形。但是 OpenType 功能可以推翻这一点。它是一组单独的翻译,由您选择的功能(以及哪种脚本,甚至是哪种语言)驱动。一个完全支持 OpenType 的界面会看到你的字形,用所需的特性提高赌注,并返回一个原始的字形索引。由于单个字体文件中可能有很多原始字形,因此并非每个编码都会指向字体中的每个字形。想象一下可怜的老 MacRoman,例如,只有 256 个可能的字符。你不可能用它来处理 Roboto 的所有 1294 个字形。即使 Unicode 具有成千上万个已定义的条目,也无法解决所有问题,因为可以为某些字符(例如花体大写字母)创建风格替代品,就 Unicode 而言,它们与它们的原始字符相同,并且用作连字的字符组合,其中完整的序列例如Zapfino被绘制为单个复杂的字形(那个是字体“Zapfino”;它很棒)。一些字体设计师允许通过“私有”Unicode 代码点访问特殊字符(意思是“每个人都可以自由定义”),但我认为 Roboto 没有这个。可以向 FreeType 询问完整的GSUB表格,然后对其进行解析以找到适合小型大写字母的翻译,如果这样做,您会很高兴地发现您的代码将适用于任何带有真正小型大写字母的字体 (! ),但这是一项巨大的工作。所以我作弊了。有些软件允许按字形检查字体,我使用 Adobe InDesign 查找所有A..Z小写字母的字形索引。不过,这确实有一个缺点。它只能(可靠地)与一个特定版本的 Roboto-Medium 一起工作,因为设计者可以为每个新版本随意添加、删除和移动字形。请记住,这对其他软件处理字体的方式没有任何影响!编码表和 OpenType 表已调整为匹配,因此软件不需要了解每种字体的字形索引的任何信息。也无法保证这些相同的索引可以与 Roboto 系列中的其他字体一起使用。设计师可能使用了完全相同的布局,也可能没有。实际上,如上所述,他们甚至不必关心它。我已经下载了最新的 2.137 版;2017;如果你没有那个,请先下载它!下面是一些示例代码,用于按顺序打印出所有小型大写字母字形的灰度值,它们与字形起点的水平偏移(左偏移)和以像素为单位的垂直高度(从基线测量)。随意调整,匹配PIL自己的文字绘制套路;可能需要一些修饰才能让它恰到好处™。(为相当基本的代码示例道歉。我设法弄乱了 Python 2.7 和 3 的 PIL 安装(某些东西),所以我无法以图形方式显示某些东西;幸运的是,该freetype模块仍然有效。)import freetypeimport os.pathsmcapGlyphs = [563,562,561,560,552,486,485,484,483,482,481,480,479, 478,477,476,475,474,473,472,471,470,469,468,467,466]def dump_bytes(buf,wide,high):    for y in range(high):        for x in range(wide):            print ('%02x ' % buf[y*wide+x], end='')        print ()face = freetype.Face(os.path.expanduser('~')+"/Library/Fonts/Roboto-Medium.ttf")face.set_char_size( 48*64 )for index,i in enumerate(smcapGlyphs):    face.load_glyph(i)    print (chr(65+index))    bitmap = face.glyph.bitmap    width  = face.glyph.bitmap.width    rows   = face.glyph.bitmap.rows    pitch = face.glyph.bitmap.pitch    print (face.glyph.bitmap_left)    print (face.glyph.bitmap_top)    dump_bytes (bitmap.buffer, pitch,rows)    print ()
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Python