Break long drawn text to multiple lines with Pillow

半城伤御伤魂 提交于 2020-02-25 08:03:38

问题


I'm creating a small python program that draws text on a small 128x48 image. It works fine for text that only reaches width of 120, but I can't figure out how to have longer text split into an additional line. How do I go about doing so?

I've attempted using textwrap3, but I couldn't get it to work with Pillow.

The program creates 128x48 images files with a black background and yellow centered text, that is later supposed to be viewed on a device that outputs up to 480i, so simply making the text much smaller to fit additional text width won't be helpful. The font currently used is Arial 18.

Here's the current code used to create the image:

from PIL import Image, ImageDraw, ImageFont

AppName = "TextGoesHere"
Font = ImageFont.truetype('./assets/Arial.ttf', 18)
img = Image.new('RGB', (128, 48), color='black')
d = ImageDraw.Draw(img)

# Get width and height of text
w, h = d.textsize(AppName, font=Font)

# Draw text
d.text(((128-w)/2, (48-h)/2), AppName, font=Font, fill=(255, 255, 0))

img.save('icon.png')

The above code outputs the image like this:

Longer text with width bigger then the image outputs like this (LongerTextGoesHere):

The wanted result should be simular to this:


回答1:


Here's some code that uses binary search with PIL/Pillow to break the text into pieces that fit.

def break_fix(text, width, font, draw):
    if not text:
        return
    lo = 0
    hi = len(text)
    while lo < hi:
        mid = (lo + hi + 1) // 2
        t = text[:mid]
        w, h = draw.textsize(t, font=font)
        if w <= width:
            lo = mid
        else:
            hi = mid - 1
    t = text[:lo]
    w, h = draw.textsize(t, font=font)
    yield t, w, h
    yield from break_fix(text[lo:], width, font, draw)

def fit_text(img, text, color, font):
    width = img.size[0] - 2
    draw = ImageDraw.Draw(img)
    pieces = list(break_fix(text, width, font, draw))
    height = sum(p[2] for p in pieces)
    if height > img.size[1]:
        raise ValueError("text doesn't fit")
    y = (img.size[1] - height) // 2
    for t, w, h in pieces:
        x = (img.size[0] - w) // 2
        draw.text((x, y), t, font=font, fill=color)
        y += h

img = Image.new('RGB', (128, 48), color='black')
fit_text(img, 'LongerTextGoesHere', (255,255,0), Font)
img.show()




回答2:


You can do that pretty simply without needing to code any Python if you use ImageMagick which is installed on most Linux distros and is available for macOS and Windows. So, just in Terminal:

convert -size 128x48 -background black -font Arial -fill yellow -gravity center caption:"Some Text" result.png

Or, with a longer text string:

convert -size 128x48 -background black -font Arial -fill yellow -gravity center caption:"Some Really Really Long Text" result.png

If you really, really want to write Python, Wand is a Python binding for ImageMagick and the function names will mirror those used above.



来源:https://stackoverflow.com/questions/58041361/break-long-drawn-text-to-multiple-lines-with-pillow

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!