Execute Shell Commands in FontForge Python Scripts
Executing external shell commands from within a FontForge Python
script is straightforward because FontForge includes a full-featured
standard Python runtime. By leveraging Python's built-in
subprocess module, you can invoke command-line utilities,
run build tools like ttfautohint or fonttools,
and automate system tasks directly from your font generation workflows
without needing specialized FontForge APIs.
Using the subprocess
Module
The recommended method for executing shell commands in Python 3 is
subprocess.run(). It blocks execution until the external
command finishes and returns a CompletedProcess instance
containing the exit code and output.
import subprocess
import fontforge
# Run a simple shell command
result = subprocess.run(
["echo", "FontForge script is executing a shell command."],
capture_output=True,
text=True,
check=True
)
print("Output:", result.stdout)Passing Arguments Safely
Avoid passing commands as single raw strings with
shell=True, as this poses security risks and can cause
platform-dependent quoting issues. Instead, supply the command and its
flags as a list of strings:
import subprocess
# Example: Checking git commit hash for font versioning
git_rev = subprocess.run(
["git", "rev-parse", "--short", "HEAD"],
capture_output=True,
text=True,
check=True
).stdout.strip()
print(f"Current Git Revision: {git_rev}")Integrating Shell Commands into a Font Workflow
A common use case is generating a font file using FontForge's native
methods and immediately processing the resulting file with an external
CLI tool. Because external programs operate on disk files, ensure you
call font.generate() before calling the shell command.
import os
import subprocess
import fontforge
# Open font and generate a binary
font = fontforge.open("MyFont.sfd")
output_path = "MyFont.ttf"
autohinted_path = "MyFont-hinted.ttf"
font.generate(output_path)
font.close()
# Run an external tool (e.g., ttfautohint) on the generated font
try:
cmd = ["ttfautohint", output_path, autohinted_path]
subprocess.run(cmd, check=True)
print(f"Successfully processed {autohinted_path}")
except FileNotFoundError:
print("Error: The specified executable was not found in your system PATH.")
except subprocess.CalledProcessError as e:
print(f"Command failed with exit code {e.returncode}")Handling Asynchronous Processes
If you need to launch a command without waiting for it to finish, or
if you need to stream continuous output, use
subprocess.Popen:
import subprocess
process = subprocess.Popen(
["ping", "-c", "3", "localhost"],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True
)
# Stream standard output line by line
for line in process.stdout:
print(line, end="")
process.wait()Key Considerations
- PATH Environment: Ensure any CLI utilities you call
are located in your system's
PATH. If running FontForge via an application bundle (such as on macOS), the environment may not inherit your shell's user-defined paths; provide absolute paths (e.g.,/usr/local/bin/ttfautohint) if commands fail to resolve. - File Locks: Always close or save the font object in FontForge before running external commands that read, overwrite, or delete the generated font files to prevent file locking conflicts.