Center-/middle-align text with PIL?

后端 未结 5 1006
长情又很酷
长情又很酷 2020-12-04 07:50

How would I center-align (and middle-vertical-align) text when using PIL?

相关标签:
5条回答
  • Use the textsize method (see docs) to figure out the dimensions of your text object before actually drawing it. Then draw it starting at the appropriate coordinates.

    0 讨论(0)
  • 2020-12-04 08:00

    The PIL docs for ImageDraw.text are a good place to start, but don't answer your question.

    Below is an example of how to center the text in an arbitrary bounding box, as opposed to the center of an image. The bounding box is defined as: (x1, y1) = upper left corner and (x2, y2) = lower right corner.

    from PIL import Image, ImageDraw, ImageFont
    
    # Create blank rectangle to write on
    image = Image.new('RGB', (300, 300), (63, 63, 63, 0))
    draw = ImageDraw.Draw(image)
    
    message = 'Stuck in\nthe middle\nwith you'
    
    bounding_box = [20, 30, 110, 160]
    x1, y1, x2, y2 = bounding_box  # For easy reading
    
    font = ImageFont.truetype('Consolas.ttf', size=12)
    
    # Calculate the width and height of the text to be drawn, given font size
    w, h = draw.textsize(message, font=font)
    
    # Calculate the mid points and offset by the upper left corner of the bounding box
    x = (x2 - x1 - w)/2 + x1
    y = (y2 - y1 - h)/2 + y1
    
    # Write the text to the image, where (x,y) is the top left corner of the text
    draw.text((x, y), message, align='center', font=font)
    
    # Draw the bounding box to show that this works
    draw.rectangle([x1, y1, x2, y2])
    
    image.show()
    image.save('text_center_multiline.png')
    

    The output shows the text centered vertically and horizontally in the bounding box.

    Whether you have a single or multiline message no longer matters, as PIL incorporated the align='center' parameter. However, it is for multiline text only. If the message is a single line, it needs to be manually centered. If the message is multiline, align='center' does the work for you on subsequent lines, but you still have to manually center the text block. Both of these cases are solved at once in the code above.

    0 讨论(0)
  • 2020-12-04 08:03

    Here is some example code which uses textwrap to split a long line into pieces, and then uses the textsize method to compute the positions.

    from PIL import Image, ImageDraw, ImageFont
    import textwrap
    
    astr = '''The rain in Spain falls mainly on the plains.'''
    para = textwrap.wrap(astr, width=15)
    
    MAX_W, MAX_H = 200, 200
    im = Image.new('RGB', (MAX_W, MAX_H), (0, 0, 0, 0))
    draw = ImageDraw.Draw(im)
    font = ImageFont.truetype(
        '/usr/share/fonts/truetype/msttcorefonts/Arial.ttf', 18)
    
    current_h, pad = 50, 10
    for line in para:
        w, h = draw.textsize(line, font=font)
        draw.text(((MAX_W - w) / 2, current_h), line, font=font)
        current_h += h + pad
    
    im.save('test.png')
    

    enter image description here

    0 讨论(0)
  • 2020-12-04 08:05

    One shall note that the Draw.textsize method is inaccurate. I was working with low pixels images, and after some testing, it turned out that textsize considers every character to be 6 pixel wide, whereas an I takes max. 2 pixels and a W takes min. 8 pixels (in my case). And so, depending on my text, it was or wasn't centered at all. Though, I guess "6" was an average, so if you're working with long texts and big images, it should still be ok.

    But now, if you want some real accuracy, you better use the getsize method of the font object you're going to use:

    arial = ImageFont.truetype("arial.ttf", 9)
    w,h = arial.getsize(msg)
    draw.text(((W-w)/2,(H-h)/2), msg, font=arial, fill="black")
    

    As used in Edilio's link.

    0 讨论(0)
  • 2020-12-04 08:15

    Use Draw.textsize method to calculate text size and re-calculate position accordingly.

    Here is an example:

    from PIL import Image, ImageDraw
    
    W, H = (300,200)
    msg = "hello"
    
    im = Image.new("RGBA",(W,H),"yellow")
    draw = ImageDraw.Draw(im)
    w, h = draw.textsize(msg)
    draw.text(((W-w)/2,(H-h)/2), msg, fill="black")
    
    im.save("hello.png", "PNG")
    

    and the result:

    If your fontsize is different, include the font like this:

    myFont = ImageFont.truetype("my-font.ttf", 16)
    draw.textsize(msg, font=myFont)
    
    0 讨论(0)
提交回复
热议问题