Files
vectorcraft/crates/pdf/src/marks.rs
T
bflatasticandClaude Opus 5.5 b379f02527 M4.75: File → Print writes the job as a print-ready PDF: paper, scale, tiles, marks and bleed, separations
file.print {settings?, path?} lays the document out on paper and writes one PDF page per sheet:
copies (collated, reversed), artboards (all, a range, ignored), blank artboards skipped, paper
size and orientation (or turned per artboard), transverse, Print Layers (template layers never
print), placement on the imageable area, no scale / fit / custom / tiles of the paper or of the
imageable area with overlap and a tile range; printer's marks and bleed as PDF export draws
them; composite or separations (one grey plate per ink that prints, overprints honoured, spots
as process), emulsion down, negative; rendering intent and preserve numbers. print.preview lists
the pages, tiles, inks and warnings; print.setup saves the settings with the document
(Document.print_setup, one undo step).

PDF export and print share one page writer; the ink separation helpers move from the renderer to
vectorcraft_doc::inks and the range parser to vectorcraft_doc::range so print can use them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 15:13:10 +07:00

105 lines
4.1 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! Marks and Bleeds: the boxes of each exported page and its printer's marks. The marks' geometry
//! is the shared [`PrinterMarks`] (print draws the same marks); here it gets the page's bleed,
//! spot inks and page information.
use vectorcraft_color::Paint;
use vectorcraft_doc::marks::{MarkStyle, PrinterMarks, outset};
use vectorcraft_doc::setup::MAX_BLEED;
use vectorcraft_doc::{Document, Node, NodeId};
use vectorcraft_geom::Rect;
use crate::{BleedSettings, MarkKind, MarkSettings, PdfSettings};
impl MarkSettings {
/// These settings as the shared mark geometry.
pub fn printer_marks(&self) -> PrinterMarks {
PrinterMarks {
trim: self.trim,
registration: self.registration,
color_bars: self.color_bars,
page_info: self.page_info,
style: match self.kind {
MarkKind::Roman => MarkStyle::Roman,
MarkKind::Japanese => MarkStyle::Japanese,
},
weight: self.weight,
offset: self.offset,
}
}
}
impl PdfSettings {
/// The bleed of `doc`'s pages, `[top, bottom, left, right]` in points: the document's (Document
/// Setup; one read from a file is kept to 0–[`MAX_BLEED`]) with Use Document Bleed, else the
/// settings' own.
pub fn bleed_of(&self, doc: &Document) -> [f64; 4] {
self.bleed.of(doc)
}
}
impl BleedSettings {
/// The bleed of `doc`'s pages, `[top, bottom, left, right]` in points: the document's with
/// [`Self::use_document`] (kept to 0–[`MAX_BLEED`]), else these values.
pub fn of(&self, doc: &Document) -> [f64; 4] {
if self.use_document { doc.setup.bleed.map(|b| if b.is_finite() { b.clamp(0.0, MAX_BLEED) } else { 0.0 }) } else { self.values() }
}
}
/// The boxes of one page, in document coordinates.
#[derive(Clone, Copy, Debug, PartialEq)]
pub(crate) struct PageBoxes {
/// The artboard: the finished page.
pub trim: Rect,
/// The trim box grown by the bleed: the art it holds is kept.
pub bleed: Rect,
/// The sheet: the bleed box, grown to hold the printer's marks.
pub media: Rect,
}
impl PageBoxes {
pub fn new(trim: Rect, bleed: [f64; 4], marks: &PrinterMarks) -> Self {
let reach = marks.reach(bleed);
let most: [f64; 4] = std::array::from_fn(|i| bleed[i].max(reach[i]));
Self { trim, bleed: outset(trim, bleed), media: outset(trim, most) }
}
}
/// The page information printed under the marks: the file's title, the artboard (its name and
/// number) and the date and time of the export (UTC).
pub(crate) fn page_info(doc: &Document, title: &str, artboard: usize, created: Option<i64>) -> String {
page_info_of(doc, title, Some(artboard), created)
}
/// [`page_info`] of a page that may show no artboard (print with artboards ignored).
pub(crate) fn page_info_of(doc: &Document, title: &str, artboard: Option<usize>, created: Option<i64>) -> String {
let title = if title.trim().is_empty() { "Untitled" } else { title.trim() };
let mut info = title.to_string();
if let Some(i) = artboard {
let name = doc.artboards.get(i).map_or("", |a| a.name.as_str());
info.push_str(&format!(" · {name} ({} of {})", i + 1, doc.artboards.len()));
}
if let Some(t) = created {
let [y, mo, d, h, mi, _] = vectorcraft_doc::metadata::civil(t);
info.push_str(&format!(" · {y:04}-{mo:02}-{d:02} {h:02}:{mi:02} UTC"));
}
info
}
/// The marks of a page as art (`None` without marks): its spot inks get colour bar patches.
pub(crate) fn art(doc: &Document, marks: &PrinterMarks, boxes: &PageBoxes, bleed: [f64; 4], info: &str) -> Option<Node> {
if !marks.any() {
return None;
}
let spots: Vec<Paint> = doc
.spot_names()
.into_iter()
.filter_map(|name| doc.global_color(&name).map(|color| Paint::Solid { color, swatch: Some(name), tint: 1.0 }))
.collect();
// The marks are drawn straight from this art, so their ids don't matter.
let mut next = 0;
Some(marks.art(boxes.trim, bleed, &spots, info, &mut || {
next += 1;
NodeId(next)
}))
}