使用PIL时字体剪裁问题

6 投票
5 回答
3831 浏览
提问于 2025-04-15 17:12

这张图片是用PIL(Python Imaging Library)制作的。你有没有注意到这张图片中的字母“g”和“y”被切掉了?我该怎么做才能避免这种情况呢?

http://img109.imageshack.us/img109/8874/screenshotep.png

生成这张图片的代码其实很简单(这里简化了一下):

import Image, ImageDraw, ImageFont

im = Image.new("RGBA", (200, 200), 'white')
draw = ImageDraw.Draw(im)

font = ImageFont.truetype("VeraSe.ttf", 12)

draw.text(
           (1, 1),
           " %s: " % "ggjyfFwe__",
           font=font,
           fill='black'
)

draw.text(
           (1, 30),
           " %s" % 15,
           font=font,
           fill='black'
)

im.show()

我试了几种不同的字体,但总是出现被切掉的情况。奇怪的是,搜索“PIL 字体被切掉”几乎没有找到有用的信息……我在Ubuntu 9.10上使用的是Python 2.6.4和PIL 1.1.6。

5 个回答

0

我的建议是,在你创建图像对象之前,先获取文本所需的大小。

这可以通过使用 font.getsize("text") 来实现(文档)。

在我写的一个图像生成脚本中,我首先通过调用类似 font.getsize("Åj") 的方法找到了单行文本的最大高度(如果你只需要处理美国标准ASCII字符,可以用 "Aj" 来找高度)。然后,我计算了所需的图像高度和行间距,包括边距和行间距。

1

我用之前提到的方法解决不了某些字体的问题,所以最后我选择使用 aggdraw,它可以作为PIL文本绘制方法的透明替代品。

如果把你的代码改成aggdraw的样子,应该是这样的:

import Image
import aggdraw

im = Image.new("RGBA", (200, 200), 'white')
draw = aggdraw.Draw(im)

# note that the color is specified in the font constructor in aggdraw
font = aggdraw.Font((0,0,0), "VeraSe.ttf", size=12, opacity=255)

draw.text((1, 1), " %s: " % "ggjyfFwe__", font) # no color here
draw.text((1, 30), " %s" % 15, font)

draw.flush() # don't forget this to update the underlying PIL image!

im.show()
3

这是对这个老问题的一个迟到的回答。

问题似乎出在PIL和Pillow这两个库会把渲染出来的文字边缘裁剪掉。这个问题通常出现在一些宽字符和下沉字符(比如'y')的后面。有时候,某些字体的顶部也会出现这个问题。这已经是一个存在了至少十年的问题。无论你在什么大小的图像上调用text(),这个问题都会发生。问题的根源在于选择边界矩形时使用了“字体大小 * 字符数量”,而不是“我实际需要渲染的内容”,这个问题发生在代码的深层部分(_imagingft.c)。修复这个问题会导致其他问题,比如逐字渲染文本时对齐的问题。

一些解决方案包括:

  • 在你的字符串末尾加一个空格。im.text(xy, my_text + ' ', ...)
  • 对于高度问题,先获取文本的宽度(font.getsize()),然后再渲染文本,加上一些合适的上升和下降部分,最后把渲染出来的文本裁剪到第一个报告的宽度和第二个实际的高度。
  • 使用其他库,比如AggDrawpyvips

这个问题在一些不同的提问中被提到过,比如PIL裁剪字体PIL裁剪字母顶部在Python中正确渲染文本并准确检测其边界。这些问题都提到了同样的根本问题,但并不是重复的问题。

撰写回答