ImageDraw - adapt Font Size dynamically to text length

这一生的挚爱 提交于 2021-01-29 10:52:29

问题


The font size has to dynamically adapt to different the text length, which can vary a lot. Currently I have a ton of code, basically manually checking how long the incoming text is and manually giving a font size for that.

There has to be a better solution. I don't think I can use ImageDraw.textsize.

Has anyone a solution for this kinda problem?

Thanks


回答1:


I don't know other method then ImageDraw.textsize() in for-loop which checks it for different font sizes.

Now I found that in version 8.0.0 they add ImageDraw.textbbox() which gives better results because it calculates also top margin which can be use to better calcualte position.

But it still needs for-loop to check it for different font sizes.


Result with textsize:

Result with textbbox (center vertically):


It is my example which calculate font size and box size to use it directly to calculate

from PIL import Image, ImageDraw, ImageFont

width = 300
height = 100

text = "Hello World"

font_name = 'Ubuntu-M'

# --- create image for text ---

img = Image.new('RGB', (width, height), (255, 255, 255))
draw = ImageDraw.Draw(img)

# --- calculate font size, box ---

# default values at start
font_size = None   # for font size
font = None        # for object truetype with correct font size
box = None         # for version 8.0.0

# test for different font sizes
for size in range(1, 500):

    # create new font
    new_font = ImageFont.truetype(font_name, size)
    
    # calculate bbox for version 8.0.0
    new_box = draw.textbbox((0, 0), text, new_font)  # need 8.0.0
    
    # `bbox` may have top/left margin so calculate real width/height
    new_w = new_box[2] - new_box[0]  # bottom-top
    new_h = new_box[3] - new_box[1]  # right-left
    #print(size, '|', new_w, new_h, '|', new_box)

    # if too big then exit with previous values
    if new_w > width or new_h > height:
        break
        
    # set new current values as current values
    font_size = size
    font = new_font
    box = new_box
    w = new_w
    h = new_h
    
# --- use it ---
    
print('font size:', font_size)
print('box:', box)

print('w <= width :', w, '<=', width)
print('h <= height:', h, '<=', height)

# calculate position (minus margins in box)
x = (width - w)//2 - box[0]   # minus left margin
y = (height - h)//2 - box[3]  # minus top margin
print('w,h (without margins):', w, h)
print('x,y (without margins):', x, y)

# draw it 
draw.text((x, y), text, (0, 0, 0), font)

# display result
img.show()

img.save('result-textbbox.png', 'png')

EDIT:

The same as function

from PIL import Image, ImageDraw, ImageFont

def get_font(img, text, font_name, width, height):
    # default values at start
    font_size = None   # for font size
    font = None        # for object truetype with correct font size
    box = None         # for version 8.0.0

    # test for different font sizes
    for size in range(1, 500):

        # create new font
        new_font = ImageFont.truetype(font_name, size)

        # calculate bbox for version 8.0.0
        new_box = draw.textbbox((0, 0), text, new_font)  # need 8.0.0

        # `bbox` may have top/left margin so calculate real width/height
        new_w = new_box[2] - new_box[0]  # bottom-top
        new_h = new_box[3] - new_box[1]  # right-left
        #print(size, '|', new_w, new_h, '|', new_box)

        # if too big then exit with previous values
        if new_w > width or new_h > height:
            break

        # set new current values as current values
        font_size = size
        font = new_font
        box = new_box
        w = new_w
        h = new_h

        # calculate position (minus margins in box)
        x = (width - w)//2 - box[0]   # minus left margin
        y = (height - h)//2 - box[1]  # minus top margin

    return font, font_size, box, w, h, x, y

# --- main ---

width = 300
height = 100

text = "World"

font_name = 'Ubuntu-M'

# --- create image for text ---

img = Image.new('RGB', (width, height), (200, 255, 255))
draw = ImageDraw.Draw(img)

# --- calculate font size, box ---

font, font_size, box, w, h, x, y = get_font(img, text, font_name, width, height)

# --- use it ---

print('font size:', font_size)
print('box:', box)

print('w <= width :', w, '<=', width)
print('h <= height:', h, '<=', height)

print('w,h (without margins):', w, h)
print('x,y (without margins):', x, y)

# draw it
draw.text((x, y), text, (0, 0, 0), font)

# display result
img.show()

img.save('result-textbbox.png', 'png')


来源:https://stackoverflow.com/questions/65494932/imagedraw-adapt-font-size-dynamically-to-text-length

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!