Compatibility
PrawnX draws the pages Prawn 2.5, prawn-table 0.2.2 and prawn-svg 0.40.4 draw. Every exception is on this page, declared in the repository and checked by the oracle.
Prawn bugs PrawnX fixes
A bug counts only when a specification or Prawn’s own documentation proves it; a mere preference becomes an option. test/divergences.yml lists each one, its proof, the examples that show it, and the exact differences the oracle must report: no more, no fewer.
| Prawn’s behaviour | Proof |
|---|---|
Encrypted documents have no /ID in their trailer, so qpdf and strict readers reject them | ISO 32000-1 §7.5.5, trailer dictionary, ID: “Required if an Encrypt entry is present” |
| Built-in fonts measure Ÿ (byte 159) as ÿ, so right-aligned or wrapped text holding it overflows | ISO 32000-1 Annex D.2: WinAnsiEncoding maps byte 237 octal to Ydieresis, not ydieresis |
| The faces of one font collection share their width cache, so text in one face is measured with another’s widths | Prawn::Font#eql? compares the file name, family and size, which every face of a .ttc shares; Prawn documents each face as a font of its own |
| TrueType-flavoured subsetting of CFF fonts changes how their glyphs rasterize | The subset Prawn embeds (through TTFunk) renders differently from the unmodified font program; PrawnX keeps outlines and hints untouched, and renders like the font itself |
SVG web requests
SVG images do not fetch anything from the web unless asked to: svg data, enable_web_requests: true. prawn-svg 0.40 fetches by default, which lets a submitted SVG make the server request any URL. prawn-svg 1.0 plans the same default as PrawnX.
Lighter files
The pages are the same; the bytes are not. PrawnX writes at least PDF 1.5, with compressed streams, one compressed object stream and a cross-reference stream, where Prawn writes plain objects and an xref table. The oracle checks that each PrawnX file passes qpdf --check and weighs no more than Prawn’s. Tools that read PDF files byte for byte, rather than through a PDF parser, see a different file.
Off unless asked for
What would move pixels away from Prawn’s stays off unless asked for:
- OpenType ligatures and GPOS pair kerning, per font:
font "Inter.ttf", features: %i[liga kern]; - PDF/A-3b and Factur-X:
Prawn::Document.new(pdf_a: true), thenfactur_x.
Loading and versions
- PrawnX raises a
LoadErrorwhen the prawn gem is in the bundle too: remove prawn, prawn-table and prawn-svg from the Gemfile. Prawn::VERSIONis"2.5.0", the API PrawnX mirrors, since Prawn extensions check it;PrawnX::VERSIONis PrawnX’s own.- The public methods of Prawn 2.5 are listed in
test/contract/prawn-2.5.0.yml, generated byscript/contract_extract.rb. PrawnX accepts every call Prawn accepts on those of its contract;test/contract/decisions.ymlsettles each method outside the Stable API, with the reason when it is left out.
How it is tested
- An oracle. It renders every example of Prawn’s and prawn-table’s manuals and every sample SVG of prawn-svg with Prawn 2.5, prawn-table 0.2.2 and prawn-svg 0.40.4 and with PrawnX, each in its own process, and compares their pages, their text and its positions to 0.01 pt, their pixels at 150 dpi in two renderers that share no code (MuPDF and Poppler), their validity (
qpdf --check) and their weight. Examples listed intest/oracle_passing.txtmust stay identical; one that becomes identical must join the list.test/oracle_examplesholds documents of our own for what the manuals do not show. - The oracle against itself.
ORACLE_CANDIDATE=prawn bundle exec rake testruns Prawn as the candidate, which proves the comparison reports nothing when nothing differs. - A document fuzzer. Seeded, it writes random documents (text in all its options, boxes, columns, tables, shapes, pages) and compares them the same way. CI adds 300 from a fresh seed on every run;
script/fuzz_minimize.rbshrinks a failing one. - An input fuzzer. It corrupts sample PNGs, JPEGs and fonts (flipped bytes, truncations, forged lengths): each must render or raise an error PrawnX names, never crash inside a parser, hang or exhaust memory.
- veraPDF. PDF/A documents are checked by veraPDF when it is installed.
FUZZ_SEED=42 FUZZ_CASES=500 bundle exec ruby -Itest test/fuzz_test.rb
ruby -Itest script/fuzz_minimize.rb tmp/fuzz/documents/fuzz_0042.rb