API Reference

Read, manipulate and write bitmap fonts in the Bitmap Distribution Format.

To get started, use bdflib.reader.read_bdf() to load a BDF file and create a bdflib.model.Font object, or just create one yourself from scratch.

Modify the font by tinkering with it directly, or by using the helpers in bdflib.effects and bdflib.glyph_combining.

When you’re done, you can use bdflib.writer.write_bdf() to write your font back out to a BDF file.

bdflib.model

Classes that represent a bitmap font, with its glyphs and metadata.

class bdflib.model.Font(name, ptSize, xdpi, ydpi)

Bases: object

Represents the entire font and font-global properties.

Parameters:
  • name (bytes) – The human-readable name of this font, ASCII encoded.
  • ptSize (int) – The nominal size of this font in PostScript points (1/72 of an inch).
  • xdpi (int) – The horizontal resolution of this font in dots-per-inch.
  • ydpi (int) – The vertical resolution of this font in dots-per-inch.

Instances of this class can be used like dict instances. bytes keys refer to the font’s properties and are associated with bytes instances, while int keys refer to the code-points the font supports, and are associated with Glyph instances.

>>> myfont = Font(
...     b"My Font",
...     ptSize=12,
...     xdpi=96,
...     ydpi=96,
... )
>>> myfont[b"POINT_SIZE"]
12
>>> a_glyph = myfont.new_glyph_from_data(
...     "capital A",
...     codepoint=65,
... )
>>> a_glyph == myfont[65]
True

Note

Some properties (the name, point-size and resolutions) are required, and although they can be examined via the dict interface, they cannot be removed with the del statement.

add_comment(comment)

Add one or more lines of text to the font’s comment field.

Parameters:comment (bytes) – Human-readable text to add to the comment, ASCII encoded. It may include newline characters.

The added text will begin on a new line.

codepoints()

Returns the codepoints that this font has glyphs for.

These codepoints can be used with the regular dict syntax to retrieve the associated glyphs

Returns:Supported codepoints
Return type:iterable of Glyph
copy()

Returns a deep copy of this font.

The new font, along with all of its properties and glyphs, may be modified without affecting this font.

Returns:A new, independent copy of this Font
Return type:Font
get_comments()

Retrieve the lines of the font’s comment field.

Returns:The comment text, ASCII encoded.
Return type:list of bytes
new_glyph_from_data(name, data=None, bbX=0, bbY=0, bbW=0, bbH=0, advance=0, codepoint=None)

Add a new Glyph to this font.

This method’s arguments are passed to the Glyph constructor.

If you include the codepoint parameter, the codepoint will be included in the result of codepoints() and you will be able to look up the glyph by codepoint later. Otherwise, it will only be available via the glyphs property.

Returns:the newly-created Glyph
Return type:Glyph
Raises:GlyphExists – if an existing glyph is already associated with the requested codepoint.
property_names()

Returns the names of this font’s properties.

These names can be used with the regular dict syntax to retrieve the associated value.

Returns:Property names
Return type:iterable of bytes
class bdflib.model.Glyph(name, data=None, bbX=0, bbY=0, bbW=0, bbH=0, advance=0, codepoint=None)

Bases: object

Represents a font glyph and associated properties.

Parameters:
  • name (bytes) – The name of this glyph, ASCII encoded.
  • data (iterable of bytes) – the bitmap data for the glyph, as ASCII encoded hexadecimal digits.
  • bbX (int) – The left-most edge of the glyph’s bounding box, in pixels.
  • bbY (int) – The bottom-most edge of the glyph’s bounding box, in pixels.
  • bbW (int) – The glyph’s bounding-box extends this many pixels right of bbX (must be >= 0).
  • bbH (int) – The glyph’s bounding-box extends this many pixels upward from bbY (must be >= 0).
  • advance (int) – After drawing this glyph, the next glyph will be drawn this many pixels to the right.
  • codepoint (int) – The Unicode codepoint that this glyph represents.

The glyph bitmap data is an iterable of bytes, where the first item represents the top row of pixels, the next item represents the next row of pixels, and so forth. There should be bbH items in total. Each item is one or more hexadecimal digits representing the pixels of that row: the first digit represents the leftmost four pixels (most significant bit to the left), the next digit represents the next four pixels, and so forth. If bbW is not an integer multiple of 4, the row should be padded with 0 bits.

For example, let’s say we want to render a capital A, like so:

.###.
#...#
#####
#...#

We encode that pattern in binary, pad to a multiple of four bits, and convert to hexadecimal:

>>> raw_data = [
...     0b01110000,
...     0b10001000,
...     0b11111000,
...     0b10001000,
... ]
>>> for each in raw_data:
...     print("{:X}".format(each))
70
88
F8
88

Therefore, to create a glyph representing that image:

>>> my_glyph = Glyph(
...     name=b"capital A",
...     data=[b'70', b'88', b'F8', b'88'],
...     bbW=5,
...     bbH=4,
...     advance=6,
...     codepoint=65,
... )

To aid debugging, the Glyph class has a __str__ method that renders the glyph on a grid, with the glyph origin marked:

>>> print(my_glyph)
|###.
#...#
#####
#---#

The glyph origin isn’t actually visible because it’s underneath the lower left corner of the glyph, but you can see the X axis across the bottom and the Y axis up the left.

advance

(int) How far to the right the next glyph should be drawn, in pixels.

data

(list of int) Glyph bitmap data.

Unlike the format passed to the constructor and returned from get_data(), this uses a more Python-friendly format:

>>> my_glyph = Glyph(
...     name=b"capital A",
...     data=[b'70', b'88', b'F8', b'88'],
...     bbW=5,
...     bbH=4,
... )
>>> for row in my_glyph.data:
...     print("{:05b}".format(row))
10001
11111
10001
01110

Each item of the .data property is a int bbW bits wide, representing the pixels of a single row. The first item in .data is the lowest row in the glyph, so that list indices increase in the same direction as pixel coordinates.

If you want to get the actual coordinates of the glyph’s drawn pixels (for example, to draw the glyph onto an image), the easiest way is to use a loop like this:

ink_coords = [
        (x, y)
        for x in range(my_glyph.bbW)
        for y in range(my_glyph.bbH)
        if my_glyph.data[y] & (1 << (my_glyph.bbW - x - 1)
]

You might also want to look at iter_pixels().

get_ascent()

Returns the distance from the Y axis to the highest point of the glyph.

This is zero if no part of the glyph is above the Y axis.

Returns:The ascent of this glyph.
Return type:int
get_bounding_box()

Returns the position and dimensions of the glyph’s bounding box.

Returns:The left, bottom, width and height of the bounding box, as passed to the constructor.
Return type:tuple of int, int, int, int
get_data()

Returns the glyph’s bitmap data.

This may use a different amount of padding than the value originally passed to the constructor, and hexadecimal digits may be upper-case instead of lower-case, but the meaning should be the same.

See also the iter_pixels() method, which gives you a bool for each pixel, and the underlying storage in the data property.

Returns:Hex-encoded bitmap data
Return type:list of bytes
get_descent()

Returns the distance from the Y axis to the lowest point of the glyph.

This is zero if no part of the glyph is below the Y axis.

Returns:The descent of this glyph.
Return type:int
iter_pixels()

Yields the state of pixels within the bounding box.

This method returns an iterable of bbH rows, from the top of the glyph (large X values) to the bottom (small X values). Each row is an iterable of bbW booleans, from left to right. Each boolean is True if that pixel should be drawn, and otherwise False.

Alternatively, you can obtain the glyph data in BDF format with get_data(), or access the raw bitmap via the data property.

Returns:the state of each pixel
Return type:iterable of iterable of bool
merge_glyph(other, atX, atY)

Draw another glyph onto this one at the given coordinates.

Parameters:
  • other (Glyph) – The other glyph to draw onto this one.
  • atX (int) – The other glyph’s origin will be placed at this X offset in this glyph.
  • atY (int) – The other glyph’s origin will be placed at this Y offset in this glyph.

This glyph’s bounding box will be stretch to include the area of the added glyph, but the advance will not be modified.

exception bdflib.model.GlyphExists

Bases: exceptions.Exception

Raised when creating a new glyph for a codepoint that already has one.

bdflib.reader

bdflib.reader.read_bdf(raw_iterable)

Read a BDF-format font from the given source.

Parameters:raw_iterable (iterable of bytes) – Each item should be a single line of the BDF file, ASCII encoded.
Returns:the resulting font object
Return type:Font

If you want to read an actual file, make sure you use the ‘b’ flag so you get bytes instead of text:

font = bdflib.reader.read_bdf(open(path, 'rb'))

bdflib.writer

bdflib.writer.write_bdf(font, stream)

Write a BDF-format font to the given stream.

Parameters:
  • font (Font) – The font to write to the given stream.
  • stream – The stream that will receive the font.

stream must be an object with at .write() method that takes a bytes. If you want to write to an actual file, make sure you use the ‘b’ flag:

bdflib.writer.write_bdf(font, open(path, 'wb'))

bdflib.effects

Automatically generate visual variants of a font.

bdflib.effects.embolden(font, maintain_spacing=True)

Create a bold version of a font by widening each glyph.

Parameters:
  • font (Font) – The font to embolden.
  • maintain_spacing (bool) – If true, each glyph’s advance width will be incremented, because a wider glyph needs more room.
Returns:

A copy of font, with each glyph emboldened.

Return type:

Font

To embolden a glyph, it is drawn over itself one pixel to the right, making each vertical stroke one pixel wider.

bdflib.effects.merge(base, custom)

Create a new font by choosing glyphs from two other fonts.

Parameters:
  • base (Font) – The lower-priority font.
  • custom (Font) – The higher-priority font.
Returns:

A font where each glyph comes from base or custom.

For any given codepoint, the resulting font will use the corresponding glyph from custom if there is one, falling back to the glyph from base. The new font’s properties and other metadata are all copied from custom.

bdflib.glyph_combining

Tools for building glyphs by combining other glyphs.

Unicode has a lot of “pre-combined” code-points that are the combination of a normal code-point and a combining code-point, like how U+014D LATIN SMALL LETTER O WITH MACRON is the combination of U+006F LATIN SMALL LETTER O with U+0304 COMBINING MACRON. Given glyphs for their individual components, glyphs for pre-combined code-points can be automatically generated.

An example of using this module to generate pre-combined glyphs:

decompositions = build_unicode_decompositions()
filler = FontFiller(myfont, decompositions)
filler.add_decomposable_glyphs_to_font()
class bdflib.glyph_combining.FontFiller(font, decompositions)

Bases: object

Build pre-combined glyphs from available component glyphs.

Parameters:
  • font (Font) – Any pre-combined glyphs will be added to this font.
  • decompositions – A dict mapping pre-combined characters to their components, as returned by build_unicode_decompositions().

Call add_decomposable_glyphs_to_font() on an instance of this class to add as many pre-combined glyphs as possible.

add_decomposable_glyphs_to_font()

Adds all the glyphs that can be built to the given font.

This calls add_glyph_to_font() for each key in the decompositions passed to the class constructor.

add_glyph_to_font(char)

Add the glyph representing char to the given font, if it can be built.

Parameters:char (unicode) – A single-codepoint Unicode string, whose glyph should be generated (if possible) and added to the font passed to the class constructor.
Returns:True if the font now contains a glyph for that character, False otherwise.

This method may return True if a glyph was generated, or if the font already contained the required glyph.

This method may return False if:

  • the decompositions passed to the class constructor do not include any components for char
  • the font passed to the class constructor is missing glyphs for one or more of char’s components, and they could not be generated
  • one of char’s components uses a combining class this code doesn’t understand
bdflib.glyph_combining.build_unicode_decompositions()

Returns a dictionary mapping unicode characters to their components.

Returns:a mapping from pre-combined characters to their components.
Return type:dict mapping unicode to list of tuple of unicode, int

Each key in the resulting dict is a single-character Unicode string, and each value is a list of single-character Unicode strings and their combining classes, the components of the key. For example, one of the items in the result should be:

u"\N{LATIN SMALL LETTER O WITH MACRON}": [
        (u"o", 0),
        (u"\N{COMBINING MACRON}", 230),
]

(where 0 indicates a regular base character, and 230 means the glyph is drawn above the base glyph. See https://www.unicode.org/reports/tr44/#Canonical_Combining_Class_Values for details.)

This function obtains information about combining characters from Python’s unicodedata standard library module. It also properly handles “soft-dotted” characters “i” and “j” where pre-combined glyphs should be built from the dotless versions of those characters.