如何使用PIL的ImageFont类获取字体像素高度?

25
我正在使用PIL的ImageFont模块加载字体来生成文本图像。 我希望文本紧密地绑定到边缘,但是当使用ImageFont获取字体高度时,它似乎包括字符的填充。如红色矩形所示。enter image description here
c = 'A'
font = ImageFont.truetype(font_path, font_size)
width = font.getsize(c)[0]
height = font.getsize(c)[1]
im = Image.new("RGBA", (width, height), (0, 0, 0))
draw = ImageDraw.Draw(im)
draw.text((0, 0), 'A', (255, 255, 255), font=font)
im.show('charimg')

如果我可以获取字符的实际高度,那么我就可以跳过底部矩形中的边界行,这些信息可以从字体中获取吗? 谢谢。

现在我编写了一个小函数,用于扫描生成的图像文本以垂直查找我使用的每种字体的填充。由于字体字符图像仅包含前景和背景颜色,因此它可以很好地工作。 - binzhang
font.getsize(c)[1] 中的 c 是什么? - Hassan Baig
谢谢指出错误,我已经修复了代码块,请检查一下。 - binzhang
@HassanBaig c代表所讨论的字符,我想是这样。 - Omega Krypton
你可以在一行代码中获取尺寸:width, height = font.getsize(c) - fdermishin
3个回答

60

具体字体大小取决于许多因素。我将向您展示如何计算不同字体度量标准。

font = ImageFont.truetype('arial.ttf', font_size)
ascent, descent = font.getmetrics()
(width, baseline), (offset_x, offset_y) = font.font.getsize(text)
  • 红色区域的高度:offset_y
  • 绿色区域的高度:ascent - offset_y
  • 蓝色区域的高度:descent
  • 黑色矩形:font.getmask(text).getbbox()

希望对您有所帮助。


2
更正:(width, height), (offset_x, offset_y) = font.font.getsize(text)。此外,font.getmask(text) 将返回一个大小为 (width, height) 的图像,“image_draw.text((0, 0), text, font)” 基本上在偏移量 (offset_x, offset_y) 处绘制由 getmask 返回的“掩码”。 - John
我猜你的解释只适用于英文字体,就我测试而言,其他语言字体会很乱 :/ - Ahmed4end
这已经过时了,而且对于非英文文本和非标准字体(例如显著的斜体)来说是毫无用处的。请参见我在下面使用Pillow 8.0.0中的新函数的答案:https://dev59.com/klgQ5IYBdhLWcg3wPxrc#70636273 - Nulano
此外,您不应使用 font.font.getsize,因为这是一个私有API(尽管遗憾的是没有带下划线前缀)。另一方面,由于历史原因,font.getsize已经损坏,无法轻松修复。 - Nulano

23
最高票答案已过时。Pillow 8.0.0中有一个新的功能:ImageDraw.textbbox。 请查看发行说明以了解Pillow 8.0.0中添加的其他与文本相关的函数。 请注意,ImageDraw.textsizeImageFont.getsizeImageFont.getoffset已经失效,不应在新代码中使用。这些函数已被具有更简洁API的新函数有效替代。请参阅文档以获取详细信息。
要获得整个字符串的紧凑边界框,您可以使用以下代码:
from PIL import Image, ImageDraw, ImageFont
image = Image.new("RGB", (200, 80))
draw = ImageDraw.Draw(image)
font = ImageFont.truetype("arial.ttf", 30)

draw.text((20, 20), "Hello World", font=font)
bbox = draw.textbbox((20, 20), "Hello World", font=font)
draw.rectangle(bbox, outline="red")
print(bbox)
# (20, 26, 175, 48)

image.show()

bounding box example


你可以将它与新的ImageDraw.textlength结合使用,以获得每个字母的单独边界框:

from PIL import Image, ImageDraw, ImageFont
image = Image.new("RGB", (200, 80))
draw = ImageDraw.Draw(image)
font = ImageFont.truetype("arial.ttf", 30)

xy = (20, 20)
text = "Example"
draw.text(xy, text, font=font)

x, y = xy
for c in text:
  bbox = draw.textbbox((x, y), c, font=font)
  draw.rectangle(bbox, outline="red")
  x += draw.textlength(c, font=font)

image.show()

letter bounding boxes example

请注意,这忽略了字距调整的影响。字距调整在基本文本布局中目前存在问题,但在Raqm布局中可能会引入轻微的不准确性。要修复这个问题,您需要添加字母对的文本长度。
for a, b in zip(text, text[1:] + " "):
  bbox = draw.textbbox((x, y), a, font=font)
  draw.rectangle(bbox, outline="red")
  x += draw.textlength(a + b, font=font) - draw.textlength(b, font=font)

顺便提一下:如果您正在寻找字体,请尝试ArialFont ="/System/Library/Fonts/Supplemental/Arial.ttf"。或者,如果您想了解您的字体,可以在MACOS上尝试此链接https://github.com/JayRizzo/JayRizzoTools/blob/master/pyGenRandFontObj.py - JayRizzo
2
你(或其他人)能否提供一个链接,解释为什么替代方案“已经失效”,以何种方式失效,并违反了哪些具体内容? - Thomas Kimber

3
from PIL import Image, ImageDraw, ImageFont

im = Image.new('RGB', (400, 300), (200, 200, 200))
text = 'AQj'
font = ImageFont.truetype('arial.ttf', size=220)
ascent, descent = font.getmetrics()
(width, height), (offset_x, offset_y) = font.font.getsize(text)
draw = ImageDraw.Draw(im)
draw.rectangle([(0, 0), (width, offset_y)], fill=(237, 127, 130))  # Red
draw.rectangle([(0, offset_y), (width, ascent)], fill=(202, 229, 134))  # Green
draw.rectangle([(0, ascent), (width, ascent + descent)], fill=(134, 190, 229))  # Blue
draw.rectangle(font.getmask(text).getbbox(), outline=(0, 0, 0))  # Black
draw.text((0, 0), text, font=font, fill=(0, 0, 0))
im.save('result.jpg')

print(width, height)
print(offset_x, offset_y)
print('Red height', offset_y)
print('Green height', ascent - offset_y)
print('Blue height', descent)
print('Black', font.getmask(text).getbbox())

结果

计算像素区域

from PIL import Image, ImageDraw, ImageFont

im = Image.new('RGB', (400, 300), (200, 200, 200))
text = 'AQj'
font = ImageFont.truetype('arial.ttf', size=220)
ascent, descent = font.getmetrics()
(width, height), (offset_x, offset_y) = font.font.getsize(text)
draw = ImageDraw.Draw(im)
draw.rectangle([(0, offset_y), (font.getmask(text).getbbox()[2], ascent + descent)], fill=(202, 229, 134))
draw.text((0, 0), text, font=font, fill=(0, 0, 0))
im.save('result.jpg')
print('Font pixel', (ascent + descent - offset_y) * (font.getmask(text).getbbox()[2]))

结果


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接