Kerning with Python Imaging Library(PIL)

I was working on a problem that requires excellent precision pixel by pixel, so I needed to have the ability to manipulate text in more ways than what PIL provides. Especially with regard to kerning. There is a feature that allows you to disable kerning, but not control the amount which is obviously incomplete.

This problem was made more challenging because of bugs in PIL that relate to accurately measuring the size of text. There are many posts about this problem but the most useful information is a Stack Overflow post here, How to get the font pixel height using PIL’s ImageFont class? and a blog article, How to properly calculate text size in PIL images.

My code is for my own use but if someone is having similar issues, I’m sure that it can easily be adapted for your own needs.

My key functions are:

            def get_text_width(text_string, font):
                return font.getmask(text_string).getbbox()[2]
            def kern(name, draw_object, y, space, font, fill):
                chars = [char for char in name]
                total_width = 0
                for char in chars:
                    width_text = get_text_width(char, font)
                    total_width += (width_text + int(space))
                __, height_text = draw_object.textsize(name, font)
                __, offset_y = font.getoffset(name)
                height_text += offset_y
                width_adjuster = 0
                for char in chars:
                    width_text = get_text_width(char, font)
                    top_left_x = (473 / 2 - total_width / 2) + width_adjuster
                    top_left_y = (40 / 2 - height_text / 2) + y
                    xy = top_left_x, top_left_y
                    width_adjuster += width_text + int(space)
                    print(f"char:{char},width_text:{width_text},xy: {xy},width_adjuster:{width_adjuster}")
                    draw_object.text(xy, char, font=font, fill=fill)

It gives a nice output below. However, it’s not entirely precise. The number of pixels between letters will vary slightly with different fonts. I have not found a way to standardize this, so I’ve just accepted the fact when I enter a value for kerning into my GUI, it is just a scalar relative to the font, not the number of pixels

Kerning with PIL