@weasel-js/svg 1.15.1 → 1.16.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.d.ts +26 -16
- package/dist/index.js +73 -29
- package/dist/index.js.map +1 -1
- package/package.json +4 -3
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Affine } from '@weasel-js/geom';
|
|
2
2
|
export { pathToD as serializePathD } from '@weasel-js/geom';
|
|
3
|
-
import { Path, Paint, ScreenLength, StrokeAlign, MarkerRef, VertexWidth, StrokeNib, BooleanShapeOp, StyledRun, TextStyle, TextVerticalAlign, Fill,
|
|
3
|
+
import { Path, Paint, ScreenLength, StrokeAlign, MarkerRef, VertexWidth, StrokeNib, Stroke, BooleanShapeOp, StyledRun, TextStyle, TextVerticalAlign, Fill, PoseLinear, MarkerEntry, TextPoseLayout, RenderNeeds, TilePatternSpec, IngestCtx, Inherit, ImageNodeData, NodeClip } from '@weasel-js/core';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Media Queries 4 evaluation against a fixed environment.
|
|
@@ -133,6 +133,12 @@ interface SvgStroke {
|
|
|
133
133
|
* more takes `width`. */
|
|
134
134
|
vertexWidths?: VertexWidth[];
|
|
135
135
|
nib?: StrokeNib;
|
|
136
|
+
/** The kit stroke's `trim`, measured along the path as written. SVG has no
|
|
137
|
+
* trim, so the serializer strokes only the span it draws; `parseSvg` never sets it. */
|
|
138
|
+
trim?: Stroke['trim'];
|
|
139
|
+
/** `stroke-dashoffset`: where along its pattern the dash starts. Written for
|
|
140
|
+
* a trimmed run that starts mid-pattern; `parseSvg` never sets it. */
|
|
141
|
+
dashOffset?: number;
|
|
136
142
|
}
|
|
137
143
|
/**
|
|
138
144
|
* Which of a node's paints its own element sets none of, so that it takes the
|
|
@@ -463,7 +469,7 @@ declare function nativeSvgSpace(space: string | null | undefined): boolean;
|
|
|
463
469
|
* Produce an SVG document string. The root `viewBox` is `opts.viewBox` if
|
|
464
470
|
* supplied, otherwise a tight bounding box around every leaf.
|
|
465
471
|
*/
|
|
466
|
-
declare function serializeSvg(
|
|
472
|
+
declare function serializeSvg(given: SvgNode[], opts?: SerializeOptions): string;
|
|
467
473
|
|
|
468
474
|
/**
|
|
469
475
|
* Escaping for the XML this package writes by hand.
|
|
@@ -562,13 +568,7 @@ declare function evaluateSupports(condition: string, opts?: SupportsOptions): bo
|
|
|
562
568
|
*/
|
|
563
569
|
declare function unpackSvgFiles(files: File[], ctx: IngestCtx): Promise<void>;
|
|
564
570
|
|
|
565
|
-
/**
|
|
566
|
-
* The kit-to-SVG direction: scene leaves back to `SvgNode`s, the inverse of
|
|
567
|
-
* `svgNodesToKitDrafts`. Each leaf is written as the built-in painter that
|
|
568
|
-
* draws it would draw it — the pose baked into the geometry, box-relative
|
|
569
|
-
* paints resolved into that box — so `serializeSvg` over the result shows what
|
|
570
|
-
* the canvas shows.
|
|
571
|
-
*/
|
|
571
|
+
/** A kit paint and stroke as the `SvgPaint` and `SvgStroke` an element is written with. */
|
|
572
572
|
|
|
573
573
|
/** A leaf's box: where it is drawn, and the frame its box-relative paints
|
|
574
574
|
* resolve against. */
|
|
@@ -582,6 +582,22 @@ interface SvgKitPose {
|
|
|
582
582
|
* so a path leaf's is baked into its geometry with its rotation. */
|
|
583
583
|
transform?: PoseLinear;
|
|
584
584
|
}
|
|
585
|
+
/** A kit paint as an `SvgPaint`. `null` is `fill="none"`; a box-relative
|
|
586
|
+
* gradient or pattern is resolved into `box`, since the geometry it paints
|
|
587
|
+
* is written already placed there. */
|
|
588
|
+
declare function svgPaintFromKit(fill: Paint | null, box: SvgKitPose): SvgPaint;
|
|
589
|
+
/** A kit `Stroke` as an `SvgStroke` — the inverse of `strokeDataFromSvg`.
|
|
590
|
+
* One with no paint or no width draws nothing and is written as no stroke. */
|
|
591
|
+
declare function svgStrokeFromKit(raw: Stroke | null | undefined, box: SvgKitPose): SvgStroke | undefined;
|
|
592
|
+
|
|
593
|
+
/**
|
|
594
|
+
* The kit-to-SVG direction: scene leaves back to `SvgNode`s, the inverse of
|
|
595
|
+
* `svgNodesToKitDrafts`. Each leaf is written as the built-in painter that
|
|
596
|
+
* draws it would draw it — the pose baked into the geometry, box-relative
|
|
597
|
+
* paints resolved into that box — so `serializeSvg` over the result shows what
|
|
598
|
+
* the canvas shows.
|
|
599
|
+
*/
|
|
600
|
+
|
|
585
601
|
/** The leaf data the kit's path, text and image painters read — what
|
|
586
602
|
* `svgNodesToKitDrafts` writes. */
|
|
587
603
|
interface SvgKitLeafData {
|
|
@@ -601,13 +617,7 @@ interface SvgKitLeafData {
|
|
|
601
617
|
overflow?: 'visible' | 'clip';
|
|
602
618
|
image?: ImageNodeData['image'];
|
|
603
619
|
}
|
|
604
|
-
|
|
605
|
-
* gradient or pattern is resolved into `box`, since the geometry it paints
|
|
606
|
-
* is written already placed there. */
|
|
607
|
-
declare function svgPaintFromKit(fill: Paint | null, box: SvgKitPose): SvgPaint;
|
|
608
|
-
/** A kit `Stroke` as an `SvgStroke` — the inverse of `strokeDataFromSvg`.
|
|
609
|
-
* One with no paint or no width draws nothing and is written as no stroke. */
|
|
610
|
-
declare function svgStrokeFromKit(raw: Stroke | null | undefined, box: SvgKitPose): SvgStroke | undefined;
|
|
620
|
+
|
|
611
621
|
/** Write a `kit:image` leaf as an `SvgImageNode` — the inverse of the image
|
|
612
622
|
* arm of `svgNodesToKitDrafts`. The pose is the image's box. */
|
|
613
623
|
declare function svgImageFromKit(image: ImageNodeData['image'], pose: SvgKitPose): SvgImageNode;
|
package/dist/index.js
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
|
-
import { identity, multiply, pathToD, invert, splitByClosure, fillsOpenSubpaths,
|
|
1
|
+
import { identity, RAD_TO_DEG, multiply, pathToD, clamp01, DEG_TO_RAD, invert, splitByClosure, fillsOpenSubpaths, pathLengthTable, lengthAtCurve, hasOpenSubpath, worldCoords, boundsOfPath as boundsOfPath$1, splitSubpaths, rotateAboutPoint, applyToPoint, joinPaths } 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, localizeWorldPoses, createInsertOp, fillInPoseFrame, resolveStroke, pathThroughPoseFrame, resolveFill, pathInPoseFrame, poseLinearOf, poseFrameOf, definesFrame, INHERIT, clipOutline, resolveScreenLength, pathFromD, isPatternPaint, resolveTextStyle, halfLeadingFor, numericWeight, strokeToPath, booleanGroupFields, toRuns, resolveRunFace, derivedDepOf, documentOf, booleanOpOf, DEFAULT_TEXT_FILL, parseColor, rgbaToHex, layoutRuns, resolveRuns, resolveAlign, PathBuilder, PATH_M, PATH_L, PATH_Z, scaleVertexWidth, getPaintKind, resolveMarkerSize, paragraphDirection, scriptMetricsFor, vertexHalfWidths, isImagePatternPaint, isTilePaint, tileSpecOf } from '@weasel-js/core';
|
|
3
|
+
import { isTileKind, boundsOfPath, transformPath, isSolidPaint, warmFonts, warmPaintKinds, isPaintKindKnown, solid, fillToBoundsFrame, getMarker, registerMarker, dwarn, unionAABB, localizeWorldPoses, createInsertOp, fillInPoseFrame, resolveStroke, pathThroughPoseFrame, resolveFill, pathInPoseFrame, poseLinearOf, poseFrameOf, asPolygonPath, definesFrame, INHERIT, clipOutline, resolveScreenLength, pathFromD, isPatternPaint, resolveTextStyle, halfLeadingFor, numericWeight, strokeToPath, booleanGroupFields, toRuns, resolveRunFace, derivedDepOf, documentOf, booleanOpOf, DEFAULT_TEXT_FILL, parseColor, rgbaToHex, layoutRuns, resolveRuns, resolveAlign, PathBuilder, PATH_M, PATH_L, PATH_Z, scaleVertexWidth, getPaintKind, resolveMarkerSize, paragraphDirection, scriptMetricsFor, vertexHalfWidths, isImagePatternPaint, isTilePaint, tileSpecOf } from '@weasel-js/core';
|
|
4
4
|
import { tileGeometry } from '@weasel-js/core/patterns-builtin';
|
|
5
|
+
import { trimSpan, trimmedPath, trimmedRuns } from '@weasel-js/stroke';
|
|
5
6
|
|
|
6
7
|
// src/types.ts
|
|
7
8
|
var IDENTITY_MATRIX = identity();
|
|
8
9
|
function applyMatrix(m, x, y) {
|
|
9
|
-
|
|
10
|
+
const [px, py] = applyToPoint(m, x, y);
|
|
11
|
+
return { x: px, y: py };
|
|
10
12
|
}
|
|
11
13
|
function isIdentity(m, eps = 1e-9) {
|
|
12
14
|
return Math.abs(m[0] - 1) < eps && Math.abs(m[1]) < eps && Math.abs(m[2]) < eps && Math.abs(m[3] - 1) < eps && Math.abs(m[4]) < eps && Math.abs(m[5]) < eps;
|
|
@@ -42,7 +44,7 @@ function parseTransform(attr, onWarn) {
|
|
|
42
44
|
const sy = nums.length >= 2 ? nums[1] : sx;
|
|
43
45
|
local = [sx, 0, 0, sy, 0, 0];
|
|
44
46
|
} else if (name === "rotate") {
|
|
45
|
-
const ang = (nums[0] ?? 0) *
|
|
47
|
+
const ang = (nums[0] ?? 0) * DEG_TO_RAD;
|
|
46
48
|
const c = Math.cos(ang);
|
|
47
49
|
const s = Math.sin(ang);
|
|
48
50
|
const rot = [c, s, -s, c, 0, 0];
|
|
@@ -54,7 +56,7 @@ function parseTransform(attr, onWarn) {
|
|
|
54
56
|
local = rot;
|
|
55
57
|
}
|
|
56
58
|
} else if (name === "skewx" || name === "skewy") {
|
|
57
|
-
const ang = (nums[0] ?? 0) *
|
|
59
|
+
const ang = (nums[0] ?? 0) * DEG_TO_RAD;
|
|
58
60
|
const t = Math.tan(ang);
|
|
59
61
|
local = name === "skewx" ? [1, 0, t, 1, 0, 0] : [1, t, 0, 1, 0, 0];
|
|
60
62
|
} else {
|
|
@@ -773,7 +775,6 @@ function dimension(v) {
|
|
|
773
775
|
const m = DIMENSION2.exec(v);
|
|
774
776
|
return m ? { n: Number(m[1]), unit: m[2].toLowerCase() } : void 0;
|
|
775
777
|
}
|
|
776
|
-
var clamp01 = (n) => n < 0 ? 0 : n > 1 ? 1 : n;
|
|
777
778
|
function pxPer(unit, fontSize) {
|
|
778
779
|
if (unit === "") return 1;
|
|
779
780
|
const ems = emsPer(unit);
|
|
@@ -1377,7 +1378,7 @@ function boxTransformAttr(node) {
|
|
|
1377
1378
|
if (!node.rotation) return null;
|
|
1378
1379
|
const cx = node.x + node.width / 2;
|
|
1379
1380
|
const cy = node.y + node.height / 2;
|
|
1380
|
-
const deg = node.rotation *
|
|
1381
|
+
const deg = node.rotation * RAD_TO_DEG;
|
|
1381
1382
|
return `transform="rotate(${trimNumber(deg)} ${trimNumber(cx)} ${trimNumber(cy)})"`;
|
|
1382
1383
|
}
|
|
1383
1384
|
|
|
@@ -1439,8 +1440,6 @@ function toggle(node, key) {
|
|
|
1439
1440
|
if (node[key]) delete node[key];
|
|
1440
1441
|
else node[key] = true;
|
|
1441
1442
|
}
|
|
1442
|
-
|
|
1443
|
-
// src/patterns.ts
|
|
1444
1443
|
function patternXml(id, paint, onWarn) {
|
|
1445
1444
|
if (isImagePatternPaint(paint)) return imagePatternXml(id, paint, onWarn);
|
|
1446
1445
|
if (!isTilePaint(paint)) {
|
|
@@ -1500,7 +1499,7 @@ function shapeXml(shape) {
|
|
|
1500
1499
|
case "circle":
|
|
1501
1500
|
return `<circle cx="${trimNumber(shape.cx)}" cy="${trimNumber(shape.cy)}" r="${trimNumber(shape.r)}" fill="${escapeAttr(shape.color)}"/>`;
|
|
1502
1501
|
case "ellipse": {
|
|
1503
|
-
const deg = shape.rotation *
|
|
1502
|
+
const deg = shape.rotation * RAD_TO_DEG;
|
|
1504
1503
|
const transform = deg === 0 ? "" : ` transform="rotate(${trimNumber(deg)} ${trimNumber(shape.cx)} ${trimNumber(shape.cy)})"`;
|
|
1505
1504
|
return `<ellipse cx="${trimNumber(shape.cx)}" cy="${trimNumber(shape.cy)}" rx="${trimNumber(shape.rx)}" ry="${trimNumber(shape.ry)}" fill="${escapeAttr(shape.color)}"${transform}/>`;
|
|
1506
1505
|
}
|
|
@@ -1566,8 +1565,6 @@ function readPlacement(el, onWarn) {
|
|
|
1566
1565
|
if (!isIdentity([m[0], m[1], m[2], m[3], 0, 0])) placement.transform = [m[0], m[1], m[2], m[3]];
|
|
1567
1566
|
return placement;
|
|
1568
1567
|
}
|
|
1569
|
-
|
|
1570
|
-
// src/gradients.ts
|
|
1571
1568
|
var WEASEL_NS = "urn:weasel-js:svg";
|
|
1572
1569
|
var WEASEL_NS_PREFIX = "wzl";
|
|
1573
1570
|
function nativeSvgKind(kind) {
|
|
@@ -1888,7 +1885,7 @@ function defaultMarkerXml(id, entry, onWarn, key, size) {
|
|
|
1888
1885
|
const d = pathToD(path);
|
|
1889
1886
|
const fill = markerPaintAttrs("fill", entry.fill, id, onWarn);
|
|
1890
1887
|
const outline = entry.outline ? ` ${markerPaintAttrs("stroke", entry.outline.paint, id, onWarn)} stroke-width="${trimNumber(entry.outline.width * unit)}" stroke-linecap="round" stroke-linejoin="round"` : "";
|
|
1891
|
-
const orient = typeof entry.orient === "number" ? trimNumber(entry.orient *
|
|
1888
|
+
const orient = typeof entry.orient === "number" ? trimNumber(entry.orient * RAD_TO_DEG) : "auto-start-reverse";
|
|
1892
1889
|
const units = size === void 0 ? 'markerUnits="strokeWidth" markerWidth="8" markerHeight="8"' : `markerUnits="userSpaceOnUse" markerWidth="${trimNumber(8 * unit)}" markerHeight="${trimNumber(8 * unit)}" ${WEASEL_NS_PREFIX}:key="${escapeAttr(name)}" ${WEASEL_NS_PREFIX}:size="${typeof size === "number" ? trimNumber(size) : `${trimNumber(size.px)}px`}"`;
|
|
1893
1890
|
const inset = writesInset(entry) ? ` ${WEASEL_NS_PREFIX}:inset="${trimNumber(entry.inset)}"` : "";
|
|
1894
1891
|
return `<marker id="${id}" ${units}${inset} refX="0" refY="0" orient="${orient}" overflow="visible"><path d="${d}" ${fill}${outline}/></marker>`;
|
|
@@ -2295,7 +2292,8 @@ function strokeOutlineXml(path, stroke, fillAttrs, transform) {
|
|
|
2295
2292
|
...stroke.align ? { align: stroke.align } : {},
|
|
2296
2293
|
...stroke.markerStart ? { markerStart: stroke.markerStart } : {},
|
|
2297
2294
|
...stroke.markerMid ? { markerMid: stroke.markerMid } : {},
|
|
2298
|
-
...stroke.markerEnd ? { markerEnd: stroke.markerEnd } : {}
|
|
2295
|
+
...stroke.markerEnd ? { markerEnd: stroke.markerEnd } : {},
|
|
2296
|
+
...stroke.trim ? { trim: stroke.trim } : {}
|
|
2299
2297
|
};
|
|
2300
2298
|
const outline = strokeToPath(path, kit);
|
|
2301
2299
|
const attrs = [`d="${pathToD(outline)}"`, ...fillAttrs, 'stroke="none"'];
|
|
@@ -3277,7 +3275,7 @@ function markerOrient(raw) {
|
|
|
3277
3275
|
if (!m) return { orient: 0, reverseAtStart: false };
|
|
3278
3276
|
const n = parseFloat(m[1]);
|
|
3279
3277
|
const unit = (m[2] ?? "deg").toLowerCase();
|
|
3280
|
-
const rad = unit === "rad" ? n : unit === "grad" ? n * Math.PI / 200 : unit === "turn" ? n * 2 * Math.PI : n *
|
|
3278
|
+
const rad = unit === "rad" ? n : unit === "grad" ? n * Math.PI / 200 : unit === "turn" ? n * 2 * Math.PI : n * DEG_TO_RAD;
|
|
3281
3279
|
return { orient: rad, reverseAtStart: false };
|
|
3282
3280
|
}
|
|
3283
3281
|
function markerPaint(style, attr, gradients, onWarn) {
|
|
@@ -3824,12 +3822,15 @@ function pathAsSvgDraws(node) {
|
|
|
3824
3822
|
const { stroke: _stroke, ...rest } = node;
|
|
3825
3823
|
return { ...rest, fill: only.fill, ...only.stroke ? { stroke: only.stroke } : {} };
|
|
3826
3824
|
}
|
|
3827
|
-
|
|
3828
|
-
|
|
3829
|
-
|
|
3830
|
-
|
|
3825
|
+
return drawsAsGroup(
|
|
3826
|
+
node,
|
|
3827
|
+
pieces.map((p) => ({ kind: "path", path: p.path, fill: p.fill, ...p.stroke ? { stroke: p.stroke } : {} }))
|
|
3828
|
+
);
|
|
3829
|
+
}
|
|
3830
|
+
function drawsAsGroup(node, children) {
|
|
3831
|
+
const group = { kind: "group", children };
|
|
3831
3832
|
if (node.rotation != null && node.rotation !== 0) {
|
|
3832
|
-
const b = pathBounds(path);
|
|
3833
|
+
const b = pathBounds(node.path);
|
|
3833
3834
|
group.transform = rotateAboutPoint((b.minX + b.maxX) / 2, (b.minY + b.maxY) / 2, node.rotation);
|
|
3834
3835
|
}
|
|
3835
3836
|
if (node.opacity != null) group.opacity = node.opacity;
|
|
@@ -3911,6 +3912,7 @@ function strokeAttrsFor(stroke, registry, warn) {
|
|
|
3911
3912
|
}
|
|
3912
3913
|
if (stroke.dash && stroke.dash.length > 0) {
|
|
3913
3914
|
attrs.push(`stroke-dasharray="${stroke.dash.map(trimNumber).join(" ")}"`);
|
|
3915
|
+
if (stroke.dashOffset) attrs.push(`stroke-dashoffset="${trimNumber(stroke.dashOffset)}"`);
|
|
3914
3916
|
}
|
|
3915
3917
|
if (stroke.miterLimit != null) {
|
|
3916
3918
|
attrs.push(`stroke-miterlimit="${trimNumber(stroke.miterLimit)}"`);
|
|
@@ -3983,6 +3985,44 @@ function fitLeafXml(xml, inherits, painted) {
|
|
|
3983
3985
|
return `<${name}${attrs}${resets.map((n) => ` ${n}="${INITIAL[n]}"`).join("")}${close2}>`;
|
|
3984
3986
|
});
|
|
3985
3987
|
}
|
|
3988
|
+
var NONE = { kind: "none" };
|
|
3989
|
+
function withTrimDrawn(node) {
|
|
3990
|
+
const { stroke, inherits: _inherits, ...rest } = node;
|
|
3991
|
+
if (!stroke || stroke.trim === void 0 || hasVariableWidth(stroke)) return node;
|
|
3992
|
+
const { trim, ...own2 } = stroke;
|
|
3993
|
+
const bare = own2.paint.kind === "gradient" ? { ...own2, paint: { ...own2.paint, paint: fillInPoseFrame(own2.paint.paint, boundsOfPath$1(node.path)) } } : own2;
|
|
3994
|
+
const cut = trimmedPath(node.path, trim);
|
|
3995
|
+
const children = [
|
|
3996
|
+
...node.fill.kind === "none" ? [] : [{ kind: "path", path: node.path, fill: node.fill }],
|
|
3997
|
+
...cut ? strokeRuns(cut, bare) : []
|
|
3998
|
+
];
|
|
3999
|
+
if (children.length === 0) return null;
|
|
4000
|
+
if (children.length === 1 && !node.rotation) return { ...rest, ...children[0] };
|
|
4001
|
+
return drawsAsGroup(node, children);
|
|
4002
|
+
}
|
|
4003
|
+
function withTrimsDrawn(nodes) {
|
|
4004
|
+
const out = [];
|
|
4005
|
+
for (const n of nodes) {
|
|
4006
|
+
const drawn = n.kind === "path" ? withTrimDrawn(n) : n.kind === "group" ? { ...n, children: withTrimsDrawn(n.children) } : n;
|
|
4007
|
+
if (drawn) out.push(drawn);
|
|
4008
|
+
}
|
|
4009
|
+
return out;
|
|
4010
|
+
}
|
|
4011
|
+
function strokeRuns(cut, stroke) {
|
|
4012
|
+
const dashed = (stroke.dash?.length ?? 0) > 0;
|
|
4013
|
+
return trimmedRuns(cut, dashed, true).map(({ path, dashPhase, fromClosed }) => {
|
|
4014
|
+
const s = fromClosed ? withoutEndMarkers(stroke) : stroke;
|
|
4015
|
+
return { kind: "path", path, fill: NONE, stroke: dashed && dashPhase > 0 ? { ...s, dashOffset: dashPhase } : s };
|
|
4016
|
+
});
|
|
4017
|
+
}
|
|
4018
|
+
var NOTHING = { start: 1, end: 0 };
|
|
4019
|
+
function strokeTrimOnPath(stroke, local, drawn) {
|
|
4020
|
+
if (stroke.trim === void 0) return stroke;
|
|
4021
|
+
const span = trimSpan(local, stroke.trim);
|
|
4022
|
+
if (!span) return { ...stroke, trim: NOTHING };
|
|
4023
|
+
const table = pathLengthTable(drawn);
|
|
4024
|
+
return { ...stroke, trim: { start: { length: lengthAtCurve(table, span.from) }, end: { length: lengthAtCurve(table, span.to) } } };
|
|
4025
|
+
}
|
|
3986
4026
|
function textXml(node, registry, namespaces, warn, given) {
|
|
3987
4027
|
const layout = given && layoutMeasured(given, node.text) ? given : null;
|
|
3988
4028
|
const resolved = resolveTextStyle(node.style);
|
|
@@ -4145,13 +4185,12 @@ function prettyXml(xml) {
|
|
|
4145
4185
|
}
|
|
4146
4186
|
return lines.join("\n");
|
|
4147
4187
|
}
|
|
4148
|
-
|
|
4149
|
-
// src/serialize.ts
|
|
4150
4188
|
var NOTHING_PAINTED = /* @__PURE__ */ new Set();
|
|
4151
|
-
function serializeSvg(
|
|
4189
|
+
function serializeSvg(given, opts = {}) {
|
|
4152
4190
|
const registry = new PaintServerRegistry();
|
|
4191
|
+
const nodes = withTrimsDrawn(given);
|
|
4153
4192
|
registerPaintServers(nodes, registry);
|
|
4154
|
-
const bounds = opts.viewBox ?? computeBounds(
|
|
4193
|
+
const bounds = opts.viewBox ?? computeBounds(given);
|
|
4155
4194
|
const vb = `${trimNumber(bounds.x)} ${trimNumber(bounds.y)} ${trimNumber(bounds.width)} ${trimNumber(bounds.height)}`;
|
|
4156
4195
|
const namespaces = opts.namespaces ?? {};
|
|
4157
4196
|
const rootAttrs = [`xmlns="http://www.w3.org/2000/svg"`];
|
|
@@ -4304,7 +4343,7 @@ function rotationAttr(node) {
|
|
|
4304
4343
|
const b = pathBounds(node.path);
|
|
4305
4344
|
const cx = b.minX + (b.maxX - b.minX) / 2;
|
|
4306
4345
|
const cy = b.minY + (b.maxY - b.minY) / 2;
|
|
4307
|
-
const deg = node.rotation *
|
|
4346
|
+
const deg = node.rotation * RAD_TO_DEG;
|
|
4308
4347
|
return `transform="rotate(${trimNumber(deg)} ${trimNumber(cx)} ${trimNumber(cy)})"`;
|
|
4309
4348
|
}
|
|
4310
4349
|
function svgNeeds(nodes, opts = {}) {
|
|
@@ -4393,7 +4432,8 @@ function strokeDataFromSvg(stroke, box) {
|
|
|
4393
4432
|
...stroke.markerMid !== void 0 ? { markerMid: stroke.markerMid } : {},
|
|
4394
4433
|
...stroke.markerEnd !== void 0 ? { markerEnd: stroke.markerEnd } : {},
|
|
4395
4434
|
...stroke.vertexWidths !== void 0 ? { vertexWidths: stroke.vertexWidths } : {},
|
|
4396
|
-
...stroke.nib !== void 0 ? { nib: stroke.nib } : {}
|
|
4435
|
+
...stroke.nib !== void 0 ? { nib: stroke.nib } : {},
|
|
4436
|
+
...stroke.trim !== void 0 ? { trim: stroke.trim } : {}
|
|
4397
4437
|
};
|
|
4398
4438
|
}
|
|
4399
4439
|
function fillInBoxFrame(fill, box) {
|
|
@@ -4699,9 +4739,12 @@ function svgStrokeFromKit(raw, box) {
|
|
|
4699
4739
|
...stroke.markerMid !== void 0 ? { markerMid: stroke.markerMid } : {},
|
|
4700
4740
|
...stroke.markerEnd !== void 0 ? { markerEnd: stroke.markerEnd } : {},
|
|
4701
4741
|
...stroke.vertexWidths?.length ? { vertexWidths: stroke.vertexWidths } : {},
|
|
4702
|
-
...stroke.nib ? { nib: stroke.nib } : {}
|
|
4742
|
+
...stroke.nib ? { nib: stroke.nib } : {},
|
|
4743
|
+
...stroke.trim !== void 0 ? { trim: stroke.trim } : {}
|
|
4703
4744
|
};
|
|
4704
4745
|
}
|
|
4746
|
+
|
|
4747
|
+
// src/fromKit.ts
|
|
4705
4748
|
function svgImageFromKit(image, pose) {
|
|
4706
4749
|
const node = {
|
|
4707
4750
|
kind: "image",
|
|
@@ -4750,9 +4793,10 @@ function svgLeafFromKit(data, pose) {
|
|
|
4750
4793
|
if (stroke) node.stroke = stroke;
|
|
4751
4794
|
const frame = poseLinearOf(pose) && poseFrameOf(pose);
|
|
4752
4795
|
if (frame) {
|
|
4753
|
-
|
|
4796
|
+
const local = node.path;
|
|
4797
|
+
node.path = pathThroughPoseFrame(stroke?.trim ? asPolygonPath(local) : local, pose);
|
|
4754
4798
|
node.fill = paintThroughMatrix(node.fill, frame);
|
|
4755
|
-
if (stroke) node.stroke = strokeThroughMatrix(stroke, frame);
|
|
4799
|
+
if (stroke) node.stroke = strokeTrimOnPath(strokeThroughMatrix(stroke, frame), local, node.path);
|
|
4756
4800
|
} else if (pose.rotation) node.rotation = pose.rotation;
|
|
4757
4801
|
return node;
|
|
4758
4802
|
}
|