Running FontForge in Docker for Font Builds
Running FontForge inside a Docker container is entirely feasible and serves as an industry best practice for automated, reproducible font building. Containerizing FontForge isolates its complex dependencies and Python bindings from your host system, ensuring consistent font generation across different developer machines and continuous integration (CI/CD) pipelines. This guide explains how to set up, configure, and execute headless FontForge builds using Docker.
Why Containerize FontForge?
FontForge relies on specific system libraries, C extensions, and Python versions that can vary dramatically between operating systems. Running it inside a Docker container eliminates the "works on my machine" problem. Key benefits include:
- Dependency Isolation: Native libraries like FreeType, Libspiro, and Cairo remain confined to the container.
- CI/CD Integration: Simplifies automated testing and compilation in environments like GitHub Actions or GitLab CI.
- Version Pinning: Guarantees that font hinting, kerning tables, and binary outputs remain identical regardless of the host environment.
Creating the Dockerfile
FontForge can be executed in a headless environment using its Python
module (fontforge). A standard Debian- or Ubuntu-based
Docker image provides official, pre-compiled packages that make setup
straightforward.
Create a file named Dockerfile:
FROM ubuntu:22.04
# Avoid prompts during package installation
ENV DEBIAN_FRONTEND=noninteractive
# Install FontForge and Python bindings
RUN apt-get update && apt-get install -y --no-install-recommends \
fontforge \
python3-fontforge \
python3-pip \
&& rm -rf /var/lib/apt/lists/*
# Set working directory for font files
WORKDIR /workspace
# Default command to run Python with FontForge
ENTRYPOINT ["python3"]Writing a Build Script
To compile fonts non-interactively, write a Python script that
utilizes the FontForge library. Create a script named
build.py:
import sys
import fontforge
def build_font(input_path, output_path):
print(f"Opening: {input_path}")
font = fontforge.open(input_path)
# Generate formats (e.g., TTF, OTF, WOFF2)
print(f"Generating: {output_path}")
font.generate(output_path)
font.close()
print("Build complete.")
if __name__ == "__main__":
if len(sys.argv) < 3:
print("Usage: python3 build.py <input-sfd-or-ufo> <output-font>")
sys.exit(1)
build_font(sys.argv[1], sys.argv[2])Building and Running the Container
Once the Dockerfile and build script are created, build
the image and run the compilation process using Docker volumes to access
local source files.
Build the Docker image:
docker build -t fontforge-builder .Run the build command by mounting your current directory into the container's
/workspace:docker run --rm -v "$(pwd):/workspace" fontforge-builder build.py myfont.sfd myfont.otf
Best Practices for Headless Builds
- Avoid GUI Dependencies: Ensure scripts do not invoke any FontForge functions that require an X11 display. Stick strictly to the standard non-interactive scripting API.
- Handle File Permissions: Containerized processes
run as
rootby default, which can cause generated font files on Linux hosts to be owned by root. Pass--user $(id -u):$(id -g)duringdocker runto ensure correct file ownership. - Font Validation: Include validation steps in your
Python script using
font.validate()to detect overlapping contours or missing glyph metrics automatically during the build process.