在PIL中换行文本

时间:2011-11-24 12:34:17

标签: python python-imaging-library

我正在使用PIL在图像上绘制文字。我如何包装一串文字。这是我的代码:

text = "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."

image = Image.open("/tmp/background-image.jpg")
draw = ImageDraw.Draw(image)
font = ImageFont.truetype("/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans.ttf"), 50, encoding='unic')
draw.text((100, 100), text, font=font, fill="#aa0000")
image.save("/tmp/image.jpg")

7 个答案:

答案 0 :(得分:12)

您需要先将文本拆分为正确长度的行,然后逐个绘制每一行。

第二部分很简单,但如果使用可变宽度字体,第一部分可能非常棘手。如果使用固定宽度字体,或者精度无关紧要,那么您可以使用textwrap模块将文本拆分为给定字符宽度的行:

margin = offset = 40
for line in textwrap.wrap(text, width=40):
    draw.text((margin, offset), line, font=font, fill="#aa0000")
    offset += font.getsize(line)[1]

答案 1 :(得分:4)

嗯,你可以手动完成,当然,每次你想要包装文本时都要使用\ n。如果你每次都有不同的字符串,但它可以完全控制结果,这不是最好的方法。 但是还有textwrap模块。 你可以这样使用它:

import textwrap
texto = "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
novo = textwrap.wrap(texto, width=20)
print(novo)

结果:

>>> 
['Lorem ipsum dolor', 'sit amet,', 'consectetur', 'adipisicing elit,', 'sed do eiusmod', 'tempor incididunt ut', 'labore et dolore', 'magna aliqua. Ut', 'enim ad minim', 'veniam, quis nostrud', 'exercitation ullamco', 'laboris nisi ut', 'aliquip ex ea', 'commodo consequat.', 'Duis aute irure', 'dolor in', 'reprehenderit in', 'voluptate velit esse', 'cillum dolore eu', 'fugiat nulla', 'pariatur. Excepteur', 'sint occaecat', 'cupidatat non', 'proident, sunt in', 'culpa qui officia', 'deserunt mollit anim', 'id est laborum.']

返回根据您确定的宽度包装的上一个字符串的术语列表。

答案 2 :(得分:1)

接受的文字基于每行40个字符的固定限制而不考虑框宽度(以像素为单位)或字体大小。这可能很容易导致底部填充或溢出。

这是一个更好的解决方案 - 使用基于字体的宽度测量来处理文本换行的简单代码片段: https://gist.github.com/turicas/1455973

答案 3 :(得分:1)

我不太喜欢使用另一个模块来实现这一目标的想法;我想使其仅与PIL中的实用程序一起使用。这适用于Python 3.7。

我编写了此函数,该函数仅根据像素宽度包装文本,然后也检查像素高度-如果存在无法容纳的单词,则将其切掉并添加省略号以显示省略(以不破坏字体的方式限制):


from PIL import Image, ImageDraw, ImageFont

def text_wrap(text,font,writing,max_width,max_height):
    lines = [[]]
    words = text.split()
    for word in words:
        # try putting this word in last line then measure
        lines[-1].append(word)
        (w,h) = writing.multiline_textsize('\n'.join([' '.join(line) for line in lines]), font=font)
        if w > max_width: # too wide
            # take it back out, put it on the next line, then measure again
            lines.append([lines[-1].pop()])
            (w,h) = writing.multiline_textsize('\n'.join([' '.join(line) for line in lines]), font=font)
            if h > max_height: # too high now, cannot fit this word in, so take out - add ellipses
                lines.pop()
                # try adding ellipses to last word fitting (i.e. without a space)
                lines[-1][-1] += '...'
                # keep checking that this doesn't make the textbox too wide, 
                # if so, cycle through previous words until the ellipses can fit
                while writing.multiline_textsize('\n'.join([' '.join(line) for line in lines]),font=font)[0] > max_width:
                    lines[-1].pop()
                    lines[-1][-1] += '...'
                break
    return '\n'.join([' '.join(line) for line in lines])

用法:

bg = Image.open('external/my_background.png')
ws = Image.open('external/my_overlay_with_alpha.png')

writing = ImageDraw.Draw(bg)

title = "My Title"
description = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat."


title_font = ImageFont.truetype("Arial Black.ttf", size=42)
desc_font = ImageFont.truetype("Arial Narrow Italic.ttf", size=16)

description_wrapped = text_wrap(description,desc_font,writing,160,124)

# write title and description
writing.text((20,5),title,font=title_font)
writing.text((140,120),description_wrapped,font=desc_font)

out = Image.alpha_composite(bg,ws)
out.save('mysubfolder/output.png')

out.show()

答案 4 :(得分:0)

这对我有用。从印刷的角度来看,添加更多语句将是不错的选择,例如,检查单词是否在中间分开,连字符,检查单词的开头和结尾是否至少有2个字母。稍后我一定会看一下。

content_raw = 'AND WHAT ABOUT SOMETHING VERY LONG'
content = ''
for i in content_raw:
    if draw.textsize(content.split('\n')[-1])[0] > 30:
        content += '\n'
    content += i

答案 5 :(得分:0)

使用textwrap。它的工作原理不言而喻。

import textwrap
from PIL import *
caption = "Obama warns far-left candidates says average American does not want to tear down the system"

wrapper = textwrap.TextWrapper(width=50) 
word_list = wrapper.wrap(text=caption) 
caption_new = ''
for ii in word_list[:-1]:
    caption_new = caption_new + ii + '\n'
caption_new += word_list[-1]

image = Image.open('obama.jpg')
draw = ImageDraw.Draw(image)

# Download the Font and Replace the font with the font file. 
font = ImageFont.truetype(text_font, size=font_size)
w,h = draw.textsize(caption_new, font=font)
W,H = image.size
x,y = 0.5*(W-w),0.90*H-h

image.save('output.png')

输入图像 DataFrame.astype

输出图像

enter image description here

答案 6 :(得分:0)

旨在实现简单且一致的行为。如其他答案中所述,library(ggplot2) ggplot(mpg, aes(displ, hwy)) + geom_point() + lapply(c(3,5,7,9), function(i) { geom_smooth( data = ~ cbind(., facet = i), method = lm, formula = y ~ splines::bs(x, i) ) }) + facet_wrap(vars(facet)) 模块仅正确处理固定宽度字体(因此它不是一致的解决方案)。这是一个简单的功能,可以在不破坏文字的情况下包装文本,并且适用于可变宽度字体。

textwrap