Vivid.BDF (vivid v1.0.0)

Copy Markdown View Source

Reads bitmap fonts in Adobe's Glyph Bitmap Distribution Format.

A BDF file is plain text, which makes it the most approachable font format there is. A header describes the font, and then each glyph is a block naming its codepoint, its box, how far the pen advances, and its rows of pixels in hexadecimal:

STARTCHAR A
ENCODING 65
DWIDTH 4 0
BBX 4 6 0 -1
BITMAP
40
A0
E0
A0
A0
00
ENDCHAR

ENCODING is a real codepoint, which is a step up on the Hershey fonts, where the mapping from character to glyph is positional and has to be supplied separately.

Rows are listed from the top of the glyph down, and each is padded to a whole number of bytes, so a glyph 4 pixels wide still spends 8 bits per row and uses only the high 4. BBX gives the size of the box and where it sits relative to the origin, and its vertical offset is normally negative, since it starts below the baseline to leave room for descenders. Vivid's Y axis points up, so rows are read in reverse.

Examples

iex> Path.join(:code.priv_dir(:vivid), "fonts/misc-fixed-4x6.bdf")
...> |> Vivid.BDF.load!()
...> |> Vivid.Font.glyph(?A)
...> |> Vivid.BDF.Glyph.pixels()
[{1, 4}, {0, 3}, {2, 3}, {0, 2}, {1, 2}, {2, 2}, {0, 1}, {2, 1}, {0, 0}, {2, 0}]

A bitmap font's em is its pixel size, so asking for that size draws it at one pixel per pixel - which for this font, drawn to be six pixels tall, is a size of six.

iex> Path.join(:code.priv_dir(:vivid), "fonts/misc-fixed-4x6.bdf")
...> |> Vivid.BDF.load!()
...> |> Vivid.Font.line("Hi!", 6)
...> |> to_string()
"@@@@@@@@@@@@\n" <>
"@ @ @@ @@@ @\n" <>
"@ @ @@@@@@ @\n" <>
"@   @  @@@ @\n" <>
"@ @ @@ @@@@@\n" <>
"@ @ @   @@ @\n" <>
"@@@@@@@@@@@@\n"

Twice that size is blocks of four pixels rather than the same pixels twice as far apart, because a glyph is drawn as a Vivid.Bitmap, whose cells cover an area.

iex> Path.join(:code.priv_dir(:vivid), "fonts/misc-fixed-4x6.bdf")
...> |> Vivid.BDF.load!()
...> |> Vivid.Font.line("Hi!", 12)
...> |> to_string()
"@@@@@@@@@@@@@@@@@@@@@@\n" <>
"@  @@  @@@@  @@@@@@  @\n" <>
"@  @@  @@@@  @@@@@@  @\n" <>
"@  @@  @@@@@@@@@@@@  @\n" <>
"@  @@  @@@@@@@@@@@@  @\n" <>
"@      @@    @@@@@@  @\n" <>
"@      @@    @@@@@@  @\n" <>
"@  @@  @@@@  @@@@@@@@@\n" <>
"@  @@  @@@@  @@@@@@@@@\n" <>
"@  @@  @@      @@@@  @\n" <>
"@  @@  @@      @@@@  @\n" <>
"@@@@@@@@@@@@@@@@@@@@@@\n"

Summary

Functions

Read the BDF font at path.

Read the BDF font at path, raising ArgumentError if it can't be read.

Functions

load(path)

@spec load(Path.t()) :: {:ok, Vivid.Font.t()} | {:error, String.t()}

Read the BDF font at path.

Returns {:error, reason} with a reason worth showing a user rather than raising.

Examples

iex> {:ok, font} = Vivid.BDF.load(Path.join(:code.priv_dir(:vivid), "fonts/misc-fixed-4x6.bdf"))
...> {font.units_per_em, map_size(font.glyphs)}
{6, 95}

iex> Vivid.BDF.load("/no/such/font.bdf")
{:error, "couldn't read /no/such/font.bdf: no such file or directory"}

iex> Vivid.BDF.load(Path.join(:code.priv_dir(:vivid), "fonts/roboto-subset.ttf"))
{:error, "not a BDF font"}

load!(path)

@spec load!(Path.t()) :: Vivid.Font.t()

Read the BDF font at path, raising ArgumentError if it can't be read.

Example

iex> Path.join(:code.priv_dir(:vivid), "fonts/misc-fixed-4x6.bdf")
...> |> Vivid.BDF.load!()
...> |> Vivid.Font.glyph(?A)
...> |> Vivid.BDF.Glyph.advance()
4