PostForge is a Python implementation of a PostScript interpreter that implements PostScript Level 3. It can render PostScript files to PNG, PDF, SVG, TIFF, or display them in an interactive Qt window.
This guide covers installation, command line usage, the interactive display, output options, and debugging features.
git clone https://github.com/AndyCappDev/postforge.git
cd postforge
./install.shThe install script checks for Python 3.13+ and Cairo, creates a virtual
environment, installs all dependencies, and installs the pf command.
To update PostForge to the latest version:
cd postforge
git pull
./install.sh # Linux/macOS
install.bat # WindowsThe install script will update dependencies and rebuild the Cython accelerator as needed. Your configuration and font cache are preserved.
pf # Interactive mode
pf input_file.ps # Render to Qt display window
pf -d png input_file.ps # Render to PNG files
pf -d pdf input_file.ps # Render to PDF
pf -d svg input_file.ps # Render to SVG
pf --version # Print version and exitThe launcher scripts ./postforge.sh (Linux/Mac) and postforge.bat (Windows)
are also available if the pf command was not installed system-wide.
Use - as the input filename to read PostScript from stdin. This lets you
pipe content from other programs or shell pipelines:
cat document.ps | pf -d png -
generate_ps.py | pf -d pdf -
curl https://example.com/file.ps | pf -Stdin input can be mixed with regular files. Each is processed as a separate job:
pf -d png header.ps - footer.psOutput files from stdin input use stdin as the base name (e.g.,
pf_output/stdin-0001.png). If no data is piped and - is specified,
PostForge prints an error and exits.
When run without an input file, PostForge starts an interactive PostScript prompt:
pf
PF[8 3 0]> 5 3 add ==
8
PF[8 3 0]> (Hello, PostScript!) ==
Hello, PostScript!
PF[8 3 0]> quitThe prompt PF[8 3 0]> shows the current interpreter state: execution stack
depth, dictionary stack depth, and operand stack item count. The numbers are
updated accordingly when items are pushed onto these stacks.
Type quit or press Ctrl-D to exit.
A PostScript program can drop into the interactive prompt at any point by
calling the executive operator. This is useful for debugging — you can
inspect stacks, query graphics state, or run arbitrary PostScript while the
program is paused:
% Inside a .ps file:
/myvar 42 def
executive % drops into interactive prompt here
myvar == % continues after you type 'continue'When using executive from within a file, the Qt display window updates
live as you enter drawing commands, just as it does in normal interactive
mode.
Two commands exit the interactive prompt:
| Command | Effect |
|---|---|
continue (or cont) |
Exit the prompt and resume the calling file |
quit |
Exit the prompt and terminate the entire job |
The executive operator is re-entrant: calling it from within the
interactive prompt opens a nested level, shown by additional > characters
in the prompt.
| Option | Description |
|---|---|
-d, --device |
Output device: png, pdf, svg, tiff, or qt (default: qt if available, otherwise png) |
-o, --output |
Output filename (base name for page numbering; device inferred from extension if -d not given) |
--output-dir |
Output directory (default: pf_output) |
| Option | Description |
|---|---|
-r, --resolution |
Device resolution in DPI (default: 300 for PNG, 72 for PDF/SVG, screen resolution for Qt) |
--pages |
Page range to output (e.g., 1-5, 3, 1-3,7,10-12) |
--antialias |
Anti-aliasing mode: none, fast, good, best, gray, subpixel (default: gray) |
--text-as-paths |
Render text as path outlines instead of native text objects. Primarily affects PDF and SVG output; bitmap devices (PNG, TIFF, Qt) already render text as paths by default. |
--lossless-images |
Use lossless compression for all images in PDF output (default uses JPEG for photographic images when smaller) |
--multipage-tiff |
Combine all pages into a single multi-page TIFF file (only with tiff device) |
--cmyk |
Output TIFF in CMYK color space using ICC profile conversion (only with tiff device) |
| Option | Description |
|---|---|
--no-icc |
Disable ICC color management and use PLRM conversion formulas instead |
--cmyk-profile |
Path to a CMYK ICC profile for DeviceCMYK color conversion |
| Option | Description |
|---|---|
--no-glyph-cache |
Disable glyph caching (useful for debugging font rendering) |
--cache-stats |
Print glyph cache hit/miss statistics after job completion |
--rebuild-font-cache |
Force rebuild of the system font discovery cache and exit |
| Option | Description |
|---|---|
-v, --verbose |
Enable verbose output |
--profile |
Enable cProfile performance profiling |
--profile-type |
Profiling backend: cprofile or none (default: cprofile) |
--profile-output |
Output file for profiling results (default: auto-generated with timestamp) |
--memory-profile |
Enable memory usage reporting |
--gc-analysis |
Enable garbage collection analysis (implies --memory-profile) |
--leak-analysis |
Enable memory leak detection (implies --memory-profile) |
| Option | Description |
|---|---|
-h, --help |
Show help message and exit |
Renders each page to a separate PNG file.
pf -d png document.ps # 300 DPI (default)
pf -d png -r 600 document.ps # 600 DPIRenders each page to a PDF file with embedded fonts. Type 1, TrueType
(Type 42), CID, and Type 3 fonts are embedded with subsetting.
Use --text-as-paths to render text as path outlines instead of embedded fonts.
By default, photographic images are compressed with JPEG when it produces a
smaller result. Use --lossless-images to force lossless (Flate) compression
for all images, which preserves exact pixel values at the cost of larger files.
pf -d pdf document.ps
pf -d pdf --lossless-images document.ps # Lossless image compressionRenders each page to a separate SVG file. Text is preserved as selectable text elements with CSS font-family fallbacks.
Because SVG does not embed fonts, any PostScript font not installed on the
viewer's system will be substituted by the browser or SVG viewer — often
with a generic serif or sans-serif font that changes spacing, line breaks,
and overall layout. Use --text-as-paths to convert all text to path
outlines, which guarantees pixel-perfect rendering regardless of which fonts
are installed. The trade-off is that the text is no longer selectable or
searchable, and file sizes may increase.
pf -d svg document.ps # Selectable text (needs matching fonts)
pf --text-as-paths -d svg document.ps # Path outlines (always looks correct)Renders each page to a separate TIFF file. Supports multi-page output and CMYK color space conversion for prepress workflows.
pf -d tiff document.ps # One .tif per page (RGB, 300 DPI)
pf -d tiff -r 600 document.ps # 600 DPI
pf -d tiff --multipage-tiff document.ps # All pages in one .tif
pf -d tiff --cmyk document.ps # CMYK output with embedded ICC profile
pf -d tiff --cmyk --multipage-tiff document.ps # CMYK multi-pageMulti-page mode (--multipage-tiff) accumulates all pages and writes them
to a single .tif file after all jobs complete. The output file is named
{base_name}.tif (without page numbering). Without this flag, each page is
saved as {base_name}-{page_number}.tif.
CMYK mode (--cmyk) converts the rendered RGB output to CMYK using the
system CMYK ICC profile (the same profile used for DeviceCMYK color
management). The ICC profile is embedded in the TIFF for downstream color
management. If no CMYK profile is found, output falls back to RGB with a
warning. Use --cmyk-profile to specify a custom profile.
Note: Most Linux image viewers (Evince, Okular, Gwenview) do not properly color-manage CMYK TIFFs. Use GIMP for accurate CMYK viewing on Linux.
Opens an interactive display window. This is the default device when Qt is available.
- In interactive mode, the window updates live as PostScript commands are entered.
- In batch mode (with an input file), the window updates on each
showpage.
pf document.ps # Opens Qt windowSee Using the Qt Display Window below for controls and keybindings.
File-based devices (PNG, PDF, SVG, TIFF) save output to a pf_output directory in
the current working directory. The directory is created automatically if it
does not exist. Use --output-dir to specify a different location.
Output files are named using the pattern:
{base_name}-{page_number}.{extension}
The base name comes from:
- The
-ofilename (without extension), if specified - The input filename (without extension), otherwise
pagein interactive mode
Examples:
| Command | Output Files |
|---|---|
pf -d png input.ps |
pf_output/input-0001.png |
pf -o result.png input.ps |
pf_output/result-0001.png |
pf --output-dir renders -d png input.ps |
renders/input-0001.png |
pf -d pdf input.ps |
pf_output/input-0001.pdf |
pf -d svg input.ps |
pf_output/input-0001.svg |
Multiple PostScript files can be passed on the command line. Each file runs as a separate job within the interpreter's job server, which provides VM isolation between files via save/restore encapsulation:
pf -d png file1.ps file2.ps file3.psThe Qt display window is PostForge's default output device. It renders PostScript pages at high resolution and provides controls for navigating multi-page documents, zooming, and panning.
Each showpage in the PostScript program pauses rendering and waits for input.
The window title changes to indicate the current state:
| Title | Meaning |
|---|---|
| PostForge | Rendering in progress |
| PostForge - Press any key to continue... | Waiting at a page break |
| PostForge - Press Q or close window to exit | Last page reached |
Press any key (other than the view controls listed below) to advance to the next page. After the last page, the window stays open until you press Q, Escape, or close it.
| Key | Action |
|---|---|
| Any key | Advance to next page (when waiting at a page break) |
| + or = | Zoom in (25% per step, up to native resolution) |
| - | Zoom out (25% per step, minimum 0.1x) |
| 0 | Reset zoom and pan (fit page to window) |
| Arrow keys | Pan the view (50 pixels per step) |
| Q | Quit PostForge |
| Escape | Quit PostForge |
The view control keys (+, -, 0, arrow keys) do not advance the page — they only adjust the view.
| Action | Effect |
|---|---|
| Scroll wheel up | Zoom in, centered on the cursor |
| Scroll wheel down | Zoom out, centered on the cursor |
| Click and drag | Pan the view |
| Double-click | Reset zoom and pan (same as pressing 0) |
- Pages are automatically scaled to fit the window while preserving aspect ratio.
- Zooming centers on the mouse cursor position (scroll wheel) or the window center (keyboard).
- The window renders at screen resolution by default, or at the resolution
specified with
-r. - Closing the window with the window's close button exits PostForge immediately.
The -r flag sets the device resolution in dots per inch. Higher values
produce larger, more detailed output.
pf -d png -r 150 document.ps # 150 DPI
pf -d png -r 300 document.ps # 300 DPI (print quality)The default resolution depends on the device: 300 DPI for PNG, 72 DPI for
PDF and SVG, and screen resolution for the Qt display. The -r flag can
be used with any device, including Qt.
Controls the anti-aliasing mode for rendered output. The default is
gray, which provides good quality for most use cases.
| Mode | Description |
|---|---|
none |
No anti-aliasing (sharp pixel edges) |
fast |
Fast, lower-quality anti-aliasing |
good |
Balanced quality/speed |
best |
Highest quality anti-aliasing |
gray |
Grayscale anti-aliasing (default) |
subpixel |
Subpixel anti-aliasing (LCD-optimized) |
pf -d png --antialias none document.ps
pf -d png --antialias best -r 300 document.psThe --pages flag selects which pages to render from a multi-page document.
The PostScript program executes fully regardless — only the device output is
filtered. This makes it fast to extract specific pages from large documents.
| Format | Meaning |
|---|---|
--pages 5 |
Page 5 only |
--pages 1-5 |
Pages 1 through 5 |
--pages 1,3,5 |
Pages 1, 3, and 5 |
--pages 1-3,7,10-12 |
Pages 1-3, 7, and 10-12 |
Page numbers are 1-based and refer to showpage invocations.
pf -d png --pages 1 document.ps # First page only
pf -d pdf --pages 2-5 document.ps # Pages 2 through 5
pf -d png --pages 1,3,5 document.ps # Specific pages- Full execution: The PostScript program runs completely. Page filtering only skips the device rendering step, so page numbering, fonts, and graphics state are unaffected.
- Early termination: Once all selected pages have been rendered, PostForge stops execution early rather than processing the remainder of the document.
- Multiple input files: When processing multiple files,
--pagesapplies independently to each file.--pages 1-3selects pages 1-3 from every input file. - PDF output: Filtered pages are omitted from the PDF entirely — the resulting file contains only the selected pages.
PostForge includes ICC-based color management for accurate color conversion between color spaces (DeviceGray, DeviceRGB, DeviceCMYK, CIEBased, ICCBased).
ICC color management is enabled by default. PostForge searches for a system-installed CMYK ICC profile and uses it for DeviceCMYK color conversion. If no profile is found, it falls back to the PLRM-specified conversion formulas. Output is correct either way, but ICC profiles produce more accurate CMYK colors.
PostForge searches the following locations for a CMYK profile:
| Platform | Locations searched |
|---|---|
| Linux | /usr/share/color/icc/ghostscript/, /usr/share/color/icc/colord/ (SWOP and FOGRA profiles) |
| macOS | /Library/ColorSync/Profiles/, ~/Library/ColorSync/Profiles/, /System/Library/ColorSync/Profiles/ |
| Windows | %SYSTEMROOT%\System32\spool\drivers\color\ |
On Linux, CMYK profiles are typically installed with GhostScript or the
colord package. On macOS, profiles are included with the system or
installed by print drivers. On Windows, CMYK profiles are only present if
installed by printer drivers or added manually.
If no system CMYK profile is found, use --cmyk-profile to specify one
(see below), or PostForge will use the PLRM conversion formulas.
Specify a custom CMYK ICC profile for DeviceCMYK color conversion:
pf -d png --cmyk-profile /path/to/profile.icc document.psFree CMYK profiles such as SWOP or FOGRA can be downloaded from the
ICC Profile Registry or
from your Linux distribution's colord package.
Use --no-icc to disable ICC color management entirely and use the PLRM
conversion formulas for all color space conversions:
pf -d png --no-icc document.psPostForge tracks the last N operations processed by the execution engine. When a PostScript error occurs, the history is displayed as a call stack trace showing exactly what the interpreter was doing when the error happened.
Execution history is disabled by default for performance. Enable it from within your PostScript code:
<< /ExecutionHistory true >> setuserparamsConfigure the number of operations to track (default: 20):
<<
/ExecutionHistory true
/ExecutionHistorySize 30
>> setuserparamsGiven this PostScript code:
<< /ExecutionHistory true >> setuserparams
3 0 {div} exec % Fails: division by zeroPostForge displays:
** EXECUTION HISTORY **
Operator - --stopped--
Array - {--cvx-- --exec--}
Operator - --cvx--
Array - {--exec--}
Operator - --exec--
Name - }
Operator - --procedure_from_mark--
Operator - --exec--
Array - {div}
Name - div
Operator - --div--
>>> ERROR OCCURRED HERE <<<
Error: /undefinedresult in --div--
Each line shows the object type and value. The sequence reads chronologically from top to bottom, ending at the point of failure.
10 array exechistorystack
% Returns a subarray containing execution history strings| Parameter | Type | Default | Description |
|---|---|---|---|
ExecutionHistory |
boolean | false |
Enable/disable tracking |
ExecutionHistorySize |
integer | 20 |
Number of operations to track |
PostScript provides built-in operators for inspecting interpreter state:
stack % Print the operand stack (non-destructive)
count == % Print number of items on operand stack
(message) print % Print a string to stdout
somevalue == % Print any value to stdoutPostForge adds several convenience procedures for interactive use:
| Command | Description |
|---|---|
/operator help |
Print the PLRM documentation for a built-in operator |
pstack |
Print the operand stack using == format (non-destructive, shows composite object structure) |
ppstack |
Same as pstack but with a header |
/moveto help % Show documentation for moveto
(hello) [1 2 3] pstack % Prints: [1 2 3]\n(hello)PostForge uses two independent caches related to fonts:
PostForge maintains a cache of system font locations
(~/.cache/postforge/system_fonts.json) that maps PostScript font names to
installed font files. This cache is built automatically on first run and
rebuilds when font directories change.
To force a rebuild (e.g., after installing new system fonts):
pf --rebuild-font-cachePostForge caches rendered glyph paths and bitmaps to avoid re-interpreting
font data for repeated characters. This applies to all scalable font types
(Type 1, Type 2/CFF, Type 3, Type 42, and CID fonts). The bitmap cache
size is controlled by the MaxFontCache system parameter (default 64 MB).
To inspect glyph cache performance:
pf --cache-stats document.psTo disable glyph caching (for debugging font rendering issues):
pf --no-glyph-cache document.ps