Как разделить неявно заданную строку на две или более строк? - PullRequest
0 голосов
/ 09 февраля 2019

В настоящее время я работаю с ботом Python, который выводит изображение с текстом, но я обнаружил, что часто текст слишком длинный, чтобы быть представленным.Таким образом, я решил разделить строку на две строки, чтобы она могла вписаться в мой образ.Я использую Pillow 5.1 для работы с изображениями.

Я неофит в программировании на Python, и я попытался найти способ разбить строку Python на две или более строк.К сожалению, все результаты, по-видимому, имеют дело только с явно заданными строками (например, «строка»).

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

Actual results

Ожидаемые результаты: текстследует перейти к следующей строке.

Ответы [ 2 ]

0 голосов
/ 09 февраля 2019

Поскольку вы имеете дело со шрифтами, вам нужно знать о методе 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

Обратите внимание, что это символна основе, а не на основе слов, так что это отстой.А поскольку большинство шрифтов пропорциональны, соотношение, вероятно, неправильное.Вы можете настроить его, используя некоторые эвристические методы, включая встроенный набор допущений по ширине.(просто вычислите ширину всех букв за один раз, сохраните их в таблице и предположите, что все шрифты используют эту ширину - все еще неправильно, но обычно это лучшее приближение, чем метод отношения, и быстрее!)

0 голосов
/ 09 февраля 2019

Я не уверен, что текстовая оболочка может помочь в вашем случае, так как вам нужно рисовать каждую строку отдельно.

Похоже, вы должны рассчитать максимальную длину символов в одной строке, а затем выполните:

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.
...