Python PIL dessine du texte multiligne sur l'image

j'essaie d'ajouter du texte au bas de l'image et en fait je l'ai fait, mais dans le cas de mon texte est plus long que la largeur de l'image il est coupé des deux côtés, pour simplifier je voudrais que le texte soit en plusieurs lignes si elle est plus longue que la largeur de l'image. Voici mon code:

FOREGROUND = (255, 255, 255)
WIDTH = 375
HEIGHT = 50
TEXT = 'Chyba najwyższy czas zadać to pytanie na śniadanie n Chyba najwyższy czas zadać to pytanie na śniadanie'
font_path = '/Library/Fonts/Arial.ttf'
font = ImageFont.truetype(font_path, 14, encoding='unic')
text = TEXT.decode('utf-8')
(width, height) = font.getsize(text)

x = Image.open('media/converty/image.png')
y = ImageOps.expand(x,border=2,fill='white')
y = ImageOps.expand(y,border=30,fill='black')

w, h = y.size
bg = Image.new('RGBA', (w, 1000), "#000000")

W, H = bg.size
xo, yo = (W-w)/2, (H-h)/2
bg.paste(y, (xo, 0, xo+w, h))
draw = ImageDraw.Draw(bg)
draw.text(((w - width)/2, w), text, font=font, fill=FOREGROUND)


bg.show()
bg.save('media/converty/test.png')
27
demandé sur user985541 2011-10-08 20:34:17

5 réponses

Vous pouvez utiliser textwrap.wrap pause text dans une liste de chaînes, chacune au plus width caractères:

import textwrap
lines = textwrap.wrap(text, width=40)
y_text = h
for line in lines:
    width, height = font.getsize(line)
    draw.text(((w - width) / 2, y_text), line, font=font, fill=FOREGROUND)
    y_text += height
37
répondu unutbu 2015-04-17 09:50:53

la réponse acceptée enveloppe le texte sans mesurer la police (max 40 caractères, peu importe la taille de la police et la largeur de la boîte), de sorte que les résultats ne sont qu'approximatifs et peuvent facilement déborder ou sous-remplir la boîte.

Voici une bibliothèque simple qui résout le problème correctement: https://gist.github.com/turicas/1455973

5
répondu pryma 2017-09-15 17:31:55
text = textwrap.fill("test ",width=35)
self.draw.text((x, y), text, font=font, fill="Black")
0
répondu ice20000101 2016-05-20 22:02:14

Toutes les recommandations concernant textwrap l'utilisation ne permet pas de déterminer la largeur correcte pour les polices non-monospacées (comme Arial, utilisé dans le code d'exemple de sujet).

j'ai écrit la classe helper simple pour envelopper le texte concernant les vraies lettres de police de taille:

class TextWrapper(object):
    """ Helper class to wrap text in lines, based on given text, font
        and max allowed line width.
    """

    def __init__(self, text, font, max_width):
        self.text = text
        self.text_lines = [
            ' '.join([w.strip() for w in l.split(' ') if w])
            for l in text.split('\n')
            if l
        ]
        self.font = font
        self.max_width = max_width

        self.draw = ImageDraw.Draw(
            Image.new(
                mode='RGB',
                size=(100, 100)
            )
        )

        self.space_width = self.draw.textsize(
            text=' ',
            font=self.font
        )[0]

    def get_text_width(self, text):
        return self.draw.textsize(
            text=text,
            font=self.font
        )[0]

    def wrapped_text(self):
        wrapped_lines = []
        buf = []
        buf_width = 0

        for line in self.text_lines:
            for word in line.split(' '):
                word_width = self.get_text_width(word)

                expected_width = word_width if not buf else \
                    buf_width + self.space_width + word_width

                if expected_width <= self.max_width:
                    # word fits in line
                    buf_width = expected_width
                    buf.append(word)
                else:
                    # word doesn't fit in line
                    wrapped_lines.append(' '.join(buf))
                    buf = [word]
                    buf_width = word_width

            if buf:
                wrapped_lines.append(' '.join(buf))
                buf = []
                buf_width = 0

        return '\n'.join(wrapped_lines)

Exemple d'utilisation:

wrapper = TextWrapper(text, image_font_intance, 800)
wrapped_text = wrapper.wrapped_text()

ce n'est probablement pas super rapide, car il rend le texte entier mot par mot, pour déterminer la largeur des mots. Mais pour la plupart des cas, cela devrait être OK.

0
répondu Igor Pomaranskiy 2018-04-08 15:28:22

Vous pouvez utiliser PIL.ImageDraw.Draw.multiline_text().

draw.multiline_text((WIDTH, HEIGHT), TEXT, fill=FOREGROUND, font=font)

même spacing ou align en utilisant les mêmes noms param.

-2
répondu Zulfugar Ismayilzadeh 2017-04-11 09:08:15