【问题标题】:How to get the font pixel height using PIL's ImageFont class?如何使用 PIL 的 ImageFont 类获取字体像素高度?
【发布时间】:2022-01-09 04:54:55
【问题描述】:

我正在使用 PIL 的 ImageFont 模块来加载字体以生成文本图像。 我希望文本紧密地绑定到边缘,但是,当使用 ImageFont 获取字体高度时,它似乎包括字符的填充。如红色矩形所示。

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

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

【问题讨论】:

  • 现在我编写了一个小函数来垂直扫描生成的图像文本以查找我使用的每种字体的填充。由于字体字符图像仅包含正面和背面颜色,因此效果很好。
  • font.getsize(c)[1] 中的c 是什么
  • 感谢指出错误,我已经修复了代码块,请检查。
  • @HassanBaig c 代表我认为有问题的角色
  • 一行可以得到size:width, height = font.getsize(c)

标签: python fonts python-imaging-library


【解决方案1】:

确切的大小取决于许多因素。我将向您展示如何计算不同的字体指标。

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

希望对你有帮助。

【讨论】:

  • 更正:(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 返回)。
  • 我猜你的解释只适用于英文字体,因为我测试它与其他语言字体很乱:/
  • 这是过时的,并且被非英文文本和非标准字体(例如重要的斜体)彻底破坏。请参阅下面使用 Pillow 8.0.0 中的新功能的答案:stackoverflow.com/a/70636273/1648883
  • 另外,你不应该使用font.font.getsize,因为这是一个私有API(虽然很遗憾没有下划线前缀)。 OTOH font.getsize 因历史原因已损坏,无法轻松修复。
【解决方案2】:

票数最高的答案已过时。 Pillow 8.0.0 中有一个新功能:ImageDraw.textbbox。 Pillow 8.0.0 中添加的其他文本相关功能请参见release notes

请注意,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()


您可以将它与新的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()

请注意,这忽略了字距调整的影响。开云目前在基本文本布局方面存在问题,但可能会在 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)

【讨论】:

    【解决方案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())
    

    result

    计算面积像素

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

    result

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-06-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-06-05
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多