13

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
  • 4,643
  • 10
  • 31
  • 58
  • 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

25

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')
Pikamander2
  • 5,875
  • 3
  • 42
  • 57
EquipDev
  • 4,643
  • 10
  • 31
  • 58
  • According to this website the PIL module is not supported in Python 3 yet http://www.pythonware.com/products/pil/. So I cannot get the above to work. – bobsmith76 Jul 28 '17 at 16:19
  • 2
    This works for me on Python3 by installing Pillow (the modern, updated version of PIL). https://pillow.readthedocs.io has installation instructions ("pip install Pillow"). – mcherm Sep 14 '17 at 12:56
  • 1
    Works in Visual Studio 2019 as well after installing the Pillow module, thanks guys! – Xiokraze Aug 10 '19 at 14:09
  • 1
    Works in Python 3.7.3 – JayJay123 Sep 16 '19 at 22:56
11

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
  • 43,220
  • 16
  • 78
  • 90
  • 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