如何在Python PIL中设置绘制文本的合适线宽?

4 投票
1 回答
3407 浏览
提问于 2025-04-18 08:27

我正在尝试使用PIL在一张任意分辨率的图片上绘制文字。我的代码是参考了以下两个问题的结果:这里这里。在这两个回答中,textwrap.wrap的宽度值被设置为:width=40。但是,当我随意更改参数size_xsize_y时,这样就会导致文字在图片中填充得过多或过少。理想情况下,我需要一种方法将字体的大小转换为PIL中的高度和宽度像素值,但我不太确定该怎么做。以下是我现在的代码:

from PIL import Image
from PIL import ImageFont
from PIL import ImageDraw 
import textwrap

size_x = 946 #This value can arbitrarily change
size_y = 300 #This value can arbitrarily change
font_size = 16 #This value can be adjusted to fit parameters of image if necessary

my_text = ['Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam scelerisque sapien convallis nisl facilisis, sed facilisis odio accumsan. Maecenas vel leo eu turpis porta dictum at vel neque. Donec sagittis felis non tellus lacinia facilisis. Vivamus vel nisi ullamcorper, feugiat lorem sagittis, pellentesque dolor. Curabitur est magna, feugiat ut nibh quis, blandit vestibulum nisl. Sed pulvinar condimentum purus et rutrum. Proin magna arcu, scelerisque at gravida ut, convallis quis orci. Mauris ipsum tortor, laoreet et leo ac, lacinia euismod tellus. Curabitur volutpat nisi a metus faucibus, vel iaculis nisl fermentum. Curabitur et orci id sapien porttitor dignissim at ac dolor. Donec nec mattis nisi. ']

tx = Image.new('RGB', (size_x, size_y),color=(255,255,255))
draw = ImageDraw.Draw(tx)

my_font = ImageFont.truetype('/Windows/Fonts/arial.ttf',size=font_size)
lines = textwrap.wrap(my_text[0], width = 130) #This width value needs to be set automatically
y_text = 0
for line in lines:
    width, height = my_font.getsize(line)
    draw.text((0, y_text), line, font = my_font, fill = (0,0,0))
    y_text += height

tx.show()

这是一个宽度为width=130的示例图片,填充得相当不错。

width = 130

这是一个宽度为width=200的示例图片,填充得过多了。

width = 200

1 个回答

4

PIL的textwrap.wrap()函数需要一个width参数,这个参数是用来指定每行最多可以有多少个字符。我觉得这样设计不太好,因为如果能指定最多的像素英寸会更有用。这是因为你可能有一个用像素表示的边界框,而可变宽度的字体让字符数量变得不太有意义。

一种解决办法是使用固定宽度的字体。这样字符数量就可以简单地通过除法计算。

另一种方法是寻找一个最大width,确保它不会超出你的边界框。我会把这个过程设置成二分查找,start=1end=len(string)。然后计算pivot=(end+start)/2。 用width=pivot进行换行,然后找出max(font.getsize(line) for line in wrapping)

  • 如果这个max大于边界框,就往左边查找。(end=pivot)
  • 否则,尝试width=pivot+1。如果超出边界,你就找到了最大width
  • 如果没有超出,就往右边查找。(start=pivot)

一般来说,这种方法不是最优的,因为每一行可能需要不同的换行宽度(这也是我觉得这个API设计糟糕的原因),但如果你处理的是段落,这个方法应该还不错。

撰写回答