16

If the font, e.g. "Times New Roman", and size, e.g. 12 pt, is known, how can the length of a string, e.g. "Hello world" be calculated in pixels, maybe only approximately?

I need this to do some manual right alignment of text shown in an Windows application, so I need to adjust the number spaces to get the alignment.

EquipDev
  • 5,573
  • 10
  • 37
  • 63
  • 2
    See https://pillow.readthedocs.org/en/3.0.0/reference/ImageFont.html#PIL.ImageFont.PIL.ImageFont.ImageFont.getsize – Selcuk Mar 03 '16 at 12:16

2 Answers2

30

As of Pillow v9.2.0, .getsize is deprecated, and will be removed in Pillow v10 on 2023-07-01. Instead, use getbbox or getlength.

Tested in python 3.11.2, pillow v9.4.0

from PIL import ImageFont
font = ImageFont.truetype('times.ttf', 12)

left, top, right, bottom = font.getbbox('Hello world')
width = font.getlength('Hello world')

print(f'left: {left}, top: {top}, right: {right}, bottom: {bottom}, width: {width}')
[out]:
left: 0, top: 2, right: 58, bottom: 11, width: 57.0

Based on comment from @Selcuk, I found an answer as:

from PIL import ImageFont
font = ImageFont.truetype('times.ttf', 12)
size = font.getsize('Hello world')
print(size)

which prints (x, y) size as:

(58, 11)

Here it is as a function:

from PIL import ImageFont

def get_pil_text_size(text, font_size, font_name):
    font = ImageFont.truetype(font_name, font_size)
    size = font.getsize(text)
    return size

get_pil_text_size('Hello world', 12, 'times.ttf')
Trenton McKinney
  • 56,955
  • 33
  • 144
  • 158
EquipDev
  • 5,573
  • 10
  • 37
  • 63
13

An alternative is to ask Windows as follows:

import ctypes

def GetTextDimensions(text, points, font):
    class SIZE(ctypes.Structure):
        _fields_ = [("cx", ctypes.c_long), ("cy", ctypes.c_long)]

    hdc = ctypes.windll.user32.GetDC(0)
    hfont = ctypes.windll.gdi32.CreateFontA(points, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, font)
    hfont_old = ctypes.windll.gdi32.SelectObject(hdc, hfont)

    size = SIZE(0, 0)
    ctypes.windll.gdi32.GetTextExtentPoint32A(hdc, text, len(text), ctypes.byref(size))

    ctypes.windll.gdi32.SelectObject(hdc, hfont_old)
    ctypes.windll.gdi32.DeleteObject(hfont)

    return (size.cx, size.cy)

print(GetTextDimensions("Hello world", 12, "Times New Roman"))
print(GetTextDimensions("Hello world", 12, "Arial"))

This would display:

(47, 12)
(45, 12)
Martin Evans
  • 45,791
  • 17
  • 81
  • 97
  • Thanks; had to add `()` for print to use on Python 3, but otherwise it worked. But odd that there is the significant x-size difference between the two methods. – EquipDev Mar 03 '16 at 12:50
  • 1
    There are quite a few dimensions you can take from a given font, so I am guessing `getsize()` is using a different one. – Martin Evans Mar 03 '16 at 12:51
  • I'm getting AttributeError: module 'ctypes' has no attribute 'windll'. This is strange because when I hit 'w' after ctypes python shows that method in the popup box. – bobsmith76 Jul 28 '17 at 16:23
  • Which version of Python are you using? It works fine in 2.7.12. Note, it will only work on Windows. – Martin Evans Jul 28 '17 at 16:25