Trato de agregar texto en la parte inferior de la imagen y, de hecho yo lo he hecho, pero en el caso de mi texto es más larga que el ancho de la imagen se corta por ambos lados, para simplificar, me gustaría texto en varias líneas si es más larga que el ancho de la imagen. Aquí está mi código:

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')
InformationsquelleAutor user985541 | 2011-10-08

6 Comentarios

  1. 42

    Usted podría utilizar textwrap.envuelva para romper text en una lista de cadenas, cada uno, en la mayoría de los width caracteres:

    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
    • Muchas gracias! sólo tienes que copiar y pegar y funciona como un encanto. Eres el mejor 🙂
    • ¿Qué 40 representan?
    • representa el número máximo de caracteres. Lo que significa que se permiten un máximo de 40 caracteres antes de que se ajusta a una nueva línea. Pero si una palabra es de 10 caracteres y, a continuación, el siguiente es el 31, se ajustará a la derecha después de la primera palabra, ya que no puede caber la primera y la segunda palabra en la línea.
    • cómo hacer esto basado en píxeles? No siempre estamos tratando con monospace
    • Si desea obtener el tamaño de una cadena de texto que puede utilizar PIL.ImageDraw.ImageDraw.textsize que devuelve una tupla de (anchura, altura) en píxeles. Usted podría utilizar este implemento una solución que intenta la cadena más larga posible antes de que el ancho es de más de un cierto umbral
  2. 9

    La aceptada respuesta ajusta el texto sin la medición de la fuente (un máximo de 40 caracteres, no importa qué el tamaño de la fuente y el ancho del cuadro es), por lo que los resultados son aproximados y pueden fácilmente contra sobrellenado o material de la caja.

    Aquí es una simple biblioteca que resuelve el problema correctamente:
    https://gist.github.com/turicas/1455973

    • esta es la mejor respuesta. Debemos tratar de cambiar aceptado responder porque fue aceptada hace 6 años..
  3. 2

    Todas las recomendaciones acerca de textwrap uso de falla para determinar el ancho correcto para no monoespaciada fuentes (como Arial, utilizado en el tema de código de ejemplo).

    Que he escrito simple clase auxiliar para ajustar el texto con respecto a la verdadera fuente de las letras de tamaño:

    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)

    Ejemplo de uso:

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

    Probablemente no es super-rápido, ya que representa todo el texto palabra por palabra, para determinar las palabras ancho. Pero para la mayoría de los casos se debe ACEPTAR.

  4. 0

    Para un trabajo completo ejemplo de uso de unutbu‘s truco (probado con Python 3.6 y Almohada 5.3.0):

    from PIL import Image, ImageDraw, ImageFont
    import textwrap
    def draw_multiple_line_text(image, text, font, text_color, text_start_height):
    '''
    From unutbu on [python PIL draw multiline text on image](https://stackoverflow.com/a/7698300/395857)
    '''
    draw = ImageDraw.Draw(image)
    image_width, image_height = image.size
    y_text = text_start_height
    lines = textwrap.wrap(text, width=40)
    for line in lines:
    line_width, line_height = font.getsize(line)
    draw.text(((image_width - line_width) / 2, y_text), 
    line, font=font, fill=text_color)
    y_text += line_height
    def main():
    '''
    Testing draw_multiple_line_text
    '''
    #image_width
    image = Image.new('RGB', (800, 600), color = (0, 0, 0))
    fontsize = 40  # starting font size
    font = ImageFont.truetype("arial.ttf", fontsize)
    text1 = "I try to add text at the bottom of image and actually I've done it, but in case of my text is longer then image width it is cut from both sides, to simplify I would like text to be in multiple lines if it is longer than image width."
    text2 = "You could use textwrap.wrap to break text into a list of strings, each at most width characters long"
    text_color = (200, 200, 200)
    text_start_height = 0
    draw_multiple_line_text(image, text1, font, text_color, text_start_height)
    draw_multiple_line_text(image, text2, font, text_color, 400)
    image.save('pil_text.png')
    if __name__ == "__main__":
    main()
    #cProfile.run('main()') # if you want to do some profiling

    Resultado:

    python PIL dibujar varias líneas de texto en la imagen

  5. -1
    text = textwrap.fill("test ",width=35)
    self.draw.text((x, y), text, font=font, fill="Black")
    • Bienvenido ENTONCES! Puede usted por favor sea tan amable de explicar cómo resuelve la pregunta? Un poco más de texto sería recorrer un largo camino para otros usuarios.
    • Esta es una versión concisa de @unutbu la respuesta. Es el mejor código sugerido actualmente, y con un poco más de explicación, debería ser aceptado respuesta.
  6. -3

    Usted podría utilizar PIL.ImageDraw.Draw.multiline_text().

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

    Que incluso spacing o align utilizando el mismo param nombres.

    • No funciona, yo sólo lo intentó. ¿Puede explicar el caso de uso? Pasé una muy larga cadena a esto como text, y no se ajusta a la línea siguiente.
    • ¿el uso de los saltos en la cuerda? Por ejemplo: «Lorem Ipsum es simplemente el maniquí \n texto de la imprenta \n y la composición de la industria.»
    • El texto viene de la entrada del usuario, y como tal no está en uso se rompe para especificar la línea siguiente. Voy a tener que controlar el desbordamiento de mí mismo.
    • El primer argumento de multiline_text está documentado como «xy – esquina Superior izquierda del texto». Usted tiene que hacer el ajuste de línea de sí mismo.

Dejar respuesta

Please enter your comment!
Please enter your name here