Files
pdfcraft/crates/create
Grzegorz MartensandClaude Opus 5.5 881c4150e9 M11: Export ▸ Images refuses a JBIG2 page larger than it exports, before decoding
The export checked an image's /Width × /Height against its 100-million-
pixel budget, and the JBIG2 page's width against /Width, but not the
page's height. The decoder decodes the whole page, rows past the
image's height too, and takes pages of up to about 2^28 pixels: a
20,000 × 4 image over a 20,000 × 10,000 JBIG2 page cost 200 million
pixels of decoding to export four rows. The page itself is now held to
the budget before it is decoded. Found by CodeRabbit on #4320.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P6yUtvWGPsJpMLyh6pxSXG
2026-10-29 20:47:53 +01:00
..

pdfcraft-create

Layer L4. Create a PDF (Acrobat's Create a PDF tool), execution plan M10.2:

  • blank(width, height, pages): an empty document;
  • from_images(&[(name, bytes)]): one page per image, sized from the image's resolution (PNG pHYs, JPEG JFIF density; 72 dpi when absent). JPEG data is embedded as is (/DCTDecode, grey, RGB or Adobe-inverted CMYK); PNG is decoded and stored with Flate, with transparency as a soft mask. An ICC profile in the file (JPEG APP2 ICC_PROFILE, PNG iCCP) that matches the image's colour space is kept as an /ICCBased colour space with the device space as /Alternate; images with the same profile share one profile object. JPEG metadata scanning skips marker fill bytes and reads application segments after the frame header, stopping at the first scan or end marker. Broken or mismatched ICC profiles still fall back to the device colour space;
  • from_images_with_resolution(images, ImageResolution::Dpi(dpi)): override the page resolution (1–1200 dpi), without resampling or recompressing image pixels. Use 72 dpi for one point per pixel, or ImageResolution::Embedded for the same behavior as from_images. The largest page side is still capped at 14,400 points;
  • from_text(text, …): plain text set in Helvetica, wrapped and paginated. Text Helvetica can't draw is refused, naming the line and the character, never set as ?.
  • from_text_escaped(text, …): the same for reports PdfCraft generates from documents (comment summaries, compare reports): such characters are written as [U+XXXX] and the first line says so (ESCAPED_NOTE).

Every function returns a pdfcraft_cos::Document; the caller writes it.

Image export and Save Image As apply each lossless image's Decode pair before converting its color components. JBIG2 images and masks (JBIG2 as their only filter) are decoded with hayro-jbig2, their /JBIG2Globals included, as JBIG2Decode gives them: black as 0. A JBIG2 page of another width than the image is skipped. Indexed Decode values select palette entries after rounding and clipping. Color-key masks compare the original samples. Missing, null or malformed Decode arrays keep the default mapping. Soft-mask Decode intervals map samples to opacity before the mask is resized, including reversed ranges and indirect entries. The existing 16-bit path still uses each sample's high byte. JPEG image Decode handling remains limited. ICC transforms and calibrated color conversion remain outside this export path.