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.

validate-font.rb
require 'ttfunk'
require 'ttfunk/subset_collection'

ttf_subsets = TTFunk::SubsetCollection.new TTFunk::File.open 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 glyph 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.

Subsetting the font (i.e., removing glyphs) is not required since Prawn only embeds the glyphs 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 manageable.

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. The Required characters section lists all the characters that a custom font is expected to provide.

One way to get the glyphs for these required characters 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 adding any required characters, 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.

Required characters

Below are the non-Latin characters (identified by Unicode code point) on which Asciidoctor PDF relies that are often missing from fonts. You need to ensure these glyphs are present in your prepared font or configure a fallback font that provides them. The fonts used by the default theme in Asciidoctor PDF provide all of these glyphs.

  • \u00a0 - no-break space (used by converter to preserve indentation in code blocks)

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

  • \u000a - line feed character (used to indicate the location of a hard line break; has zero width)

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

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

  • \u2011 - non-breaking hyphen (used for hyphenation; not required if hyphenation not in use)

  • \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’s missing from the font. Unlike other glyphs, the .notdef glyph is referenced by name only, meaning it does not have a designated Unicode code point.

Although Asciidoctor PDF uses the \u0000 character as a placeholder for inline anchors, the font is not required to provide this character. Asciidoctor PDF assumes that the primary font provides this character and uses the font metrics from that font as though it were there.

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

The font will also need to provide glyphs for all characters used in the document content, such as emoji.

If you don’t want to add additional glyphs to the font you’re preparing, you can configure a fallback font instead. A symbol font, such as Symbola, is a good choice as a fallback font since it’s focus is on filling in these glyphs for other fonts.