Files
vectorcraft/crates/engine/src/cmd/printtiling.rs
T
bflatasticandClaude Opus 5.5 1b9b37454b M4.80: Print Tiling tool and View > Show Print Tiling
View > Show Print Tiling (view.printTiling) draws the pages of the
document's print settings on the canvas, dashed and numbered, from
print.preview's own layout (print.tiling), so the canvas and the
printed job agree. The Print Tiling tool replaces the no-op tool: a
drag places the first page's imageable corner (print.tiling.set,
saved as print_setup.tileOrigin, one undo step); a double click on
the canvas or the tool button resets it. Tiles start at the origin
and extend to cover the art; the Print dialog shows the tool's
placement with a Reset button.

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

110 lines
4.8 KiB
Rust

//! View → Show Print Tiling (`view.printTiling`) and the Print Tiling tool (`print.tiling.set`):
//! the pages of the document's print settings drawn on the canvas, from `print.preview`'s layout
//! (`print.tiling`), and where the tool put them ([`vectorcraft_pdf::TileOrigin`], saved with
//! the print settings).
use serde_json::{Value, json};
use vectorcraft_doc::Document;
use vectorcraft_geom::Point;
use vectorcraft_pdf::{TileOrigin, TilingPage};
use super::fileio::pdf::pdf_error;
use super::print::{settings, to_json};
use super::*;
const SET: &str = "print.tiling.set";
pub fn specs() -> Vec<CommandSpec> {
vec![
cmd!(
query "view.printTiling",
"Show Print Tiling",
["View"],
None,
"{on?: bool (default: toggle)} show the pages of the document's print settings on the canvas (each document has its own setting; the Print Tiling tool shows them too) → {on}",
has_doc,
show
),
cmd!(
"print.tiling.set",
"Print Tiling",
[],
None,
"{origin: [x, y] (document point: the top-left corner of the first page's imageable area), artboard? (0-based: the artboard it is measured from; default the printed artboard containing origin, else the first; ignored with artboards: ignore)} | {reset: true} place the printed pages as the Print Tiling tool does, saved with the print settings (tileOrigin: {placed, x, y}, from the artboard's top-left corner; it replaces the placement, and tiles start there), as one undo step; reset → the placement places them again → {tileOrigin}",
has_doc,
set_origin
),
cmd!(
query "print.tiling",
"Print Tiling Pages",
[],
None,
"{settings?: {…print.setup settings} (over the document's)} the pages as View → Show Print Tiling draws them, from print.preview's layout → {tileOrigin, pages: [{artboard (0-based; null with artboards ignored), number (1-based tile across then down, or page), printed (tiles outside tileRange don't), page: [x0, y0, x1, y1] (the paper's edge), imageable: [x0, y0, x1, y1] (inside the unprintable margin)}] (document space; every tile when tiling, else each page once)}",
has_doc,
tiling
),
]
}
fn show(s: &mut Session, p: &Value) -> Result<Value> {
let st = s.doc_mut()?;
st.print_tiling = p.get("on").and_then(Value::as_bool).unwrap_or(!st.print_tiling);
Ok(json!({ "on": st.print_tiling }))
}
/// The pages of `doc`'s print settings in document space (what View → Show Print Tiling draws);
/// an error when they can't print.
pub fn pages(doc: &Document) -> Result<Vec<TilingPage>> {
pages_with(doc, &json!({}))
}
fn pages_with(doc: &Document, p: &Value) -> Result<Vec<TilingPage>> {
const C: &str = "print.tiling";
let set = settings(C, doc, p)?;
let pv = vectorcraft_pdf::preview(doc, &set).map_err(|e| pdf_error(C, e))?;
Ok(vectorcraft_pdf::tiling(&pv, &set))
}
fn tiling(s: &mut Session, p: &Value) -> Result<Value> {
let doc = &s.doc()?.doc;
let origin = settings("print.tiling", doc, p)?.tile_origin;
Ok(json!({ "tileOrigin": origin, "pages": pages_with(doc, p)? }))
}
/// `[x, y]` at `key` of `p`.
fn point(p: &Value, key: &str) -> Option<Point> {
let a = p.get(key)?.as_array()?;
Some(Point::new(a.first()?.as_f64()?, a.get(1)?.as_f64()?))
}
fn set_origin(s: &mut Session, p: &Value) -> Result<Value> {
let doc = &s.doc()?.doc;
let current = settings(SET, doc, &json!({}))?;
let mut set = current.clone();
set.tile_origin = if bool_or(p, "reset", false) {
TileOrigin::default()
} else {
let at = point(p, "origin").filter(|q| q.x.is_finite() && q.y.is_finite());
let at = at.ok_or_else(|| bad(SET, "give origin: [x, y], or reset: true"))?;
let regions = vectorcraft_pdf::print_regions(doc, &set).map_err(|e| pdf_error(SET, e))?;
let picked = match p.get("artboard").filter(|v| !v.is_null()) {
Some(v) => {
let i = v.as_u64().ok_or_else(|| bad(SET, "artboard must be a 0-based index"))? as usize;
regions.iter().find(|(a, _)| a.is_none() || *a == Some(i)).ok_or_else(|| bad(SET, format!("artboard {i} doesn't print")))?
}
None => regions.iter().find(|(_, r)| r.contains(at)).or(regions.first()).ok_or_else(|| bad(SET, "nothing prints"))?,
};
let corner = picked.1.origin();
TileOrigin { placed: true, x: at.x - corner.x, y: at.y - corner.y }
};
set.check().map_err(|e| pdf_error(SET, e))?;
if set != current {
let stored = to_json(&set)?;
s.edit("Print Tiling", |d, _| {
d.print_setup = Some(stored);
Ok(())
})?;
}
Ok(json!({ "tileOrigin": set.tile_origin }))
}