A New Font Format for GRUB

The PF2 Bitmap Font File Format

Author: Colin D. Bennett <colin@gibibit.com>
Date: 8 January 2009

Introduction

The goal of this format is to provide a bitmap font format that is simple to use, compact, and cleanly supports Unicode.

Goals of the GRUB Font Format

  • Simple to read and use. Since GRUB will only be reading the font files, we are more concerned with making the code to read the font simple than we are with writing the font.
  • Compact storage. The fonts will generally be stored in a small boot partition where GRUB is located, and this may be on a removable storage device such as a CD or USB flash drive where space is more limited than it is on most hard drives.
  • Unicode. GRUB should not have to deal with multiple character encodings. The font should always use Unicode character codes for simple internationalization.

Why Another Font Format?

There are many existing bitmap font formats that GRUB could use. However, there are aspects of these formats that may make them less than suitable for use in GRUB at this time:

Font format Reason format is suboptimal for GRUB
BDF Inefficient storage; uses ASCII to describe properties and hexadecimal numbers in ASCII for the bitmap rows.
PCF Many format variations such as byte order and bitmap padding (rows padded to byte, word, etc.) would result in more complex code to handle the font format.

File Structure

A file section consists of a 4-byte name, a 32-bit big-endian length (not including the name or length), and then length more section-type-specific bytes.

The standard file extension for PFF2 font files is .pf2.

Section Types

FILE
File type ID (ASCII string). This must be the first section in the file. It has length 4 and the contents are the four bytes of the ASCII string PFF2.
NAME
Font name (ASCII string). This is the full font name including family, weight, style, and point size. For instance, "Helvetica Bold Italic 14".
FAMI
Font family name (ASCII string). For instance, "Helvetica". This should be included so that intelligent font substitution can take place.
WEIG
Font weight (ASCII string). Valid values are bold and normal. This should be included so that intelligent font substitution can take place.
SLAN
Font slant (ASCII string). Valid values are italic and normal. This should be included so that intelligent font substitution can take place.
PTSZ
Font point size (uint16be).
MAXW
Maximum character width in pixels (uint16be).
MAXH
Maximum character height in pixels (uint16be).
ASCE
Ascent in pixels (uint16be). See Font Metrics for details.
DESC
Descent in pixels (uint16be). See Font Metrics for details.
CHIX

Character index. The character index begins with a 32-bit big-endian unsigned integer indicating the total size of the section, not including this size value. For each character, there is an instance of the following entry structure:

  1. Unicode code point. (32-bit big-endian integer.)
  2. Storage flags. (byte.)
    • Bits 2..0:
      • If equal to 000 binary, then the character data is stored uncompressed beginning at the offset indicated by the character's offset value.
      • If equal to 001 binary, then the character data is stored within a compressed character definition block that begins at the offset within the file indicated by the character's offset value.
  3. Offset. (32-bit big-endian integer.)
DATA

A marker that indicates the remainder of the file is data accessed via the character index (CHIX) section. When reading this font file, the rest of the file can be ignored when scanning the sections. The length should be set to -1 (0xFFFFFFFF).

Supported data structures:

Character definition

Each character definition consists of:

  1. Width. Width of the bitmap in pixels. The bitmap's extents represent the glyph's bounding box. uint16be.

  2. Height. Height of the bitmap in pixels. The bitmap's extents represent the glyph's bounding box. uint16be.

  3. X offset. The number of pixels to shift the bitmap by horizontally before drawing the character. int16be.

  4. Y offset. The number of pixels to shift the bitmap by vertically before drawing the character. int16be.

  5. Device width. The number of pixels to advance horizontally from this character's origin to the origin of the next character. int16be.

  6. Bitmap data. This is encoded as a string of bits. It is organized as a row-major, top-down, left-to-right bitmap. The most significant bit of each byte is taken to be the leftmost or uppermost bit in the byte. For the sake of compact storage, rows are not padded to byte boundaries (i.e., a single byte may contain bits belonging to multiple rows). The last byte of the bitmap is padded with zero bits in the bits positions to the right of the last used bit if the bitmap data does not fill the last byte.

    The length of the bitmap data field is (width * height + 7) / 8 using integer arithmetic, which is equivalent to ceil(width * height / 8) using real number arithmetic.

    It remains to be determined whether bitmap fonts usually make all glyph bitmaps the same height, or if smaller glyphs are stored with bitmaps having a lesser height. In the latter case, the baseline would have to be used to calculate the location the bitmap should be anchored at on screen.

Compressed character definition block (Not yet implemented.)

This contains a set of character definitions which are compressed as a single block within the file. Compressed character definitions will be implemented at a later date.

[TODO: Implement compression/decompression using LZMA.]

Font Metrics

Ascent.
The distance from the baseline to the top of most characters. Note that in some cases characters may extend above the ascent.
Descent.
The distance from the baseline to the bottom of most characters. Note that in some cases characters may extend below the descent.
Leading.
The amount of space, in pixels, to leave between the descent of one line of text and the ascent of the next line. This metrics is not specified in the current file format; instead, the font rendering engine calculates a reasonable leading value based on the other font metrics.
Horizonal leading.
The amount of space, in pixels, to leave horizontally between the left and right edges of two adjacent glyphs. The device width field determines the effective leading value that is used to render the font.
Illustration of font metrics on characters.

An illustration of how the various font metrics apply to characters.

Converting Fonts to PFF2 Format

Note: A new font converter, grub-mkfont (which is written in C instead of Java), has recently been created. It will take the place of the Java font converter in the near future.

The GRUB font tool is composed of font viewer and font converter programs. It is a Java program and can be built from source using the Ant tool:

$ cd util/fonttool
util/fonttool $ ant jar
Buildfile: build.xml

compile:
    [mkdir] Created dir: /home/cdb/grub/util/fonttool/build/src
    [javac] Compiling 15 source files to /home/cdb/grub/util/fonttool/build/src

jar:
      [jar] Building jar: /home/cdb/grub/util/fonttool/build/fonttool.jar

BUILD SUCCESSFUL
Total time: 2 seconds

This results in a JAR file in the util/fonttool/build directory that contains the compiled class files.

To run the font viewer, which can view either PFF2 or BDF fonts:

util/fonttool $ java -cp build/fonttool.jar org.gnu.grub.fonttool.Viewer myfile.bdf

To convert a BDF font into PFF2 format, run:

util/fonttool $ java -cp build/fonttool.jar org.gnu.grub.fonttool.Converter --in=font.bdf --out=font.pf2

There is also a script called makepf2.sh in the util/fonttool directory as well which allows easy batch conversion of many BDF fonts to PFF2 format at once.