Truncate Unicode Ranges in a BDF Font with bdftruncate

bdftruncate exists because old Xlib code chokes when a BDF bitmap font declares thousands of high Unicode codepoints it barely uses. Point it at a source BDF and a threshold, and it rewrites every glyph at or above that value as unencoded, shrinking the sparse-encoding metadata that Xlib and the X11 protocol handle badly. The source file is never touched.

You need the bdftruncate command from the xfonts-utils package and a readable ISO 10646-1 encoded BDF file. The examples use the locally installed xfonts-utils version 1:7.7+6build3. Budget about ten minutes; you will not need elevated privileges unless the font files sit somewhere your account cannot read or write.

1. Confirm the installed command

Check the executable and package version before choosing a threshold:

$ command -v bdftruncate
/usr/bin/bdftruncate
$ dpkg-query -W -f='${Package} ${Version}\n' xfonts-utils
xfonts-utils 1:7.7+6build3

The installed program has no separate version option: bdftruncate --version treats the option text as an invalid threshold and prints usage information instead. Use the package query when you need to record a version.

Checkpoint: Continue only if the command path is the one you expect and the package query reports an installed package. If the command is missing, install or repair xfonts-utils through your normal package-management process before going further.

2. Choose a threshold and a separate output file

The syntax is a threshold followed by shell redirection:

bdftruncate THRESHOLD < source.bdf > destination.bdf

The threshold is a code value, and the cutoff is inclusive: glyphs with codes at or above it are rewritten to unencoded storage, shown as ENCODING -1 in the result. The manpage's own example uses 0x3200, a sensible boundary when lower codepoints should keep their ordinary encodings.

Warning: Shell redirection creates the destination file before bdftruncate runs, so pointing both sides at the same path destroys the source before the command ever reads it. There is no undo for an overwritten file, so always write to a new destination while testing.

Set real paths and let the shell refuse to clobber anything before you run the conversion:

SOURCE_BDF='/path/to/source.bdf'
DEST_BDF='/path/to/source-truncated.bdf'
THRESHOLD='0x3200'

if [ ! -r "$SOURCE_BDF" ]; then
    printf 'Source is not readable: %s\n' "$SOURCE_BDF" >&2
    exit 1
fi
if [ -e "$DEST_BDF" ]; then
    printf 'Refusing to overwrite existing output: %s\n' "$DEST_BDF" >&2
    exit 1
fi

Those checks are ordinary shell guards. If the destination already exists and you deliberately want to replace it, move it aside first so recovery stays possible:

$ mv -- /path/to/source-truncated.bdf /path/to/source-truncated.bdf.previous

To undo that preparation, move the saved file back once you are sure the new output is not needed.

3. Generate the truncated font

Run the documented filter with the variables from the previous step:

bdftruncate "$THRESHOLD" < "$SOURCE_BDF" > "$DEST_BDF"
status=$?
if [ "$status" -ne 0 ]; then
    printf 'bdftruncate failed with status %s\n' "$status" >&2
    rm -- "$DEST_BDF"
    exit "$status"
fi
printf 'Wrote %s\n' "$DEST_BDF"

A successful run writes the transformed BDF to standard output, so the terminal stays quiet apart from the wrapper's final message. If the command fails, the wrapper removes the newly created destination; that is safe here because the earlier check already refused to overwrite an existing file.

Checkpoint: Expect a message like this, with your actual path:

Wrote /path/to/source-truncated.bdf

4. Verify the changed encodings

Check that the output still looks like a BDF and contains at least one unencoded glyph:

$ head -n 1 "$DEST_BDF"
STARTFONT 2.1
$ grep -c '^ENCODING -1$' "$DEST_BDF"
NUMBER_OF_UNENCODED_GLYPHS

Replace NUMBER_OF_UNENCODED_GLYPHS with the number your machine actually prints. The count depends on the source font and threshold: zero is not automatically wrong, it just means nothing in that input reached the boundary.

For a more targeted check, list every encoded value in the output, including ones that stayed below the threshold:

$ grep '^ENCODING ' "$DEST_BDF" | head -n 12
ENCODING 32
ENCODING 65
ENCODING 160
ENCODING -1

The order and values are font-specific. What matters is that qualifying glyphs now read -1 while lower glyphs keep their original encodings. Do not guess a glyph's identity from the position of an ENCODING line; read its surrounding STARTCHAR block if you need to be sure.

5. Handle the common failures

An invalid threshold is rejected before useful output is produced:

$ bdftruncate nope < source.bdf > destination.bdf
Illegal threshold nope
Usage: bdftruncate [+w|-w] threshold <source.bdf >destination.bdf

Do not edit the generated BDF in place while debugging. Keep the original, the generated file and any comparison output as separate paths. To discard a result after inspection, remove only the explicit generated path:

$ rm -- /path/to/source-truncated.bdf

Recovery: Confirm the path with printf '%s\n' "$DEST_BDF" first, and never run that removal against the source file.

Done means