mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-25 10:17:02 -06:00
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>
105 lines
4.1 KiB
Rust
105 lines
4.1 KiB
Rust
//! 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)
|
||
}))
|
||
}
|