1

What I wanted to do is a function that by given an image width and height and text will fit the text into the image with a perfect font size. I achieved this by trial-error method. Here's my code:

# -*- coding: utf-8 -*-

from PIL import ImageFont

def get_total_area(width, height):
    return width * height
def get_font(name, size):
    return ImageFont.truetype(font, size)

def fit(width, height, font, size, text):
    font = get_font(font, size)
    text_w, text_h = font.getsize(text)
    text_area = get_total_area(text_w, text_h)
    total_area = get_total_area(width, height)
    if total_area>text_area:
        action(size)
        return True
    else:
        return size
def action(size):
    print("Succes!")
counter = 0
text = """One way of doing it is to pass the text with a default font size of, say 20, to imagettfbbox and retrieve the width from it. You can then calculate how much smaller or bigger the text should be to fit the size you want by calculating a scale factor:"""
size = 60
font = 'my-font.ttf'
while True:
    counter += 1
    ans = fit(500, 500, font, size, text)
    if ans == True: 
        break
    else:
        size = ans
        size -=1
print("Font_Size: {}".format(size))
print("Repetitions: {} ".format(counter))

In this specific example (from my code above) it takes 16 repetitions and the perfect font size is 45. Now, investigating a little in StackOverflow I found questions regarding the same problem and one This solutions called my attention: https://stackoverflow.com/a/10689312/5106998

So I modified my code as follows:

# -*- coding: utf-8 -*-

from PIL import ImageFont

def get_total_area(width, height):
    return width * height
def get_font(name, size):
    return ImageFont.truetype(font, size)

def fit(width, height, font, size, text):
    font = get_font(font, size)
    text_w, text_h = font.getsize(text)
    text_area = get_total_area(text_w, text_h)
    total_area = get_total_area(width, height)
    if total_area>text_area:
        action(size)
        return True
    else:
        scale = total_area/text_area
        size = int(round(size*scale))
        return size
def action(size):
    print("Succes!")
counter = 0
text = """One way of doing it is to pass the text with a default font size of, say 20, to imagettfbbox and retrieve the width from it. You can then calculate how much smaller or bigger the text should be to fit the size you want by calculating a scale factor:"""
size = 60
font = 'my-font.ttf'
while True:
    counter += 1
    ans = fit(500, 500, font, size, text)
    if ans == True: 
        break
    else:
        size = ans
        size -=1
print("Font_Size: {}".format(size))
print("Repetitions: {} ".format(counter))

The second code works but not as expected: It only takes 2 repetitions to conclude that the perfect font size is 34 (We know already that it is actually 45). But obviously this second code, taking into consideration a scale factor runs a lot faster.

My question is: where is the mistake? Or is this the best approximation that we can get using this method? Do you have any other ideas to tackle down this problem?

I don't want a perfect font size at once, what I want is to reduce the repetitions.

Community
  • 1
  • 1
gglasses
  • 826
  • 11
  • 30
  • I would make a set of strings, long, short, in other Latin-alphabet languages, and test the algorithm across all of them to get my "best" and "fastest" numbers. With one string you risk getting the best algorithm just for that string. (Nice problem!) – cphlewis Jul 14 '15 at 17:57
  • @cphlewis the thing is that the width and height of the image will also be variable. For the sake of the question I let them be fixed but in my application those specs are variable and so are the strings. I thought maybe creating a database with like pre-processed -short and long - strings. Is that what you meant? And yes, it's a nice problem right there! – gglasses Jul 14 '15 at 18:11
  • I meant, before deciding that one algorithm was better than another (as you do in your question) I'd run it on wildly differing strings. Maybe you do, it's just not evident in your question when you say 45 is better than 34. There might not be an algorithm that's best for all strings. – cphlewis Jul 14 '15 at 21:26
  • Have you tried doubling/bisection as your search method, rather than `-=`? E.g., try 12, too small, 24, too big; then try (12 + 24)/2.; keep testing the midpoint of too big/too small. – cphlewis Jul 14 '15 at 21:30

0 Answers0