Python 使用枕头绘制TrueType小大写文字

Python 使用枕头绘制TrueType小大写文字,python,python-imaging-library,Python,Python Imaging Library,我正在使用枕头在图像上绘制文本: 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="whit

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

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')

但是我不知道如何用小写字母而不是普通字符来绘制字符串。

最新版本的Roboto Medium确实包含按大小绘制的小写字母,但它们没有映射到Unicode码点。这意味着在简单字符串中指定字符的常规方法将无法工作

OpenType感知软件可以通过OpenType功能访问字形
c2sc
(用大写字母替换其等效的小写字母)和
smcp
(其功能相同,但仅适用于小写字母)。这些翻译表位于TrueType
GSUB
表中的字体内,它们以一种特别不规则的格式编码。这可能就是为什么PIL程序员从不费心去实现它(以及其他可能有用的OpenType特性,如每种语言的超级和订阅、紧排和本地化字母形式)

Python模块
freetype
可以访问原始图示符。但是,类似地,FreeType开发人员也曾短暂考虑过将OpenType功能解析添加到他们的库中,但最终决定不这样做,并且FreeType只关注绘图。尽管如此,您仍然可以使用
freetype
获取并光栅化所有字体中的任何字形。唯一的问题是,您将对glyph索引进行硬编码

“字形索引”只不过是“什么编码值对应于哪个图像”,从0开始,一直到最后一个字形。字体的可能编码位于字体数据的其他位置;它可能包含确定的Windows编码(ol的“代码页”东西——但更大)、完整的Unicode编码、MacRoman和少量其他编码。您只需选择一种编码并告诉您需要该编码中的字符代码,
freetype
将为您查找正确的字形

但是OpenType特性可以推翻这一点。这是一组独立的翻译,由您选择的功能(以及脚本,甚至是语言)驱动。一个完全支持OpenType的界面将看到您的glyph,使用所需的特性提高赌注,并返回原始glyph索引

由于单个字体文件中可能有许多原始字形,因此并非每个编码都指向字体中的每个字形。例如,想象一下可怜的老麦克罗曼,他只有256个可能的字符。你不可能用这个来处理所有机器人的1294个铭文。就Unicode而言,即使有成千上万个已定义的条目,Unicode也无法解决所有这些问题,因为可以为某些字符(如花式大写字母)创建与原始字符相同的样式替代,以及用作连字的字符组合,其中一个完整的序列,如
Zapfino
被绘制为一个复杂的字形(该字形为“Zapfino”;非常棒)

一些字体设计师允许通过“私有”Unicode代码点(意思是“每个人都可以自由定义”)访问特殊字符,但我认为Roboto没有这个功能

您可以向FreeType索取完整的
GSUB
表格,然后对其进行解析,以找到适合小写字母的翻译,如果您这样做,您将很高兴发现您的代码可以使用任何真正大写字母的字体(!),但这是一项巨大的工作

所以我作弊了。一些软件允许每个字形检查字体,我使用Adobe InDesign查找所有大写字母
A..Z
的字形索引

不过,这确实有一个缺点。它只能(可靠地)在Roboto Medium的一个特定版本中工作,因为设计者可以为每个新版本随意添加、删除和移动标志符号。请记住,这对其他软件处理字体的方式没有任何影响!编码表和OpenType表被调整为匹配,因此软件不需要知道任何关于每个字体字形索引的信息。
也不能保证这些相同的索引将与Roboto系列中的其他字体一起使用。设计师可能使用了完全相同的布局,但可能没有。实际上,如上所述,他们甚至不必关心它

我已下载最新版本2.137;2017; 如果你没有,先下载吧

下面是一些示例代码,用于按顺序打印所有小写标志符号的灰度值,以及它们相对于标志符号起点的水平偏移(左偏移)和从基线测量的垂直高度(以像素为单位)。随意调整以匹配PIL自己的文本绘制程序;可能要花些功夫才能把它做好™.

(对于这个相当基本的代码示例,我深表歉意。我成功地将Python2.7和3的PIL安装搞砸了。(有些东西),因此我无法以图形方式显示一些东西;幸运的是,
freetype
模块仍然工作。)


如果你没有一个小大写版本的字体,这将是理想的,你似乎可以简单地改变所有的字母大写,并使用一个稍微小一点的字体大小来绘制它们-没有任何内置的,这样做很好。请注意,如果做我在前面的评论中建议的,每封信都需要单独处理。您可以使用Pillow的方法来确定下一个字符的放置位置,以查看每个字符占用的空间。Jonathan,仅供参考:我已经使用一个OpenType解析器创建了一个解析器,我在过去一直在使用该解析器–嗯,month@usr2564301这将非常有用。我设法从您的代码片段中获得了所需的内容(并检查了glyphs)。然而,令人惊讶的是,没有一个Python库来解析这一点。
import freetype
import os.path

smcapGlyphs = [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 ()