mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-24 09:46:59 -06:00
Place keeps a clipped group's clip
Place flattened every top-level layer into its objects, so an SVG's clipped top-level group lost its clip and its clipping path came in as a loose, invisible path; sublayers lost their clips the same way. A clipping layer is now placed as one clip group, for SVG/DXF and for the art placed from an artboard (PDF, EPS, placed documents) alike. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
committed by
bflatastic
co-authored by
Claude Opus 5.5
parent
d0c302c1a3
commit
8d288337f5
@@ -222,7 +222,7 @@ fn load(p: &Value, cmd: &str, board: Option<Rect>) -> Result<Loaded> {
|
||||
// default, as Illustrator places one, it is its art's bounds.
|
||||
let framed = matches!(format.id, "svg" | "svgz") && crop && p.get("crop").is_some();
|
||||
let frame = doc.artboards.first().map(|a| a.rect).filter(|_| framed);
|
||||
(art_of(layers.iter().filter(|l| placeable(l)).flat_map(|l| l.children().into_iter().flatten())), frame)
|
||||
(art_of(layer_art(&layers)), frame)
|
||||
} else {
|
||||
// The page placed is a PDF import's only one.
|
||||
let index = if pdf { 0 } else { page - 1 };
|
||||
@@ -240,7 +240,16 @@ fn load(p: &Value, cmd: &str, board: Option<Rect>) -> Result<Loaded> {
|
||||
/// The placeable art of `layers` that lies on `board`.
|
||||
fn on_artboard(layers: &[Arc<Node>], board: Rect) -> Vec<Node> {
|
||||
let on_board = |c: &&Arc<Node>| c.visual_bounds().is_some_and(|b| b.intersect(board).area() > 0.0 || board.contains(b.origin()));
|
||||
art_of(layers.iter().filter(|l| placeable(l)).flat_map(|l| l.children().into_iter().flatten()).filter(on_board))
|
||||
art_of(layer_art(layers).filter(on_board))
|
||||
}
|
||||
|
||||
/// The objects of the placeable `layers`, a clipping layer as itself: its objects alone would lose
|
||||
/// their clip, and its clipping path would come in as a loose path.
|
||||
fn layer_art(layers: &[Arc<Node>]) -> impl Iterator<Item = &Arc<Node>> {
|
||||
layers.iter().filter(|l| placeable(l)).flat_map(|l| match &l.kind {
|
||||
NodeKind::Layer { clip: true, .. } => std::slice::from_ref(l),
|
||||
_ => l.children().map_or(&[][..], Vec::as_slice),
|
||||
})
|
||||
}
|
||||
|
||||
/// A clipping path of rectangle `r`, with id `id`.
|
||||
@@ -257,14 +266,15 @@ fn placeable(l: &Node) -> bool {
|
||||
l.visible && !matches!(l.kind, NodeKind::Layer { template: true, .. })
|
||||
}
|
||||
|
||||
/// The placeable art among `nodes`: guides left out, visible sublayers as groups.
|
||||
/// The placeable art among `nodes`: guides left out, visible sublayers as groups (clip groups for
|
||||
/// clipping layers).
|
||||
fn art_of<'a>(nodes: impl Iterator<Item = &'a Arc<Node>>) -> Vec<Node> {
|
||||
nodes
|
||||
.filter_map(|c| match &c.kind {
|
||||
NodeKind::Path { guide: true, .. } => None,
|
||||
NodeKind::Layer { children, .. } => placeable(c).then(|| {
|
||||
NodeKind::Layer { children, clip, .. } => placeable(c).then(|| {
|
||||
let mut g = (**c).clone();
|
||||
g.kind = NodeKind::Group { children: art_of(children.iter()).into_iter().map(Arc::new).collect(), clip: false };
|
||||
g.kind = NodeKind::Group { children: art_of(children.iter()).into_iter().map(Arc::new).collect(), clip: *clip };
|
||||
g
|
||||
}),
|
||||
_ => Some((**c).clone()),
|
||||
|
||||
@@ -375,3 +375,20 @@ fn an_svg_placed_with_crop_is_framed_by_its_viewbox() {
|
||||
assert!(near(path_bounds(&s, &r["ids"][0]), Rect::new(120.0, 100.0, 280.0, 300.0)), "{r}");
|
||||
}
|
||||
}
|
||||
|
||||
/// An SVG whose top-level group is clipped is placed as one clip group: the clip is kept, and its
|
||||
/// clipping path is not left behind as a loose path.
|
||||
#[test]
|
||||
fn a_clipped_top_level_group_stays_clipped() {
|
||||
let svg = br##"<svg xmlns="http://www.w3.org/2000/svg" width="200" height="100" viewBox="0 0 200 100">
|
||||
<defs><clipPath id="c"><path d="M0 0 L200 0 L200 100 L0 100 Z"/></clipPath></defs>
|
||||
<g id="Layer_1" clip-path="url(#c)"><path d="M100 -20 L250 -20 L250 40 L100 40 Z" fill="#f00"/></g>
|
||||
</svg>"##;
|
||||
let mut s = session();
|
||||
let r = place(&mut s, "c.svg", svg, json!({"link": false}));
|
||||
assert_eq!((r["width"].as_f64().unwrap(), r["height"].as_f64().unwrap()), (200.0, 100.0), "{r}");
|
||||
let placed = node(&s, &r["ids"][0]);
|
||||
let NodeKind::Group { children, clip: false } = &placed.kind else { panic!("{:?}", placed.kind) };
|
||||
let [inner] = children.as_slice() else { panic!("one object: {children:?}") };
|
||||
assert!(matches!(&inner.kind, NodeKind::Group { children, clip: true } if children.len() == 2), "{:?}", inner.kind);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user