@weasel-js/svg 1.11.0 → 1.11.1
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/README.md +8 -0
- package/dist/index.d.ts +13 -11
- package/dist/index.js +20 -45
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -183,6 +183,14 @@ writes a `kit:image` leaf back as an `SvgImageNode`.
|
|
|
183
183
|
in (a non-`none` value warns) and written back as `none`, so a source file
|
|
184
184
|
that relied on letterboxing imports stretched.
|
|
185
185
|
|
|
186
|
+
## Writing XML by hand
|
|
187
|
+
|
|
188
|
+
`escapeAttr(s)` and `escapeText(s)` are the escaping this package writes XML
|
|
189
|
+
with, for a caller writing its own. Both drop the characters XML 1.0 cannot
|
|
190
|
+
hold, even as references, and turn a vertical tab or form feed into a line
|
|
191
|
+
break. `escapeAttr` also writes tabs and line breaks as references, since a
|
|
192
|
+
parser reads a literal one in an attribute as a space.
|
|
193
|
+
|
|
186
194
|
## License
|
|
187
195
|
|
|
188
196
|
MIT
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Affine } from '@weasel-js/geom';
|
|
2
|
+
export { pathToD as serializePathD } from '@weasel-js/geom';
|
|
2
3
|
import { Path, Paint, ScreenLength, StrokeAlign, MarkerRef, VertexWidth, StrokeNib, StyledRun, TextStyle, TextVerticalAlign, Fill, Stroke, PoseLinear, MarkerEntry, TextPoseLayout, RenderNeeds, TilePatternSpec, ImageNodeData, IngestCtx } from '@weasel-js/core';
|
|
3
4
|
|
|
4
5
|
/**
|
|
@@ -441,18 +442,19 @@ declare function nativeSvgSpace(space: string | null | undefined): boolean;
|
|
|
441
442
|
declare function serializeSvg(nodes: SvgNode[], opts?: SerializeOptions): string;
|
|
442
443
|
|
|
443
444
|
/**
|
|
444
|
-
*
|
|
445
|
-
* lowers to `M h v h Z`; `PolygonPath` walks the (commands, coords)
|
|
446
|
-
* arrays, emitting one SVG command per kit command, with corner shapes
|
|
447
|
-
* expanded into the geometry they draw.
|
|
445
|
+
* Escaping for the XML this package writes by hand.
|
|
448
446
|
*
|
|
449
|
-
*
|
|
450
|
-
*
|
|
451
|
-
*
|
|
447
|
+
* XML 1.0 cannot hold most C0 controls, U+FFFE, U+FFFF, or half a surrogate
|
|
448
|
+
* pair, even as character references, and a parser rejects the whole
|
|
449
|
+
* document over one. A vertical tab or form feed is a line break where text
|
|
450
|
+
* comes from (a word processor's clipboard), so those become `\n`; the rest
|
|
451
|
+
* are dropped.
|
|
452
452
|
*/
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
declare function
|
|
453
|
+
/** A parser turns a literal tab or line break in an attribute value into a
|
|
454
|
+
* space, so those are written as character references. */
|
|
455
|
+
declare function escapeAttr(s: string): string;
|
|
456
|
+
/** A literal carriage return in content is read back as a newline. */
|
|
457
|
+
declare function escapeText(s: string): string;
|
|
456
458
|
|
|
457
459
|
/**
|
|
458
460
|
* What `serializeSvg(nodes)` reads that loads on demand: the paint kind of
|
|
@@ -701,4 +703,4 @@ declare function svgNodesToKitDrafts<TData = Record<string, unknown>>(input: Par
|
|
|
701
703
|
*/
|
|
702
704
|
declare function unpackSvgFiles(files: File[], ctx: IngestCtx): Promise<void>;
|
|
703
705
|
|
|
704
|
-
export { DEFAULT_MEDIA_ENVIRONMENT, IDENTITY_MATRIX, type Matrix, type NamespaceMeta, type NamespacedElement, type ParseOptions, type ParseResult, type SerializeOptions, type SupportsOptions, type SvgDraftBounds, type SvgDraftPose, type SvgGroupNode, type SvgImageNode, type SvgKitLeafData, type SvgKitPose, type SvgKitTree, type SvgKitTreeNode, type SvgLeafNode, type SvgMediaEnvironment, type SvgNode, type SvgNodesFromKitOptions, type SvgNodesToKitDraftsOptions, type SvgPaint, type SvgPathNode, type SvgSceneDraft, type SvgStroke, type SvgTextNode, evaluateMediaQuery, evaluateSupports, fillDataFromSvg, nativeSvgKind, nativeSvgSpace, parseSvg,
|
|
706
|
+
export { DEFAULT_MEDIA_ENVIRONMENT, IDENTITY_MATRIX, type Matrix, type NamespaceMeta, type NamespacedElement, type ParseOptions, type ParseResult, type SerializeOptions, type SupportsOptions, type SvgDraftBounds, type SvgDraftPose, type SvgGroupNode, type SvgImageNode, type SvgKitLeafData, type SvgKitPose, type SvgKitTree, type SvgKitTreeNode, type SvgLeafNode, type SvgMediaEnvironment, type SvgNode, type SvgNodesFromKitOptions, type SvgNodesToKitDraftsOptions, type SvgPaint, type SvgPathNode, type SvgSceneDraft, type SvgStroke, type SvgTextNode, escapeAttr, escapeText, evaluateMediaQuery, evaluateSupports, fillDataFromSvg, nativeSvgKind, nativeSvgSpace, parseSvg, serializeSvg, strokeDataFromSvg, svgImageFromKit, svgLeafFromKit, svgNeeds, svgNodesFromKit, svgNodesToKitDrafts, svgPaintFromKit, svgStrokeFromKit, tilePreviewCssUrl, tilePreviewSvg, unpackSvgFiles, warmSvg };
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { identity, multiply, invert, splitByClosure, fillsOpenSubpaths, rotateAboutPoint, hasOpenSubpath, splitSubpaths } from '@weasel-js/geom';
|
|
2
|
-
|
|
1
|
+
import { identity, multiply, pathToD, invert, splitByClosure, fillsOpenSubpaths, rotateAboutPoint, hasOpenSubpath, splitSubpaths } from '@weasel-js/geom';
|
|
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
4
|
import { tileGeometry } from '@weasel-js/core/patterns-builtin';
|
|
4
5
|
|
|
5
6
|
// src/types.ts
|
|
@@ -1383,40 +1384,6 @@ function readPattern(el, onWarn) {
|
|
|
1383
1384
|
if (!isIdentity([m[0], m[1], m[2], m[3], 0, 0])) paint.transform = [m[0], m[1], m[2], m[3]];
|
|
1384
1385
|
return paint;
|
|
1385
1386
|
}
|
|
1386
|
-
function serializePathD(given) {
|
|
1387
|
-
const path = resolveCorners(given);
|
|
1388
|
-
if (path.kind === "rect") {
|
|
1389
|
-
const { x, y, width: width2, height: height2 } = path;
|
|
1390
|
-
return `M${trimNumber(x)} ${trimNumber(y)}h${trimNumber(width2)}v${trimNumber(height2)}h${trimNumber(-width2)}Z`;
|
|
1391
|
-
}
|
|
1392
|
-
const cmds = path.commands;
|
|
1393
|
-
const xs = path.coords;
|
|
1394
|
-
const out = [];
|
|
1395
|
-
let ci = 0;
|
|
1396
|
-
for (let i = 0; i < cmds.length; i++) {
|
|
1397
|
-
const c = cmds[i];
|
|
1398
|
-
if (c === PATH_M) {
|
|
1399
|
-
out.push(`M${trimNumber(xs[ci])} ${trimNumber(xs[ci + 1])}`);
|
|
1400
|
-
ci += 2;
|
|
1401
|
-
} else if (c === PATH_L) {
|
|
1402
|
-
out.push(`L${trimNumber(xs[ci])} ${trimNumber(xs[ci + 1])}`);
|
|
1403
|
-
ci += 2;
|
|
1404
|
-
} else if (c === PATH_C) {
|
|
1405
|
-
out.push(
|
|
1406
|
-
`C${trimNumber(xs[ci])} ${trimNumber(xs[ci + 1])} ${trimNumber(xs[ci + 2])} ${trimNumber(xs[ci + 3])} ${trimNumber(xs[ci + 4])} ${trimNumber(xs[ci + 5])}`
|
|
1407
|
-
);
|
|
1408
|
-
ci += 6;
|
|
1409
|
-
} else if (c === PATH_Q) {
|
|
1410
|
-
out.push(
|
|
1411
|
-
`Q${trimNumber(xs[ci])} ${trimNumber(xs[ci + 1])} ${trimNumber(xs[ci + 2])} ${trimNumber(xs[ci + 3])}`
|
|
1412
|
-
);
|
|
1413
|
-
ci += 4;
|
|
1414
|
-
} else if (c === PATH_Z) {
|
|
1415
|
-
out.push("Z");
|
|
1416
|
-
}
|
|
1417
|
-
}
|
|
1418
|
-
return out.join("");
|
|
1419
|
-
}
|
|
1420
1387
|
|
|
1421
1388
|
// src/gradients.ts
|
|
1422
1389
|
var WEASEL_NS = "urn:weasel-js:svg";
|
|
@@ -1722,7 +1689,7 @@ var PaintServerRegistry = class {
|
|
|
1722
1689
|
}
|
|
1723
1690
|
for (const path of this.clips) {
|
|
1724
1691
|
parts.push(
|
|
1725
|
-
`<clipPath id="${this.clipIds.get(path)}"><path d="${
|
|
1692
|
+
`<clipPath id="${this.clipIds.get(path)}"><path d="${pathToD(path)}"/></clipPath>`
|
|
1726
1693
|
);
|
|
1727
1694
|
}
|
|
1728
1695
|
parts.push("</defs>");
|
|
@@ -1736,7 +1703,7 @@ function defaultMarkerXml(id, entry, onWarn, key, size) {
|
|
|
1736
1703
|
const name = key ?? id;
|
|
1737
1704
|
const unit = size === void 0 ? 1 : resolveMarkerSize({ key: name, size }, 1);
|
|
1738
1705
|
const path = entry.path({ size: unit, stroke: { paint: { kind: "solid", color: "#000" } } });
|
|
1739
|
-
const d =
|
|
1706
|
+
const d = pathToD(path);
|
|
1740
1707
|
const fill = markerPaintAttrs("fill", entry.fill, id, onWarn);
|
|
1741
1708
|
const outline = entry.outline ? ` ${markerPaintAttrs("stroke", entry.outline.paint, id, onWarn)} stroke-width="${trimNumber(entry.outline.width * unit)}" stroke-linecap="round" stroke-linejoin="round"` : "";
|
|
1742
1709
|
const orient = typeof entry.orient === "number" ? trimNumber(entry.orient * 180 / Math.PI) : "auto-start-reverse";
|
|
@@ -2159,7 +2126,7 @@ function strokeOutlineXml(path, stroke, fillAttrs, transform) {
|
|
|
2159
2126
|
...stroke.markerEnd ? { markerEnd: stroke.markerEnd } : {}
|
|
2160
2127
|
};
|
|
2161
2128
|
const outline = strokeToPath(path, kit);
|
|
2162
|
-
const attrs = [`d="${
|
|
2129
|
+
const attrs = [`d="${pathToD(outline)}"`, ...fillAttrs, 'stroke="none"'];
|
|
2163
2130
|
if (transform) attrs.push(transform);
|
|
2164
2131
|
attrs.push(`${WEASEL_NS_PREFIX}:${ROLE}="${OUTLINE}"`);
|
|
2165
2132
|
return `<path ${attrs.join(" ")}/>`;
|
|
@@ -2403,13 +2370,17 @@ var LineTracker = class {
|
|
|
2403
2370
|
return Math.abs(lineHeight - DEFAULT_LINE_HEIGHT) < 1e-6 ? void 0 : lineHeight;
|
|
2404
2371
|
}
|
|
2405
2372
|
};
|
|
2373
|
+
var holdsOnlyText = (el) => !Array.from(el.childNodes).some(
|
|
2374
|
+
(child) => child.nodeType === 1
|
|
2375
|
+
/* ELEMENT_NODE */
|
|
2376
|
+
);
|
|
2406
2377
|
function warnUnreadPositions(el, onWarn) {
|
|
2407
2378
|
const tag = el.tagName.toLowerCase();
|
|
2408
2379
|
for (const name of ["x", "y"]) {
|
|
2409
2380
|
const raw = el.getAttribute(name)?.trim() ?? "";
|
|
2410
2381
|
if (/[\s,]/.test(raw)) onWarn(`<${tag} ${name}="${raw}"> positions each glyph; only the first value is read`);
|
|
2411
2382
|
}
|
|
2412
|
-
for (const name of tag === "tspan" ? ["rotate"] : ["dx", "rotate"]) {
|
|
2383
|
+
for (const name of tag === "tspan" || holdsOnlyText(el) ? ["rotate"] : ["dx", "rotate"]) {
|
|
2413
2384
|
const raw = el.getAttribute(name)?.trim() ?? "";
|
|
2414
2385
|
if (raw.split(/[\s,]+/).some((v) => parseFloat(v) !== 0 && v !== "")) {
|
|
2415
2386
|
onWarn(`<${tag} ${name}="${raw}"> is not read; the glyphs keep their own advance and angle`);
|
|
@@ -2540,8 +2511,9 @@ function placeTspanRun(tspan, run, lines, inherited2, onWarn) {
|
|
|
2540
2511
|
return 0;
|
|
2541
2512
|
}
|
|
2542
2513
|
const raw = tspan.getAttribute("dx");
|
|
2514
|
+
const tag = tspan.tagName.toLowerCase();
|
|
2543
2515
|
if (COLLAPSIBLE.test(run.text)) {
|
|
2544
|
-
onWarn(
|
|
2516
|
+
onWarn(`<${tag} dx="${raw}"> is not read: its text has white space SVG collapses, which moves the glyphs the list names`);
|
|
2545
2517
|
line.push(run);
|
|
2546
2518
|
return 0;
|
|
2547
2519
|
}
|
|
@@ -2554,7 +2526,7 @@ function placeTspanRun(tspan, run, lines, inherited2, onWarn) {
|
|
|
2554
2526
|
} else if (dx[0] !== 0 && lines.length === 1) {
|
|
2555
2527
|
lead = dx[0];
|
|
2556
2528
|
} else if (dx[0] !== 0) {
|
|
2557
|
-
onWarn(
|
|
2529
|
+
onWarn(`<${tag} dx="${raw}"> opens a line; the line starts at the box edge, and the rest of the list is read`);
|
|
2558
2530
|
}
|
|
2559
2531
|
line.push(...spread(run, dx.slice(1), inherited2));
|
|
2560
2532
|
return lead;
|
|
@@ -2716,7 +2688,10 @@ function readRuns(el, tracker, inheritedShift, letterSpacing, gradients, leafSty
|
|
|
2716
2688
|
const push = (run) => {
|
|
2717
2689
|
runLines[runLines.length - 1].push(run);
|
|
2718
2690
|
};
|
|
2719
|
-
|
|
2691
|
+
if (holdsOnlyText(el) && el.hasAttribute("dx")) {
|
|
2692
|
+
const t = el.textContent ?? "";
|
|
2693
|
+
if (t) lead = placeTspanRun(el, { text: t, ...inheritedShift }, runLines, letterSpacing, onWarn);
|
|
2694
|
+
} else for (let i = 0; i < el.childNodes.length; i++) {
|
|
2720
2695
|
const child = el.childNodes[i];
|
|
2721
2696
|
if (child.nodeType === 3) {
|
|
2722
2697
|
const t = child.textContent ?? "";
|
|
@@ -3809,7 +3784,7 @@ function groupXml(node, registry, namespaces, warn, layoutOf) {
|
|
|
3809
3784
|
return `${head}${body}${metaElementsXml(node.meta, namespaces)}</g>`;
|
|
3810
3785
|
}
|
|
3811
3786
|
function pathXml(node, registry, namespaces, warn) {
|
|
3812
|
-
const attrs = [`d="${
|
|
3787
|
+
const attrs = [`d="${pathToD(node.path)}"`];
|
|
3813
3788
|
const fillAttrs = paintAttrs(node.fill, "fill", registry);
|
|
3814
3789
|
for (const a of fillAttrs) attrs.push(a);
|
|
3815
3790
|
if (node.path.kind === "polygon" && node.path.fillRule === "evenodd") {
|
|
@@ -4257,6 +4232,6 @@ function svgNodesFromKit(tree, options = {}) {
|
|
|
4257
4232
|
return out;
|
|
4258
4233
|
}
|
|
4259
4234
|
|
|
4260
|
-
export { DEFAULT_MEDIA_ENVIRONMENT, IDENTITY_MATRIX, evaluateMediaQuery, evaluateSupports, fillDataFromSvg, nativeSvgKind, nativeSvgSpace, parseSvg,
|
|
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 };
|
|
4261
4236
|
//# sourceMappingURL=index.js.map
|
|
4262
4237
|
//# sourceMappingURL=index.js.map
|