Skip to content

M11: Add Printer Marks (crop, bleed, registration, colour bars, page information) - #3038

Open
saad-mughal435 wants to merge 1 commit into
storytold:mainfrom
saad-mughal435:feat/printer-marks
Open

saad-mughal435 wants to merge 1 commit into
storytold:mainfrom
saad-mughal435:feat/printer-marks

Conversation

@saad-mughal435

Copy link
Copy Markdown

What this changes

Print Production ▸ Add Printer Marks, the first of gap 18's print-production tools: crop and bleed marks, registration targets, a CMYK colour bar and a page-information line, placed around any page box (the trim box by default).

They are written as printer's mark annotations, the subtype ISO 32000-2 §12.5.6.20 defines for exactly these graphics (registration targets, colour bars, cut marks), each carrying the spec's /MN name. Annotations rather than baked page content, so adding marks is not a destructive edit: the page's content stream and resources are untouched and the marks come off again. The rest of the codebase already handles the subtype correctly (a11y keeps printer marks out of the structure tree, redact keeps them, the renderer's inspector skips them), so this adds the producer those filters were already written for.

Details worth knowing:

  • Crop, bleed and registration marks and the page information draw in a Separation with the colorant name All (§8.6.6.4), so they reach every plate. The colour bar's patches are DeviceCMYK.
  • Boxes resolve as §14.11.2 requires: the crop box defaults to the media box and the bleed, trim and art boxes to the crop box, each clipped to the media box.
  • The colour bar and page information are placed beyond whatever the corner marks and targets actually occupy, measured from those marks rather than re-derived from the style. Deriving it twice is how a bar ends up inside a registration target, or inside bleed marks that sit further out than the reference box does; a test over four very different styles pins this.
  • Marks reach outside the box by design, so they need room on the sheet. grow_media enlarges the media box to fit them, writing it on the page so a shared /Pages node keeps the size other pages inherit; removal puts the original back, or takes the entry away again when the page had no media box of its own. Without grow_media, the pages whose marks fall outside are reported, never dropped.
  • Only marks PdfCraft wrote (/PCPrinterMark) are removed, so another producer's marks survive.

The code lives in a new crates/prepress — the L4 slot xtask/src/layers.rs and its SIDEWAYS list already reserved for print production, so this fills a reserved seat rather than inventing a crate. Registry ids print.printer_marks and print.printer_marks.remove; tools doc_printer_marks and doc_remove_printer_marks.

How you verified it

cargo fmt --check -p pdfcraft-prepress -p pdfcraft-engine -p pdfcraft-automation
cargo clippy -p pdfcraft-prepress -p pdfcraft-engine -p pdfcraft-automation --all-targets -- -D warnings
cargo test -p pdfcraft-prepress          # 22 passed
cargo test -p pdfcraft-engine            # 274 + 2 + 1 + 6 passed, 1 ignored (private fixture)
cargo test -p pdfcraft-automation        # 35 + 176 + 8 + 9 + 4 + 2 passed
cargo xtask layers                       # OK: 38 crates, no layering violations
cargo xtask assets                       # ok (no assets added)
cargo xtask parity                       # L area 39 features, 17 shipped, 1 partial, 44.9%
  • cargo fmt --check
  • clippy with -D warnings on the crates I changed
  • tests pass on the crates I changed
  • cargo xtask layers and cargo xtask assets

Tests. 22 unit tests in crates/prepress/src/tests.rs (box resolution and malformed boxes, per-mark geometry, the no-overlap property across four styles, refusals, media-box growth and restoration, hostile pages) plus an end-to-end printer_marks_go_on_report_what_did_not_fit_and_come_off_again in crates/automation/tests/automation.rs that goes through the tools, saves, reopens and removes.

Three of them I watched fail before the fix, by reverting it:

  1. The clearance bug was found by its own test, not written after it. The first version derived the band from offset + registration_reach, the distance to a target's centre; the target extends another reach past that, so the colour bar landed inside it. The default style hid this (24 > 22.25) and the extreme style in the table exposed it. Reverting to the style-derived band fails the_colour_bar_and_page_information_clear_the_corner_marks_and_targets.
  2. Dropping the media-box restoration fails the end-to-end test on removal puts the sheet back (left: (248.25, 374.65), right: (200.0, 300.0)).
  3. Always writing the saved box back, instead of removing the entry for a page that inherited it, fails growing_an_inherited_media_box_leaves_the_other_pages_and_the_file_as_they_were.

Never-crash

  • No new unwrap/expect/panic!/unreachable!/todo! outside tests (the crate carries the deny attribute)
  • No input-derived indexing; MAX_MEASURE caps every style measurement and non-finite values are refused with a message naming which one; saturating_add on the counters and page numbers
  • No unsafe
  • hostile_pages_and_an_empty_selection_never_panic runs both entry points over huge, reversed, degenerate, non-numeric and rotated pages, a dangling /Annots reference and a junk /Annots array, with and without grow_media

Provenance

  • No asset from an Adobe product; nothing traced or imitated. No assets at all: every mark is drawn from operators generated in code.
  • No font files committed. The page-information line uses the standard-14 Helvetica with WinAnsiEncoding through pdfcraft-fonts, and text it cannot draw is refused rather than written as ?, matching add_watermark.
  • No GPL/AGPL code copied in
  • Behaviour comes from ISO 32000-2 (§12.5.6.20 printer's mark annotations and their /MN names, §8.6.6.4 the All colorant, §14.11.2 page boxes, §7.7.3.4 inheritance) and from prepress practice, not from reading another implementation
  • cargo xtask assets passes

Security

  • No paths are taken or resolved: the tools act only on an open document id, so --root confinement is untouched
  • MCP and the control channel unchanged
  • No new outbound network access, and no new dependency: prepress uses cos, fonts, model, serde and thiserror, all already in the tree

Parity and reach

  • print.printer-marks set to partial, with notes saying what is still missing: a dialog, Acrobat's mark styles beyond the default geometry, Japanese mark styles, marks on a layer, and text that reads upright on a rotated page
  • Reachable headlessly: doc_printer_marks and doc_remove_printer_marks, which report marks, clipped, grown and no_bleed
  • Docs updated: new crates/prepress/README.md; docs/gaps.md gap 18; docs/architecture.md (the L4 row, and prepress taken off the not-yet-existing list); ROADMAP.md progress log and revision history; docs/target-app-parity.md L Print row

Anything a reviewer should look at first

Three judgement calls I would like pinned or corrected:

  1. Annotations, not page content. I took §12.5.6.20 plus the existing PrinterMark handling across a11y, redact and render as the intended design. If PdfCraft would rather bake marks into the content stream (which is what some prepress workflows expect on output), say so: the geometry and the drawing code are reusable, only the writing changes.
  2. A new crate. prepress was reserved in the layer table and in SIDEWAYS, so I filled it rather than putting this in print (which is L4 and so could not reach organize's page-box code anyway) or in edit. If you would rather it went elsewhere, it is a contained move.
  3. docs/target-app-parity.md L Print said 15 shipped where the checklist measures 17, so it was already stale before this PR. I set the row to the measured numbers and noted in the revision history that the counts were re-read, following the precedent of the 2026-10-11 row. I did not re-judge the readiness score or the hours. Say the word and I will revert that row to text-only changes.

One deliberate restraint: marks are placed in unrotated user space, so on a page with /Rotate they stay locked to the trim geometry and the page-information text turns with the page rather than reading upright. That is in the README, the parity notes and a test. /PCStampRotate and #1398 show the house pattern for counter-rotating, so this is a clean follow-up, but I did not want to guess at the right behaviour for marks (which belong to the sheet, not the reading orientation) without a steer.

Crop and bleed marks, registration targets, a CMYK colour bar and a
page-information line, placed around any page box (the trim box by
default) for commercial printing.

They are written as printer's mark annotations, which is the subtype
ISO 32000-2 12.5.6.20 defines for exactly these graphics, each carrying
the spec's /MN name. Annotations rather than baked page content, so that
adding marks is not a destructive edit: the page's content stream and
resources are untouched, and the marks come off again. The rest of the
codebase already handles the subtype (a11y keeps printer marks out of
the structure tree, redact keeps them, the renderer's inspector skips
them), so this adds the producer those filters were waiting for.

Crop, bleed and registration marks and the page information draw in a
Separation with the colorant name All (8.6.6.4), so they reach every
plate. The colour bar's patches are DeviceCMYK.

Boxes resolve as 14.11.2 requires: the crop box defaults to the media
box and the bleed, trim and art boxes to the crop box, each clipped to
the media box. The colour bar and page information are placed beyond
whatever the corner marks and targets actually occupy, measured from
those marks rather than re-derived from the style, so they cannot land
inside a registration target or inside bleed marks that sit further out
than the reference box does.

Marks reach outside the box by design, so they need room on the sheet.
grow_media enlarges the media box to fit them, on the page itself so a
shared /Pages node keeps the size other pages inherit; removal puts the
original back, or takes the entry away again when the page had no media
box of its own. Without grow_media the pages whose marks fall outside
are reported, never dropped. Only marks PdfCraft wrote are removed, so
another producer's survive.

The code lives in a new crates/prepress, the L4 slot the layer table
and the sideways-edge list already reserved for print production.
Registry ids print.printer_marks and print.printer_marks.remove, tools
doc_printer_marks and doc_remove_printer_marks.

Still missing, and said so in the parity notes: a dialog, Acrobat's
mark styles beyond the default geometry, marks on a layer, and text that
reads upright on a page with /Rotate.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant