如何将一个非明确给定的字符串分成两行或更多行?

How does one split up a non-explicitly given string into two or more lines?

我目前正在使用 Python 机器人输出带有文本的图像,但我发现文本通常太长而无法显示。因此,我决定将字符串分成两行,以便它适合我的图像。我正在使用 Pillow 5.1 进行图像处理。

我是 Python 编程的新手,我试图搜索如何将 Python 字符串分成两行或更多行。不幸的是,所有结果似乎只处理明确给出的字符串(即 'string')。

print("Ababoubian wisdom!")
ababou2 = ababou() #returns a string, how to split into two lines?
if("Ababou" in ababou2):
    ababou2 = ababou()
font = ImageFont.truetype("Arial.ttf", 14)
img = Image.new('RGB', (300, 200), color = (random.randint(0, 255),random.randint(0, 255), random.randint(0, 255)))
d = ImageDraw.Draw(img)
d.text((0, 0), ababou2, font=font) #draws text

预期结果:文本应该换行到下一行。

我不确定文本包装器对您的情况是否有帮助,因为您需要分别绘制每一行。

看来你应该计算单行字符的最大长度,然后做:

ababou2 = ababou() # Whats the point of assigning the same value twice? 
                   # does ababou() returns a different value each time?
if "Ababou" in ababou2:
    ababou2 = ababou()

res_text = list()
ababou2 = ababou2.split(' ') # Splits the string by space\ ' ', to a list of strings 
curr_txt = ''
for word in ababou2:
    if len(curr_txt) < MAX_CHARS_PER_LINE: # This you need to figure out
        curr_txt += ' '  + word
    else:
        res_text.append(curr_txt)
        curr_txt = word

font = ImageFont.truetype("Arial.ttf", 14)
img = Image.new('RGB', (300, 200), color = (random.randint(0, 255),random.randint(0, 255), random.randint(0, 255)))
d = ImageDraw.Draw(img)
y = 0
for line in res_text:
    d.text((0, y), line, font=font) #draws text
    y += SINGLE_ROW_SPACE # Figure out what is the distance between two rows.

因为您正在处理字体,所以您需要了解 getsize 方法。这将帮助您了解如何拆分文本。

假设你有一个字符串:

def get_a_string():
    return "here is some string"

text = get_a_string()

现在,您还有一张图片,其宽度和高度为:

bgcolor = (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
img = Image.new('RGB', (300, 200), color=bgcolor)

# Read the size tuple, unpack into height, width
img_height, img_width = img.size

如果文字太长,您应该缩短它:

font = ImageFont.truetype("Arial.ttf", 14)

text_height, text_width = font.getsize(text)

if text_width > (0.95 * img_width):
    # Not enough room. Break the text
    lines = split_by_words()

如何缩短它?首先,尝试使用单词边界:

def split_by_words(text):
    text = text.rstrip() # Strip trailing whitespace

    words = text.split()
    lines = []

    while words:
        # I use None instead of '' to allow for leading spaces
        line = None

        for i, word in enumerate(words):
            new_line = word if line is None else line + ' ' + word
            (h, w) = font.getsize(new_line)

            if w > img_width:
                # New line won't fit? Break, keeping old line value.
                break
            else:
                # Still fits? Save it!
                line = new_line

        if i == 0:
            # First word was too long. Try character-by-character
            lines.extend(split_by_character(words[0]))
            # TODO: You might want to put lines[-1] into words[0] to join long first word
            # remainder with short second word.
            words = words[1:]
        else:
            lines.append(line)
            words = words[i:]

    return lines

这将多次调用 getsize,这可能会很昂贵。 (或者不是:如果你从猫的照片中生成模因,它可能不会花费太多,因为文本很短。如果你正在编写文字处理器,请注意!)

另一种方法是计算原始文本的大小,然后假设所有字符的宽度相同,并根据图像大小与文本大小的比率猜测应该在哪里拆分:

th, tw = font.getsize(text)
ih, iw = img.size

ratio = iw / tw  # 300 / 622, say
split_pos = int(len(text) * ratio)  # 0.51 * text len, about halfway along

line1 = text[:split_pos]

if font.getsize(line1) > iw:
    while True:
        split_pos -= 1
        line1 = line1[:-1]
        if font.getsize(line1) <= iw:
            break
else: # too short
    while True:
        line1 += text[split_pos]
        if font.getsize(line1) > iw:
            line1 = line1[:-1]
            break
        split_pos += 1

请注意,这是基于字符的,而不是基于单词的,所以有点糟糕。而且由于大多数字体都是成比例的,所以比例可能是错误的。您可以使用一些启发式方法来调整它,包括一组内置的宽度假设。 (只计算一次所有字母的宽度,将它们存储在 table 中,并假设所有字体都使用该宽度 - 仍然错误,但通常比比率方法更好,更快!)