Commit Graph
14 Commits
Author SHA1 Message Date
Ken MantleandKenM76 f3b643d081 prepress: separate a page in bands, past the 4 MP plate cap
* prepress: separate a page in bands, past the 4 MP plate cap

`plates_in_bands` finds a page's inks once, then separates it in row
bands of at most MAX_PLATE_PIXELS each, rendering every band through
craft-pdf raster's size/offset viewport. A page can now be separated at
print resolution with bounded memory: only the renderer's 8192-pixel
side limit and a 256 MP per-plate total (MAX_BANDED_PIXELS) lower the
resolution. `plates` is unchanged in behaviour: it is the one-band case
of the same code.

Tests: bands of 7 rows join byte for byte into the whole-page render of
an anti-aliased disc under an overprinting spot (fails when the band
offset is dropped), and a 40 pt page at 3700 dpi separates at full
resolution in bands under the cap (fails when banding uses the plate
cap's scale).

Refs #838.

* prepress: bands pass the renderer's whole-page limits

The page's pixel size came from `raster::dimensions` on the whole page,
which enforces the renderer's 64 MP and 8192-pixel side limits, so a
Letter page past about 830 dpi failed with OutputLimit instead of
banding. The size is now computed as the renderer floors it, and only
each band's viewport reaches the renderer. Bands are also capped at
MAX_SIDE rows, and ink discovery renders within the renderer's limits.

A Letter page at 900 dpi now separates in 19 bands at full resolution;
across the page the side limit still lowers it, near 960 dpi.

Test: a 10 x 2000 pt strip at 600 dpi (16 666 rows) separates in three
bands with its black square on the right rows. It fails with either the
whole-page dimensions call or the uncapped band height restored.

* prepress: check the page before discovery; say what the banded cap bounds

The page-size check now runs before ink discovery renders anything.
MAX_BANDED_PIXELS bounds pixels, not the number of bands; the doc
comment and the README caps table say so.

* prepress: count a sub-pixel side as one pixel in the banded cap

A side scaled under one pixel still renders one, so a very thin, tall
page could pass MAX_BANDED_PIXELS by far. banded_scale now fits the
other side alone in that case, and separate() refuses any floored size
over the cap before it renders a band.

* prepress: keep the banded area scale under the cap after rounding

The area scale came from the exact cap, so f32 rounding could floor a
page one row past it (17 x 15 058 824 pt at 72 dpi), which then failed
with OutputLimit. The same 0.1% margin as the one-pixel branches now
applies to the area scale too, with a test on that page.

---------

Co-authored-by: KenM76 <KenM76>
2026-11-12 10:41:24 -05:00
.CooCoo 3debdb13fc prepress: one wide curve no longer slows region cleanup for every other curve
* prepress: one wide curve no longer slows region cleanup for every other curve

The flattener's region cleanup looks up the input curves whose bounds hold
both ends of an output piece. The lookup searched back from a point by the
width of the widest input curve, so one curve as wide as the page made every
lookup scan nearly every input curve, and the cleanup grew quadratically with
the number of curves.

The curves are now grouped by bounds width, each group within a factor of two,
and each group is searched within its own widest bounds. The lookup returns
the same curves in the same order as before, so the regions are unchanged.

* prepress: region cleanup keeps a curve held just inside its right edge

Far from the origin, `x - width` can round above the left edge of a curve
whose bounds hold `x`, and the width-group search then skipped that curve.
The search's left edge is now rounded outward.

---------

Co-authored-by: dotCooCoo <RobertLeeLW@gmail.com>
2026-11-12 10:08:20 -05:00
Ken MantleandKenM76 35b4918481 prepress: composite the plates into a chosen output profile
Plates::composite converts through sRGB, which clips the press gamut
before a print profile sees it. Output converts press CMYK straight into
a gray, RGB or CMYK output profile (CMYK -> PCS -> output) at a chosen
intent and black point compensation, and Plates::composite_to combines
spot inks in that space: a multiply in gray or RGB, a multiply of the
complements in CMYK. CmsConversion::profile exposes the press profile.

The spot ramps move into shared helpers, used by composite and output;
the Lab encoding moves into one used by cms and output.

Part of #838 (item 2). The printer-leg semantics are adapted from pdfcer
(MIT, same author), crates/pdfcer-print/src/icc_proof.rs at 0a249a73;
the code is new, on craft-cms.

---------

Co-authored-by: KenM76 <KenM76>
2026-11-12 10:05:44 -05:00
1fc4c17fff prepress: printer's marks (#754)
* prepress: printer's marks

Marks::new lays out crop marks, bleed marks, registration targets and
process color bars around a trim box, with the sheet that holds them and
room for a page-information line. Marks::draw paints them onto a krilla
surface: lines in the registration color (Separation /All), bars in
DeviceCMYK. ISO 32000-2 14.11.3, 14.11.2, 8.6.6.4.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* prepress: keep the info line clear of the bottom target

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* prepress: clear targets only when drawn; cap line width at a third of the mark

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: KenM76 <KenM76>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-12 06:44:39 +01:00
Dylan M. Taylor 32589246be prepress: index Tidy::is_kept by x and y, validate a collinear run once (#851)
Findings 9ef8d922 and ec807eda (vectorcraft pathops boolean.rs analogues).
2026-11-11 15:48:26 -05:00
Dylan M. Taylor 0230aef364 prepress: bound flattener, tiling, image, raster grid and ink-set work (#545)
* prepress: bound flattener, tiling, image, raster grid and ink-set work

- compose: a group with more than MAX_GROUP_SEGMENTS (200k) path segments
  is rasterized whole (ComplexReason::TooManySegments) before its
  arrangement, whose crossings grow with the square of the segment count.
- geom: transparent_groups returns at once when nothing is transparent;
  overlap_groups folds the open boxes into one group past
  MAX_OVERLAP_COMPARISONS (4M) instead of comparing every pair.
- pdf: item_count charges path elements and glyphs, so the tiling budget
  bounds the geometry a cell expansion copies; Channels::apply refuses to
  spread samples past MAX_IMAGE_SAMPLES (256 MiB).
- shade: pixel_size refuses grids past MAX_GRID_PIXELS after rounding, so
  a hairline-thin area cannot make the other side unbounded.
- raster: Grid::within_budget rechecks the grid after scaling and scales
  by the pixel ratio when the square-root pass is not enough.
- ink: InkSet keeps a name index, so adding many spots is linear.

* address review: count image report only after channels apply

* address review: validate finiteness before segment-limit fallback, loop raster grid budget reduction
2026-11-11 05:55:27 -05:00
aiongg 24a472717a prepress: transparency flattener (flatten feature) (#194)
* 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
2026-10-24 16:15:41 -04:00
aiongg bdace448d9 prepress: non_exhaustive public enums, and checked plate sizes for hand-made Plates 2026-10-24 02:42:36 +00:00
aiongg fde8b638c3 prepress: to_pdf_color returns None for tints of no ink 2026-10-24 02:32:01 +00:00
aiongg f7b71a80a1 prepress: decode spot names from the PDF once, so A#41 and AA get their own plates 2026-10-24 02:29:33 +00:00
aiongg bd5c21682c hayro: DeviceCMYK luminosity masks take their values from the inks
A luminosity soft mask whose group is DeviceCMYK draws the group's four process plates over the backdrop and applies 1 - min(1, 0.3c + 0.59m + 0.11y + k) (ISO 32000-2 §10.4.2.2), so K 100% hides the masked object. Spot colorants on those plates paint through their alternate space (Plate::spots_as_process).
2026-10-23 07:26:21 +00:00
aiongg 4839279102 prepress: InkSet::alias_end and a public spot appearance ramp
Ink managers need the end of a spot's alias chain even when it prints as
process color, which plate_of reports as None. composite::appearance
gives apps the same spot swatch ramp the composite uses.
2026-10-20 14:09:30 +00:00
aiongg e6d2a24641 prepress: to_pdf_color writes ink colors as DeviceCMYK, Separation or DeviceN
Process tints become DeviceCMYK, one spot a Separation with its alternate, other mixes a
DeviceN space with process inks first, registration and None the All and None
separations. Lab spot alternates convert through the printing condition, as resolve does.
2026-10-20 03:57:10 +00:00
aiongg 4585efee97 prepress: rename craft-separations to craft-prepress 2026-10-20 03:39:36 +00:00