Skip to content

Commit 564b1cb

Browse files
committed
Add TIFF output device with multi-page and CMYK support
New output device that renders PostScript to TIFF via Cairo + Pillow. Features single-page (one .tif per page) and multi-page (--multipage-tiff) modes, with optional RGB-to-CMYK conversion (--cmyk) using ICC profiles. Also removes accidentally committed code-quality-audit file and bumps version to 0.9.2.
1 parent a2e2224 commit 564b1cb

14 files changed

Lines changed: 412 additions & 152 deletions

README.md

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<p align="center"><strong>A modern, open-source PostScript interpreter written in Python.</strong></p>
44

55
<p align="center">
6-
<a href="https://github.com/AndyCappDev/postforge/releases"><img src="https://img.shields.io/badge/Version-0.9.1-green.svg" alt="Version 0.9.0"></a>
6+
<a href="https://github.com/AndyCappDev/postforge/releases"><img src="https://img.shields.io/badge/Version-0.9.2-green.svg" alt="Version 0.9.0"></a>
77
<a href="LICENSE.txt"><img src="https://img.shields.io/badge/License-AGPL--3.0-blue.svg" alt="License: AGPL-3.0"></a>
88
<a href="https://www.python.org/downloads/"><img src="https://img.shields.io/badge/Python-3.13%2B-blue.svg" alt="Python 3.13+"></a>
99
</p>
@@ -13,7 +13,7 @@
1313
PostForge is a complete PostScript interpreter that faithfully implements the
1414
PostScript Level 2 specification while adding selected Level 3 enhancements.
1515
It reads PostScript files and produces PNG images, PDF documents, SVG files,
16-
or renders them in an interactive Qt display window.
16+
TIFF images, or renders them in an interactive Qt display window.
1717

1818
PostForge is designed as an open-source alternative to GhostScript for use
1919
cases where Python integration, transparency, and debuggability matter more
@@ -52,9 +52,10 @@ See the [sample gallery](docs/samples.md) for larger rendered examples.
5252
exploration, debugging, and experimentation
5353
- **Cython-Accelerated Execution** — Optional Cython-compiled execution loop
5454
providing 15–40% speedup depending on workload
55-
- **Multiple Output Formats** — PNG, PDF, and SVG output via Cairo graphics
56-
backend, plus an interactive Qt display window with a PostScript command
57-
prompt; extensible architecture makes it straightforward to add new devices
55+
- **Multiple Output Formats** — PNG, PDF, SVG, and TIFF output via Cairo
56+
graphics backend, plus an interactive Qt display window with a PostScript
57+
command prompt; TIFF supports multi-page and CMYK output for prepress
58+
workflows; extensible architecture makes it straightforward to add new devices
5859
- **PDF Font Embedding** — Type 1 font reconstruction and subsetting,
5960
TrueType/CID font extraction with CIDToGIDMap and ToUnicode support
6061
- **EPS Support** — Automatic page cropping to EPS content dimensions with
@@ -154,6 +155,15 @@ pf -d pdf samples/tiger.ps
154155
# Save as SVG → pf_output/tiger-0001.svg
155156
pf -d svg samples/tiger.ps
156157

158+
# Save as TIFF → pf_output/tiger-0001.tif
159+
pf -d tiff samples/tiger.ps
160+
161+
# Multi-page TIFF → pf_output/document.tif
162+
pf -d tiff --multipage-tiff document.ps
163+
164+
# CMYK TIFF for prepress → pf_output/tiger-0001.tif
165+
pf -d tiff --cmyk samples/tiger.ps
166+
157167
# Render an EPS file (auto-crops to content)
158168
pf -d png samples/fancy.eps
159169

@@ -164,7 +174,7 @@ pf -o result.png --output-dir renders samples/tiger.ps
164174
pf -d pdf --pages 1-3,7 document.ps
165175

166176
# SVG with text rendered as path outlines instead of <text> elements
167-
pf --text-as-paths -d svg samples/tiger.ps
177+
pf --text-as-paths -d svg samples/test1.ps
168178

169179
# Pipe PostScript from stdin
170180
cat document.ps | pf -d png -
@@ -242,7 +252,7 @@ most rendering tasks.*
242252
- [Architecture Overview](docs/developer/architecture-overview.md) — How PostForge works internally
243253
- [Implementing Operators](docs/developer/operator-implementation.md) — Adding new PostScript operators
244254
- [Testing Guide](docs/developer/testing-guide.md) — Writing and running PostScript-based tests
245-
- [Visual Regression Testing](docs/developer/visual-regression-testing.md) — PNG, PDF, and SVG visual comparison
255+
- [Visual Regression Testing](docs/developer/visual-regression-testing.md) — PNG, PDF, SVG, and TIFF visual comparison
246256
- [Adding Output Devices](docs/developer/adding-output-devices.md) — Creating new output backends
247257
- [Profiling](docs/developer/profiling.md) — Performance and memory analysis
248258

docs/developer/adding-output-devices.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,14 @@ device takes the PostScript display list — the accumulated graphics operations
55
from a page — and renders it to some output format (image file, PDF, screen
66
display, etc.).
77

8-
PostForge ships with four devices:
8+
PostForge ships with five devices:
99

1010
| Device | Output | Complexity |
1111
|--------|--------|------------|
1212
| **PNG** | One PNG file per page | Simple — stateless, single-page |
1313
| **PDF** | Single multi-page PDF | Complex — persistent state, font embedding |
1414
| **SVG** | One SVG file per page | Moderate — post-processing for text elements |
15+
| **TIFF** | One TIFF per page or multi-page | Moderate — optional multi-page accumulation, CMYK conversion |
1516
| **Qt** | Interactive window | Complex — live rendering, zoom/pan |
1617

1718
The PNG device is the simplest and is used as the primary example throughout
@@ -542,6 +543,17 @@ for font matching. Each page is a separate `.svg` file.
542543
Key features: text as selectable/searchable elements, CSS font-family
543544
fallbacks, Cairo-based vector rendering.
544545

546+
### TIFF (`postforge/devices/tiff/`)
547+
548+
Renders to a Cairo ImageSurface (like PNG), then converts to a PIL Image for
549+
TIFF encoding via Pillow. Supports single-page (one `.tif` per page) and
550+
multi-page (all pages in one `.tif`) modes. Optional CMYK output via ICC
551+
profile conversion (sRGB→CMYK using the system CMYK profile). ~200 lines of
552+
code.
553+
554+
Key features: multi-page accumulation with `finalize()`, CMYK conversion via
555+
ImageCms, ICC profile embedding, LZW compression, DPI metadata.
556+
545557
### Qt (`postforge/devices/qt/`)
546558

547559
The default preview device — when you run `./postforge.sh samples/tiger.ps`

docs/developer/architecture-overview.md

Lines changed: 32 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ Every PostScript program flows through the same pipeline:
1212
```
1313
┌──────────────┐
1414
PostScript Source ──► Tokenizer ──► Execution ──► │ Display List │──► Output Device
15-
(.ps file, extracts Engine │ (per page) │ (PNG, PDF,
16-
string, or tokens from (exec_exec) └──────────────┘ SVG, Qt)
15+
(.ps file, extracts Engine │ (per page) │ (PNG, PDF, SVG,
16+
string, or tokens from (exec_exec) └──────────────┘ TIFF, Qt)
1717
interactive) byte streams
1818
```
1919

@@ -480,30 +480,27 @@ without involving Cairo at all.
480480
### Device Architecture
481481

482482
```
483-
┌────────────────────────────────┐
484-
│ Shared Cairo Rendering │
485-
│ (devices/common/) │
486-
│ │
487-
│ cairo_renderer.py - dispatch │
488-
│ cairo_images.py - images │
489-
│ cairo_patterns.py - patterns │
490-
│ cairo_shading.py - shading │
491-
└──────┬───────┬───────┬─────────┘
492-
│ │ │
493-
┌───────────┘ │ └───────────┐
494-
▼ ▼ ▼
495-
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
496-
│ PNG │ │ PDF │ │ SVG │ │ Qt │
497-
│ device │ │ device │ │ device │ │ device │
498-
└──────────┘ └──────────┘ └──────────┘ └──────────┘
499-
500-
┌───────┴─────────┐
501-
│ Font embedding │
502-
│ (font_embedder, │
503-
│ cid_font_ │
504-
│ embedder, │
505-
│ pdf_injector) │
506-
└─────────────────┘
483+
┌────────────────────────────────────────────────────────────────┐
484+
│ Shared Cairo Rendering │
485+
│ (devices/common/) │
486+
│ │
487+
│ cairo_renderer.py - dispatch cairo_patterns.py - patterns │
488+
│ cairo_images.py - images cairo_shading.py - shading │
489+
└─────┬────────────┬────────────┬─────────────┬────────────┬─────┘
490+
│ │ │ │ │
491+
▼ ▼ ▼ ▼ ▼
492+
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
493+
│ PNG │ │ PDF │ │ SVG │ │ TIFF │ │ Qt │
494+
│ device │ │ device │ │ device │ │ device │ │ device │
495+
└──────────┘ └────┬─────┘ └──────────┘ └──────────┘ └──────────┘
496+
497+
┌────────┴────────┐
498+
│ Font embedding │
499+
│ (font_embedder, │
500+
│ cid_font_ │
501+
│ embedder, │
502+
│ pdf_injector) │
503+
└─────────────────┘
507504
```
508505

509506
**PNG** (`postforge/devices/png/png.py`) — Creates a Cairo ImageSurface, calls
@@ -522,14 +519,21 @@ elements with CSS font-family fallback chains. This can be overridden with
522519
`--text-as-paths` to render text as path outlines instead. Each page produces
523520
a separate `.svg` file.
524521

522+
**TIFF** (`postforge/devices/tiff/tiff.py`) — Renders to a Cairo ImageSurface
523+
(like PNG), then converts to a PIL Image for TIFF encoding via Pillow. Supports
524+
single-page (one `.tif` per page) and multi-page (all pages in one `.tif` via
525+
`--multipage-tiff`). Optional CMYK output via ICC profile conversion
526+
(`--cmyk`), with the ICC profile embedded in the TIFF for downstream color
527+
management.
528+
525529
**Qt** (`postforge/devices/qt/qt.py`) — Interactive display window. Renders to
526530
a Cairo ImageSurface and displays it in a Qt widget. In interactive mode, the
527531
display updates live as display list elements are added; in batch mode, it
528532
updates on `showpage`.
529533

530534
### PostScript-Side Device Setup
531535

532-
The PostScript configuration files (e.g., `png.ps`, `pdf.ps`, `svg.ps`) define the page
536+
The PostScript configuration files (e.g., `png.ps`, `pdf.ps`, `svg.ps`, `tiff.ps`) define the page
533537
device dictionary — page size, resolution, margins, color space, and
534538
Install/BeginPage/EndPage procedures. When a device is selected, this
535539
dictionary is loaded and merged into the graphics state's `page_device`.
@@ -651,6 +655,7 @@ A quick reference to the directory structure:
651655
| `postforge/devices/common/` | Shared Cairo rendering backend |
652656
| `postforge/devices/png/` | PNG output device |
653657
| `postforge/devices/pdf/` | PDF output device + font embedding |
658+
| `postforge/devices/tiff/` | TIFF output device (multi-page, CMYK) |
654659
| `postforge/devices/qt/` | Interactive Qt display |
655660
| `postforge/utils/` | Memory analysis, profiling |
656661
| `postforge/resources/Init/` | PostScript initialization scripts |

docs/developer/code-quality-audit-2026-02-20.md

Lines changed: 0 additions & 108 deletions
This file was deleted.

docs/developer/contributing.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ python -m postforge samples/tiger.ps # After pip install -e .
3535
|-----------|---------|
3636
| `postforge/core/` | PostScript execution infrastructure (types, tokenizer, error handling, color spaces) |
3737
| `postforge/operators/` | PostScript language operators organized by functional area |
38-
| `postforge/devices/` | Output devices (PNG, PDF, SVG, Qt) |
38+
| `postforge/devices/` | Output devices (PNG, PDF, SVG, TIFF, Qt) |
3939
| `postforge/utils/` | System utilities (memory analysis, profiling) |
4040
| `postforge/resources/` | PostScript resource files (fonts, encodings, initialization scripts, device configs) |
4141
| `unit_tests/` | PostScript-based test suite |

docs/developer/visual-regression-testing.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ This catches unintended visual changes introduced by code modifications.
66

77
Three output devices are tested: **PNG** (direct pixel output), **PDF**
88
(rasterized via PyMuPDF at 300 DPI), and **SVG** (rasterized via PyMuPDF
9-
at 300 DPI). All three are tested by default.
9+
at 300 DPI). All three are tested by default. The TIFF device uses the same
10+
Cairo rendering pipeline as PNG and is not separately tested.
1011

1112
## Requirements
1213

0 commit comments

Comments
 (0)