mirror of
https://github.com/storytold/figcraft.git
synced 2026-11-11 16:30:12 -06:00
M6: Sketch import: shape groups that are clipping masks clip
Sketch 51 and older files make masks from shape groups; the mask flag was lost when the group became its shape or boolean operation, so a masked map spilled out of its card. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
d77d57912b
commit
8f32e4f504
@@ -143,6 +143,7 @@ pub(crate) fn shape_group(cx: &mut Cx, l: &Value, parent: Affine, depth: usize,
|
||||
super::style::clear(m);
|
||||
super::style::apply(cx, st, cw, ch, m);
|
||||
}
|
||||
super::tree::mask(l, m);
|
||||
}
|
||||
return Some(node);
|
||||
}
|
||||
@@ -166,6 +167,7 @@ pub(crate) fn shape_group(cx: &mut Cx, l: &Value, parent: Affine, depth: usize,
|
||||
let mut node = base(l, "BOOLEAN_OPERATION", parent, world, w, h, root);
|
||||
node.insert("booleanOperation".into(), json!(op));
|
||||
node.insert("children".into(), Value::Array(chain));
|
||||
super::tree::mask(l, &mut node);
|
||||
if let Some(st) = l.get("style") {
|
||||
super::style::apply(cx, st, w, h, &mut node);
|
||||
}
|
||||
|
||||
@@ -233,3 +233,18 @@ fn style_on_frames_and_text_kinds_untouched() {
|
||||
assert!(matches!(node(&doc, "Board").kind, NodeKind::Frame));
|
||||
assert_eq!(node(&doc, "Board").fills.len(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn shape_group_masks_clip() {
|
||||
// Sketch 51 and older: the mask is a shape group.
|
||||
let inner = with(r("Outline"), "booleanOperation", json!(-1));
|
||||
let mask = with(shape_group("Card", 0.0, 0.0, 100.0, 50.0, vec![inner]), "hasClippingMask", json!(true));
|
||||
let a = rect("A", 0.0, 0.0, 300.0, 300.0);
|
||||
let b = with(r("B"), "shouldBreakMaskChain", json!(true));
|
||||
let (doc, _) = open(vec![group("G", 0.0, 0.0, 300.0, 300.0, vec![mask, a, b])]);
|
||||
let m = node(&doc, "Card");
|
||||
assert!(m.is_mask);
|
||||
let holder = doc.node(m.parent.unwrap()).unwrap();
|
||||
let kids: Vec<&str> = holder.children.iter().map(|c| doc.node(*c).unwrap().name.as_str()).collect();
|
||||
assert_eq!(kids, ["Card", "A"]);
|
||||
}
|
||||
|
||||
@@ -95,10 +95,7 @@ pub(crate) fn layer(cx: &mut Cx, l: &Value, parent: Affine, depth: usize, root:
|
||||
let shape = super::path::shape(cx, l, w, h)?;
|
||||
super::path::put_shape(cx, shape, &mut node);
|
||||
}
|
||||
if bool_field(l, "hasClippingMask") == Some(true) {
|
||||
node.insert("isMask".into(), json!(true));
|
||||
node.insert("maskType".into(), json!(if l.get("clippingMaskMode").and_then(Value::as_i64) == Some(1) { "ALPHA" } else { "VECTOR" }));
|
||||
}
|
||||
mask(l, &mut node);
|
||||
match class {
|
||||
"artboard" | "frame" => {
|
||||
let clips = if class == "artboard" { true } else { bool_field(l, "clipsContent").unwrap_or(true) };
|
||||
@@ -129,6 +126,14 @@ pub(crate) fn layer(cx: &mut Cx, l: &Value, parent: Affine, depth: usize, root:
|
||||
Some(Value::Object(node))
|
||||
}
|
||||
|
||||
/// A clipping mask's flag and kind (outline or alpha).
|
||||
pub(crate) fn mask(l: &Value, node: &mut JsonMap<String, Value>) {
|
||||
if bool_field(l, "hasClippingMask") == Some(true) {
|
||||
node.insert("isMask".into(), json!(true));
|
||||
node.insert("maskType".into(), json!(if l.get("clippingMaskMode").and_then(Value::as_i64) == Some(1) { "ALPHA" } else { "VECTOR" }));
|
||||
}
|
||||
}
|
||||
|
||||
/// Count one more layer; false (with a warning) once the file's budget is spent.
|
||||
fn count(cx: &mut Cx) -> bool {
|
||||
if cx.nodes >= cx.max_nodes {
|
||||
|
||||
Reference in New Issue
Block a user