ImageFont Module

doc:http://pillow.readthedocs.io/en/4.1.x/reference/ImageFont.html, http://effbot.org/imagingbook/imagefont.htm

Using TrueType (or OpenType) Fonts

The official documents says:

Starting with version 1.1.4, PIL can be configured to support TrueType and OpenType fonts (as well as other font formats supported by the FreeType library). For earlier versions, TrueType support is only available as part of the imToolkit package

If you want to use TrueType (or OpenType) fonts, normally, the only thing what you need is to know truetype factory function.

truetype factory function

doc:http://pillow.readthedocs.io/en/4.1.x/reference/ImageFont.html#PIL.ImageFont.truetype, http://effbot.org/imagingbook/imagefont.htm#tag-ImageFont.truetype

The example for the font having various faces:

# This example is for Windows.
from PIL import Image, ImageDraw, ImageFont

pangram = "Pack my box with five dozen liquor jugs."

fonts = [
    ImageFont.truetype('gulim', 28, index=i)
    for i in range(4)]
texts = [
    str(font.getname()) + ": " + pangram
    for font in fonts
    ]
#
size = [0, 0]
for s in [fonts[i].getsize(txt) for txt in texts]:
    size = [max(size[0], s[0]), size[1] + s[1]]
#
img = Image.new("L", size, 255)
draw = ImageDraw.Draw(img)
y = 0
for font, txt in zip(fonts, texts):
    draw.text((0, y), txt, font=font, fill=0)
    y += font.getsize(txt)[1]
#img.show()
img.save("result/ImageFont_gulim_01.png")
del draw

ImageFont.res1

This example draws a character using the Microsoft Symbol font, which uses the “symb” encoding and characters in the range 0xF000 to 0xF0FF:

# -*- coding: utf-8 -*-
# This example is for Windows.
#
# This example draws a character using the Microsoft Symbol font,
# which uses the "symb" encoding and characters
# in the range 0xF000 to 0xF0FF.
#
from __future__ import unicode_literals
from textwrap import TextWrapper
from PIL import Image, ImageDraw, ImageFont
try:
    unichr(0)
except:
    unichr = chr
txtwrap = TextWrapper(width=32).fill

font = ImageFont.truetype("symbol.ttf", 35, encoding="symb")
txt = txtwrap("".join([unichr(0xF000 + i) for i in range(0x100)]))
img = Image.new("L", (900, 360), 255)
draw = ImageDraw.Draw(img)
draw.multiline_text((0, 0), txt, font=font)
#img.show()
img.save("result/ImageFont_symbol_01.png")
del draw

ImageFont.res2

The other practical examples are found at ImageDraw.text, etc.

class FreeTypeFont

Note

Note that this class is desired not to create explicitly. You should use truetype factory function to create this object.

When you want to use text() (or multiline_text()), you might need to load fonts, and if you want to use TrueType or OpenType fonts, you will have to call truetype. Because the truetype factory function requests the name of the font file, so you may not know what the font file you want. You can show the family and style from the font file like this:

>>> from glob import iglob
>>> from PIL import ImageFont
>>>
>>> ttf = ImageFont.truetype(font="c:/Windows/Fonts/cour.ttf")  # returns new FreeTypeFont object
>>> ttf.getname()  # returns `family' and `type'.
('Courier New', 'Regular')
>>>
>>> for fn in iglob("c:/Windows/Fonts/*.*"):
...     try:
...         ttf = ImageFont.truetype(font=fn)
...         print("FT {}: {}".format(fn, ttf.getname()))
...     except Exception as e:
...         print("XX {}: {}".format(fn, e))
...
XX c:/Windows/Fonts\8514fix.fon: invalid pixel size
XX c:/Windows/Fonts\8514fixe.fon: invalid pixel size
   -- (snip) --
FT c:/Windows/Fonts\ahronbd.ttf: ('Aharoni', 'Bold')
FT c:/Windows/Fonts\andlso.ttf: ('Andalus', 'Regular')
   -- (snip) --
FT c:/Windows/Fonts\CENTURY.TTF: ('Century', 'Regular')
XX c:/Windows/Fonts\cga40737.fon: invalid pixel size
   -- (snip) --
FT c:/Windows/Fonts\consola.ttf: ('Consolas', 'Regular')
FT c:/Windows/Fonts\consolab.ttf: ('Consolas', 'Bold')
FT c:/Windows/Fonts\consolai.ttf: ('Consolas', 'Italic')
FT c:/Windows/Fonts\consolaz.ttf: ('Consolas', 'Bold Italic')
   -- (snip) --

You might want to know available faces, you can show up these like this:

>>> from glob import iglob
>>> from fnmatch import fnmatch
>>> from PIL import ImageFont
>>>
>>> for fn in iglob("c:/Windows/Fonts/*.tt*"):
...     if not fnmatch(fn, "*.tt[cf]"):
...         continue
...     try:
...         for i in range(5):
...             ttf = ImageFont.truetype(font=fn, index=i)
...             print("{} (face={}): {}".format(fn, i, ttf.getname()))
...     except IOError as e:
...         pass
...
c:/Windows/Fonts\ahronbd.ttf (face=0): ('Aharoni', 'Bold')
c:/Windows/Fonts\andlso.ttf (face=0): ('Andalus', 'Regular')
    --- (snip) ---
c:/Windows/Fonts\batang.ttc (face=0): ('Batang', 'Regular')
c:/Windows/Fonts\batang.ttc (face=1): ('BatangChe', 'Regular')
c:/Windows/Fonts\batang.ttc (face=2): ('Gungsuh', 'Regular')
c:/Windows/Fonts\batang.ttc (face=3): ('GungsuhChe', 'Regular')
    --- (snip) ---

Though the font_variant method is not visible in the official documents, but it might be sometime useful:

>>> from PIL import Image, ImageDraw, ImageFont
>>>
>>> gulim_face0 = ImageFont.truetype("gulim", 16)
>>> gulim_face0.getsize("aaa")
(28, 14)
>>> gulim_face0.font_variant(index=1).getname()
('GulimChe', 'Regular')
>>> gulim_face0.font_variant(index=2).getname()
('Dotum', 'Regular')
>>> gulim_face0.font_variant(index=3).getname()
('DotumChe', 'Regular')
>>>
>>> # or...
>>> gulim_face0.font_variant(size=32).getsize("aaa")
(54, 28)

You can also get the metrics of the font object, but normally you don’t need to care about it. :

>>> from PIL import ImageFont
>>> ttf = ImageFont.truetype("CENTURY.TTF")
>>> ttf.getmetrics()  # returns (ascent, descent)
(10, 2)

The getsize method of this class can calculate the size of given text:

>>> from PIL import Image, ImageDraw, ImageFont
>>>
>>> text = "Jackdaws love my big sphinx of quartz."
>>>
>>> ttf = ImageFont.truetype("CENTURY.TTF", 64)
>>> ttf.getsize(text)
(1163, 75)

But note that you can use ImageDraw.textsize and ImageDraw.multiline_textsize. Especially for the multiline text, it is better to use ImageDraw.multiline_textsize rather than getsize, because the getsize method of this class is not what you want.:

>>> from PIL import Image, ImageDraw, ImageFont
>>>
>>> text = """\
... Pack my box with five dozen liquor jugs.
... Jackdaws love my big sphinx of quartz.
... The five boxing wizards jump quickly.
... How vexingly quick daft zebras jump!
... Bright vixens jump; dozy fowl quack.
... Sphinx of black quartz, judge my vow.
... """
>>>
>>> ttf = ImageFont.truetype("CENTURY.TTF", 64)
>>> ttf.getsize(text)
(7088, 75)
>>> img = Image.new("L", (1200, 500))
>>> dctx = ImageDraw.Draw(img)
>>> dctx.multiline_textsize(text, font=ttf)
(1191, 450)

Though the getoffset method is undocumented, but it should be easy to know about the meaning of the getoffset method by the following demonstration:

# -*- coding: utf-8 -*-
from PIL import Image, ImageDraw, ImageFont

iw, ih = 500, 120
text = "although"
img = Image.new("RGB", (iw, ih), color="white")
dctx = ImageDraw.Draw(img)

# courbi.ttf: Courier New Bold Italic (on Microsoft Windows)
ttf = ImageFont.truetype("courbi.ttf", 100)
w, h = ttf.getsize(text)
off_x, off_y = ttf.getoffset(text)
mx, my = (iw - w) // 2, (ih - h) // 2

dctx.line(((mx + off_x, my + off_y), (iw - mx, my + off_y)),
          fill="blue")

dctx.rectangle(((mx, my), (iw - mx, ih - my)), outline="red")
dctx.text((mx, my), text, font=ttf, fill="black")
#img.show()
img.save("result/ImageFont_getoffset_01.png")
del dctx

ImageFont.getoffset.res1

The getmask method returns the rectangle Image object having the size (font.getsize()[0] - font.getoffset()[0], font.getsize()[1] - font.getoffset()[1], but you ordinally don’t need this method, furthermore this might not be what you want if the text has multiline. This method is internally used by ImageDraw.text and ImageDraw.multiline_text.

Using Bitmap Fonts

A long time ago, the bitmap font was the world. All the fonts were based on bitmap. That is, those were fixed sized image, and were not scalable.

Eventually a storm of a scalable font came and the world changed. No more people might need the bitmap fonts.

The only way to deal with the fonts for PIL was also to use bitmap fonts. This section explains that traditional way how to deal with the fonts in PIL. But if you have no special reason not to use scalable font, you should wonder why not to use the truetype.

load_default

You can always use the “better than nothing” font, it is surely better than nothing:

from PIL import Image, ImageFont, ImageDraw

text = "better than nothing"
font = ImageFont.load_default()
img = Image.new("L", font.getsize(text), 255)
dctx = ImageDraw.Draw(img)
dctx.text((0, 0), text, font=font)
del dctx
img = img.resize((img.width * 8, img.height * 8))
#img.show()
img.save("resut/ImageFont_load_default_01.png")

ImageFont_load_default.res1

pilfont utility script

If you are not satisfied with the font provided by load_default, you might have to setup the PIL fonts to your system.

Of course, the first thing you need to do is to get the BDF (or PCF) font descriptors (X window font formats) if you do not have those. However, many of recent systems are considered to have a tendency not to install them by default, in addition, those systems’s package managers might even not manage bitmap fonts. If so, it is a good opportunity to ask yourself if you really have to do this.

Unless using truetype, PIL uses its own font file format to store bitmap fonts. So, the second thing you need to do is to compile the BDF (or PCF) font descriptors (X window font formats) by using pilfont utility script:

me@host: ~$ pilfont.py
PILFONT 0.4 -- PIL font compiler.

Usage: pilfont fontfiles...

Convert given font files to the PIL raster font format.
This version of pilfont supports X BDF and PCF fonts.
me@host: ~$ ls
FuBar-Bold.bdf
FuBar-BoldOblique.bdf
FuBar-ExtraLight.bdf
FuBar-Oblique.bdf
FuBar.bdf
me@host: ~$ pilfont.py *.bdf
FuBar-Bold.bdf... OK
FuBar-BoldOblique.bdf... OK
FuBar-ExtraLight.bdf... OK
FuBar-Oblique.bdf... OK
FuBar.bdf... OK
me@host: ~$ ls
FuBar-Bold.bdf
FuBar-Bold.pbm
FuBar-Bold.pil
FuBar-BoldOblique.bdf
FuBar-BoldOblique.pbm
FuBar-BoldOblique.pil
FuBar-ExtraLight.bdf
FuBar-ExtraLight.pbm
FuBar-ExtraLight.pil
FuBar-Oblique.bdf
FuBar-Oblique.pbm
FuBar-Oblique.pil
FuBar.bdf
FuBar.pbm
FuBar.pil

Now you can place them in the right place. The meaning of “the right place” is where the load or load_path function can refer.

load, load_path

Once you place the PIL fonts in the right place, you can:

from PIL import Image, ImageFont, ImageDraw

text = "Are you hangry?"
font = ImageFont.load("/path/to/FuBar-Bold.pil")
#font = ImageFont.load_path("FuBar-Bold.pil")  # searches for a bitmap font along the Python path.
img = Image.new("L", font.getsize(text), 255)
dctx = ImageDraw.Draw(img)
dctx.text((0, 0), text, font=font)
del dctx
img = img.resize((img.width * 8, img.height * 8))
img.show()

class ImageFont

To create this object, use load, load_path, or load_default.

This class exposes only two methods, getsize and getmask. These two methods are the same as those in FreeTypeFont. See FreeTypeFont.