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

时间:2014-06-03 17:38:18

标签: python python-3.x python-imaging-library

我正在尝试使用PIL将文本绘制成具有任意分辨率的图像。我目前的代码是咨询以下两个问题herehere的结果。在这两个答案中,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 个答案:

答案 0 :(得分: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很糟糕),但是如果你正在做段落那么它应该相当不错。