Files
pdfcraft/crates/edit/tests/printer_marks_boxes.rs
T
Osvaldo DuranandClaude Opus 5.5 8c6c5a261b Print production: Add printer marks (dialog, edit, tool)
Trim marks at the trim box's corners, dashed bleed marks at the bleed box,
registration targets, a colour bar and a page-information line, drawn outside
the trim box as a removable page mark (MarkKind::PrinterMarks); the page is
widened to make room when asked and the trim box stays, so the printed page
cuts the same. Edit::AddPrinterMarks (scope Full, undoable) and Remove printer
marks through Edit::RemoveMarks; Dialog::PrinterMarks reachable from the
catalog, the palette and --view printer-marks; tools doc_printer_marks and
doc_remove_marks kind printer_marks; rows in every language; parity
print.printer-marks shipped.

Carries the review fixes:
- ATTRIBUTION.toml: the language files' checksums after the new rows
- Printer marks: drop the unreachable AddPrinterMarks pattern in scope_of
- Tests: printer marks read the document's marks through upstream's marks() (#1434)
- Printer marks: make room inside the crop box and keep the trim box on rerun
- Format with rustfmt
- Printer marks: keep accented labels and make room for the bleed marks
- Printer marks test: the page is letter size, so the bleed marks (33 pt beyond the trim box) fit
- i18n(ui): carry this PR's new wordings in the catalogues that must cover them
- Parity: evidence paths follow the tests moved into tests/it
- Fix the build after merging upstream/main
- fix(printer-marks): refuse numbers out of range, print on every plate

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-01 18:53:50 -05:00

109 lines
5.3 KiB
Rust

//! Printer marks and the page boxes: the marks must show inside the crop box, and running
//! again must not move the trim box or grow the page.
use pdfcraft_cos::{Dict, Document, Object};
use pdfcraft_edit::{PrinterMarks, add_printer_marks};
fn rect(r: [f64; 4]) -> Object {
Object::Array(r.iter().map(|v| Object::Real(*v)).collect())
}
/// One empty page with these boxes set on it.
fn page_with(boxes: &[(&[u8], [f64; 4])]) -> Document {
let mut doc = Document::new_empty();
let pages = doc.get(doc.root().unwrap()).as_dict().unwrap().reference(b"Pages").unwrap();
let mut page = Dict::new();
page.set(b"Type".to_vec(), Object::name("Page"));
page.set(b"Parent".to_vec(), Object::Ref(pages));
for (key, r) in boxes {
page.set(key.to_vec(), rect(*r));
}
let page = doc.add(page);
doc.update_dict(pages, |d| {
d.set(b"Kids".to_vec(), Object::Array(vec![Object::Ref(page)]));
d.set(b"Count".to_vec(), Object::Int(1));
})
.unwrap();
doc
}
fn page_box(doc: &Document, key: &[u8]) -> Option<Vec<f64>> {
let page = pdfcraft_model::pages(doc).first().cloned()?;
let a = page.dict.get(key).map(|o| doc.resolve(o))?;
Some(a.as_array()?.iter().filter_map(|v| doc.resolve(v).as_f64()).collect())
}
/// A crop box cut to the trim inside a large media box: the marks fit the media box but
/// not the crop box, so the crop box grows to show them and the trim box stays.
#[test]
fn printer_marks_widen_crop_inside_media() {
let mut doc = page_with(&[(b"MediaBox", [0.0, 0.0, 1000.0, 1000.0]), (b"CropBox", [200.0, 200.0, 800.0, 800.0])]);
let marks = PrinterMarks::default();
let reach = marks.reach();
let widened = add_printer_marks(&mut doc, &[0], &marks, false).unwrap();
assert_eq!(widened, vec![0]);
assert_eq!(page_box(&doc, b"MediaBox"), Some(vec![0.0, 0.0, 1000.0, 1000.0]), "the media box already had room");
let crop = page_box(&doc, b"CropBox").unwrap();
assert_eq!(crop, vec![200.0 - reach, 200.0 - reach, 800.0 + reach, 800.0 + reach]);
assert_eq!(page_box(&doc, b"TrimBox"), Some(vec![200.0, 200.0, 800.0, 800.0]));
}
/// No trim box given: it defaults to the crop box, which widening moves. Running again with
/// replace must keep the trim box and the page size from the first run.
#[test]
fn printer_marks_rerun_keeps_trim() {
let mut doc = page_with(&[(b"MediaBox", [0.0, 0.0, 612.0, 792.0])]);
let marks = PrinterMarks::default();
add_printer_marks(&mut doc, &[0], &marks, false).unwrap();
let media = page_box(&doc, b"MediaBox");
let crop = page_box(&doc, b"CropBox");
assert_eq!(page_box(&doc, b"TrimBox"), Some(vec![0.0, 0.0, 612.0, 792.0]));
assert_eq!(page_box(&doc, b"BleedBox"), Some(vec![0.0, 0.0, 612.0, 792.0]));
let widened = add_printer_marks(&mut doc, &[0], &marks, true).unwrap();
assert!(widened.is_empty(), "the page already has room");
assert_eq!(page_box(&doc, b"MediaBox"), media, "the page did not grow again");
assert_eq!(page_box(&doc, b"CropBox"), crop);
assert_eq!(page_box(&doc, b"TrimBox"), Some(vec![0.0, 0.0, 612.0, 792.0]));
}
/// A trim box past the media box is clipped to it. Widening the media box must not let it
/// grow to what was written.
#[test]
fn printer_marks_keep_the_trim_box_in_effect() {
let mut doc = page_with(&[(b"MediaBox", [0.0, 0.0, 100.0, 100.0]), (b"TrimBox", [-20.0, -20.0, 120.0, 120.0])]);
add_printer_marks(&mut doc, &[0], &PrinterMarks::default(), false).unwrap();
assert_eq!(page_box(&doc, b"TrimBox"), Some(vec![0.0, 0.0, 100.0, 100.0]));
}
/// Line weights and room out of range are refused, not clamped, and nothing past the largest
/// page a PDF can have is written.
#[test]
fn printer_marks_refuse_numbers_out_of_range() {
let mut doc = page_with(&[(b"MediaBox", [0.0, 0.0, 612.0, 792.0])]);
for line_weight in [-1.0, 0.0, 0.05, 5.0, 10_000.0, f64::NAN, f64::INFINITY] {
assert!(add_printer_marks(&mut doc, &[0], &PrinterMarks { line_weight, ..PrinterMarks::default() }, false).is_err(), "{line_weight}");
}
for room in [-1.0, 289.0, 1e9, f64::NAN, f64::INFINITY] {
assert!(add_printer_marks(&mut doc, &[0], &PrinterMarks { make_room: Some(room), ..PrinterMarks::default() }, false).is_err(), "{room}");
}
let mut big = page_with(&[(b"MediaBox", [0.0, 0.0, 14_400.0, 1000.0])]);
assert!(add_printer_marks(&mut big, &[0], &PrinterMarks::default(), false).is_err());
assert!(add_printer_marks(&mut big, &[0], &PrinterMarks { make_room: None, ..PrinterMarks::default() }, false).is_ok(), "left as it is");
}
/// Registration colour is the /All Separation, in the page's resources beside what it had.
#[test]
fn printer_marks_use_the_all_separation() {
let mut doc = page_with(&[(b"MediaBox", [0.0, 0.0, 612.0, 792.0])]);
add_printer_marks(&mut doc, &[0], &PrinterMarks::default(), false).unwrap();
let page = pdfcraft_model::pages(&doc).swap_remove(0);
let res = doc.resolve(page.dict.get(b"Resources").unwrap());
let res = res.as_dict().unwrap();
assert!(res.get(b"Font").is_some());
let spaces = doc.resolve(res.get(b"ColorSpace").unwrap());
let cs = doc.resolve(spaces.as_dict().unwrap().get(b"PCReg").unwrap());
let cs = cs.as_array().unwrap();
assert_eq!(cs[..3], [Object::name("Separation"), Object::name("All"), Object::name("DeviceCMYK")]);
assert!(cs[3].as_dict().unwrap().int(b"FunctionType") == Some(2));
}