@weasel-js/svg 1.11.1 → 1.13.0-rc.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { identity, multiply, pathToD, invert, splitByClosure, fillsOpenSubpaths, rotateAboutPoint, hasOpenSubpath, splitSubpaths } from '@weasel-js/geom';
2
2
  export { pathToD as serializePathD } from '@weasel-js/geom';
3
- import { isTileKind, boundsOfPath, transformPath, isSolidPaint, warmFonts, warmPaintKinds, isPaintKindKnown, solid, fillToBoundsFrame, getMarker, registerMarker, dwarn, unionAABB, createInsertOp, fillInPoseFrame, pathThroughPoseFrame, DEFAULT_SHAPE_FILL, pathInPoseFrame, poseLinearOf, poseFrameOf, DEFAULT_TEXT_STYLE, resolveScreenLength, pathFromD, isPatternPaint, resolveTextStyle, strokeToPath, toRuns, resolveRunFace, parseColor, rgbaToHex, layoutRuns, resolveRuns, resolveAlign, PathBuilder, PATH_M, PATH_L, PATH_Z, scaleVertexWidth, getPaintKind, resolveMarkerSize, paragraphDirection, scriptMetricsFor, vertexHalfWidths, isTilePaint, tileSpecOf } from '@weasel-js/core';
3
+ import { isTileKind, boundsOfPath, transformPath, isSolidPaint, warmFonts, warmPaintKinds, isPaintKindKnown, solid, fillToBoundsFrame, getMarker, registerMarker, dwarn, unionAABB, createInsertOp, fillInPoseFrame, resolveStroke, pathThroughPoseFrame, resolveFill, pathInPoseFrame, poseLinearOf, poseFrameOf, INHERIT, clipOutline, resolveScreenLength, pathFromD, isPatternPaint, resolveTextStyle, strokeToPath, toRuns, resolveRunFace, DEFAULT_TEXT_FILL, parseColor, rgbaToHex, layoutRuns, resolveRuns, resolveAlign, PathBuilder, PATH_M, PATH_L, PATH_Z, scaleVertexWidth, getPaintKind, resolveMarkerSize, paragraphDirection, scriptMetricsFor, vertexHalfWidths, isTilePaint, tileSpecOf } from '@weasel-js/core';
4
4
  import { tileGeometry } from '@weasel-js/core/patterns-builtin';
5
5
 
6
6
  // src/types.ts
@@ -3024,6 +3024,32 @@ function markerEntryFrom(el, id, role, strokeWidth, gradients, onWarn) {
3024
3024
  };
3025
3025
  }
3026
3026
 
3027
+ // src/groupPaint.ts
3028
+ var NO_GROUP_PAINT = { fill: false, stroke: false };
3029
+ var STROKE_PROPS = INHERITED_PROPERTIES.filter((p) => p.startsWith("stroke") || p.startsWith("marker"));
3030
+ var sets = (el, prop) => {
3031
+ const v = ownProp(el, prop);
3032
+ return v != null && v.trim() !== "inherit";
3033
+ };
3034
+ var setsFill = (el) => sets(el, "fill");
3035
+ var setsStroke = (el) => STROKE_PROPS.some((p) => sets(el, p));
3036
+ function viewportPaintFrom(el, from) {
3037
+ return { fill: from.fill && !setsFill(el), stroke: from.stroke && !setsStroke(el) };
3038
+ }
3039
+ function inheritsFrom(el, from) {
3040
+ const fill = from.fill && !setsFill(el);
3041
+ const stroke = from.stroke && !setsStroke(el);
3042
+ return fill || stroke ? { inherits: { ...fill ? { fill: true } : {}, ...stroke ? { stroke: true } : {} } } : {};
3043
+ }
3044
+ function groupPaint(el, from, style, ctm, gradients, onWarn) {
3045
+ const children = { fill: from.fill || setsFill(el), stroke: from.stroke || setsStroke(el) };
3046
+ const paint = {};
3047
+ if (children.fill) paint.fill = paintThroughMatrix(readPaint2(style, "fill", "#000000", gradients, onWarn), ctm);
3048
+ const stroke = children.stroke ? readStroke(style, gradients, onWarn) : void 0;
3049
+ if (stroke) paint.stroke = strokeThroughMatrix(stroke, ctm);
3050
+ return { paint: { ...paint, ...inheritsFrom(el, from) }, children };
3051
+ }
3052
+
3027
3053
  // src/parse.ts
3028
3054
  var SUPPORTED_GROUP_TAGS = /* @__PURE__ */ new Set(["g", "svg"]);
3029
3055
  function parseSvg(svg, opts = {}) {
@@ -3056,7 +3082,7 @@ function parseSvg(svg, opts = {}) {
3056
3082
  for (const [id, paint] of collectPatterns(root, onWarn)) gradients.set(id, paint);
3057
3083
  const rootStyle = deriveStyle(EMPTY_STYLE, root);
3058
3084
  const clips = collectClipPaths(root, onWarn);
3059
- const nodes = parseChildren(root, IDENTITY_MATRIX, rootStyle, gradients, clips, onWarn, uriToPrefix, opts.keepHidden === true);
3085
+ const nodes = parseChildren(root, IDENTITY_MATRIX, rootStyle, NO_GROUP_PAINT, gradients, clips, onWarn, uriToPrefix, opts.keepHidden === true);
3060
3086
  const markers = ingestMarkers(root, nodes, gradients, onWarn);
3061
3087
  const result = { nodes, warnings };
3062
3088
  if (markers.length > 0) result.markers = markers;
@@ -3157,7 +3183,7 @@ function collectElementMeta(el, uriToPrefix) {
3157
3183
  }
3158
3184
  return Object.keys(meta).length > 0 ? meta : void 0;
3159
3185
  }
3160
- function parseChildren(parent, ctm, style, gradients, clips, onWarn, uriToPrefix, keepHidden) {
3186
+ function parseChildren(parent, ctm, style, from, gradients, clips, onWarn, uriToPrefix, keepHidden) {
3161
3187
  const out = [];
3162
3188
  for (let i = 0; i < parent.children.length; i++) {
3163
3189
  const el = parent.children[i];
@@ -3165,7 +3191,7 @@ function parseChildren(parent, ctm, style, gradients, clips, onWarn, uriToPrefix
3165
3191
  if (ns && ns !== "http://www.w3.org/2000/svg" || isStrokeOutline(el)) continue;
3166
3192
  const hidden = isHiddenElement(el, style);
3167
3193
  if (hidden && !keepHidden) continue;
3168
- const node = unwrapStrokeGroup(el, parseElement(el, ctm, style, gradients, clips, onWarn, uriToPrefix, keepHidden));
3194
+ const node = unwrapStrokeGroup(el, parseElement(el, ctm, style, from, gradients, clips, onWarn, uriToPrefix, keepHidden));
3169
3195
  if (!node) continue;
3170
3196
  const parsed = Array.isArray(node) ? node : [node];
3171
3197
  if (hidden) for (const n of parsed) n.hidden = true;
@@ -3173,7 +3199,7 @@ function parseChildren(parent, ctm, style, gradients, clips, onWarn, uriToPrefix
3173
3199
  }
3174
3200
  return out;
3175
3201
  }
3176
- function parseElement(el, ctm, style, gradients, clips, onWarn, uriToPrefix, keepHidden) {
3202
+ function parseElement(el, ctm, style, from, gradients, clips, onWarn, uriToPrefix, keepHidden) {
3177
3203
  const tag = el.tagName.toLowerCase();
3178
3204
  if (IGNORED_TAGS.has(tag)) return null;
3179
3205
  if (tag === "g" && el.hasAttribute("data-weasel-image")) {
@@ -3188,20 +3214,7 @@ function parseElement(el, ctm, style, gradients, clips, onWarn, uriToPrefix, kee
3188
3214
  if (clipRaw != null && readProperty("clip-path", clipRaw) !== "none") {
3189
3215
  onWarn(`clip-path on <${tag}> is not modeled; only a <g> clips, so it is drawn unclipped`);
3190
3216
  }
3191
- if (tag === "g") {
3192
- const local = parseTransform(el.getAttribute("transform"), onWarn);
3193
- const childCtm = multiply(ctm, local);
3194
- const childStyle = deriveStyle(style, el);
3195
- const children = parseChildren(el, childCtm, childStyle, gradients, clips, onWarn, uriToPrefix, keepHidden);
3196
- const opacity2 = ownValue(el, "opacity");
3197
- const group = { kind: "group", children };
3198
- const clip = clipFor(el, childCtm, clips, onWarn);
3199
- if (clip) group.clip = clip;
3200
- if (opacity2 != null) group.opacity = opacity2;
3201
- const meta2 = collectElementMeta(el, uriToPrefix);
3202
- if (meta2) group.meta = meta2;
3203
- return group;
3204
- }
3217
+ if (tag === "g") return parseGroup(el, ctm, style, from, gradients, clips, onWarn, uriToPrefix, keepHidden);
3205
3218
  if (SUPPORTED_GROUP_TAGS.has(tag)) {
3206
3219
  const childStyle = deriveStyle(style, el);
3207
3220
  const x = parseFloat(el.getAttribute("x") ?? "0");
@@ -3210,13 +3223,14 @@ function parseElement(el, ctm, style, gradients, clips, onWarn, uriToPrefix, kee
3210
3223
  if (el.hasAttribute("viewBox")) {
3211
3224
  onWarn("nested <svg> viewBox is not modeled; its children are not rescaled");
3212
3225
  }
3213
- return parseChildren(el, multiply(ctm, offset), childStyle, gradients, clips, onWarn, uriToPrefix, keepHidden);
3226
+ return parseChildren(el, multiply(ctm, offset), childStyle, viewportPaintFrom(el, from), gradients, clips, onWarn, uriToPrefix, keepHidden);
3214
3227
  }
3215
3228
  if (tag === "text") {
3216
3229
  const textNode = parseTextElement(el, ctm, style, gradients, onWarn);
3217
3230
  if (textNode && !Array.isArray(textNode) && textNode.kind === "text") {
3218
3231
  const meta2 = collectElementMeta(el, uriToPrefix);
3219
3232
  if (meta2) textNode.meta = meta2;
3233
+ Object.assign(textNode, inheritsFrom(el, from));
3220
3234
  }
3221
3235
  return textNode;
3222
3236
  }
@@ -3271,6 +3285,7 @@ function parseElement(el, ctm, style, gradients, clips, onWarn, uriToPrefix, kee
3271
3285
  if (stroke) node.stroke = stroke;
3272
3286
  if (opacity != null) node.opacity = opacity;
3273
3287
  if (rotation != null) node.rotation = rotation;
3288
+ Object.assign(node, inheritsFrom(el, from));
3274
3289
  if (tag === "line" && (leafStyle["fill"] ?? null) == null) {
3275
3290
  node.fill = { kind: "none" };
3276
3291
  }
@@ -3278,6 +3293,20 @@ function parseElement(el, ctm, style, gradients, clips, onWarn, uriToPrefix, kee
3278
3293
  if (meta) node.meta = meta;
3279
3294
  return node;
3280
3295
  }
3296
+ function parseGroup(el, ctm, style, from, gradients, clips, onWarn, uriToPrefix, keepHidden) {
3297
+ const childCtm = multiply(ctm, parseTransform(el.getAttribute("transform"), onWarn));
3298
+ const childStyle = deriveStyle(style, el);
3299
+ const paint = groupPaint(el, from, childStyle, childCtm, gradients, onWarn);
3300
+ const children = parseChildren(el, childCtm, childStyle, paint.children, gradients, clips, onWarn, uriToPrefix, keepHidden);
3301
+ const opacity = ownValue(el, "opacity");
3302
+ const group = { kind: "group", children, ...paint.paint };
3303
+ const clip = clipFor(el, childCtm, clips, onWarn);
3304
+ if (clip) group.clip = clip;
3305
+ if (opacity != null) group.opacity = opacity;
3306
+ const meta = collectElementMeta(el, uriToPrefix);
3307
+ if (meta) group.meta = meta;
3308
+ return group;
3309
+ }
3281
3310
  function isHiddenElement(el, style) {
3282
3311
  const tag = el.tagName.toLowerCase();
3283
3312
  if (IGNORED_TAGS.has(tag)) return false;
@@ -3512,6 +3541,61 @@ function strokeAttrsFor(stroke, registry, warn) {
3512
3541
  }
3513
3542
  return attrs;
3514
3543
  }
3544
+
3545
+ // src/groupPaintXml.ts
3546
+ var FILL_ATTRS = ["fill", "fill-opacity"];
3547
+ var STROKE_ATTRS = [
3548
+ "stroke",
3549
+ "stroke-width",
3550
+ "stroke-opacity",
3551
+ "stroke-linecap",
3552
+ "stroke-linejoin",
3553
+ "stroke-dasharray",
3554
+ "stroke-dashoffset",
3555
+ "stroke-miterlimit",
3556
+ "marker-start",
3557
+ "marker-mid",
3558
+ "marker-end"
3559
+ ];
3560
+ var INITIAL = {
3561
+ "fill": "#000000",
3562
+ "fill-opacity": "1",
3563
+ "stroke": "none",
3564
+ "stroke-width": "1",
3565
+ "stroke-opacity": "1",
3566
+ "stroke-linecap": "butt",
3567
+ "stroke-linejoin": "miter",
3568
+ "stroke-dasharray": "none",
3569
+ "stroke-dashoffset": "0",
3570
+ "stroke-miterlimit": "4",
3571
+ "marker-start": "none",
3572
+ "marker-mid": "none",
3573
+ "marker-end": "none"
3574
+ };
3575
+ function groupPaintAttrs(node, registry, warn) {
3576
+ const out = [];
3577
+ if (node.fill && !node.inherits?.fill) out.push(...paintAttrs(node.fill, "fill", registry));
3578
+ if (node.stroke && !node.inherits?.stroke) out.push(...strokeAttrsFor(node.stroke, registry, warn));
3579
+ return out;
3580
+ }
3581
+ function attrNames(attrs) {
3582
+ return attrs.map((a) => a.slice(0, a.indexOf("=")));
3583
+ }
3584
+ var START_TAG = /<(path|text)\b([^>]*?)(\/?)>/g;
3585
+ var ATTR = /\s([\w:-]+)="[^"]*"/g;
3586
+ function fitLeafXml(xml, inherits, painted) {
3587
+ if (painted.size === 0 && !inherits) return xml;
3588
+ const dropped = /* @__PURE__ */ new Set([...inherits?.fill ? FILL_ATTRS : [], ...inherits?.stroke ? STROKE_ATTRS : []]);
3589
+ let first = true;
3590
+ return xml.replace(START_TAG, (_tag, name, body, close2) => {
3591
+ let attrs = body;
3592
+ if (first) attrs = attrs.replace(ATTR, (whole, n) => dropped.has(n) ? "" : whole);
3593
+ first = false;
3594
+ const has = new Set([...attrs.matchAll(ATTR)].map((m) => m[1]));
3595
+ const resets = [...painted].filter((n) => !has.has(n) && !dropped.has(n) && n in INITIAL);
3596
+ return `<${name}${attrs}${resets.map((n) => ` ${n}="${INITIAL[n]}"`).join("")}${close2}>`;
3597
+ });
3598
+ }
3515
3599
  function textXml(node, registry, namespaces, warn, given) {
3516
3600
  const layout = given && layoutMeasured(given, node.text) ? given : null;
3517
3601
  const x = node.x + anchorOffset(node.style, node.width, node.text);
@@ -3665,6 +3749,7 @@ function prettyXml(xml) {
3665
3749
  }
3666
3750
 
3667
3751
  // src/serialize.ts
3752
+ var NOTHING_PAINTED = /* @__PURE__ */ new Set();
3668
3753
  function serializeSvg(nodes, opts = {}) {
3669
3754
  const registry = new PaintServerRegistry();
3670
3755
  registerPaintServers(nodes, registry);
@@ -3707,6 +3792,8 @@ function registerPaintServers(nodes, registry) {
3707
3792
  for (const n of nodes) {
3708
3793
  if (n.kind === "group") {
3709
3794
  if (n.clip) registry.clipId(n.clip);
3795
+ if (n.fill?.kind === "gradient" && !n.inherits?.fill) registry.register(n.fill.paint);
3796
+ if (n.stroke?.paint.kind === "gradient" && !n.inherits?.stroke) registry.register(n.stroke.paint.paint);
3710
3797
  registerPaintServers(n.children, registry);
3711
3798
  } else if (n.kind === "path") {
3712
3799
  if (n.fill.kind === "gradient") registry.register(n.fill.paint);
@@ -3732,17 +3819,18 @@ function registerMarkers(stroke, registry) {
3732
3819
  if (ref !== void 0) registry.markerId(ref);
3733
3820
  }
3734
3821
  }
3735
- function nodeXml(node, registry, namespaces, warn, layoutOf) {
3736
- const xml = kindXml(node, registry, namespaces, warn, layoutOf);
3822
+ function nodeXml(node, registry, namespaces, warn, layoutOf, painted = NOTHING_PAINTED) {
3823
+ const kind = kindXml(node, registry, namespaces, warn, layoutOf, painted);
3824
+ const xml = node.kind === "path" || node.kind === "text" ? fitLeafXml(kind, node.inherits, painted) : kind;
3737
3825
  if (!node.hidden) return xml;
3738
3826
  return xml.replace(/^<[\w:]+/, (open) => `${open} display="none"`).replace(/<path (?=[^>]*:role="stroke-outline")/, '<path display="none" ');
3739
3827
  }
3740
- function kindXml(node, registry, namespaces, warn, layoutOf) {
3741
- if (node.kind === "group") return groupXml(node, registry, namespaces, warn, layoutOf);
3828
+ function kindXml(node, registry, namespaces, warn, layoutOf, painted) {
3829
+ if (node.kind === "group") return groupXml(node, registry, namespaces, warn, layoutOf, painted);
3742
3830
  if (node.kind === "text") return textXml(node, registry, namespaces, warn, layoutOf?.(node));
3743
3831
  if (node.kind === "image") return imageXml(node, namespaces);
3744
3832
  const drawn = pathAsSvgDraws(node);
3745
- return drawn.kind === "group" ? groupXml(drawn, registry, namespaces, warn, layoutOf) : pathXml(drawn, registry, namespaces, warn);
3833
+ return drawn.kind === "group" ? groupXml(drawn, registry, namespaces, warn, layoutOf, painted) : pathXml(drawn, registry, namespaces, warn);
3746
3834
  }
3747
3835
  function imageXml(node, namespaces) {
3748
3836
  const box = `x="${trimNumber(node.x)}" y="${trimNumber(node.y)}" width="${trimNumber(node.width)}" height="${trimNumber(node.height)}"`;
@@ -3766,8 +3854,11 @@ function imageXml(node, namespaces) {
3766
3854
  const head = ['<g data-weasel-image="true"', ...outer].join(" ");
3767
3855
  return `${head}${metaAttrs}><svg ${box} viewBox="${viewBox}" preserveAspectRatio="none"><image ${href} x="0" y="0" width="1" height="1" preserveAspectRatio="none"${flip}/></svg>${metaEls}</g>`;
3768
3856
  }
3769
- function groupXml(node, registry, namespaces, warn, layoutOf) {
3857
+ function groupXml(node, registry, namespaces, warn, layoutOf, painted = NOTHING_PAINTED) {
3770
3858
  const attrs = [];
3859
+ const paint = groupPaintAttrs(node, registry, warn);
3860
+ for (const a of paint) attrs.push(a);
3861
+ const below = paint.length === 0 ? painted : /* @__PURE__ */ new Set([...painted, ...attrNames(paint)]);
3771
3862
  if (node.transform) {
3772
3863
  const m = formatMatrix(node.transform);
3773
3864
  if (m) attrs.push(`transform="${m}"`);
@@ -3780,7 +3871,7 @@ function groupXml(node, registry, namespaces, warn, layoutOf) {
3780
3871
  }
3781
3872
  const metaAttrs = metaAttrsXml(node.meta, namespaces);
3782
3873
  const head = attrs.length > 0 ? `<g ${attrs.join(" ")}${metaAttrs}>` : `<g${metaAttrs}>`;
3783
- const body = node.children.map((c) => nodeXml(c, registry, namespaces, warn, layoutOf)).join("");
3874
+ const body = node.children.map((c) => nodeXml(c, registry, namespaces, warn, layoutOf, below)).join("");
3784
3875
  return `${head}${body}${metaElementsXml(node.meta, namespaces)}</g>`;
3785
3876
  }
3786
3877
  function pathXml(node, registry, namespaces, warn) {
@@ -3867,15 +3958,18 @@ function fadeRun(run, k) {
3867
3958
  ...run.stroke ? { stroke: fadeStroke(run.stroke, k) } : {}
3868
3959
  };
3869
3960
  }
3870
- var CASCADE_OFFSET_PX = 24;
3871
- var VIEWPORT_FIT = 0.9;
3872
- function readFileText(file) {
3873
- return new Promise((resolve, reject) => {
3874
- const reader = new FileReader();
3875
- reader.onload = () => resolve(String(reader.result ?? ""));
3876
- reader.onerror = () => reject(reader.error);
3877
- reader.readAsText(file);
3878
- });
3961
+
3962
+ // src/draftTakes.ts
3963
+ var NO_TAKES = {};
3964
+ function takesOf(n, from, k) {
3965
+ const inherits = n.kind === "path" || n.kind === "text" || n.kind === "group" ? n.inherits : void 0;
3966
+ return { fill: inherits?.fill === true && from.fill === k, stroke: inherits?.stroke === true && from.stroke === k };
3967
+ }
3968
+ var solidOrNone = (p) => p === void 0 || p.kind === "solid" || p.kind === "none";
3969
+ function groupTakes(group, takes, k) {
3970
+ const fill = group.inherits?.fill ? takes.fill : solidOrNone(group.fill);
3971
+ const stroke = group.inherits?.stroke ? takes.stroke : solidOrNone(group.stroke?.paint);
3972
+ return { ...fill ? { fill: k } : {}, ...stroke ? { stroke: k } : {} };
3879
3973
  }
3880
3974
  function fillDataFromSvg(paint, box) {
3881
3975
  if (!paint) return void 0;
@@ -3928,23 +4022,30 @@ function svgNodesToKitDrafts(input, nextId, options = {}) {
3928
4022
  const id = nextId(source);
3929
4023
  const out = options.leaf ? options.leaf(id, { pose, data }, source) : data;
3930
4024
  if (out === null) return null;
3931
- drafts.push({ kind: "leaf", id, parentId, pose, data: out, ...source.hidden ? { hidden: true } : {} });
4025
+ drafts.push({ kind: "mark", id, parentId, pose, data: out, ...source.hidden ? { hidden: true } : {} });
3932
4026
  return pose;
3933
4027
  };
3934
- const visit = (n, parentId, inherited2) => {
4028
+ const visit = (n, parentId, inherited2, from) => {
3935
4029
  const k = inherited2 * (n.opacity ?? 1);
4030
+ const takes = takesOf(n, from, k);
3936
4031
  if (n.kind === "group") {
4032
+ const [own2, kids, ...more] = n.children;
4033
+ if (own2 && own2.kind !== "group" && (kids === void 0 || kids.kind === "group") && options.mark?.(n)) {
4034
+ if (n.clip) return visit(clippingGroup(n), parentId, inherited2, from);
4035
+ return visitMark(n, own2, kids, more, parentId, k, takes);
4036
+ }
3937
4037
  const draft = {
3938
- kind: "container",
4038
+ kind: "group",
3939
4039
  id: nextId(n),
3940
4040
  parentId,
3941
4041
  pose: { x: 0, y: 0, width: 0, height: 0 },
3942
4042
  data: n.clip ? { path: n.clip, fill: null } : {},
4043
+ ...n.clip ? { clip: "outline" } : {},
3943
4044
  ...n.hidden ? { hidden: true } : {}
3944
4045
  };
3945
4046
  drafts.push(draft);
3946
4047
  const at = drafts.length - 1;
3947
- const acc = unionAABB(n.children.map((c) => visit(c, draft.id, k)));
4048
+ const acc = unionAABB(n.children.map((c) => visit(c, draft.id, k, groupTakes(n, takes, k))));
3948
4049
  if (!acc) {
3949
4050
  const kept = drafts.length > at + 1 || (options.keepEmpty?.(n) ?? false);
3950
4051
  if (!kept) drafts.splice(at, 1);
@@ -3952,63 +4053,106 @@ function svgNodesToKitDrafts(input, nextId, options = {}) {
3952
4053
  return null;
3953
4054
  }
3954
4055
  draft.pose = n.clip ? boxOfPath(n.clip) : acc;
4056
+ Object.assign(draft.data, containerPaint(n, takes, draft.pose, k));
3955
4057
  return draft.pose;
3956
4058
  }
3957
- if (n.kind === "text") {
3958
- const pose2 = {
3959
- x: n.x,
3960
- y: n.y,
3961
- width: n.width,
3962
- height: n.height
3963
- };
3964
- if (n.rotation) pose2.rotation = n.rotation;
3965
- if (n.transform) pose2.transform = n.transform;
3966
- const box = {
3967
- x: pose2.x,
3968
- y: pose2.y,
3969
- width: pose2.width,
3970
- height: pose2.height
3971
- };
3972
- const fill2 = n.fill === void 0 && k !== 1 ? DEFAULT_TEXT_STYLE.fill : n.fill;
3973
- return pushLeaf(n, parentId, pose2, {
3974
- text: n.text,
3975
- ...n.style ? { style: n.style } : {},
3976
- ...n.verticalAlign ? { verticalAlign: n.verticalAlign } : {},
3977
- ...n.runs ? { runs: n.runs.map((r) => fadeRun(runInBoxFrame(r, box), k)) } : {},
3978
- // `!== undefined`, not a truthiness test: `null` is the document
3979
- // saying `fill="none"`, and absent takes the painter's default.
3980
- ...fill2 !== void 0 ? { fill: fill2 && mapFillPaint(fill2, (p) => fadeFill(fillInBoxFrame(p, box), k)) } : {},
3981
- ...n.stroke !== void 0 ? { stroke: fadeStroke(strokeInBoxFrame(n.stroke, box), k) } : {}
3982
- });
3983
- }
3984
- if (n.kind === "image") {
3985
- const pose2 = { x: n.x, y: n.y, width: n.width, height: n.height };
3986
- if (n.rotation) pose2.rotation = n.rotation;
3987
- if (n.transform) pose2.transform = n.transform;
3988
- return pushLeaf(n, parentId, pose2, {
3989
- image: {
3990
- src: n.href,
3991
- ...k !== 1 ? { opacity: k } : {},
3992
- ...n.source ? { source: { ...n.source } } : {},
3993
- ...n.flipX ? { flipX: true } : {},
3994
- ...n.flipY ? { flipY: true } : {}
3995
- }
3996
- });
4059
+ const { pose, data } = leafDraft(n, takes, k);
4060
+ return pushLeaf(n, parentId, pose, data);
4061
+ };
4062
+ const visitMark = (n, own2, kids, more, parentId, k, takes) => {
4063
+ const inner = groupTakes(n, takes, k);
4064
+ const pose = visit(own2, parentId, k, inner);
4065
+ const mark = pose === null ? null : drafts.at(-1);
4066
+ const under = mark?.id ?? parentId;
4067
+ if (mark && n.hidden) mark.hidden = true;
4068
+ if (mark && kids?.clip) mark.clip = "outline";
4069
+ const poses = [pose];
4070
+ if (kids) {
4071
+ const at = drafts.length;
4072
+ const kk = k * (kids.opacity ?? 1);
4073
+ const below = groupTakes(kids, takesOf(kids, inner, kk), kk);
4074
+ for (const c of kids.children) poses.push(visit(c, under, kk, below));
4075
+ if (kids.hidden) {
4076
+ for (const d of drafts.slice(at)) if (d.parentId === under) d.hidden = true;
4077
+ }
3997
4078
  }
3998
- const b = boxOfPath(n.path);
3999
- const pose = { x: b.x, y: b.y, width: b.width, height: b.height };
4000
- if (n.rotation) pose.rotation = n.rotation;
4001
- const fill = fillDataFromSvg(n.fill, b);
4002
- const strokeFromSvg = strokeDataFromSvg(n.stroke, b);
4003
- return pushLeaf(n, parentId, pose, {
4004
- path: n.path,
4005
- ...fill !== void 0 ? { fill: fill && { paint: fadeFill(fill, k) } } : {},
4006
- ...strokeFromSvg !== void 0 ? { stroke: fadeStroke(strokeFromSvg, k) } : {}
4007
- });
4079
+ for (const c of more) poses.push(visit(c, under, k, inner));
4080
+ return unionAABB(poses);
4008
4081
  };
4009
- for (const n of nodes) visit(n, null, 1);
4082
+ for (const n of nodes) visit(n, null, 1, NO_TAKES);
4010
4083
  return drafts;
4011
4084
  }
4085
+ function leafDraft(n, takes, k) {
4086
+ if (n.kind === "text") {
4087
+ const pose2 = {
4088
+ x: n.x,
4089
+ y: n.y,
4090
+ width: n.width,
4091
+ height: n.height
4092
+ };
4093
+ if (n.rotation) pose2.rotation = n.rotation;
4094
+ if (n.transform) pose2.transform = n.transform;
4095
+ const box = {
4096
+ x: pose2.x,
4097
+ y: pose2.y,
4098
+ width: pose2.width,
4099
+ height: pose2.height
4100
+ };
4101
+ const fill2 = n.fill === void 0 ? DEFAULT_TEXT_FILL : n.fill;
4102
+ return { pose: pose2, data: {
4103
+ text: n.text,
4104
+ ...n.style ? { style: n.style } : {},
4105
+ ...n.verticalAlign ? { verticalAlign: n.verticalAlign } : {},
4106
+ ...n.runs ? { runs: n.runs.map((r) => fadeRun(runInBoxFrame(r, box), k)) } : {},
4107
+ // `null` is the document saying `fill="none"`.
4108
+ ...takes.fill ? { fill: INHERIT } : { fill: fill2 && mapFillPaint(fill2, (p) => fadeFill(fillInBoxFrame(p, box), k)) },
4109
+ ...takes.stroke ? { stroke: INHERIT } : n.stroke !== void 0 ? { stroke: fadeStroke(strokeInBoxFrame(n.stroke, box), k) } : {}
4110
+ } };
4111
+ }
4112
+ if (n.kind === "image") {
4113
+ const pose2 = { x: n.x, y: n.y, width: n.width, height: n.height };
4114
+ if (n.rotation) pose2.rotation = n.rotation;
4115
+ if (n.transform) pose2.transform = n.transform;
4116
+ return { pose: pose2, data: {
4117
+ image: {
4118
+ src: n.href,
4119
+ ...k !== 1 ? { opacity: k } : {},
4120
+ ...n.source ? { source: { ...n.source } } : {},
4121
+ ...n.flipX ? { flipX: true } : {},
4122
+ ...n.flipY ? { flipY: true } : {}
4123
+ }
4124
+ } };
4125
+ }
4126
+ const b = boxOfPath(n.path);
4127
+ const pose = { x: b.x, y: b.y, width: b.width, height: b.height };
4128
+ if (n.rotation) pose.rotation = n.rotation;
4129
+ const fill = fillDataFromSvg(n.fill, b);
4130
+ const strokeFromSvg = strokeDataFromSvg(n.stroke, b);
4131
+ return { pose, data: {
4132
+ path: n.path,
4133
+ ...takes.fill ? { fill: INHERIT } : fill !== void 0 ? { fill: fill && { paint: fadeFill(fill, k) } } : {},
4134
+ ...takes.stroke ? { stroke: INHERIT } : strokeFromSvg !== void 0 ? { stroke: fadeStroke(strokeFromSvg, k) } : {}
4135
+ } };
4136
+ }
4137
+ function clippingGroup(n) {
4138
+ const { clip, ...unclipped } = n;
4139
+ return {
4140
+ kind: "group",
4141
+ clip,
4142
+ children: [unclipped],
4143
+ ...n.fill ? { fill: n.fill } : {},
4144
+ ...n.stroke ? { stroke: n.stroke } : {},
4145
+ ...n.inherits ? { inherits: n.inherits } : {}
4146
+ };
4147
+ }
4148
+ function containerPaint(n, takes, box, k) {
4149
+ const fill = fillDataFromSvg(n.fill, box);
4150
+ const stroke = strokeDataFromSvg(n.stroke, box);
4151
+ return {
4152
+ ...takes.fill ? { fill: INHERIT } : fill ? { fill: { paint: fadeFill(fill, k) } } : {},
4153
+ ...takes.stroke ? { stroke: INHERIT } : stroke ? { stroke: fadeStroke(stroke, k) } : {}
4154
+ };
4155
+ }
4012
4156
  function boxOfPath(path) {
4013
4157
  const b = boundsOfPath(path);
4014
4158
  return { x: b.x, y: b.y, width: b.width, height: b.height };
@@ -4016,6 +4160,16 @@ function boxOfPath(path) {
4016
4160
  function isNodeList(input) {
4017
4161
  return Array.isArray(input);
4018
4162
  }
4163
+ var CASCADE_OFFSET_PX = 24;
4164
+ var VIEWPORT_FIT = 0.9;
4165
+ function readFileText(file) {
4166
+ return new Promise((resolve, reject) => {
4167
+ const reader = new FileReader();
4168
+ reader.onload = () => resolve(String(reader.result ?? ""));
4169
+ reader.onerror = () => reject(reader.error);
4170
+ reader.readAsText(file);
4171
+ });
4172
+ }
4019
4173
  function scaleLeafData(data, scale) {
4020
4174
  if (scale === 1) return data;
4021
4175
  const out = { ...data };
@@ -4044,7 +4198,7 @@ async function unpackSvgFiles(files, ctx) {
4044
4198
  const wrapperId = ctx.scene.mintId();
4045
4199
  const union2 = unionAABB(roots.map((d) => d.pose));
4046
4200
  drafts = [
4047
- { kind: "container", id: wrapperId, parentId: null, pose: union2, data: {} },
4201
+ { kind: "group", id: wrapperId, parentId: null, pose: union2, data: {} },
4048
4202
  ...drafts.map((d) => d.parentId === null ? { ...d, parentId: wrapperId } : d)
4049
4203
  ];
4050
4204
  }
@@ -4076,8 +4230,9 @@ async function unpackSvgFiles(files, ctx) {
4076
4230
  kind: d.kind,
4077
4231
  layer,
4078
4232
  pose: place(d.pose),
4079
- data: d.kind === "leaf" ? scaleLeafData(d.data, scale) : d.data,
4233
+ data: d.kind === "mark" ? scaleLeafData(d.data, scale) : d.data,
4080
4234
  parent: d.parentId,
4235
+ ...d.clip ? { clip: d.clip } : {},
4081
4236
  ...d.hidden ? { hidden: true } : {}
4082
4237
  },
4083
4238
  label: "Insert SVG"
@@ -4097,8 +4252,9 @@ function svgPaintFromKit(fill, box) {
4097
4252
  }
4098
4253
  return { kind: "gradient", paint: fillInPoseFrame(fill, box) };
4099
4254
  }
4100
- function svgStrokeFromKit(stroke, box) {
4101
- if (!stroke?.paint) return void 0;
4255
+ function svgStrokeFromKit(raw, box) {
4256
+ const stroke = resolveStroke(raw);
4257
+ if (!stroke) return void 0;
4102
4258
  const width2 = stroke.width ?? 1;
4103
4259
  if (!((typeof width2 === "object" ? width2.px : width2) > 0)) return void 0;
4104
4260
  const opacity = stroke.paint.opacity;
@@ -4152,17 +4308,16 @@ function svgLeafFromKit(data, pose) {
4152
4308
  const text = svgTextFromKit({ ...data, text: data.text }, pose);
4153
4309
  const boxPath = { kind: "rect", x: pose.x, y: pose.y, width: pose.width, height: pose.height };
4154
4310
  const clip = data.overflow === "clip" ? { clip: pathThroughPoseFrame(boxPath, pose) } : {};
4155
- if (!data.background?.paint) return clip.clip ? { kind: "group", children: [text], ...clip } : text;
4311
+ if (!resolveFill(data.background)) return clip.clip ? { kind: "group", children: [text], ...clip } : text;
4156
4312
  const box = svgLeafFromKit({ path: boxPath, fill: data.background }, pose);
4157
4313
  return { kind: "group", children: [box, text], ...clip };
4158
4314
  }
4159
4315
  if (!data.path) return null;
4160
- const stroke = svgStrokeFromKit(data.stroke, pose);
4161
- const fill = data.fill !== void 0 ? data.fill : stroke ? null : DEFAULT_SHAPE_FILL;
4316
+ const stroke = svgStrokeFromKit(resolveStroke(data.stroke), pose);
4162
4317
  const node = {
4163
4318
  kind: "path",
4164
4319
  path: pathInPoseFrame(data.path, pose),
4165
- fill: svgPaintFromKit(fill?.paint ?? null, pose)
4320
+ fill: svgPaintFromKit(resolveFill(data.fill)?.paint ?? null, pose)
4166
4321
  };
4167
4322
  if (stroke) node.stroke = stroke;
4168
4323
  const frame = poseLinearOf(pose) && poseFrameOf(pose);
@@ -4186,52 +4341,96 @@ function svgTextFromKit(data, pose) {
4186
4341
  if (data.style) node.style = data.style;
4187
4342
  if (data.runs) node.runs = data.runs.map((r) => runInPoseFrame(r, pose));
4188
4343
  if (data.verticalAlign) node.verticalAlign = data.verticalAlign;
4189
- if (data.fill !== void 0) node.fill = data.fill && mapFillPaint(data.fill, (p) => fillInPoseFrame(p, pose));
4190
- if (data.stroke) node.stroke = strokeInPoseFrame(data.stroke, pose);
4344
+ const fill = resolveFill(data.fill);
4345
+ node.fill = fill && mapFillPaint(fill, (p) => fillInPoseFrame(p, pose));
4346
+ const stroke = resolveStroke(data.stroke);
4347
+ if (stroke) node.stroke = strokeInPoseFrame(stroke, pose);
4191
4348
  if (pose.rotation) node.rotation = pose.rotation;
4192
4349
  if (pose.transform) node.transform = pose.transform;
4193
4350
  return node;
4194
4351
  }
4195
4352
  function svgNodesFromKit(tree, options = {}) {
4196
- const { include, leaf, group } = options;
4197
- const emit = (id) => {
4353
+ const { include, leaf, group, mark } = options;
4354
+ const emit = (id, up) => {
4198
4355
  const node = tree.get(id);
4199
- const out2 = node && (!include || include(id)) ? lower(id, node) : null;
4356
+ const out2 = node && (!include || include(id)) ? lower(id, node, up) : null;
4200
4357
  return out2 && node?.hidden === true ? { ...out2, hidden: true } : out2;
4201
4358
  };
4202
- const lower = (id, node) => {
4203
- if (node.kind === "container") {
4204
- const emitChildren = () => {
4205
- const children = [];
4206
- for (const c of tree.childrenOf(id)) {
4207
- const child = emit(c);
4208
- if (child) children.push(child);
4209
- }
4210
- return children;
4211
- };
4212
- const path = node.data?.path;
4213
- const clip = path ? pathThroughPoseFrame(pathInPoseFrame(path, node.pose), node.pose) : void 0;
4214
- const copies = node.copiesFromPose?.(node.pose, node) ?? [];
4215
- const content = () => ({ kind: "group", children: emitChildren(), ...clip ? { clip } : {} });
4216
- let g = content();
4217
- if (copies.length > 0) {
4218
- g = {
4219
- kind: "group",
4220
- children: [g, ...copies.map((transform) => ({ kind: "group", transform, children: [content()] }))]
4221
- };
4359
+ const lower = (id, node, up) => {
4360
+ const own2 = node.data;
4361
+ const isGroup = node.kind === "group";
4362
+ const self = isGroup ? null : markInherits(
4363
+ leaf ? leaf(id, node, up) : svgLeafFromKit(resolvedData(own2 ?? {}, up), node.pose),
4364
+ own2 ?? {},
4365
+ up
4366
+ );
4367
+ const kids = tree.childrenOf(id);
4368
+ if (!isGroup && kids.length === 0) return self;
4369
+ const below = {
4370
+ fill: own2?.fill === INHERIT ? up.fill : resolveFill(own2?.fill),
4371
+ stroke: own2?.stroke === INHERIT ? up.stroke : resolveStroke(own2?.stroke)
4372
+ };
4373
+ const emitChildren = () => {
4374
+ const children = [];
4375
+ for (const c of kids) {
4376
+ const child = emit(c, below);
4377
+ if (child) children.push(child);
4222
4378
  }
4379
+ return children;
4380
+ };
4381
+ const clip = node.clip === "outline" ? clipOutline(node, node.pose) ?? void 0 : void 0;
4382
+ const copies = node.copiesFromPose?.(node.pose, node) ?? [];
4383
+ const content = () => ({ kind: "group", children: emitChildren(), ...clip ? { clip } : {} });
4384
+ const paint = groupPaintFromKit(own2, below, node.pose);
4385
+ const copied = () => copies.map((transform) => ({ kind: "group", transform, children: [content()] }));
4386
+ const withCopies = (g) => copies.length === 0 ? g : { kind: "group", children: [g, ...copied()] };
4387
+ if (isGroup) {
4388
+ const g = withCopies({ ...content(), ...paint });
4223
4389
  return group ? group(id, g) : g;
4224
4390
  }
4225
- return leaf ? leaf(id, node) : svgLeafFromKit(node.data, node.pose);
4391
+ if (mark && self && self.kind !== "group") {
4392
+ const kids2 = content();
4393
+ return mark(id, { kind: "group", children: kids2.children.length === 0 ? [self] : [self, kids2, ...copied()], ...paint });
4394
+ }
4395
+ const inner = withCopies(content());
4396
+ const parts = clip || copies.length > 0 ? [inner] : inner.children;
4397
+ return { kind: "group", children: self ? [self, ...parts] : parts, ...paint };
4226
4398
  };
4227
4399
  const out = [];
4228
4400
  for (const id of options.roots ?? tree.roots) {
4229
- const n = emit(id);
4401
+ const n = emit(id, NO_UP_PAINT);
4230
4402
  if (n) out.push(n);
4231
4403
  }
4232
4404
  return out;
4233
4405
  }
4406
+ var NO_UP_PAINT = { fill: null, stroke: null };
4407
+ var carries = (p) => p != null && isSolidPaint(p.paint);
4408
+ function groupPaintFromKit(own2, below, pose) {
4409
+ const out = {};
4410
+ if (below.fill?.paint) out.fill = svgPaintFromKit(below.fill.paint, pose);
4411
+ const stroke = svgStrokeFromKit(below.stroke, pose);
4412
+ if (stroke) out.stroke = stroke;
4413
+ const fill = own2?.fill === INHERIT && carries(below.fill);
4414
+ const strokes = own2?.stroke === INHERIT && carries(below.stroke);
4415
+ if (fill || strokes) out.inherits = { ...fill ? { fill: true } : {}, ...strokes ? { stroke: true } : {} };
4416
+ return out;
4417
+ }
4418
+ function resolvedData(data, up) {
4419
+ if (data.fill !== INHERIT && data.stroke !== INHERIT) return data;
4420
+ return {
4421
+ ...data,
4422
+ ...data.fill === INHERIT ? { fill: up.fill } : {},
4423
+ ...data.stroke === INHERIT ? { stroke: up.stroke } : {}
4424
+ };
4425
+ }
4426
+ function markInherits(out, data, up) {
4427
+ if (out === null || out.kind !== "path" && out.kind !== "text") return out;
4428
+ const fill = data.fill === INHERIT && carries(up.fill);
4429
+ const stroke = data.stroke === INHERIT && carries(up.stroke);
4430
+ if (fill || stroke) out.inherits = { ...fill ? { fill: true } : {}, ...stroke ? { stroke: true } : {} };
4431
+ return out;
4432
+ }
4234
4433
 
4235
- export { DEFAULT_MEDIA_ENVIRONMENT, IDENTITY_MATRIX, escapeAttr, escapeText, evaluateMediaQuery, evaluateSupports, fillDataFromSvg, nativeSvgKind, nativeSvgSpace, parseSvg, serializeSvg, strokeDataFromSvg, svgImageFromKit, svgLeafFromKit, svgNeeds, svgNodesFromKit, svgNodesToKitDrafts, svgPaintFromKit, svgStrokeFromKit, tilePreviewCssUrl, tilePreviewSvg, unpackSvgFiles, warmSvg };
4434
+ export { DEFAULT_MEDIA_ENVIRONMENT, IDENTITY_MATRIX, compareSpecificity, escapeAttr, escapeText, evaluateMediaQuery, evaluateSupports, fillDataFromSvg, nativeSvgKind, nativeSvgSpace, parseSvg, serializeSvg, specificity, splitTopLevel, strokeDataFromSvg, svgImageFromKit, svgLeafFromKit, svgNeeds, svgNodesFromKit, svgNodesToKitDrafts, svgPaintFromKit, svgStrokeFromKit, tilePreviewCssUrl, tilePreviewSvg, unpackSvgFiles, warmSvg };
4236
4435
  //# sourceMappingURL=index.js.map
4237
4436
  //# sourceMappingURL=index.js.map