The test encoder takes several components, scans of any of them, segments
between scans and per-table conditioning, and drops trailing zero bytes as
libjpeg does.
tests/mutation.rs runs a seeded mutation pass over the test images and
small SOF9, SOF10 and 12-bit seeds, checking that decode never panics
and stays within small limits. fuzz/ is a cargo-fuzz crate (own
workspace) with a decode target and those seeds. jpeg.yml runs fmt,
clippy, tests and a wasm32 check on Linux. The README gains a testing
section.
Check coefficients, planes and output against max_alloc together. Stop an
arithmetic scan once it has consumed more than 8 zero bytes past its data or
misses a restart marker, and cap the decoded blocks at 64 passes over the
frame instead of capping the scan count.
Commit testorig.jpg, testimgari.jpg and testorig12.jpg with a jpegtran
Huffman transcode of testimgari.jpg and libjpeg-turbo's 12-bit decode
of testorig12.jpg. The arithmetic-coded image must match its transcode
exactly; the 8-bit and 12-bit decodes are compared against zune-jpeg
and libjpeg-turbo within a small tolerance. Replaces the ignored
conformance_samples test.
Every scan walks its whole MCU grid, and an arithmetic-coded scan does so
even with no entropy-coded data (the decoder is fed zeros), so a small file
repeating empty scan headers multiplied the work by the frame size. Scans
past the cap are dropped and the image is reported damaged, keeping what
the earlier scans decoded.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The jpeg_ext path set exif, xmp and dpi but not photoshop_resources, so APP13 8BIM blocks
(paths, Photoshop settings) were lost on import and re-export. Both decoder paths now fill
the same Metadata. Test: a 12-bit JPEG with an APP13 block keeps it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Bits now tracks how many buffered bits are zero padding supplied at a
marker or the end of the data; a read that consumes padding marks the
scan exhausted and the block returns an error, so the scan is reported
as damaged (or fails if no MCU decoded) even when EOI is present.
Arithmetic termination is unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
zune-jpeg decodes neither 12-bit extended sequential (SOF1, P=12) nor
arithmetic-coded (SOF9/SOF10, DAC) JPEGs. Route those files to a new
clean-room T.81 decoder (codecs/jpeg_ext.rs): Huffman sequential 12-bit,
Annex D arithmetic decoding with DAC conditioning (sequential F.2.4 and
progressive G.1.3, 8 or 12-bit), restart intervals, 1/3/4 components and
any sampling factors. Float IDCT, centred linear upsampling, the existing
YCbCr / Adobe CMYK / YCCK conventions; 12-bit output is U16 (scaled).
Allocations are checked against Limits first; every stream read is
bounds-checked and damaged entropy data stops the scan with a Truncated
warning instead of panicking.
Tests: a test-only arithmetic encoder round-trips coefficients through
sequential and progressive (incl. successive approximation) scans with
restarts; a hand-built 12-bit SOF1 block; baseline files relabelled SOF1
match zune; truncation/byte-flip sweeps never panic. Locally the
conformance samples testorig12.jpg and testimgari.jpg decode with a mean
abs diff < 1 level vs testorig.jpg (samples not committed).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* hayro-interpret: ink colors, overprint and font bytes for the flattener
Local patches for craft-prepress's PDF-in adapter, labeled Craft patch
(flatten) and listed in vendor/README.craft-reader.md:
- Device::set_overprint and Device::set_isolated, called where the
blend mode is set and after an isolated group is pushed.
- Color::ink_paint, ColorSpace::ink_space and ink_values: colors and
image samples as the inks they print, without converting to RGB.
RasterImage::with_inks gives native samples with Decode and Matte.
- OutlineFontData::embedded and index, and the CFF bytes of Type1C
fonts, so text can be embedded again.
- The sh operator ignores /Background (ISO 32000-2 8.7.4.5.1).
* plate-test: flattened-page checks, image alpha and scenes F01, F02
- report::run_flattened separates each scene's page after a caller's
flattener as well as the original, and structure::transparency lists
what transparency a page still has. plate-test doesn't depend on the
flattener.
- Scene images take an alpha channel, written as /SMask.
- F01: a partly transparent image over a flat fill; where its alpha is
0 the fill keeps its exact inks (vectorcraft#2160).
- F02: art hidden under opaque fills leaves no trace after flattening
(vectorcraft#2161).
* prepress: transparency flattener (flatten feature)
craft_prepress::flatten turns PDF pages with transparency into pages
without it, keeping inks, overprint and live text, for PDF/X-1a and
other output that can't carry transparency.
- pdf: reads a page into a display list in inks through hayro-interpret:
paths, outlined strokes, clips, groups, soft masks, overprint, spot
and DeviceN colors, images in their own color space, gradients,
rasterized mesh shadings, tiling patterns and glyph runs.
- font: prepares the source fonts for embedding again (TrueType,
OpenType, CFF wrapped as OpenType); others fall back to outlines with
ActualText.
- geom: planar regions with the shapes that cover each one (linesweeper).
- compose: composites each region per ink (separable and non-separable
blend modes, isolated and knockout groups, overprint per plate as ISO
32000 defines it); regions under images, gradients and soft masks are
marked complex.
- raster: complex regions as DeviceN images through plate mode, grid
snapped with a one-pixel outset; flatten_page.
- write: writes the result with the vendored krilla; flatten_document
writes every page into one PDF with the output intent, page boxes and
fonts shared across pages when their bytes match.
The regions and the compositor are ported from VectorCraft (commit
c2b15982, crates/pathops and crates/engine/src/cmd/flatten.rs), credited
in the crate README. scripts/plate-check.sh --flatten separates every
scene's flattened page with Ghostscript (local only). CI runs
craft-prepress with all features.
* krilla: DeviceN /Colorants from PDF 1.3, NChannel attributes from 1.6 (patch 11)
* prepress: flatten_document output PDF version and PDF/X version
* prepress: format the PDF/X-1a flatten test
* krilla, craft-prepress: write GTS_PDFXConformance next to GTS_PDFXVersion
PDF/X-1a:2003 files carry GTS_PDFXVersion (PDF/X-1:2003) and
GTS_PDFXConformance (PDF/X-1a:2003). Krilla patch 12 adds
SerializeSettings::pdfx_conformance, and FlattenDocumentOptions passes it
through.
* krilla, craft-pdf, craft-prepress: carry the modification date over
Krilla patch 13 adds Metadata::modification_date, written as /ModDate and
xmp:ModifyDate in place of the creation date. craft-pdf's Metadata gains
modified, and flatten_document takes it from the source's ModDate.
* craft-prepress: count the print annotations a read leaves out
ReadReport::dropped_annotations counts the annotations a printer would
paint (Print flag, not Hidden, with a normal appearance) when
ReadOptions::annotations is false; FlattenTotals sums it.
* craft-prepress: degenerate groups rasterize without a soft mask
A group the region sweep fails on is clipped to its members' boxes, which
no other group's items reach, so the paper is its only backdrop. Its
image is written opaque, as the plates of the items over paper, and the
page keeps no transparency for PDF/X-1a.
* craft-prepress: write untouched JPEG images as they are
A DCT-encoded DeviceGray or DeviceCMYK image that krilla's JPEG
passthrough writes with the same meaning (8 bits, no decode parameters,
the default gray Decode or the inverted Adobe CMYK one) keeps its data
in ImageItem::jpeg, and the writer embeds it unchanged when the image
has no soft mask. Other images are still 8-bit Flate samples.
* craft-prepress: keep group members with no transparency above them
Groups are transitive by bounding box, so a background frame put all the
body text on it under a transparent object far away, and the text came
back as outlined fills. A member is now drawn as it is when it is opaque
and, in every region its shapes are in, only members kept this way are
above it. The region fills under it are the composite of the other
members, and it is drawn over them in paint order.
* prepress: keep the backdrop under elements of a knockout group
* prepress: rasterize complex regions thinner than a point
* prepress: mark flatten errors, reasons and reports non_exhaustive
* prepress: bound the items a tiling pattern fill expands into
Each of up to MAX_TILING_CELLS cells copied the whole cell content, and a
cell can hold many items or an expanded pattern of its own. A fill whose
cells would hold more than MAX_TILING_ITEMS items in all, counting groups,
masks and nested patterns, is read as an image.
* prepress: stroke solid past the dash budget
A dash pattern that cuts a stroke into more than 250,000 pieces is outlined
solid, with the estimate hayro's renderer uses.
* prepress: bound the sampling and memory of rasterized tiling patterns
Each pixel sampled every cell over it, which for a BBox far larger than
its step is millions of cells per pixel; past MAX_TILING_CELLS cells per
pixel the fill paints nothing, and below it the image resolution drops to
keep the samples within MAX_TILING_SAMPLES. The canvases also hold a value
per page ink, so their pixel limits shrink with the ink count to keep the
memory of CMYK canvases, and the canvas size uses checked arithmetic.
* prepress: mark the page reader's error and report non_exhaustive
* prepress: format the tiling tests
* prepress: CodeRabbit fixes: no nonzero overprint mode for images and shadings, count dropped tilings, widen the hhea ascender
* prepress: refuse DeviceN spaces past the output's colorant limit and regions past the separated spot inks
* docking: default, compact and fill heights for dock column groups
DockContent::dock_heights gives a panel's body heights in a dock column while the
user has not resized its group. Groups at their default give way down to their
compact heights so the filling group gets its fill height; then every group gives
way down to its minimum. Saved heights stay. Without it, layouts are unchanged.
* docking: dock column defaults give way nearest the filler first
The group nearest the filling group shrinks to its compact height before the next
one gives way, so groups further up keep their defaults.
* docking: shared groups with different minimums split the rest evenly above their minimums
* docking: a group asking for a fill height fills the column beside groups without dock heights
* krilla: link annotations refuse non-finite or degenerate geometry (patch 13)
Link quad points that are empty or non-finite, link rects and page boxes
that overflow when flipped into PDF coordinates, and XYZ destinations with
a missing page or a non-finite point made krilla panic, which aborts a
WASM app. Document::finish returns InvalidAnnotationGeometry,
InvalidPageBox or InvalidDestination instead. Constructor signatures are
unchanged.
* krilla: error messages format page indices without overflow (patch 13)
* craft-pdf: write URI links only for http(s) with a host and mailto
A document from someone else can carry any link target, and an export
that turns every hyperlink into a PDF URI action hands those targets to
whoever opens the file: script and data URIs, OS protocol handlers,
UNC paths, //host/share, file:// and smb:// links to a host, and
Windows Search links naming a share.
safe_url allows only http and https URLs with a host and mailto with an
address, after trimming whitespace and comparing the scheme
case-insensitively. It is the rule DeckCraft uses for its slide show
and export, and is available without any feature.
write::uri_target returns the Krilla link target for an allowed URL and
None otherwise, so an app that builds its URI links with it writes no
link annotation for a refused target and leaves the page content as
drawn.
* craft-pdf: safe_url needs a real http(s) host and a mailto recipient
* security: bound vendored hayro CCITT rows of unknown height and JPEG 2000 codestream size
- CCITT: `/Rows 0` decoded to the end of the data; a bit of 2D code can repeat a whole row,
so a small stream produced gigabytes. Rows are now capped by the output limit (in the
stream's 1-bit rows) and 2^28 pixels.
- JPEG 2000: the decoder allocated its output from the codestream's own dimensions,
infallibly. Over 2^28 pixels or the output limit (at least 64 MiB) is refused before
decoding, and the buffer is reserved fallibly.
- SOURCE.json local hashes updated (ascii_85.rs's had not been after #253).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(ccitt): pass the output limit in the columns test
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
- Patch-mesh triangles are rebuilt after inflation, so the stored dot products match
the vertices.
- A stencil image restores the fill alpha itself when the saved-state bound skipped
its save.
- A resolved JP2 palette decodes to 8 bits whatever /BitsPerComponent says.
Cache the color space of an /ICCBased profile by its stream object, and on
a miss by its dictionary key and raw bytes, instead of the dictionary's
bytes alone, so two profiles with the same /N and /Length each draw through
their own profile. InterpreterCache::icc_profile_reads counts profile reads.
PdfCraft's two raster tests are adapted in tests/upstream_raster.rs.
A resource dictionary's /DefaultGray, /DefaultRGB and /DefaultCMYK
replace the device spaces that color operators, cs/CS, images and
shadings select (ISO 32000-2 8.6.5.6). Decoded images and shadings are
cached per default.
Defaults follow this tree's resource scoping (a form with /Resources
sees only its own), and they do not apply on a plate, so device color
separates onto the same inks as before. PdfCraft's raster tests are
adapted in upstream_raster.rs, with a plate test in plates.rs.
EncodedShadingPattern::extent records the device pixels a mesh's triangles
touched when it was encoded. set_paint returns PaintSetup: the mesh texture
covers that extent with Extend::Pad instead of the whole path, and a mesh with
no triangle on the canvas paints nothing. Output is unchanged; the knockout
erase pass and plate drawing are untouched. Adds hayro's mesh_extent_tests.
The page group composites over a transparent backdrop and is then put on the background, so a blend mode over an empty part of the page keeps its color. With a color replacement, and on a plate render, the background stays inside the group as its backdrop, so plate films are as before. Ledger hayro (18); the PdfCraft raster test is adapted in upstream_raster.rs.
An optional content membership dictionary with a visibility expression (/And, /Or, /Not over listed groups) shows its content when the expression is true, ahead of /OCGs and /P. Malformed expressions, or ones nested deeper than 32 levels, fall back to /OCGs and /P. Ledger hayro-interpret (40); the PdfCraft raster test is adapted in upstream_raster.rs.
From PdfCraft #3438 with its look-ahead bound (01af6cba3): an inline
image without a filter ends at the byte length its /W, /H, /CS, /BPC
and /IM give when white space and EI follow there, so an EI inside its
samples does not end it. Anything else keeps the EI look-ahead. A
computed end past the content window waits for more data only while it
fits within MAX_DECODED_STREAM.
Tests: PdfCraft's six content::tests unit tests.
A JPX image whose dictionary has no /ColorSpace, or one that does not resolve, takes its colors from the JP2 palette instead of drawing its raw indices as gray. Resolved palette colors are 8-bit samples whatever the index precision. From PdfCraft #3184, with its three jpx unit tests.
A trailer /Prev written as N G R is followed: it is the offset the referenced object holds when the section's own entries place it, and otherwise offset N. Each section's entries are read before its Prev is followed. From PdfCraft #2400, with its two xref unit tests; the local xref-stream bounds are kept.
From PdfCraft 0b670caf4:
- P7 (PdfCraft #3109, #3116): text in mode 7 (clip only) installs its
clip and reaches the device as GlyphDrawMode::Clip, which the hayro
renderer does not paint, so extraction that leaves out invisible OCR
text keeps a heading drawn through its clip. Test:
clipping_only_text_reaches_the_device_apart_from_invisible_text in
crates/pdf/tests/upstream_text.rs.
- (49) (PdfCraft #2949 and 09668ce88): a TrueType post table version
1.0, 2.0 or 3.0 written with its 16-bit halves swapped is accepted,
and a swapped 2.0 table's glyph names are read. Unit test on a
synthetic sfnt in font::blob::tests.
- (50) (PdfCraft #3958): the CFF glyph-name map is built once on first
use and shared between clones instead of scanning the charset per
code. PdfCraft's unit test comes with it.
From PdfCraft 0b670caf4:
- (51): the saved-state accounting (state_bytes, the saved_bytes update,
num_states) uses saturating sums, so a huge dash array or clip list
stays over the 64 MiB input bound on 32-bit targets instead of
wrapping.
- (46) and the hayro stencil paint (PdfCraft #2977): an image XObject's
group applies the fill opacity, so a stencil mask's paint no longer
carries it again (a mask at ca 0.5 was drawn at a quarter), a pattern
paint drops the opacity it captured at scn (Pattern::without_opacity),
and the mask is painted with the fill paint rather than the stroke
paint.
- PdfCraft #3246 (unledgered): a shading pattern takes the alpha current
at paint time (Pattern::set_alpha), so a gs after scn or SCN still
applies; plate paints take it the same way.
- (36) (PdfCraft #3035): a mesh pixel whose corner no triangle holds
strictly takes the triangle that covers its center, edges included
(Triangle::covers), so shared edges leave no seam. Marked
"PdfCraft patch (#3035)": the plate-mode code already uses (35) and
(36) for #2770's numbers.
Tests: the PdfCraft raster tests for (46), #3246 and (36), adapted in
crates/pdf/tests/upstream_raster.rs.
Document::add_optional_content_group writes an OCG; Surface::start_optional_content and
end_optional_content mark content with /OC BDC/EMC, registering the group under the stream's
/Properties resources. Marks nest independently of tagging, and misuse returns or records a
KrillaError instead of panicking. The catalog gets /OCProperties (hidden groups in /OFF) and,
opt-in, /PageMode /UseOC. PDF/A-1 reports ValidationError::OptionalContent; below PDF 1.5 the
version requirement is registered too.