Prepare a Custom Font

Any TTF or OTF font can be used with Prawn—​and hence Asciidoctor PDF—​without modifications (unless, of course, it’s corrupt or contains errors). However, you may discover that kerning is disabled and certain required glyphs are missing (or replaced with boxes). To address these problems, you need to prepare the font using a font program such as FontForge. These instructions will cover how to prepare a TTF font, but the same applies for an OTF font.

Validate the font

Before using the font, you may want to check that the font is valid. To do so, create the following script, which will verify that the font is free from errors.

require 'ttfunk'
require 'ttfunk/subset_collection'

ttf_subsets = ARGV[0]
(0...(ttf_subsets.instance_variable_get :@subsets).size).each {|idx| ttf_subsets[idx].encode }

Run the script on your font as follows:

$ ruby validate-font.rb path/to/font.ttf

If this script fails, the font will not work with Asciidoctor PDF. To repair it, open the font in FontForge and resave it using File  Generate Fonts…​  Generate. Dismiss any warning dialogs.

Resaving the font in FontForge will usually resolve any errors in the font. (If not, you may need to find another font, or at least another copy of it).

Modifying the font

To ready your font for use with Asciidoctor PDF, you’ll need to modify it using a font program. We recommend using FontForge. But don’t let this scare you off. FontForge essentially works like a vector-drawing tool, in which each character is a separate canvas. You can find a crash course in how to use the program on the FontForge project site.

Here are the modifications you need to apply to a custom font for it to work best with Asciidoctor PDF:

  • Convert the font to TTF or OTF (only required if the font is a TTC or other format not supported by Prawn).

  • Add the glyphs for the required characters if missing from the font (optional if using a fallback font).

  • Subset the font to exclude unused characters to reduce the file size (optional).

  • Save the file using the old-style kern table to activate kerning.

Technically, subsetting the font (i.e., removing glyphs) is not required since Prawn only embeds the characters from the font used in the document (i.e., it automatically subsets the font). However, if you plan to commit the font to a repository, subsetting helps keep the file size down.

Most fonts do not provide glyphs for all the Unicode character ranges (i.e., scripts). (A glyph is the corresponding vector image for a Unicode character). In fact, many fonts only include glyphs for Latin (Basic, Supplement, and Extended) and a few other scripts (e.g., Cyrillic, Greek). That means certain glyphs Asciidoctor PDF relies on may be missing from the font.

Below are the non-Latin characters (identified by Unicode code point) on which Asciidoctor PDF relies that are often missing from fonts. Unless you’re using a fallback font that fills in the missing glyphs, you need to ensure these glyphs are present in your font (and add them if not).

  • \u00a0 - no-break space

  • \ufeff - zero width no-break space

  • \u200b - zero width space (used for line break hints)

  • \u000a - line feed character (zero width)

  • \u2009 - thin spaced (used in the button UI element)

  • \u202f - narrow no-break space (used in the keybinding UI element)

  • \u2011 - non-breaking hyphen

  • \u2022 - disc (used for first-level unordered list level)

  • \u25e6 - circle (used for second-level unordered list level)

  • \u25aa - square (used for third-level unordered list level)

  • \u2611 - ballot box checked (used for checked list item)

  • \u2610 - ballot box unchecked (used for unchecked list item)

  • \u2014 - em-dash (used in quote attribute)

  • \u203a - single right-pointing quotation mark (used in the menu UI element)

  • \u25ba - right pointer (used for media play icon when icon fonts are disabled)

  • .notdef - used as the default glyph when a requested character is missing from the font (usually a box)

If the .notdef glyph is non-empty (i.e., contains splines), it will be used as the default glyph when the document requests a character that is missing from the font. Unlike other glyphs, the .notdef glyph is referenced by name only. It does not have a designated Unicode code point.

If you’re preparing a font for use in verbatim blocks (e.g., a listing block), you’ll also need this range of characters:

  • \u2460 to \u2468 - circled numbers

One way to get these glyphs is to steal them from another font (or from another character in the same font). To do so, open the other font in FontForge, select the character, press Ctrl+c, switch back to your font, select the character again, and press Ctrl+v. You may need to scale the glyph so it fits properly in the art box.

If you’re copying a non-visible character, be sure to set the width to 0 using Metrics  Set Width…​, enter 0 into Set Width To, then click OK.

When you’re done, save the font with the old-style kern table enabled. To do so, select File  Generate Fonts…​, select TrueType, click Options, make sure only the following options are checked (equivalent to the flags 0x90 + 0x08):

  • OpenType

    • Old style 'kern'

Then click OK, then uncheck Validate Before Saving, and finally click Generate to generate and save the font.

Your font file is now ready to be used with Asciidoctor PDF.

Scripting the font modifications

Performing all this font modification manually can be tedious (not to mention hard to reproduce). Fortunately, FontForge provides a scripting interface, which you can use to automate the process.

In fact, that’s what we use to prepare the fonts that are bundled with Asciidoctor PDF. You can find that FontForge script, the Bash script that calls it, and the Docker image in which it is run in the scripts directory of this project. You can use that script as a starting point or reference for your own font preparation / modification script.