测量文本宽度 (Python/PIL)

Measuring width of text (Python/PIL)

我正在使用以下两种方法来计算示例字符串的呈现 宽度 对于设置的字体类型和大小:

font = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", 14)
sample = "Lorem ipsum dolor sit amet, partem periculis an duo, eum lorem paulo an, mazim feugiat lobortis sea ut. In est error eirmod vituperata, prima iudicabit rationibus mel et. Paulo accumsan ad sit, et modus assueverit eum. Quod homero adversarium vel ne, mel noster dolorum te, qui ea senserit argumentum complectitur. Duo at laudem explicari deterruisset, eu quo hinc mnesarchum. Vel autem insolens atomorum at, dolorum suavitate voluptatum duo ex."
#METHOD 1
draw_txt = ImageDraw.Draw(img)
width, height = draw_txt.textsize(sample, font=font)
print width
#METHOD 2
width = 0
for c in sample:
    width += font.getsize(c)[0]
print width

METHOD 1 产生宽度 3236,而 METHOD 2 产生 3270。为什么会出现差异?此外,我还注意到示例文本越短,这两种方法之间的差异就越小。

幕后发生了什么?哪个宽度可以认为是渲染句子的 true 宽度?最后,我可以做一些调整让两种方法报告大致相同的宽度吗?

注意:示例文本长度为 445 个字符

字距调整

你在这里做了两件不同的事情:

  • 求长文本的宽度。
  • 求出所有字符的宽度,然后盲目相加

如果您使用的是等宽字体,情况可能会有所不同,但字体通常会使用一种叫做 kerning 的东西来使文本更平滑、更紧凑。

Wikipedia 说:

In typography, kerning is the process of adjusting the spacing between characters in a proportional font, usually to achieve a visually pleasing result. Kerning adjusts the space between individual letter forms, while tracking (letter-spacing) adjusts spacing uniformly over a range of characters. In a well-kerned font, the two-dimensional blank spaces between each pair of characters all have a visually similar area.

这是一些 kerning of the DejaVuSans font:

引擎盖下

在幕后,Pillow 对您的两种方法并没有太大的不同。只是你用不同的方式称呼他们而已。

如果你添加第三种方法来获取整个句子的宽度,使用与方法二相同的函数,你也将获得与方法一获取整个句子相同的宽度:

# METHOD 3
width = font.getsize(sample)[0]
print width

这是 Pillow 的 ImageDraw.textsize(来自方法一和方法三):

def textsize(self, text, font=None, *args, **kwargs):
    """Get the size of a given string, in pixels."""
    if self._multiline_check(text):
        return self.multiline_textsize(text, font, *args, **kwargs)

    if font is None:
        font = self.getfont()
    return font.getsize(text)

对于单行文本,只返回font.getsize,同方法二。 (对于多行文本,它只是将其分成几行和 returns 几个 font.getsize 调用的总和。)