mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-09 04:54:45 +00:00
Print: every copy on a sheet draws the same symbol instances
Each copy on a sheet draws within an equal share of the symbol objects the export has left (MAX_SYMBOL_NODES), so a document near the limit leaves out the same instances in every copy instead of dropping them from the later copies. The limit stays export-wide; one copy per page draws as before.
This commit is contained in:
@@ -118,6 +118,9 @@ pub(crate) struct Exporter<'a> {
|
||||
/// Objects the symbol instances written held, together
|
||||
/// ([`vectorcraft_doc::MAX_SYMBOL_NODES`]).
|
||||
symbol_nodes: u64,
|
||||
/// The most [`Self::symbol_nodes`] may reach: [`vectorcraft_doc::MAX_SYMBOL_NODES`], or less
|
||||
/// while one of the copies on a sheet is drawn ([`Writer::page`]).
|
||||
pub(crate) symbol_limit: u64,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -47,6 +47,7 @@ impl<'a> Exporter<'a> {
|
||||
in_layer: 0,
|
||||
paper: Rect::ZERO,
|
||||
symbol_nodes: 0,
|
||||
symbol_limit: vectorcraft_doc::MAX_SYMBOL_NODES,
|
||||
overprint: false,
|
||||
overprinted: false,
|
||||
invisible: false,
|
||||
|
||||
@@ -272,20 +272,23 @@ impl Exporter<'_> {
|
||||
let count = art.count() as u64;
|
||||
let map = doc.symbol_slice_map(symbol, *xf);
|
||||
let most = map.map_or(count, |m| m.most_placed(art, doc));
|
||||
let placed = vectorcraft_doc::charge_symbol_art(&mut self.symbol_nodes, most, || {
|
||||
let art = match map {
|
||||
Some(m) => m.place(art, doc),
|
||||
None => {
|
||||
let mut a = art.clone();
|
||||
a.transform(*xf, false);
|
||||
a
|
||||
}
|
||||
};
|
||||
// As `most_placed`, placed art counts its opacity-mask art.
|
||||
let count = if map.is_some() { art.count_with_masks() } else { art.count() } as u64;
|
||||
(art, count)
|
||||
let within = self.symbol_nodes.saturating_add(most) <= self.symbol_limit;
|
||||
let placed = within.then(|| {
|
||||
vectorcraft_doc::charge_symbol_art(&mut self.symbol_nodes, most, || {
|
||||
let art = match map {
|
||||
Some(m) => m.place(art, doc),
|
||||
None => {
|
||||
let mut a = art.clone();
|
||||
a.transform(*xf, false);
|
||||
a
|
||||
}
|
||||
};
|
||||
// As `most_placed`, placed art counts its opacity-mask art.
|
||||
let count = if map.is_some() { art.count_with_masks() } else { art.count() } as u64;
|
||||
(art, count)
|
||||
})
|
||||
});
|
||||
if let Some(art) = placed {
|
||||
if let Some(art) = placed.flatten() {
|
||||
self.node(s, &art, page, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,7 +149,12 @@ impl Writer {
|
||||
let doc = ex.doc;
|
||||
let page_group = fits && !self.pdf13 && (doc.page_isolate || doc.page_knockout);
|
||||
let cmyk_page_group = fits && self.cmyk && !page_group && ex.shows_transparency();
|
||||
// Each copy draws within an equal share of the symbol objects the export has left
|
||||
// ([`vectorcraft_doc::MAX_SYMBOL_NODES`]), so every copy leaves out the same instances.
|
||||
let limit = ex.symbol_limit;
|
||||
let share = limit.saturating_sub(ex.symbol_nodes) / moves.len() as u64;
|
||||
for m in moves {
|
||||
ex.symbol_limit = ex.symbol_nodes.saturating_add(share);
|
||||
let place = place_at(*m);
|
||||
let placed = place != Affine::IDENTITY;
|
||||
if placed {
|
||||
@@ -180,6 +185,7 @@ impl Writer {
|
||||
s.pop();
|
||||
}
|
||||
}
|
||||
ex.symbol_limit = limit;
|
||||
if fits && let Some(art) = sheet.marks {
|
||||
// The drawing space the page shows (the marks' area).
|
||||
let shown = sheet.view.inverse().transform_rect_bbox(paper);
|
||||
|
||||
@@ -301,6 +301,56 @@ fn each_copy_prints_with_its_marks() {
|
||||
assert_eq!(marks, 8 * copies, "8 trim marks around each copy");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn every_copy_on_a_sheet_draws_the_same_symbol_instances() {
|
||||
// A symbol of three squares (4 objects with its group), placed twice.
|
||||
let mut d = doc(100.0, 100.0, &[]);
|
||||
let squares = (0..3)
|
||||
.map(|i| {
|
||||
let r = Rect::new(f64::from(i) * 10.0, 0.0, f64::from(i) * 10.0 + 5.0, 5.0);
|
||||
Arc::new(Node::path(d.alloc_id(), shapes::rectangle(r), Appearance::basic(rgb(1.0, 0.0, 0.0), Paint::None, 0.0)))
|
||||
})
|
||||
.collect();
|
||||
let art = Node::group(d.alloc_id(), squares);
|
||||
d.symbols.push(vectorcraft_doc::Symbol { name: "Squares".into(), art: Arc::new(art) });
|
||||
let layer = d.default_layer().unwrap();
|
||||
for y in [10.0, 50.0] {
|
||||
let instance = NodeKind::SymbolInstance { symbol: "Squares".into(), xf: vectorcraft_geom::Affine::translate((10.0, y)) };
|
||||
let id = d.alloc_id();
|
||||
d.insert(Some(layer), 0, Node::new(id, instance)).unwrap();
|
||||
}
|
||||
let set = PdfSettings { compression: CompressionSettings { compress_text: false, ..Default::default() }, ..Default::default() };
|
||||
let mut w = crate::export::Writer::new(&d, &set, "Copies", None, false).unwrap();
|
||||
let mut ex = crate::export::Exporter::new(&d, &set);
|
||||
// Room for 10 symbol objects: the first copy could write both instances (8) and leave the
|
||||
// second copy none.
|
||||
ex.symbol_limit = 10;
|
||||
let copies = [vectorcraft_geom::Vec2::ZERO, vectorcraft_geom::Vec2::new(200.0, 0.0)];
|
||||
let page = Rect::new(0.0, 0.0, 300.0, 100.0);
|
||||
let sheet = crate::export::Sheet {
|
||||
size: (300.0, 100.0),
|
||||
unit: 1.0,
|
||||
trim: page,
|
||||
bleed: page,
|
||||
view: vectorcraft_geom::Affine::IDENTITY,
|
||||
window: None,
|
||||
place: vectorcraft_geom::Affine::IDENTITY,
|
||||
copies: &copies,
|
||||
area: Rect::new(0.0, 0.0, 100.0, 100.0),
|
||||
clip: false,
|
||||
marks: None,
|
||||
negative: false,
|
||||
links: false,
|
||||
};
|
||||
w.page(&mut ex, &sheet).unwrap();
|
||||
assert_eq!(ex.symbol_limit, 10, "the limit is the export's again after the sheet");
|
||||
w.absorb(ex);
|
||||
let (bytes, _) = w.finish(&mut std::collections::HashSet::new(), false).unwrap();
|
||||
let p = pages(&bytes);
|
||||
let left = p[0].1.iter().filter(|(r, _)| r.x0 < 200.0).count();
|
||||
assert_eq!((left, p[0].1.len() - left), (3, 3), "one instance in each copy: {:?}", p[0].1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tiles_cover_the_artboard_with_overlap() {
|
||||
// 1000 × 700 on Letter tiles (612 × 792), 36 pt overlap: 2 × 1.
|
||||
|
||||
Reference in New Issue
Block a user