mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-15 07:10:13 +00:00
Export: an HP-GL plot of the art's bounds measures images by the art their effects add
An image or a gradient mesh with a visible Crop Marks effect, or with a Vector Halftone effect that makes dots, is measured in a plot of the art's bounds. A halftone that makes no dots, such as one past the effect's cell limit, leaves the image out as before.
This commit is contained in:
@@ -1027,15 +1027,18 @@ fn overlaps(a: Rect, b: Rect) -> bool {
|
||||
/// Whether a plot draws nothing of `n` wherever it is, apart from what every export leaves out
|
||||
/// (hidden objects, guides and template layers): an object at 0% opacity, a non-printing layer, a
|
||||
/// placed image, a gradient mesh whose every point is at 0% opacity, or a path or compound path
|
||||
/// without effects whose fills and strokes paint nothing. A clipping path still clips when the
|
||||
/// plot draws nothing of it.
|
||||
pub fn plots_nothing(n: &Node) -> bool {
|
||||
/// without effects whose fills and strokes paint nothing. An image or a mesh in `doc` with a
|
||||
/// visible Crop Marks effect, or with a Vector Halftone effect that makes dots, is not one of them.
|
||||
/// A clipping path still clips when the plot draws nothing of it.
|
||||
pub fn plots_nothing(doc: &Document, n: &Node) -> bool {
|
||||
let unpainted = || n.appearance.effects.is_empty() && top_stroke(n).is_none() && top_fill(n).is_none();
|
||||
let dots = || vectorcraft_effects::addons::halftone::halftone_art(doc, n).is_some_and(|g| g.children().is_some_and(|c| !c.is_empty()));
|
||||
let adds_art = || vectorcraft_effects::has_crop_marks(n) || dots();
|
||||
n.opacity <= 0.0
|
||||
|| match &n.kind {
|
||||
NodeKind::Layer { printable, .. } => !printable,
|
||||
NodeKind::Image(_) => true,
|
||||
NodeKind::Mesh(m) => m.points.iter().all(|p| p.opacity <= 0.0),
|
||||
NodeKind::Image(_) => !adds_art(),
|
||||
NodeKind::Mesh(m) => !adds_art() && m.points.iter().all(|p| p.opacity <= 0.0),
|
||||
NodeKind::Path { .. } | NodeKind::Compound { .. } => unpainted(),
|
||||
_ => false,
|
||||
}
|
||||
|
||||
@@ -183,7 +183,7 @@ fn meshes_in_blends_are_outlines_and_clear_meshes_draw_nothing() {
|
||||
#[test]
|
||||
fn plots_nothing_picks_what_a_plot_draws_nothing_of() {
|
||||
type Make = fn(&mut Document) -> Node;
|
||||
let cases: [(&str, Make, bool); 6] = [
|
||||
let cases: [(&str, Make, bool); 7] = [
|
||||
("stroked", |d| stroked(d, rect(10.0, 10.0, 40.0, 40.0), Color::BLACK), false),
|
||||
("unpainted", |d| Node::path(d.alloc_id(), rect(10.0, 10.0, 40.0, 40.0), Appearance::basic(Paint::None, Paint::None, 1.0)), true),
|
||||
(
|
||||
@@ -211,6 +211,24 @@ fn plots_nothing_picks_what_a_plot_draws_nothing_of() {
|
||||
},
|
||||
true,
|
||||
),
|
||||
(
|
||||
"image with Crop Marks",
|
||||
|d| {
|
||||
let image = vectorcraft_doc::ImageObject {
|
||||
key: "raster-1".into(),
|
||||
width: 4,
|
||||
height: 2,
|
||||
xf: Affine::translate((10.0, 20.0)) * Affine::scale(5.0),
|
||||
link: None,
|
||||
placement: Default::default(),
|
||||
pixels: None,
|
||||
};
|
||||
let mut n = Node::new(d.alloc_id(), NodeKind::Image(Box::new(image)));
|
||||
n.appearance.effects.push(vectorcraft_doc::Effect { id: "cropMarks".into(), params: serde_json::json!({}), visible: true });
|
||||
n
|
||||
},
|
||||
false,
|
||||
),
|
||||
("mesh", |d| Node::new(d.alloc_id(), NodeKind::Mesh(mesh_at(10.0))), false),
|
||||
(
|
||||
"clear mesh",
|
||||
@@ -230,16 +248,16 @@ fn plots_nothing_picks_what_a_plot_draws_nothing_of() {
|
||||
vec![n]
|
||||
});
|
||||
let Some(n) = held else { panic!("{name}") };
|
||||
assert_eq!(plots_nothing(&n), nothing, "{name}");
|
||||
assert_eq!(plots_nothing(&d, &n), nothing, "{name}");
|
||||
assert_eq!(text(&plot(&d, &opts(&d))) == "IN;PU;SP0;", nothing, "{name}");
|
||||
}
|
||||
let mut d = doc_with(|d| vec![stroked(d, rect(10.0, 10.0, 40.0, 40.0), Color::BLACK)]);
|
||||
let layer = d.layers[0].id;
|
||||
assert!(!plots_nothing(&d.layers[0]));
|
||||
assert!(!plots_nothing(&d, &d.layers[0]));
|
||||
if let Some(NodeKind::Layer { printable, .. }) = d.node_mut(layer).map(|n| &mut n.kind) {
|
||||
*printable = false;
|
||||
}
|
||||
assert!(plots_nothing(&d.layers[0]));
|
||||
assert!(plots_nothing(&d, &d.layers[0]));
|
||||
assert_eq!(text(&plot(&d, &opts(&d))), "IN;PU;SP0;");
|
||||
}
|
||||
|
||||
|
||||
@@ -444,7 +444,7 @@ pub(super) fn art_page(cmd: &str, doc: &Document, p: &Value, left_out: impl Fn(&
|
||||
/// when there is none, which such an export refuses.
|
||||
pub fn export_art_bounds(doc: &Document, format: &str) -> Option<Rect> {
|
||||
let doc = &*doc.without_edit_modes();
|
||||
bounds_without(doc, &|n| format == "hpgl" && vectorcraft_cad::hpgl::write::plots_nothing(n))
|
||||
bounds_without(doc, &|n| format == "hpgl" && vectorcraft_cad::hpgl::write::plots_nothing(doc, n))
|
||||
}
|
||||
|
||||
/// The bounds of the visible art of `doc` with the objects that `left_out` picks hidden
|
||||
@@ -770,7 +770,7 @@ fn encode_files(doc: &Document, f: &Format, p: &Value) -> Result<Encoded> {
|
||||
{
|
||||
return Ok(enc);
|
||||
}
|
||||
let left_out = |n: &Node| f.id == "hpgl" && vectorcraft_cad::hpgl::write::plots_nothing(n);
|
||||
let left_out = |n: &Node| f.id == "hpgl" && vectorcraft_cad::hpgl::write::plots_nothing(doc, n);
|
||||
if use_artboards == Some(false)
|
||||
&& let Some((art, q)) = art_page(C, doc, p, left_out)?
|
||||
{
|
||||
|
||||
@@ -254,6 +254,29 @@ fn a_plot_of_the_arts_bounds_leaves_out_images_in_symbol_art() {
|
||||
}
|
||||
}
|
||||
|
||||
/// A placed image with a Vector Halftone effect is measured when the effect makes dots, and left
|
||||
/// out when it would take more cells than the effect makes.
|
||||
#[test]
|
||||
fn a_plot_of_the_arts_bounds_measures_images_by_their_halftone_dots() {
|
||||
let mut s = two_boards();
|
||||
let plot = |s: &mut Session| s.execute("document.export", &json!({"format": "hpgl", "useArtboards": false})).map(|v| bytes(&v));
|
||||
let printing = s.doc().unwrap().doc.layers[0].id;
|
||||
s.execute("layer.new", &json!({})).unwrap();
|
||||
let mut png = vec![];
|
||||
image::DynamicImage::new_rgb8(100, 100).write_to(&mut std::io::Cursor::new(&mut png), image::ImageFormat::Png).unwrap();
|
||||
s.execute("file.place", &json!({"name": "dots.png", "dataBase64": b64(&png), "at": [300, 300]})).unwrap();
|
||||
s.execute("layer.setProps", &json!({"id": printing, "visible": false})).unwrap();
|
||||
|
||||
// At 300 lines per inch a 100 pt image takes more cells than the effect makes.
|
||||
s.execute("effect.apply", &json!({"effect": "addon.color.halftone", "params": {"frequency": 300}})).unwrap();
|
||||
let e = plot(&mut s).unwrap_err().to_string();
|
||||
assert!(e.contains("no visible art"), "{e}");
|
||||
|
||||
s.execute("effect.apply", &json!({"effect": "addon.color.halftone", "params": {"frequency": 20}})).unwrap();
|
||||
let out = String::from_utf8(plot(&mut s).unwrap()).unwrap();
|
||||
assert!(out.contains("PD"), "{out}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn export_options_are_checked() {
|
||||
let mut s = two_boards();
|
||||
|
||||
@@ -1089,6 +1089,7 @@ way. `document.exportPdf {useArtboards: false}` writes that one page as well, an
|
||||
(unless `includeNonPrinting` or `createLayers` keeps them; a `.ai` file keeps layers by default). An HP-GL plot of the
|
||||
art's bounds is measured without what the plot leaves out: non-printing layers, objects at 0% opacity, placed images,
|
||||
gradient meshes whose every point is at 0% opacity, and paths without effects whose fills and strokes paint nothing.
|
||||
An image or a mesh with a Crop Marks effect, or with a Vector Halftone effect that makes dots, is measured.
|
||||
Such objects in the art of a symbol that is not dynamic are left out of its instances too, and an instance whose
|
||||
symbol holds nothing else is left out.
|
||||
A document whose only visible art is left out is refused, and Export As refuses an HP-GL plot of it before the save
|
||||
|
||||
Reference in New Issue
Block a user