FontForge Freezing During Python Batch Scripts
When executing large batch operations in FontForge using Python, the program can become unresponsive due to memory leaks in its underlying C architecture, hidden graphical user interface prompts, or corrupt glyph outlines causing algorithmic deadlocks. This guide outlines the exact causes of these freezes and provides actionable solutions—such as running FontForge headlessly, isolating tasks with multiprocessing, managing memory explicitly, and logging file progression—to ensure your automated font conversions and modifications run to completion without manual intervention.
Run in Headless CLI Mode
FontForge often hangs if it attempts to render UI elements or display error dialogs in an environment that expects them. Never run batch scripts through the FontForge GUI interface. Instead, run the script from the command line using the standalone binary or as a direct Python module import:
fontforge -script your_script.pyIf you installed FontForge via system package managers that include
the Python extension (e.g., python3-fontforge on Ubuntu),
run the script directly through standard Python:
python3 your_script.pyRunning in headless mode ensures that non-fatal font warnings do not trigger interactive blocking pop-ups.
Isolate Fonts Using Subprocesses
FontForge's internal C-bindings do not always release memory back to the operating system when processing hundreds of files sequentially inside a single loop. Over time, the memory footprint balloons, leading to severe slowdowns or a complete freeze.
To prevent this, use Python's subprocess module to spawn
an isolated, fresh FontForge process for each font or small batch:
import subprocess
import glob
font_files = glob.glob("path/to/fonts/*.ttf")
for font_path in font_files:
# Runs an isolated script for a single font file
result = subprocess.run(
["fontforge", "-script", "process_single_font.py", font_path],
capture_output=True,
text=True,
timeout=120 # Kill the process if it hangs longer than 2 minutes
)
if result.returncode != 0:
print(f"Failed processing {font_path}: {result.stderr}")Setting a timeout parameter ensures that if an
individual font causes an infinite calculation loop, the runner
terminates it and proceeds to the next file instead of hanging
indefinitely.
Force Memory Cleanup Explicitly
If you must process files within a single long-running Python script, explicitly close the font objects and invoke Python's garbage collector after every iteration. Merely letting a variable fall out of scope often fails to free the C-level font data:
import fontforge
import gc
for font_path in font_list:
font = fontforge.open(font_path)
try:
# Perform font operations here
font.generate("output/" + font.fontname + ".otf")
finally:
font.close()
del font
gc.collect()Bypass Problematic Path Operations
Complex operations such as font.removeOverlap() or
automatic hint generation (font.autoHint()) can enter
endless loops when processing malformed Bezier curves or
self-intersecting contours.
Wrap High-Risk Calls: Isolate calls like
removeOverlap()orsimplify()within standardtry...exceptblocks.Set Validation Flags: When saving, disable extensive validation checks that can freeze on corrupted glyphs:
font.generate("output.ttf", flags=('opentype', 'omit-instructions'))Skip Broken Glyphs: If a specific glyph is locking the thread, iterate through
font.glyphs()individually so you can catch errors per glyph rather than failing the entire file.
Implement Verbose Logging
Determine whether the script is genuinely frozen or simply working slowly through heavy outline calculations by writing timestamps to a log file or terminal before and after critical milestones:
import sys
def log(message):
print(message, file=sys.stderr, flush=True)
log(f"Opening {font_path}...")
font = fontforge.open(font_path)
log("Generating outlines...")
font.generate(output_path)
log("Done.")Using flush=True prevents print buffers from delaying
status updates, allowing you to see the exact file and operation that
triggered the freeze.