@octane-xplat/nano-icons 0.1.0 → 0.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.
Files changed (137) hide show
  1. package/LICENSE +21 -0
  2. package/LICENSES.md +31 -0
  3. package/README.md +218 -1
  4. package/bin/nano-icons.mjs +2 -0
  5. package/dist/build/build-BbWkFu_9.js +4308 -0
  6. package/dist/build/cli.js +59 -0
  7. package/dist/build/index.js +2 -0
  8. package/package.json +103 -5
  9. package/src/NanoIcon.macos.tsrx +102 -0
  10. package/src/NanoIcon.tsrx +78 -0
  11. package/src/NanoIcon.web.tsrx +105 -0
  12. package/src/base64.ts +51 -0
  13. package/src/build/cli.ts +94 -0
  14. package/src/build/core/colors.ts +152 -0
  15. package/src/build/core/config.ts +30 -0
  16. package/src/build/core/fingerPrint.ts +68 -0
  17. package/src/build/core/flatten/arcs.ts +187 -0
  18. package/src/build/core/flatten/document.ts +237 -0
  19. package/src/build/core/flatten/dom.ts +268 -0
  20. package/src/build/core/flatten/element.ts +111 -0
  21. package/src/build/core/flatten/geometry.ts +95 -0
  22. package/src/build/core/flatten/index.ts +12 -0
  23. package/src/build/core/flatten/inherit.ts +281 -0
  24. package/src/build/core/flatten/path.ts +554 -0
  25. package/src/build/core/flatten/pathops.ts +294 -0
  26. package/src/build/core/flatten/shape.ts +513 -0
  27. package/src/build/core/flatten/stages/clip-path.ts +44 -0
  28. package/src/build/core/flatten/stages/collapse.ts +119 -0
  29. package/src/build/core/flatten/stages/defs.ts +60 -0
  30. package/src/build/core/flatten/stages/discard.ts +62 -0
  31. package/src/build/core/flatten/stages/fill-rule.ts +8 -0
  32. package/src/build/core/flatten/stages/nested-svg.ts +174 -0
  33. package/src/build/core/flatten/stages/opacity.ts +8 -0
  34. package/src/build/core/flatten/stages/shapes.ts +49 -0
  35. package/src/build/core/flatten/stages/stroke.ts +49 -0
  36. package/src/build/core/flatten/stages/styles.ts +32 -0
  37. package/src/build/core/flatten/stages/use.ts +98 -0
  38. package/src/build/core/flatten/stages/validate.ts +50 -0
  39. package/src/build/core/flatten/stages.ts +56 -0
  40. package/src/build/core/flatten/transform.ts +265 -0
  41. package/src/build/core/font/compile.ts +115 -0
  42. package/src/build/core/font/metrics.ts +56 -0
  43. package/src/build/core/font/quadratic.ts +61 -0
  44. package/src/build/core/fontIdentity.ts +24 -0
  45. package/src/build/core/glyph/merge.ts +74 -0
  46. package/src/build/core/glyph/parse.ts +147 -0
  47. package/src/build/core/glyph/placement.ts +82 -0
  48. package/src/build/core/glyph/validate.ts +57 -0
  49. package/src/build/core/naming.ts +18 -0
  50. package/src/build/core/parse.ts +64 -0
  51. package/src/build/core/pathkit/contours.ts +770 -0
  52. package/src/build/core/pathkit/evenodd.ts +49 -0
  53. package/src/build/core/pathkit/load.ts +34 -0
  54. package/src/build/core/pathkit/types.ts +81 -0
  55. package/src/build/core/pathkit/winding.ts +81 -0
  56. package/src/build/core/prepare.ts +98 -0
  57. package/src/build/core/prepareIcon.ts +115 -0
  58. package/src/build/core/runFontPipeline.ts +190 -0
  59. package/src/build/core/svgPatterns.ts +51 -0
  60. package/src/build/core/types.ts +54 -0
  61. package/src/build/externals.d.ts +17 -0
  62. package/src/build/iconify.ts +52 -0
  63. package/src/build/index.ts +304 -0
  64. package/src/build/module.ts +62 -0
  65. package/src/font.macos.ts +133 -0
  66. package/src/font.ts +137 -0
  67. package/src/font.web.ts +71 -0
  68. package/src/glyphmap.ts +129 -0
  69. package/src/index.macos.ts +31 -0
  70. package/src/index.ts +30 -0
  71. package/src/index.web.ts +31 -0
  72. package/src/props.ts +59 -0
  73. package/src/set.ts +140 -0
  74. package/types/generated/.tsrx-typegen-manifest.json +68 -0
  75. package/types/generated/NanoIcon.d.ts +7 -0
  76. package/types/generated/NanoIcon.macos.d.ts +7 -0
  77. package/types/generated/NanoIcon.web.d.ts +2 -0
  78. package/types/generated/base64.d.ts +4 -0
  79. package/types/generated/build/cli.d.ts +1 -0
  80. package/types/generated/build/core/colors.d.ts +1 -0
  81. package/types/generated/build/core/config.d.ts +11 -0
  82. package/types/generated/build/core/flatten/arcs.d.ts +2 -0
  83. package/types/generated/build/core/flatten/document.d.ts +35 -0
  84. package/types/generated/build/core/flatten/dom.d.ts +24 -0
  85. package/types/generated/build/core/flatten/element.d.ts +12 -0
  86. package/types/generated/build/core/flatten/geometry.d.ts +21 -0
  87. package/types/generated/build/core/flatten/index.d.ts +2 -0
  88. package/types/generated/build/core/flatten/inherit.d.ts +13 -0
  89. package/types/generated/build/core/flatten/path.d.ts +19 -0
  90. package/types/generated/build/core/flatten/pathops.d.ts +14 -0
  91. package/types/generated/build/core/flatten/shape.d.ts +29 -0
  92. package/types/generated/build/core/flatten/stages/clip-path.d.ts +4 -0
  93. package/types/generated/build/core/flatten/stages/collapse.d.ts +2 -0
  94. package/types/generated/build/core/flatten/stages/defs.d.ts +4 -0
  95. package/types/generated/build/core/flatten/stages/discard.d.ts +4 -0
  96. package/types/generated/build/core/flatten/stages/fill-rule.d.ts +2 -0
  97. package/types/generated/build/core/flatten/stages/nested-svg.d.ts +2 -0
  98. package/types/generated/build/core/flatten/stages/opacity.d.ts +2 -0
  99. package/types/generated/build/core/flatten/stages/shapes.d.ts +7 -0
  100. package/types/generated/build/core/flatten/stages/stroke.d.ts +3 -0
  101. package/types/generated/build/core/flatten/stages/styles.d.ts +2 -0
  102. package/types/generated/build/core/flatten/stages/use.d.ts +4 -0
  103. package/types/generated/build/core/flatten/stages/validate.d.ts +2 -0
  104. package/types/generated/build/core/flatten/stages.d.ts +2 -0
  105. package/types/generated/build/core/flatten/transform.d.ts +35 -0
  106. package/types/generated/build/core/font/compile.d.ts +21 -0
  107. package/types/generated/build/core/font/metrics.d.ts +1 -0
  108. package/types/generated/build/core/font/quadratic.d.ts +2 -0
  109. package/types/generated/build/core/fontIdentity.d.ts +4 -0
  110. package/types/generated/build/core/glyph/merge.d.ts +7 -0
  111. package/types/generated/build/core/glyph/parse.d.ts +27 -0
  112. package/types/generated/build/core/glyph/placement.d.ts +28 -0
  113. package/types/generated/build/core/glyph/validate.d.ts +8 -0
  114. package/types/generated/build/core/naming.d.ts +2 -0
  115. package/types/generated/build/core/parse.d.ts +1 -0
  116. package/types/generated/build/core/pathkit/contours.d.ts +75 -0
  117. package/types/generated/build/core/pathkit/evenodd.d.ts +12 -0
  118. package/types/generated/build/core/pathkit/load.d.ts +2 -0
  119. package/types/generated/build/core/pathkit/types.d.ts +69 -0
  120. package/types/generated/build/core/prepare.d.ts +20 -0
  121. package/types/generated/build/core/prepareIcon.d.ts +26 -0
  122. package/types/generated/build/core/runFontPipeline.d.ts +32 -0
  123. package/types/generated/build/core/svgPatterns.d.ts +39 -0
  124. package/types/generated/build/core/types.d.ts +49 -0
  125. package/types/generated/build/externals.d.ts +17 -0
  126. package/types/generated/build/iconify.d.ts +18 -0
  127. package/types/generated/build/index.d.ts +75 -0
  128. package/types/generated/build/module.d.ts +14 -0
  129. package/types/generated/font.d.ts +10 -0
  130. package/types/generated/font.macos.d.ts +9 -0
  131. package/types/generated/font.web.d.ts +8 -0
  132. package/types/generated/glyphmap.d.ts +62 -0
  133. package/types/generated/index.d.ts +21 -0
  134. package/types/generated/index.macos.d.ts +22 -0
  135. package/types/generated/index.web.d.ts +22 -0
  136. package/types/generated/props.d.ts +55 -0
  137. package/types/generated/set.d.ts +41 -0
@@ -0,0 +1,4308 @@
1
+ import crypto from "node:crypto";
2
+ import fs from "node:fs/promises";
3
+ import fs$1 from "node:fs";
4
+ import { createRequire } from "node:module";
5
+ import path from "node:path";
6
+ import { getIcons } from "@iconify/utils";
7
+ import { iconToSVG } from "@iconify/utils/lib/svg/build";
8
+ import { DOMParser } from "@xmldom/xmldom";
9
+ import { Font, woff2 } from "fonteditor-core";
10
+ import svg2ttf from "svg2ttf";
11
+ import cubic2quadImport from "cubic2quad";
12
+ //#region src/build/core/svgPatterns.ts
13
+ /** Path already starts with a moveto. */
14
+ var SVG_MOVE_PREFIX = /^[Mm]/;
15
+ /** Trailing close-path command (+ optional whitespace). */
16
+ var SVG_TRAILING_CLOSE = /[Zz]\s*$/;
17
+ /** A signed int/float token. */
18
+ var SVG_NUMBER = /-?\d+(?:\.\d+)?/g;
19
+ /** Whitespace separator (e.g. viewBox parts). */
20
+ var WHITESPACE = /\s+/;
21
+ /** `<mask …>` element (unsupported). */
22
+ var XML_MASK = /<mask[\s/>]/i;
23
+ /** `<filter …>` element (unsupported). */
24
+ var XML_FILTER = /<filter[\s/>]/i;
25
+ /** `<image …>` element (unsupported). */
26
+ var XML_IMAGE = /<image[\s/>]/i;
27
+ var XML_UNCONVERTIBLE = /<(text|style|foreignObject|a|switch)[\s/>]/i;
28
+ /** An `xmlns=` attribute. */
29
+ var XML_XMLNS = /xmlns\s*=/;
30
+ /** Opening `<svg` tag. */
31
+ var SVG_OPEN_TAG = /<svg\b/;
32
+ var PX_ATTRIBUTE_VALUE = /=(["'])(\s*-?(?:\d+\.?\d*|\.\d+))px\s*\1/g;
33
+ var PX_STYLE_VALUE = /(:\s*-?(?:\d+\.?\d*|\.\d+))px(?=\s*(?:;|["']|$))/g;
34
+ /** viewBox: comma or whitespace separated. */
35
+ var VIEWBOX_SEPARATOR = /,|\s+/;
36
+ /** transform() arguments: comma and/or whitespace separated. */
37
+ var TRANSFORM_ARG_SEPARATOR = /\s*[,\s]\s*/;
38
+ /** stroke-dasharray: single comma or space separated. */
39
+ var DASHARRAY_SEPARATOR = /[, ]/;
40
+ /** One transform function call, e.g. `rotate(30)`; captures name and args. */
41
+ var TRANSFORM_FUNCTION = /(matrix|translate|scale|rotate|skewX|skewY)\s*\(([^)]*)\)/gi;
42
+ /** `url(#id)` reference; captures the id. */
43
+ var URL_ID_REFERENCE = /^url[(]#([\w-]+)[)]$/;
44
+ /** A well-formed XML attribute name. */
45
+ var XML_ATTRIBUTE_NAME = /^[A-Za-z_][\w.-]*$/;
46
+ var XML_AMP = /&/g;
47
+ var XML_LT = /</g;
48
+ var XML_GT = />/g;
49
+ var XML_QUOT = /"/g;
50
+ /** svg2ttf error `glyph "u<hex>"` → captures the hex codepoint. */
51
+ var GLYPH_CODEPOINT = /glyph\s+"u([0-9a-fA-F]+)"/;
52
+ //#endregion
53
+ //#region src/build/core/flatten/dom.ts
54
+ var SVG_NS = "http://www.w3.org/2000/svg";
55
+ var XLINK_NS = "http://www.w3.org/1999/xlink";
56
+ function svgTag(local) {
57
+ return `{${SVG_NS}}${local}`;
58
+ }
59
+ function xlinkHrefAttr() {
60
+ return `{${XLINK_NS}}href`;
61
+ }
62
+ function splitNs(name) {
63
+ if (name.startsWith("{")) {
64
+ const end = name.indexOf("}");
65
+ return [name.slice(1, end), name.slice(end + 1)];
66
+ }
67
+ return [null, name];
68
+ }
69
+ function stripNs(name) {
70
+ return splitNs(name)[1];
71
+ }
72
+ var XEl = class XEl {
73
+ tag;
74
+ attrib;
75
+ children;
76
+ parent = null;
77
+ constructor(tag, attrib) {
78
+ this.tag = tag;
79
+ this.attrib = new Map(attrib ?? []);
80
+ this.children = [];
81
+ }
82
+ append(child) {
83
+ child.detach();
84
+ child.parent = this;
85
+ this.children.push(child);
86
+ }
87
+ insert(index, child) {
88
+ child.detach();
89
+ child.parent = this;
90
+ this.children.splice(index, 0, child);
91
+ }
92
+ detach() {
93
+ if (this.parent) {
94
+ const idx = this.parent.children.indexOf(this);
95
+ if (idx !== -1) this.parent.children.splice(idx, 1);
96
+ this.parent = null;
97
+ }
98
+ }
99
+ index(child) {
100
+ return this.children.indexOf(child);
101
+ }
102
+ deepClone() {
103
+ const copy = new XEl(this.tag, this.attrib);
104
+ for (const child of this.children) copy.append(child.deepClone());
105
+ return copy;
106
+ }
107
+ *iter() {
108
+ yield this;
109
+ for (const child of this.children) yield* child.iter();
110
+ }
111
+ };
112
+ function findAll(root, pred) {
113
+ const out = [];
114
+ for (const el of root.iter()) if (pred(el)) out.push(el);
115
+ return out;
116
+ }
117
+ function replaceEl(el, replacements) {
118
+ const parent = el.parent;
119
+ if (!parent) throw new Error("Cannot replace element without parent");
120
+ const idx = parent.index(el);
121
+ el.detach();
122
+ replacements.forEach((child, childIdx) => {
123
+ parent.insert(idx + childIdx, child);
124
+ });
125
+ }
126
+ function delAttrs(el, ...attrNames) {
127
+ for (const name of attrNames) el.attrib.delete(name);
128
+ }
129
+ function domNsToTag(node) {
130
+ const ns = node.namespaceURI;
131
+ return ns ? `{${ns}}${node.localName}` : node.nodeName;
132
+ }
133
+ function convertElement(node) {
134
+ const el = new XEl(domNsToTag(node));
135
+ const attrs = node.attributes;
136
+ for (let i = 0; i < attrs.length; i++) {
137
+ const attr = attrs.item(i);
138
+ if (attr.prefix === "xmlns" || attr.nodeName === "xmlns") continue;
139
+ const name = attr.namespaceURI ? `{${attr.namespaceURI}}${attr.localName}` : attr.nodeName;
140
+ el.attrib.set(name, attr.value);
141
+ }
142
+ const children = node.childNodes;
143
+ for (let i = 0; i < children.length; i++) {
144
+ const child = children.item(i);
145
+ if (child.nodeType === 1) el.append(convertElement(child));
146
+ }
147
+ return el;
148
+ }
149
+ function parseSvgDocument(content) {
150
+ let text = content;
151
+ if (text.includes("xlink:") && !text.includes("xmlns:xlink")) text = text.replace(/<svg([^>]*)>/, (_m, attrs) => `<svg${attrs} xmlns:xlink="${XLINK_NS}">`);
152
+ let parseError = null;
153
+ const root = new DOMParser({ onError: (_level, message) => {
154
+ parseError = parseError ?? message;
155
+ } }).parseFromString(text, "text/xml").documentElement;
156
+ if (!root || parseError) throw new Error(`Unable to parse SVG: ${parseError ?? "no root element"}`);
157
+ return convertElement(root);
158
+ }
159
+ function escapeAttr(value) {
160
+ return value.replace(XML_AMP, "&amp;").replace(XML_LT, "&lt;").replace(XML_GT, "&gt;").replace(XML_QUOT, "&quot;");
161
+ }
162
+ function attrDisplayName(name) {
163
+ const [ns, local] = splitNs(name);
164
+ if (ns === "http://www.w3.org/1999/xlink") return `xlink:${local}`;
165
+ if (ns === "http://www.w3.org/2000/svg" || ns === null) return local;
166
+ return local;
167
+ }
168
+ function tagDisplayName(tag) {
169
+ const [ns, local] = splitNs(tag);
170
+ if (ns === "http://www.w3.org/2000/svg" || ns === null) return local;
171
+ throw new Error(`Cannot serialize tag outside svg namespace: ${tag}`);
172
+ }
173
+ function usesXlink(root) {
174
+ for (const el of root.iter()) for (const name of el.attrib.keys()) if (splitNs(name)[0] === "http://www.w3.org/1999/xlink") return true;
175
+ return false;
176
+ }
177
+ function serializeEl(el, indent, out) {
178
+ const pad = " ".repeat(indent);
179
+ const parts = [`${pad}<${tagDisplayName(el.tag)}`];
180
+ for (const [name, value] of el.attrib) parts.push(` ${attrDisplayName(name)}="${escapeAttr(value)}"`);
181
+ if (!el.children.length) {
182
+ parts.push("/>");
183
+ out.push(parts.join(""));
184
+ return;
185
+ }
186
+ parts.push(">");
187
+ out.push(parts.join(""));
188
+ for (const child of el.children) serializeEl(child, indent + 1, out);
189
+ out.push(`${pad}</${tagDisplayName(el.tag)}>`);
190
+ }
191
+ function serializeSvg(root) {
192
+ const out = [];
193
+ const attrs = [`xmlns="${SVG_NS}"`];
194
+ if (usesXlink(root)) attrs.push(`xmlns:xlink="${XLINK_NS}"`);
195
+ for (const [name, value] of root.attrib) attrs.push(`${attrDisplayName(name)}="${escapeAttr(value)}"`);
196
+ if (!root.children.length) out.push(`<${tagDisplayName(root.tag)} ${attrs.join(" ")}/>`);
197
+ else {
198
+ out.push(`<${tagDisplayName(root.tag)} ${attrs.join(" ")}>`);
199
+ for (const child of root.children) serializeEl(child, 1, out);
200
+ out.push(`</${tagDisplayName(root.tag)}>`);
201
+ }
202
+ return out.join("\n") + "\n";
203
+ }
204
+ //#endregion
205
+ //#region src/build/iconify.ts
206
+ /**
207
+ * Materialize an Iconify collection (e.g. `@iconify-json/heroicons`) as
208
+ * standalone SVG documents for `buildNanoIconFont`. Iconify geometry comes
209
+ * from the icon's own `width`/`height`/`left`/`top` — the artwork is not
210
+ * rescaled; glyph placement maps the viewBox into the font's em square.
211
+ * `currentColor` fills are preserved as tintable `currentColor` layers.
212
+ */
213
+ function iconifyCollectionToSvgs(collection) {
214
+ const names = [...Object.keys(collection.icons), ...Object.keys(collection.aliases ?? {})];
215
+ const resolved = getIcons(collection, names);
216
+ if (!resolved) throw new Error(`Iconify collection "${collection.prefix}" has no resolvable icons`);
217
+ const inputs = [];
218
+ for (const [name, icon] of Object.entries(resolved.icons)) {
219
+ const doc = iconifyIconToSvg(name, icon);
220
+ inputs.push({
221
+ name: `${resolved.prefix}:${name}`,
222
+ file: `${resolved.prefix}:${name}.svg`,
223
+ svg: doc
224
+ });
225
+ }
226
+ return inputs;
227
+ }
228
+ /** Standalone SVG document for one resolved Iconify icon. */
229
+ function iconifyIconToSvg(name, icon) {
230
+ const result = iconToSVG(icon, { height: "unset" });
231
+ const { viewBox } = result.attributes;
232
+ return `<svg xmlns="${SVG_NS}" viewBox="${viewBox}">${result.body}</svg>`;
233
+ }
234
+ //#endregion
235
+ //#region src/build/core/config.ts
236
+ /** Create directory if it does not exist; do not remove existing contents (for shared output dirs). */
237
+ function ensureDir(dir) {
238
+ fs$1.mkdirSync(dir, { recursive: true });
239
+ }
240
+ function writeFileAtomic(filePath, data) {
241
+ const tmpPath = `${filePath}.tmp`;
242
+ fs$1.writeFileSync(tmpPath, data);
243
+ fs$1.renameSync(tmpPath, filePath);
244
+ }
245
+ //#endregion
246
+ //#region src/build/core/naming.ts
247
+ /** Word separators in a set name (split → PascalCase parts). */
248
+ var NAME_SEPARATORS = /[^a-zA-Z0-9]+/;
249
+ /** Manifest identifiers must start with a letter. */
250
+ var STARTS_WITH_LETTER = /^[a-zA-Z]/;
251
+ /** "my-tab icons" → "MyTabIcons" — PascalCase base for manifest identifiers. */
252
+ function manifestBaseName(setName) {
253
+ const pascal = setName.split(NAME_SEPARATORS).filter(Boolean).map((part) => part[0].toUpperCase() + part.slice(1)).join("");
254
+ return STARTS_WITH_LETTER.test(pascal) ? pascal : `Set${pascal}`;
255
+ }
256
+ //#endregion
257
+ //#region src/build/module.ts
258
+ /** Escape a string for embedding inside a single-quoted TS literal. */
259
+ function tsString(value) {
260
+ return `'${value.replace(/\\/g, "\\\\").replace(/'/g, "\\'")}'`;
261
+ }
262
+ /**
263
+ * Emit `<fontFamily>.ts` beside the font assets. The module exports the
264
+ * glyphmap, a bound `NanoIconSet`, a `NanoIcon` alias for `set.Icon`, and a
265
+ * union type of icon names. `url` is a lazy getter so non-web runtimes never
266
+ * evaluate `import.meta.url` resolution.
267
+ */
268
+ function emitNanoIconSetModule(options) {
269
+ const base = manifestBaseName(options.fontFamily);
270
+ const fontFile = `${options.family}.ttf`;
271
+ const names = Object.keys(options.glyphMap.i);
272
+ const urlLine = options.web ? `get url() {\n\t\treturn new URL('./${options.family}.woff2', import.meta.url).href;\n\t},` : `get url() {\n\t\treturn new URL('./${fontFile}', import.meta.url).href;\n\t},`;
273
+ const source = `// Generated by @octane-xplat/nano-icons. Regenerate with the nano-icons build — do not edit.
274
+ import { createNanoIconSet } from '@octane-xplat/nano-icons';
275
+ import type { NanoIconSet } from '@octane-xplat/nano-icons';
276
+
277
+ export const glyphMap = ${JSON.stringify(options.glyphMap)} as const;
278
+
279
+ /** iOS/Android: copy ${tsString(fontFile)} into the app's fonts directory (see @octane-xplat/nano-icons docs). */
280
+ export const ${base}Icons: NanoIconSet<${base}IconName> = createNanoIconSet(glyphMap, {
281
+ font: {
282
+ // NativeScript resolves app fonts by file name; keep it equal to the family.
283
+ fileName: ${tsString(fontFile)},
284
+ // Web font source; evaluated lazily so native bundles never touch import.meta.url.
285
+ ${urlLine}
286
+ },
287
+ });
288
+
289
+ export const NanoIcon = ${base}Icons.Icon;
290
+
291
+ export type ${base}IconName =
292
+ ${names.length ? names.map((n) => `\t| ${tsString(n)}`).join("\n") : " | never"};
293
+ `;
294
+ ensureDir(options.outputDir);
295
+ const modulePath = path.join(options.outputDir, `${options.fontFamily}.ts`);
296
+ writeFileAtomic(modulePath, source);
297
+ return modulePath;
298
+ }
299
+ //#endregion
300
+ //#region src/build/core/font/metrics.ts
301
+ var FIXED_TIMESTAMP_MS = Date.UTC(1970, 0, 1, 0, 0, 1);
302
+ function forceTtfMetrics(ttfBuffer, upm, ascent, descent, lineGap) {
303
+ const font = Font.create(ttfBuffer, {
304
+ type: "ttf",
305
+ hinting: false,
306
+ compound2simple: false,
307
+ combinePath: false
308
+ });
309
+ const data = font.get();
310
+ data.head.unitsPerEm = upm;
311
+ data.hhea.ascent = ascent;
312
+ data.hhea.descent = -Math.abs(descent);
313
+ data.hhea.lineGap = lineGap;
314
+ data["OS/2"].usWinAscent = ascent;
315
+ data["OS/2"].usWinDescent = Math.abs(descent);
316
+ data["OS/2"].sTypoAscender = ascent;
317
+ data["OS/2"].sTypoDescender = -Math.abs(descent);
318
+ data["OS/2"].sTypoLineGap = lineGap;
319
+ data["OS/2"].fsSelection = (data["OS/2"].fsSelection || 0) | 128;
320
+ data.head.created = FIXED_TIMESTAMP_MS;
321
+ data.head.modified = FIXED_TIMESTAMP_MS;
322
+ data.post.format = 3;
323
+ for (const glyph of data.glyf) delete glyph.name;
324
+ font.set(data);
325
+ const out = font.write({
326
+ type: "ttf",
327
+ hinting: false
328
+ });
329
+ return Buffer.from(out);
330
+ }
331
+ //#endregion
332
+ //#region src/build/core/font/compile.ts
333
+ function escapeXml(s) {
334
+ return s.replace(XML_AMP, "&amp;").replace(XML_QUOT, "&quot;");
335
+ }
336
+ /**
337
+ * Build an SVG font XML string from pre-transformed glyph data.
338
+ */
339
+ function buildSvgFontXml(opts) {
340
+ const { fontName, glyphs, upm, ascent, descent } = opts;
341
+ const glyphLines = glyphs.map((g) => {
342
+ const hex = g.codepoint.toString(16);
343
+ return `<glyph glyph-name="${`u${hex.padStart(4, "0")}`}" unicode="&#x${hex};" horiz-adv-x="${g.advanceWidth}" d="${escapeXml(g.d)}"/>`;
344
+ });
345
+ return `<?xml version="1.0" standalone="no"?>
346
+ <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
347
+ <svg xmlns="${SVG_NS}">
348
+ <defs>
349
+ <font id="${escapeXml(fontName)}" horiz-adv-x="${upm}">
350
+ <font-face font-family="${escapeXml(fontName)}" units-per-em="${upm}" ascent="${ascent}" descent="${-Math.abs(descent)}"/>
351
+ <missing-glyph horiz-adv-x="0"/>
352
+ ${glyphLines.join("\n")}
353
+ </font>
354
+ </defs>
355
+ </svg>`;
356
+ }
357
+ function parseCompileTtfFromGlyphsError(err, codepointToIcon) {
358
+ const msg = err instanceof Error ? err.message : String(err);
359
+ const cpMatch = msg.match(GLYPH_CODEPOINT);
360
+ if (cpMatch) {
361
+ const cp = parseInt(cpMatch[1], 16);
362
+ const iconName = codepointToIcon.get(cp);
363
+ const detail = iconName ? `icon "${iconName}" (codepoint u${cpMatch[1]})` : `codepoint u${cpMatch[1]}`;
364
+ throw new Error(`Font compilation failed for ${detail}: ${msg}`);
365
+ }
366
+ throw err;
367
+ }
368
+ /**
369
+ * Compile a TTF font from pre-transformed glyph data.
370
+ * Builds SVG font XML directly (no intermediate files), then converts via svg2ttf.
371
+ */
372
+ async function compileTtfFromGlyphs(opts) {
373
+ const { glyphs, outTtfPath, fontName, upm, ascent, descent } = opts;
374
+ const lineGap = opts.lineGap ?? 0;
375
+ if (glyphs.length === 0) throw new Error("No glyphs to compile");
376
+ const svgFontString = buildSvgFontXml({
377
+ fontName,
378
+ glyphs,
379
+ upm,
380
+ ascent,
381
+ descent
382
+ });
383
+ const ttfRaw = svg2ttf(svgFontString, { ts: 0 });
384
+ const fixedBuf = forceTtfMetrics(Buffer.from(ttfRaw.buffer), upm, ascent, descent, lineGap);
385
+ fs$1.mkdirSync(path.dirname(outTtfPath), { recursive: true });
386
+ writeFileAtomic(outTtfPath, fixedBuf);
387
+ return fixedBuf;
388
+ }
389
+ var woff2Ready;
390
+ async function compileWoff2FromTtf(ttfBuffer) {
391
+ woff2Ready ??= woff2.init();
392
+ await woff2Ready;
393
+ return Buffer.from(woff2.encode(ttfBuffer));
394
+ }
395
+ //#endregion
396
+ //#region src/build/core/fontIdentity.ts
397
+ var BUILD_HASH_LENGTH = 8;
398
+ var BUILD_HASH = `[0-9a-f]{${BUILD_HASH_LENGTH}}`;
399
+ new RegExp(`-${BUILD_HASH}$`);
400
+ new RegExp(`-${BUILD_HASH}\\.ttf$`);
401
+ function buildFontFamily(fontFamily, inputHash) {
402
+ return `${fontFamily}-${inputHash.slice(0, BUILD_HASH_LENGTH)}`;
403
+ }
404
+ //#endregion
405
+ //#region src/build/core/pathkit/load.ts
406
+ var require = createRequire(import.meta.url);
407
+ var instance = null;
408
+ async function loadPathKit() {
409
+ if (instance) return instance;
410
+ const PathKitInit = require("pathkit-wasm/bin/pathkit.js");
411
+ const pathkitJsPath = require.resolve("pathkit-wasm/bin/pathkit.js");
412
+ const pathkitBinDir = path.dirname(pathkitJsPath);
413
+ const pathkitWasmPath = path.join(pathkitBinDir, "pathkit.wasm");
414
+ const pkInit = PathKitInit({
415
+ wasmBinary: await fs.readFile(pathkitWasmPath),
416
+ locateFile: (file) => path.join(pathkitBinDir, file)
417
+ });
418
+ instance = await (typeof pkInit?.ready === "function" ? pkInit.ready() : pkInit);
419
+ return instance;
420
+ }
421
+ //#endregion
422
+ //#region src/build/core/font/quadratic.ts
423
+ var cubic2quad = cubic2quadImport;
424
+ var COMMAND = /([MLQCZ])([^MLQCZ]*)/g;
425
+ var NUMBER = /-?(?:\d+\.?\d*|\.\d+)(?:e[-+]?\d+)?/gi;
426
+ var QUADRATIC_ERROR_BOUND_EM = 1 / 512;
427
+ function toQuadraticPath(d, errorBound) {
428
+ const out = [];
429
+ let x = 0;
430
+ let y = 0;
431
+ for (const [, cmd, rawArgs] of d.matchAll(COMMAND)) {
432
+ const args = (rawArgs.match(NUMBER) ?? []).map(Number);
433
+ if (cmd === "C") {
434
+ for (let i = 0; i + 5 < args.length; i += 6) {
435
+ const quads = cubic2quad(x, y, args[i], args[i + 1], args[i + 2], args[i + 3], args[i + 4], args[i + 5], errorBound);
436
+ for (let j = 2; j + 3 < quads.length; j += 4) out.push(`Q${quads[j]} ${quads[j + 1]} ${quads[j + 2]} ${quads[j + 3]}`);
437
+ x = args[i + 4];
438
+ y = args[i + 5];
439
+ }
440
+ continue;
441
+ }
442
+ out.push(`${cmd}${rawArgs.trim()}`);
443
+ if (cmd !== "Z" && args.length >= 2) {
444
+ x = args[args.length - 2];
445
+ y = args[args.length - 1];
446
+ }
447
+ }
448
+ return out.join("");
449
+ }
450
+ //#endregion
451
+ //#region src/build/core/colors.ts
452
+ var CSS_NAMED_COLORS = {
453
+ aliceblue: [
454
+ 240,
455
+ 248,
456
+ 255
457
+ ],
458
+ antiquewhite: [
459
+ 250,
460
+ 235,
461
+ 215
462
+ ],
463
+ aqua: [
464
+ 0,
465
+ 255,
466
+ 255
467
+ ],
468
+ aquamarine: [
469
+ 127,
470
+ 255,
471
+ 212
472
+ ],
473
+ azure: [
474
+ 240,
475
+ 255,
476
+ 255
477
+ ],
478
+ beige: [
479
+ 245,
480
+ 245,
481
+ 220
482
+ ],
483
+ bisque: [
484
+ 255,
485
+ 228,
486
+ 196
487
+ ],
488
+ black: [
489
+ 0,
490
+ 0,
491
+ 0
492
+ ],
493
+ blanchedalmond: [
494
+ 255,
495
+ 235,
496
+ 205
497
+ ],
498
+ blue: [
499
+ 0,
500
+ 0,
501
+ 255
502
+ ],
503
+ blueviolet: [
504
+ 138,
505
+ 43,
506
+ 226
507
+ ],
508
+ brown: [
509
+ 165,
510
+ 42,
511
+ 42
512
+ ],
513
+ burlywood: [
514
+ 222,
515
+ 184,
516
+ 135
517
+ ],
518
+ cadetblue: [
519
+ 95,
520
+ 158,
521
+ 160
522
+ ],
523
+ chartreuse: [
524
+ 127,
525
+ 255,
526
+ 0
527
+ ],
528
+ chocolate: [
529
+ 210,
530
+ 105,
531
+ 30
532
+ ],
533
+ coral: [
534
+ 255,
535
+ 127,
536
+ 80
537
+ ],
538
+ cornflowerblue: [
539
+ 100,
540
+ 149,
541
+ 237
542
+ ],
543
+ cornsilk: [
544
+ 255,
545
+ 248,
546
+ 220
547
+ ],
548
+ crimson: [
549
+ 220,
550
+ 20,
551
+ 60
552
+ ],
553
+ cyan: [
554
+ 0,
555
+ 255,
556
+ 255
557
+ ],
558
+ darkblue: [
559
+ 0,
560
+ 0,
561
+ 139
562
+ ],
563
+ darkcyan: [
564
+ 0,
565
+ 139,
566
+ 139
567
+ ],
568
+ darkgoldenrod: [
569
+ 184,
570
+ 134,
571
+ 11
572
+ ],
573
+ darkgray: [
574
+ 169,
575
+ 169,
576
+ 169
577
+ ],
578
+ darkgreen: [
579
+ 0,
580
+ 100,
581
+ 0
582
+ ],
583
+ darkgrey: [
584
+ 169,
585
+ 169,
586
+ 169
587
+ ],
588
+ darkkhaki: [
589
+ 189,
590
+ 183,
591
+ 107
592
+ ],
593
+ darkmagenta: [
594
+ 139,
595
+ 0,
596
+ 139
597
+ ],
598
+ darkolivegreen: [
599
+ 85,
600
+ 107,
601
+ 47
602
+ ],
603
+ darkorange: [
604
+ 255,
605
+ 140,
606
+ 0
607
+ ],
608
+ darkorchid: [
609
+ 153,
610
+ 50,
611
+ 204
612
+ ],
613
+ darkred: [
614
+ 139,
615
+ 0,
616
+ 0
617
+ ],
618
+ darksalmon: [
619
+ 233,
620
+ 150,
621
+ 122
622
+ ],
623
+ darkseagreen: [
624
+ 143,
625
+ 188,
626
+ 143
627
+ ],
628
+ darkslateblue: [
629
+ 72,
630
+ 61,
631
+ 139
632
+ ],
633
+ darkslategray: [
634
+ 47,
635
+ 79,
636
+ 79
637
+ ],
638
+ darkslategrey: [
639
+ 47,
640
+ 79,
641
+ 79
642
+ ],
643
+ darkturquoise: [
644
+ 0,
645
+ 206,
646
+ 209
647
+ ],
648
+ darkviolet: [
649
+ 148,
650
+ 0,
651
+ 211
652
+ ],
653
+ deeppink: [
654
+ 255,
655
+ 20,
656
+ 147
657
+ ],
658
+ deepskyblue: [
659
+ 0,
660
+ 191,
661
+ 255
662
+ ],
663
+ dimgray: [
664
+ 105,
665
+ 105,
666
+ 105
667
+ ],
668
+ dimgrey: [
669
+ 105,
670
+ 105,
671
+ 105
672
+ ],
673
+ dodgerblue: [
674
+ 30,
675
+ 144,
676
+ 255
677
+ ],
678
+ firebrick: [
679
+ 178,
680
+ 34,
681
+ 34
682
+ ],
683
+ floralwhite: [
684
+ 255,
685
+ 250,
686
+ 240
687
+ ],
688
+ forestgreen: [
689
+ 34,
690
+ 139,
691
+ 34
692
+ ],
693
+ fuchsia: [
694
+ 255,
695
+ 0,
696
+ 255
697
+ ],
698
+ gainsboro: [
699
+ 220,
700
+ 220,
701
+ 220
702
+ ],
703
+ ghostwhite: [
704
+ 248,
705
+ 248,
706
+ 255
707
+ ],
708
+ gold: [
709
+ 255,
710
+ 215,
711
+ 0
712
+ ],
713
+ goldenrod: [
714
+ 218,
715
+ 165,
716
+ 32
717
+ ],
718
+ gray: [
719
+ 128,
720
+ 128,
721
+ 128
722
+ ],
723
+ green: [
724
+ 0,
725
+ 128,
726
+ 0
727
+ ],
728
+ greenyellow: [
729
+ 173,
730
+ 255,
731
+ 47
732
+ ],
733
+ grey: [
734
+ 128,
735
+ 128,
736
+ 128
737
+ ],
738
+ honeydew: [
739
+ 240,
740
+ 255,
741
+ 240
742
+ ],
743
+ hotpink: [
744
+ 255,
745
+ 105,
746
+ 180
747
+ ],
748
+ indianred: [
749
+ 205,
750
+ 92,
751
+ 92
752
+ ],
753
+ indigo: [
754
+ 75,
755
+ 0,
756
+ 130
757
+ ],
758
+ ivory: [
759
+ 255,
760
+ 255,
761
+ 240
762
+ ],
763
+ khaki: [
764
+ 240,
765
+ 230,
766
+ 140
767
+ ],
768
+ lavender: [
769
+ 230,
770
+ 230,
771
+ 250
772
+ ],
773
+ lavenderblush: [
774
+ 255,
775
+ 240,
776
+ 245
777
+ ],
778
+ lawngreen: [
779
+ 124,
780
+ 252,
781
+ 0
782
+ ],
783
+ lemonchiffon: [
784
+ 255,
785
+ 250,
786
+ 205
787
+ ],
788
+ lightblue: [
789
+ 173,
790
+ 216,
791
+ 230
792
+ ],
793
+ lightcoral: [
794
+ 240,
795
+ 128,
796
+ 128
797
+ ],
798
+ lightcyan: [
799
+ 224,
800
+ 255,
801
+ 255
802
+ ],
803
+ lightgoldenrodyellow: [
804
+ 250,
805
+ 250,
806
+ 210
807
+ ],
808
+ lightgray: [
809
+ 211,
810
+ 211,
811
+ 211
812
+ ],
813
+ lightgreen: [
814
+ 144,
815
+ 238,
816
+ 144
817
+ ],
818
+ lightgrey: [
819
+ 211,
820
+ 211,
821
+ 211
822
+ ],
823
+ lightpink: [
824
+ 255,
825
+ 182,
826
+ 193
827
+ ],
828
+ lightsalmon: [
829
+ 255,
830
+ 160,
831
+ 122
832
+ ],
833
+ lightseagreen: [
834
+ 32,
835
+ 178,
836
+ 170
837
+ ],
838
+ lightskyblue: [
839
+ 135,
840
+ 206,
841
+ 250
842
+ ],
843
+ lightslategray: [
844
+ 119,
845
+ 136,
846
+ 153
847
+ ],
848
+ lightslategrey: [
849
+ 119,
850
+ 136,
851
+ 153
852
+ ],
853
+ lightsteelblue: [
854
+ 176,
855
+ 196,
856
+ 222
857
+ ],
858
+ lightyellow: [
859
+ 255,
860
+ 255,
861
+ 224
862
+ ],
863
+ lime: [
864
+ 0,
865
+ 255,
866
+ 0
867
+ ],
868
+ limegreen: [
869
+ 50,
870
+ 205,
871
+ 50
872
+ ],
873
+ linen: [
874
+ 250,
875
+ 240,
876
+ 230
877
+ ],
878
+ magenta: [
879
+ 255,
880
+ 0,
881
+ 255
882
+ ],
883
+ maroon: [
884
+ 128,
885
+ 0,
886
+ 0
887
+ ],
888
+ mediumaquamarine: [
889
+ 102,
890
+ 205,
891
+ 170
892
+ ],
893
+ mediumblue: [
894
+ 0,
895
+ 0,
896
+ 205
897
+ ],
898
+ mediumorchid: [
899
+ 186,
900
+ 85,
901
+ 211
902
+ ],
903
+ mediumpurple: [
904
+ 147,
905
+ 112,
906
+ 219
907
+ ],
908
+ mediumseagreen: [
909
+ 60,
910
+ 179,
911
+ 113
912
+ ],
913
+ mediumslateblue: [
914
+ 123,
915
+ 104,
916
+ 238
917
+ ],
918
+ mediumspringgreen: [
919
+ 0,
920
+ 250,
921
+ 154
922
+ ],
923
+ mediumturquoise: [
924
+ 72,
925
+ 209,
926
+ 204
927
+ ],
928
+ mediumvioletred: [
929
+ 199,
930
+ 21,
931
+ 133
932
+ ],
933
+ midnightblue: [
934
+ 25,
935
+ 25,
936
+ 112
937
+ ],
938
+ mintcream: [
939
+ 245,
940
+ 255,
941
+ 250
942
+ ],
943
+ mistyrose: [
944
+ 255,
945
+ 228,
946
+ 225
947
+ ],
948
+ moccasin: [
949
+ 255,
950
+ 228,
951
+ 181
952
+ ],
953
+ navajowhite: [
954
+ 255,
955
+ 222,
956
+ 173
957
+ ],
958
+ navy: [
959
+ 0,
960
+ 0,
961
+ 128
962
+ ],
963
+ oldlace: [
964
+ 253,
965
+ 245,
966
+ 230
967
+ ],
968
+ olive: [
969
+ 128,
970
+ 128,
971
+ 0
972
+ ],
973
+ olivedrab: [
974
+ 107,
975
+ 142,
976
+ 35
977
+ ],
978
+ orange: [
979
+ 255,
980
+ 165,
981
+ 0
982
+ ],
983
+ orangered: [
984
+ 255,
985
+ 69,
986
+ 0
987
+ ],
988
+ orchid: [
989
+ 218,
990
+ 112,
991
+ 214
992
+ ],
993
+ palegoldenrod: [
994
+ 238,
995
+ 232,
996
+ 170
997
+ ],
998
+ palegreen: [
999
+ 152,
1000
+ 251,
1001
+ 152
1002
+ ],
1003
+ paleturquoise: [
1004
+ 175,
1005
+ 238,
1006
+ 238
1007
+ ],
1008
+ palevioletred: [
1009
+ 219,
1010
+ 112,
1011
+ 147
1012
+ ],
1013
+ papayawhip: [
1014
+ 255,
1015
+ 239,
1016
+ 213
1017
+ ],
1018
+ peachpuff: [
1019
+ 255,
1020
+ 218,
1021
+ 185
1022
+ ],
1023
+ peru: [
1024
+ 205,
1025
+ 133,
1026
+ 63
1027
+ ],
1028
+ pink: [
1029
+ 255,
1030
+ 192,
1031
+ 203
1032
+ ],
1033
+ plum: [
1034
+ 221,
1035
+ 160,
1036
+ 221
1037
+ ],
1038
+ powderblue: [
1039
+ 176,
1040
+ 224,
1041
+ 230
1042
+ ],
1043
+ purple: [
1044
+ 128,
1045
+ 0,
1046
+ 128
1047
+ ],
1048
+ rebeccapurple: [
1049
+ 102,
1050
+ 51,
1051
+ 153
1052
+ ],
1053
+ red: [
1054
+ 255,
1055
+ 0,
1056
+ 0
1057
+ ],
1058
+ rosybrown: [
1059
+ 188,
1060
+ 143,
1061
+ 143
1062
+ ],
1063
+ royalblue: [
1064
+ 65,
1065
+ 105,
1066
+ 225
1067
+ ],
1068
+ saddlebrown: [
1069
+ 139,
1070
+ 69,
1071
+ 19
1072
+ ],
1073
+ salmon: [
1074
+ 250,
1075
+ 128,
1076
+ 114
1077
+ ],
1078
+ sandybrown: [
1079
+ 244,
1080
+ 164,
1081
+ 96
1082
+ ],
1083
+ seagreen: [
1084
+ 46,
1085
+ 139,
1086
+ 87
1087
+ ],
1088
+ seashell: [
1089
+ 255,
1090
+ 245,
1091
+ 238
1092
+ ],
1093
+ sienna: [
1094
+ 160,
1095
+ 82,
1096
+ 45
1097
+ ],
1098
+ silver: [
1099
+ 192,
1100
+ 192,
1101
+ 192
1102
+ ],
1103
+ skyblue: [
1104
+ 135,
1105
+ 206,
1106
+ 235
1107
+ ],
1108
+ slateblue: [
1109
+ 106,
1110
+ 90,
1111
+ 205
1112
+ ],
1113
+ slategray: [
1114
+ 112,
1115
+ 128,
1116
+ 144
1117
+ ],
1118
+ slategrey: [
1119
+ 112,
1120
+ 128,
1121
+ 144
1122
+ ],
1123
+ snow: [
1124
+ 255,
1125
+ 250,
1126
+ 250
1127
+ ],
1128
+ springgreen: [
1129
+ 0,
1130
+ 255,
1131
+ 127
1132
+ ],
1133
+ steelblue: [
1134
+ 70,
1135
+ 130,
1136
+ 180
1137
+ ],
1138
+ tan: [
1139
+ 210,
1140
+ 180,
1141
+ 140
1142
+ ],
1143
+ teal: [
1144
+ 0,
1145
+ 128,
1146
+ 128
1147
+ ],
1148
+ thistle: [
1149
+ 216,
1150
+ 191,
1151
+ 216
1152
+ ],
1153
+ tomato: [
1154
+ 255,
1155
+ 99,
1156
+ 71
1157
+ ],
1158
+ turquoise: [
1159
+ 64,
1160
+ 224,
1161
+ 208
1162
+ ],
1163
+ violet: [
1164
+ 238,
1165
+ 130,
1166
+ 238
1167
+ ],
1168
+ wheat: [
1169
+ 245,
1170
+ 222,
1171
+ 179
1172
+ ],
1173
+ white: [
1174
+ 255,
1175
+ 255,
1176
+ 255
1177
+ ],
1178
+ whitesmoke: [
1179
+ 245,
1180
+ 245,
1181
+ 245
1182
+ ],
1183
+ yellow: [
1184
+ 255,
1185
+ 255,
1186
+ 0
1187
+ ],
1188
+ yellowgreen: [
1189
+ 154,
1190
+ 205,
1191
+ 50
1192
+ ]
1193
+ };
1194
+ //#endregion
1195
+ //#region src/build/core/parse.ts
1196
+ function parseColor(color) {
1197
+ const c = color.trim();
1198
+ if (c.indexOf("rgba(") === 0) {
1199
+ const p = c.slice(5, c.indexOf(")")).split(",");
1200
+ return [
1201
+ +(p[0] ?? "0"),
1202
+ +(p[1] ?? "0"),
1203
+ +(p[2] ?? "0"),
1204
+ +(p[3] ?? "1")
1205
+ ];
1206
+ }
1207
+ if (c.indexOf("rgb(") === 0) {
1208
+ const p = c.slice(4, c.indexOf(")")).split(",");
1209
+ return [
1210
+ +(p[0] ?? "0"),
1211
+ +(p[1] ?? "0"),
1212
+ +(p[2] ?? "0"),
1213
+ 1
1214
+ ];
1215
+ }
1216
+ if (c.charCodeAt(0) === 35) {
1217
+ if (c.length === 9) return [
1218
+ parseInt(c.slice(1, 3), 16),
1219
+ parseInt(c.slice(3, 5), 16),
1220
+ parseInt(c.slice(5, 7), 16),
1221
+ parseInt(c.slice(7, 9), 16) / 255
1222
+ ];
1223
+ if (c.length === 7) return [
1224
+ parseInt(c.slice(1, 3), 16),
1225
+ parseInt(c.slice(3, 5), 16),
1226
+ parseInt(c.slice(5, 7), 16),
1227
+ 1
1228
+ ];
1229
+ if (c.length === 4) return [
1230
+ parseInt(c.slice(1, 2), 16) * 17,
1231
+ parseInt(c.slice(2, 3), 16) * 17,
1232
+ parseInt(c.slice(3, 4), 16) * 17,
1233
+ 1
1234
+ ];
1235
+ }
1236
+ const named = CSS_NAMED_COLORS[c.toLowerCase()];
1237
+ if (named !== void 0) return [
1238
+ named[0],
1239
+ named[1],
1240
+ named[2],
1241
+ 1
1242
+ ];
1243
+ return [
1244
+ 0,
1245
+ 0,
1246
+ 0,
1247
+ 1
1248
+ ];
1249
+ }
1250
+ //#endregion
1251
+ //#region src/build/core/glyph/parse.ts
1252
+ function resolveInheritedFill(el) {
1253
+ let current = el.parentElement;
1254
+ while (current !== null) {
1255
+ const fill = current.getAttribute("fill");
1256
+ if (fill !== null && fill !== "inherit") return fill;
1257
+ current = current.parentElement;
1258
+ }
1259
+ return "black";
1260
+ }
1261
+ function calculateOpColor(fill, opacity, el) {
1262
+ const [r, g, b, a] = parseColor(fill ?? resolveInheritedFill(el));
1263
+ return `rgba(${r},${g},${b},${+(a * opacity).toFixed(4)})`;
1264
+ }
1265
+ /**
1266
+ * If a flattened path lost its initial moveto (e.g. an empty `Mx y z`
1267
+ * subpath was dropped), prepend `M` using the path's last coordinate pair.
1268
+ * For closed icon shapes the endpoint equals the start point.
1269
+ */
1270
+ function sanitizePathData(d) {
1271
+ const trimmed = d.trim();
1272
+ if (!trimmed || SVG_MOVE_PREFIX.test(trimmed)) return {
1273
+ d: trimmed,
1274
+ sanitized: false
1275
+ };
1276
+ const nums = trimmed.replace(SVG_TRAILING_CLOSE, "").match(SVG_NUMBER);
1277
+ if (!nums || nums.length < 2) return {
1278
+ d: trimmed,
1279
+ sanitized: false
1280
+ };
1281
+ return {
1282
+ d: `M${nums[nums.length - 2]},${nums[nums.length - 1]} ${trimmed}`,
1283
+ sanitized: true
1284
+ };
1285
+ }
1286
+ var parsePath = (p) => {
1287
+ const d = p.getAttribute("d") ?? "";
1288
+ const op = p.getAttribute("opacity");
1289
+ const fillOp = p.getAttribute("fill-opacity");
1290
+ const fill = p.getAttribute("fill");
1291
+ const fillRule = p.getAttribute("fill-rule") === "evenodd" || p.getAttribute("clip-rule") === "evenodd" ? "evenodd" : void 0;
1292
+ if (op !== null || fillOp !== null) return {
1293
+ d,
1294
+ fill: calculateOpColor(fill, (op !== null ? parseFloat(op) : 1) * (fillOp !== null ? parseFloat(fillOp) : 1), p),
1295
+ fillRule
1296
+ };
1297
+ return {
1298
+ d,
1299
+ fill,
1300
+ fillRule
1301
+ };
1302
+ };
1303
+ function parseFlattenedSvg(flattenedSvg, options) {
1304
+ const svgEl = new DOMParser().parseFromString(flattenedSvg, "image/svg+xml").documentElement;
1305
+ const viewBoxRaw = svgEl?.getAttribute("viewBox")?.split(WHITESPACE).map(Number);
1306
+ let viewBox;
1307
+ if (viewBoxRaw?.length === 4 && viewBoxRaw.every(Number.isFinite)) viewBox = [
1308
+ viewBoxRaw[0],
1309
+ viewBoxRaw[1],
1310
+ viewBoxRaw[2],
1311
+ viewBoxRaw[3]
1312
+ ];
1313
+ else {
1314
+ const width = parseFloat(svgEl?.getAttribute("width") ?? "");
1315
+ const height = parseFloat(svgEl?.getAttribute("height") ?? "");
1316
+ viewBox = width > 0 && height > 0 ? [
1317
+ 0,
1318
+ 0,
1319
+ width,
1320
+ height
1321
+ ] : [
1322
+ 0,
1323
+ 0,
1324
+ 100,
1325
+ 100
1326
+ ];
1327
+ options?.onMissingViewBox?.(viewBox);
1328
+ }
1329
+ const paths = (svgEl ? Array.from(svgEl.getElementsByTagName("path")) : []).map(parsePath).filter((p) => p.d.trim() !== "").map((p) => {
1330
+ const { d, sanitized } = sanitizePathData(p.d);
1331
+ if (sanitized) options?.onSanitize?.(p.d);
1332
+ return {
1333
+ ...p,
1334
+ d
1335
+ };
1336
+ });
1337
+ return {
1338
+ viewBox,
1339
+ paths
1340
+ };
1341
+ }
1342
+ function shouldSkipPath(d, fill) {
1343
+ if (!d || d.trim() === "") return true;
1344
+ const f = (fill ?? "").trim().toLowerCase();
1345
+ return f === "transparent" || f === "none";
1346
+ }
1347
+ //#endregion
1348
+ //#region src/build/core/glyph/placement.ts
1349
+ function roundInt(n) {
1350
+ return Math.round(n);
1351
+ }
1352
+ function computePlacement(opts) {
1353
+ const [vx, vy, vw, vh] = opts.viewBox;
1354
+ const safeVh = vh === 0 ? 1 : vh;
1355
+ const scale = opts.safeZone / safeVh;
1356
+ const totalPadding = (opts.upm - opts.safeZone) / 2;
1357
+ const scaledW = vw * scale;
1358
+ const scaledH = vh * scale;
1359
+ const adv = Math.max(1, roundInt(scaledW + totalPadding * 2));
1360
+ return {
1361
+ vx,
1362
+ vy,
1363
+ scale,
1364
+ xOff: (adv - scaledW) / 2,
1365
+ yOff: (opts.upm - scaledH) / 2,
1366
+ adv
1367
+ };
1368
+ }
1369
+ /**
1370
+ * Transform an SVG path `d` string from source SVG coordinates into font
1371
+ * glyph coordinates (Y-up, with placement scaling and centering applied).
1372
+ *
1373
+ * Combines placement transform (translate + scale + translate) with the
1374
+ * SVG→font Y-axis flip into a single affine transform applied via PathKit.
1375
+ */
1376
+ function transformPathForFont(PathKit, d, opts) {
1377
+ const { vx, vy, scale, xOff, yOff, upm } = opts;
1378
+ const p = PathKit.FromSVGString(d);
1379
+ if (!p) return d;
1380
+ const scaleX = scale;
1381
+ const scaleY = -scale;
1382
+ const transX = xOff - vx * scale;
1383
+ const transY = upm - yOff + vy * scale;
1384
+ p.transform(scaleX, 0, transX, 0, scaleY, transY, 0, 0, 1);
1385
+ const result = p.toSVGString();
1386
+ p.delete?.();
1387
+ return result;
1388
+ }
1389
+ //#endregion
1390
+ //#region src/build/core/flatten/geometry.ts
1391
+ var DEFAULT_ALMOST_EQUAL_TOLERANCE = 1e-9;
1392
+ function pt(x = 0, y = 0) {
1393
+ return {
1394
+ x,
1395
+ y
1396
+ };
1397
+ }
1398
+ function almostEqual(c1, c2, tolerance = DEFAULT_ALMOST_EQUAL_TOLERANCE) {
1399
+ return Math.abs(c1 - c2) <= tolerance;
1400
+ }
1401
+ function ptEquals(a, b) {
1402
+ return a.x === b.x && a.y === b.y;
1403
+ }
1404
+ function ptAlmostEquals(a, b, tolerance = DEFAULT_ALMOST_EQUAL_TOLERANCE) {
1405
+ return almostEqual(a.x, b.x, tolerance) && almostEqual(a.y, b.y, tolerance);
1406
+ }
1407
+ function ptSub(a, b) {
1408
+ return {
1409
+ x: a.x - b.x,
1410
+ y: a.y - b.y
1411
+ };
1412
+ }
1413
+ function ptAdd(a, b) {
1414
+ return {
1415
+ x: a.x + b.x,
1416
+ y: a.y + b.y
1417
+ };
1418
+ }
1419
+ function ptMul(a, scalar) {
1420
+ return {
1421
+ x: a.x * scalar,
1422
+ y: a.y * scalar
1423
+ };
1424
+ }
1425
+ function pythonRound(x, ndigits) {
1426
+ if (!Number.isFinite(x) || Number.isInteger(x) && ndigits >= 0) return x;
1427
+ const buf = /* @__PURE__ */ new DataView(/* @__PURE__ */ new ArrayBuffer(8));
1428
+ buf.setFloat64(0, x);
1429
+ const bits = buf.getBigUint64(0);
1430
+ const negative = bits >> 63n === 1n;
1431
+ const expBits = Number(bits >> 52n & 2047n);
1432
+ const fracBits = bits & 4503599627370495n;
1433
+ let mantissa;
1434
+ let exp;
1435
+ if (expBits === 0) {
1436
+ mantissa = fracBits;
1437
+ exp = -1074;
1438
+ } else {
1439
+ mantissa = fracBits | 1n << 52n;
1440
+ exp = expBits - 1075;
1441
+ }
1442
+ const pow10 = 10n ** BigInt(Math.abs(ndigits));
1443
+ let num = mantissa * (ndigits >= 0 ? pow10 : 1n);
1444
+ let den = ndigits >= 0 ? 1n : pow10;
1445
+ if (exp >= 0) num <<= BigInt(exp);
1446
+ else den <<= BigInt(-exp);
1447
+ let k = num / den;
1448
+ const twice = num % den * 2n;
1449
+ if (twice > den || twice === den && k % 2n === 1n) k += 1n;
1450
+ const scaled = ndigits >= 0 ? Number(k) / Number(pow10) : Number(k) * Number(pow10);
1451
+ return negative ? -scaled : scaled;
1452
+ }
1453
+ function ntos(n) {
1454
+ return Object.is(n, -0) ? "0" : String(n);
1455
+ }
1456
+ function rectEmpty(r) {
1457
+ return r.w === 0 || r.h === 0;
1458
+ }
1459
+ //#endregion
1460
+ //#region src/build/core/flatten/transform.ts
1461
+ var RADIAN_ARG_OPS = /* @__PURE__ */ new Set([
1462
+ "rotate",
1463
+ "skewx",
1464
+ "skewy"
1465
+ ]);
1466
+ var Affine2D = class Affine2D {
1467
+ a;
1468
+ b;
1469
+ c;
1470
+ d;
1471
+ e;
1472
+ f;
1473
+ constructor(a, b, c, d, e, f) {
1474
+ this.a = a;
1475
+ this.b = b;
1476
+ this.c = c;
1477
+ this.d = d;
1478
+ this.e = e;
1479
+ this.f = f;
1480
+ }
1481
+ static _identity = new Affine2D(1, 0, 0, 1, 0, 0);
1482
+ static _degenerate = new Affine2D(0, 0, 0, 0, 0, 0);
1483
+ static identity() {
1484
+ return Affine2D._identity;
1485
+ }
1486
+ static degenerate() {
1487
+ return Affine2D._degenerate;
1488
+ }
1489
+ static fromString(rawTransform) {
1490
+ let transform = Affine2D.identity();
1491
+ const re = new RegExp(TRANSFORM_FUNCTION);
1492
+ let match;
1493
+ while ((match = re.exec(rawTransform)) !== null) {
1494
+ const op = match[1].toLowerCase();
1495
+ const args = match[2].trim().split(TRANSFORM_ARG_SEPARATOR).map((p) => parseFloat(p));
1496
+ if (RADIAN_ARG_OPS.has(op)) args[0] = args[0] * Math.PI / 180;
1497
+ switch (op) {
1498
+ case "matrix":
1499
+ transform = transform.matrix(args[0], args[1], args[2], args[3], args[4], args[5]);
1500
+ break;
1501
+ case "translate":
1502
+ transform = transform.translate(args[0], args[1]);
1503
+ break;
1504
+ case "scale":
1505
+ transform = transform.scale(args[0], args[1]);
1506
+ break;
1507
+ case "rotate":
1508
+ transform = transform.rotate(args[0], args[1], args[2]);
1509
+ break;
1510
+ case "skewx":
1511
+ transform = transform.skewx(args[0]);
1512
+ break;
1513
+ case "skewy": transform = transform.skewy(args[0]);
1514
+ }
1515
+ }
1516
+ return transform;
1517
+ }
1518
+ values() {
1519
+ return [
1520
+ this.a,
1521
+ this.b,
1522
+ this.c,
1523
+ this.d,
1524
+ this.e,
1525
+ this.f
1526
+ ];
1527
+ }
1528
+ equals(other) {
1529
+ return this.a === other.a && this.b === other.b && this.c === other.c && this.d === other.d && this.e === other.e && this.f === other.f;
1530
+ }
1531
+ toString() {
1532
+ const [tx, ty] = this.getTranslate();
1533
+ if (this.equals(Affine2D.identity().translate(tx, ty))) return `translate(${[tx, ty].map(ntos).join(", ")})`;
1534
+ return `matrix(${this.values().map(ntos).join(" ")})`;
1535
+ }
1536
+ matMul(other) {
1537
+ return new Affine2D(this.a * other.a + this.c * other.b, this.b * other.a + this.d * other.b, this.a * other.c + this.c * other.d, this.b * other.c + this.d * other.d, this.a * other.e + this.c * other.f + this.e, this.b * other.e + this.d * other.f + this.f);
1538
+ }
1539
+ matrix(a, b, c, d, e, f) {
1540
+ return this.matMul(new Affine2D(a, b, c, d, e, f));
1541
+ }
1542
+ translate(tx, ty = 0) {
1543
+ if (tx === 0 && ty === 0) return this;
1544
+ return this.matrix(1, 0, 0, 1, tx, ty);
1545
+ }
1546
+ getTranslate() {
1547
+ return [this.e, this.f];
1548
+ }
1549
+ scale(sx, sy) {
1550
+ if (sy === void 0) sy = sx;
1551
+ return this.matrix(sx, 0, 0, sy, 0, 0);
1552
+ }
1553
+ rotate(a, cx = 0, cy = 0) {
1554
+ return this.translate(cx, cy).matrix(Math.cos(a), Math.sin(a), -Math.sin(a), Math.cos(a), 0, 0).translate(-cx, -cy);
1555
+ }
1556
+ skewx(a) {
1557
+ return this.matrix(1, 0, Math.tan(a), 1, 0, 0);
1558
+ }
1559
+ skewy(a) {
1560
+ return this.matrix(1, Math.tan(a), 0, 1, 0, 0);
1561
+ }
1562
+ determinant() {
1563
+ return this.a * this.d - this.b * this.c;
1564
+ }
1565
+ isDegenerate() {
1566
+ return Math.abs(this.determinant()) <= Number.EPSILON;
1567
+ }
1568
+ inverse() {
1569
+ if (this.equals(Affine2D.identity())) return this;
1570
+ else if (this.isDegenerate()) return Affine2D.degenerate();
1571
+ const det = this.determinant();
1572
+ const a = this.d / det;
1573
+ const b = -this.b / det;
1574
+ const c = -this.c / det;
1575
+ const d = this.a / det;
1576
+ const e = -a * this.e - c * this.f;
1577
+ const f = -b * this.e - d * this.f;
1578
+ return new Affine2D(a, b, c, d, e, f);
1579
+ }
1580
+ mapPoint(p) {
1581
+ return {
1582
+ x: this.a * p.x + this.c * p.y + this.e,
1583
+ y: this.b * p.x + this.d * p.y + this.f
1584
+ };
1585
+ }
1586
+ mapVector(v) {
1587
+ return {
1588
+ x: this.a * v.x + this.c * v.y,
1589
+ y: this.b * v.x + this.d * v.y
1590
+ };
1591
+ }
1592
+ static composeLtr(affines) {
1593
+ let result = Affine2D.identity();
1594
+ for (let i = affines.length - 1; i >= 0; i--) result = result.matMul(affines[i]);
1595
+ return result;
1596
+ }
1597
+ static _ALIGN_VALUES = /* @__PURE__ */ new Set([
1598
+ "none",
1599
+ "xminymin",
1600
+ "xminymid",
1601
+ "xminymax",
1602
+ "xmidymin",
1603
+ "xmidymid",
1604
+ "xmidymax",
1605
+ "xmaxymin",
1606
+ "xmaxymid",
1607
+ "xmaxymax"
1608
+ ]);
1609
+ static _MEET_OR_SLICE = /* @__PURE__ */ new Set(["meet", "slice"]);
1610
+ static rectToRect(src, dst, preserveAspectRatio = "none") {
1611
+ if (rectEmpty(src)) return Affine2D.identity();
1612
+ if (rectEmpty(dst)) return new Affine2D(0, 0, 0, 0, 0, 0);
1613
+ let sx = dst.w / src.w;
1614
+ let sy = dst.h / src.h;
1615
+ const normalized = preserveAspectRatio.toLowerCase().trim();
1616
+ const spaceIdx = normalized.indexOf(" ");
1617
+ const align = spaceIdx === -1 ? normalized : normalized.slice(0, spaceIdx);
1618
+ const meetOrSlice = spaceIdx === -1 ? "" : normalized.slice(spaceIdx + 1).trim();
1619
+ if (!Affine2D._ALIGN_VALUES.has(align) || meetOrSlice && !Affine2D._MEET_OR_SLICE.has(meetOrSlice)) throw new Error(`Invalid preserveAspectRatio: ${preserveAspectRatio}`);
1620
+ if (align !== "none") sx = sy = meetOrSlice.includes("slice") ? Math.max(sx, sy) : Math.min(sx, sy);
1621
+ let tx = dst.x - src.x * sx;
1622
+ let ty = dst.y - src.y * sy;
1623
+ if (align.includes("xmid")) tx += (dst.w - src.w * sx) / 2;
1624
+ else if (align.includes("xmax")) tx += dst.w - src.w * sx;
1625
+ if (align.includes("ymid")) ty += (dst.h - src.h * sy) / 2;
1626
+ else if (align.includes("ymax")) ty += dst.h - src.h * sy;
1627
+ return new Affine2D(sx, 0, 0, sy, tx, ty);
1628
+ }
1629
+ };
1630
+ //#endregion
1631
+ //#region src/build/core/flatten/inherit.ts
1632
+ var ATTRIB_DEFAULTS = {
1633
+ "clip-path": "",
1634
+ "clip-rule": "nonzero",
1635
+ fill: "black",
1636
+ "fill-opacity": 1,
1637
+ "fill-rule": "nonzero",
1638
+ stroke: "none",
1639
+ "stroke-width": 1,
1640
+ "stroke-linecap": "butt",
1641
+ "stroke-linejoin": "miter",
1642
+ "stroke-miterlimit": 4,
1643
+ "stroke-dasharray": "none",
1644
+ "stroke-dashoffset": 0,
1645
+ "stroke-opacity": 1,
1646
+ opacity: 1,
1647
+ transform: "",
1648
+ style: "",
1649
+ display: "inline",
1650
+ d: "",
1651
+ id: ""
1652
+ };
1653
+ function attribDefault(name) {
1654
+ const value = ATTRIB_DEFAULTS[name];
1655
+ if (value === void 0) throw new Error(`No entry for '${name}' and no default given`);
1656
+ return value;
1657
+ }
1658
+ var COMMON_SHAPE_FIELDS = [
1659
+ "id",
1660
+ "clip_path",
1661
+ "clip_rule",
1662
+ "fill",
1663
+ "fill_opacity",
1664
+ "fill_rule",
1665
+ "stroke",
1666
+ "stroke_width",
1667
+ "stroke_linecap",
1668
+ "stroke_linejoin",
1669
+ "stroke_miterlimit",
1670
+ "stroke_dasharray",
1671
+ "stroke_dashoffset",
1672
+ "stroke_opacity",
1673
+ "opacity",
1674
+ "transform",
1675
+ "style",
1676
+ "display"
1677
+ ];
1678
+ var VALID_FIELDS = {
1679
+ path: /* @__PURE__ */ new Set([...COMMON_SHAPE_FIELDS, "d"]),
1680
+ circle: /* @__PURE__ */ new Set([
1681
+ ...COMMON_SHAPE_FIELDS,
1682
+ "r",
1683
+ "cx",
1684
+ "cy"
1685
+ ]),
1686
+ ellipse: /* @__PURE__ */ new Set([
1687
+ ...COMMON_SHAPE_FIELDS,
1688
+ "rx",
1689
+ "ry",
1690
+ "cx",
1691
+ "cy"
1692
+ ]),
1693
+ line: /* @__PURE__ */ new Set([
1694
+ ...COMMON_SHAPE_FIELDS,
1695
+ "x1",
1696
+ "y1",
1697
+ "x2",
1698
+ "y2"
1699
+ ]),
1700
+ polygon: /* @__PURE__ */ new Set([...COMMON_SHAPE_FIELDS, "points"]),
1701
+ polyline: /* @__PURE__ */ new Set([...COMMON_SHAPE_FIELDS, "points"]),
1702
+ rect: /* @__PURE__ */ new Set([
1703
+ ...COMMON_SHAPE_FIELDS,
1704
+ "x",
1705
+ "y",
1706
+ "width",
1707
+ "height",
1708
+ "rx",
1709
+ "ry"
1710
+ ]),
1711
+ linearGradient: /* @__PURE__ */ new Set([
1712
+ "id",
1713
+ "x1",
1714
+ "y1",
1715
+ "x2",
1716
+ "y2",
1717
+ "gradientTransform",
1718
+ "gradientUnits",
1719
+ "spreadMethod"
1720
+ ]),
1721
+ radialGradient: /* @__PURE__ */ new Set([
1722
+ "id",
1723
+ "cx",
1724
+ "cy",
1725
+ "r",
1726
+ "fx",
1727
+ "fy",
1728
+ "fr",
1729
+ "gradientTransform",
1730
+ "gradientUnits",
1731
+ "spreadMethod"
1732
+ ]),
1733
+ stop: /* @__PURE__ */ new Set([
1734
+ "offset",
1735
+ "stop_color",
1736
+ "stop_opacity"
1737
+ ])
1738
+ };
1739
+ function attrName(fieldName) {
1740
+ return fieldName.replace(/_/g, "-");
1741
+ }
1742
+ function fieldName(attr) {
1743
+ return attr.replace(/-/g, "_");
1744
+ }
1745
+ function attrSupported(el, attr) {
1746
+ const fields = VALID_FIELDS[stripNs(el.tag)];
1747
+ if (fields) return fields.has(fieldName(attr));
1748
+ return true;
1749
+ }
1750
+ function inheritCopy(attrib, child, attr) {
1751
+ if (child.attrib.has(attr)) return;
1752
+ if (attr in attrib) child.attrib.set(attr, attrib[attr]);
1753
+ }
1754
+ function inheritMultiply(attrib, child, attr) {
1755
+ if (!(attr in attrib) && !child.attrib.has(attr)) return;
1756
+ let value = parseFloat(attrib[attr] ?? "1");
1757
+ value *= parseFloat(child.attrib.get(attr) ?? "1");
1758
+ child.attrib.set(attr, ntos(value));
1759
+ }
1760
+ function inheritClipPath(attrib, child, _attr) {
1761
+ const clips = [...(child.attrib.get("clip-path") ?? "").split(","), attrib["clip-path"] ?? ""].sort();
1762
+ child.attrib.set("clip-path", clips.filter((c) => c).join(","));
1763
+ }
1764
+ function inheritNondefaultOverflow(attrib, child, attr) {
1765
+ if ((attrib[attr] ?? "visible") !== "visible") inheritCopy(attrib, child, attr);
1766
+ }
1767
+ function inheritNondefaultDisplay(attrib, child, attr) {
1768
+ const value = attrib[attr] ?? "";
1769
+ if (value === "none") child.attrib.set(attr, value);
1770
+ else inheritCopy(attrib, child, attr);
1771
+ }
1772
+ function inheritMatrixMultiply(attrib, child, attr) {
1773
+ let transform = Affine2D.identity();
1774
+ if (attr in attrib) transform = Affine2D.fromString(attrib[attr]);
1775
+ if (child.attrib.has(attr)) transform = Affine2D.composeLtr([Affine2D.fromString(child.attrib.get(attr)), transform]);
1776
+ if (!transform.equals(Affine2D.identity())) child.attrib.set(attr, transform.toString());
1777
+ else child.attrib.delete(attr);
1778
+ }
1779
+ var DO_NOT_INHERIT = () => {};
1780
+ var INHERIT_ATTRIB_HANDLERS = {
1781
+ "clip-rule": inheritCopy,
1782
+ color: inheritCopy,
1783
+ display: inheritNondefaultDisplay,
1784
+ fill: inheritCopy,
1785
+ "fill-rule": inheritCopy,
1786
+ style: inheritCopy,
1787
+ transform: inheritMatrixMultiply,
1788
+ stroke: inheritCopy,
1789
+ "stroke-width": inheritCopy,
1790
+ "stroke-linecap": inheritCopy,
1791
+ "stroke-linejoin": inheritCopy,
1792
+ "stroke-miterlimit": inheritCopy,
1793
+ "stroke-dasharray": inheritCopy,
1794
+ "stroke-dashoffset": inheritCopy,
1795
+ "stroke-opacity": inheritCopy,
1796
+ "fill-opacity": inheritCopy,
1797
+ opacity: inheritMultiply,
1798
+ "clip-path": inheritClipPath,
1799
+ id: DO_NOT_INHERIT,
1800
+ "data-name": DO_NOT_INHERIT,
1801
+ "enable-background": DO_NOT_INHERIT,
1802
+ overflow: inheritNondefaultOverflow
1803
+ };
1804
+ var INHERITABLE_ATTRIB = new Set(Object.entries(INHERIT_ATTRIB_HANDLERS).filter(([, handler]) => handler !== DO_NOT_INHERIT).map(([name]) => name));
1805
+ var ATTRIB_W_CUSTOM_INHERITANCE = /* @__PURE__ */ new Set([
1806
+ "clip-path",
1807
+ "opacity",
1808
+ "transform"
1809
+ ]);
1810
+ var INHERITABLE_ATTRIB_DEFAULTS = Object.fromEntries(Object.entries(INHERIT_ATTRIB_HANDLERS).filter(([name, handler]) => name in ATTRIB_DEFAULTS && handler === inheritCopy).map(([name]) => {
1811
+ const value = ATTRIB_DEFAULTS[name];
1812
+ return [name, typeof value === "number" ? ntos(value) : value];
1813
+ }));
1814
+ function inheritAttrib(attrib, child, skipUnhandled = false, skips = /* @__PURE__ */ new Set()) {
1815
+ const work = attrib instanceof Map ? Object.fromEntries(attrib) : { ...attrib };
1816
+ for (const attr of Object.keys(work).sort()) {
1817
+ if (skips.has(attr) || !attrSupported(child, attr)) {
1818
+ delete work[attr];
1819
+ continue;
1820
+ }
1821
+ const handler = INHERIT_ATTRIB_HANDLERS[attr];
1822
+ if (!handler) continue;
1823
+ handler(work, child, attr);
1824
+ delete work[attr];
1825
+ }
1826
+ if (Object.keys(work).length && !skipUnhandled) throw new Error(`Unable to process attrib ${JSON.stringify(work)}`);
1827
+ }
1828
+ function attribToPassOn(currentAttrib, el, skips = ATTRIB_W_CUSTOM_INHERITANCE) {
1829
+ const attrCatcher = new XEl("dummy");
1830
+ inheritAttrib(el.attrib, attrCatcher, true, skips);
1831
+ inheritAttrib(currentAttrib, attrCatcher, false, skips);
1832
+ return Object.fromEntries(attrCatcher.attrib);
1833
+ }
1834
+ function dropDefaultAttrib(attrib) {
1835
+ for (const attr of [...attrib.keys()].sort()) {
1836
+ const defaultValue = ATTRIB_DEFAULTS[attr];
1837
+ if (defaultValue === void 0) continue;
1838
+ const value = attrib.get(attr);
1839
+ if (typeof defaultValue === "number") {
1840
+ if (parseFloat(value) === defaultValue) attrib.delete(attr);
1841
+ } else if (value === defaultValue) attrib.delete(attr);
1842
+ }
1843
+ }
1844
+ //#endregion
1845
+ //#region src/build/core/flatten/arcs.ts
1846
+ var TWO_PI = 2 * Math.PI;
1847
+ var PI_OVER_TWO = .5 * Math.PI;
1848
+ function isStraightLine(arc) {
1849
+ const rx = Math.abs(arc.rx);
1850
+ const ry = Math.abs(arc.ry);
1851
+ return !(rx && ry);
1852
+ }
1853
+ function isZeroLength(arc) {
1854
+ return ptEquals(arc.endPoint, arc.startPoint);
1855
+ }
1856
+ function correctOutOfRangeRadii(arc) {
1857
+ if (isStraightLine(arc) || isZeroLength(arc)) return arc;
1858
+ const midPointDistance = ptMul(ptSub(arc.startPoint, arc.endPoint), .5);
1859
+ const angle = arc.rotation * Math.PI / 180;
1860
+ const transformedMidPoint = Affine2D.identity().rotate(-angle).mapVector(midPointDistance);
1861
+ let rx = arc.rx;
1862
+ let ry = arc.ry;
1863
+ const squareRx = rx * rx;
1864
+ const squareRy = ry * ry;
1865
+ const squareX = transformedMidPoint.x * transformedMidPoint.x;
1866
+ const squareY = transformedMidPoint.y * transformedMidPoint.y;
1867
+ const radiiScale = squareX / squareRx + squareY / squareRy;
1868
+ if (radiiScale > 1) {
1869
+ rx *= Math.sqrt(radiiScale);
1870
+ ry *= Math.sqrt(radiiScale);
1871
+ return {
1872
+ ...arc,
1873
+ rx,
1874
+ ry
1875
+ };
1876
+ }
1877
+ return arc;
1878
+ }
1879
+ function endToCenterParametrization(arc) {
1880
+ const angle = arc.rotation * Math.PI / 180;
1881
+ const pointTransform = Affine2D.identity().scale(1 / arc.rx, 1 / arc.ry).rotate(-angle);
1882
+ const point1 = pointTransform.mapPoint(arc.startPoint);
1883
+ const point2 = pointTransform.mapPoint(arc.endPoint);
1884
+ let delta = ptSub(point2, point1);
1885
+ const d = delta.x * delta.x + delta.y * delta.y;
1886
+ const scaleFactorSquared = Math.max(1 / d - .25, 0);
1887
+ let scaleFactor = Math.sqrt(scaleFactorSquared);
1888
+ if (arc.sweep === arc.large) scaleFactor = -scaleFactor;
1889
+ delta = ptMul(delta, scaleFactor);
1890
+ let centerPoint = ptAdd(ptAdd(point1, ptMul(ptSub(point2, point1), .5)), pt(-delta.y, delta.x));
1891
+ const v1 = ptSub(point1, centerPoint);
1892
+ const v2 = ptSub(point2, centerPoint);
1893
+ const theta1 = Math.atan2(v1.y, v1.x);
1894
+ let thetaArc = Math.atan2(v2.y, v2.x) - theta1;
1895
+ if (thetaArc < 0 && arc.sweep) thetaArc += TWO_PI;
1896
+ else if (thetaArc > 0 && !arc.sweep) thetaArc -= TWO_PI;
1897
+ centerPoint = pointTransform.inverse().mapPoint(centerPoint);
1898
+ return {
1899
+ theta1,
1900
+ thetaArc,
1901
+ centerPoint
1902
+ };
1903
+ }
1904
+ function* ellipticalArcToCubic(arc) {
1905
+ arc = correctOutOfRangeRadii(arc);
1906
+ const arcParams = endToCenterParametrization(arc);
1907
+ const pointTransform = Affine2D.identity().translate(arcParams.centerPoint.x, arcParams.centerPoint.y).rotate(arc.rotation * Math.PI / 180).scale(arc.rx, arc.ry);
1908
+ const numSegments = Math.ceil(Math.abs(arcParams.thetaArc / (PI_OVER_TWO + .001)));
1909
+ for (let i = 0; i < numSegments; i++) {
1910
+ const startTheta = arcParams.theta1 + i * arcParams.thetaArc / numSegments;
1911
+ const endTheta = arcParams.theta1 + (i + 1) * arcParams.thetaArc / numSegments;
1912
+ const t = 4 / 3 * Math.tan(.25 * (endTheta - startTheta));
1913
+ if (!Number.isFinite(t)) return;
1914
+ const sinStartTheta = Math.sin(startTheta);
1915
+ const cosStartTheta = Math.cos(startTheta);
1916
+ const sinEndTheta = Math.sin(endTheta);
1917
+ const cosEndTheta = Math.cos(endTheta);
1918
+ let point1 = pt(cosStartTheta - t * sinStartTheta, sinStartTheta + t * cosStartTheta);
1919
+ let endPoint = pt(cosEndTheta, sinEndTheta);
1920
+ let point2 = ptAdd(endPoint, pt(t * sinEndTheta, -t * cosEndTheta));
1921
+ point1 = pointTransform.mapPoint(point1);
1922
+ point2 = pointTransform.mapPoint(point2);
1923
+ if (i === numSegments - 1) endPoint = arc.endPoint;
1924
+ else endPoint = pointTransform.mapPoint(endPoint);
1925
+ yield [
1926
+ point1,
1927
+ point2,
1928
+ endPoint
1929
+ ];
1930
+ }
1931
+ }
1932
+ function* arcToCubic(startPoint, rx, ry, rotation, large, sweep, endPoint) {
1933
+ const arc = {
1934
+ startPoint,
1935
+ rx,
1936
+ ry,
1937
+ rotation,
1938
+ large,
1939
+ sweep,
1940
+ endPoint
1941
+ };
1942
+ if (isZeroLength(arc)) return;
1943
+ else if (isStraightLine(arc)) yield [
1944
+ null,
1945
+ null,
1946
+ arc.endPoint
1947
+ ];
1948
+ else yield* ellipticalArcToCubic(arc);
1949
+ }
1950
+ //#endregion
1951
+ //#region src/build/core/flatten/path.ts
1952
+ var CMD_ARGS = {
1953
+ m: 2,
1954
+ z: 0,
1955
+ l: 2,
1956
+ h: 1,
1957
+ v: 1,
1958
+ c: 6,
1959
+ s: 4,
1960
+ q: 4,
1961
+ t: 2,
1962
+ a: 7
1963
+ };
1964
+ for (const [k, v] of Object.entries(CMD_ARGS)) CMD_ARGS[k.toUpperCase()] = v;
1965
+ var CMD_COORDS = {
1966
+ m: [[0], [1]],
1967
+ z: [[], []],
1968
+ l: [[0], [1]],
1969
+ h: [[0], []],
1970
+ v: [[], [0]],
1971
+ c: [[
1972
+ 0,
1973
+ 2,
1974
+ 4
1975
+ ], [
1976
+ 1,
1977
+ 3,
1978
+ 5
1979
+ ]],
1980
+ s: [[0, 2], [1, 3]],
1981
+ q: [[0, 2], [1, 3]],
1982
+ t: [[0], [1]],
1983
+ a: [[5], [6]]
1984
+ };
1985
+ for (const [k, v] of Object.entries(CMD_COORDS)) CMD_COORDS[k.toUpperCase()] = v;
1986
+ function numArgs(cmd) {
1987
+ const n = CMD_ARGS[cmd];
1988
+ if (n === void 0) throw new Error(`Invalid svg command "${cmd}"`);
1989
+ return n;
1990
+ }
1991
+ function checkCmd(cmd, args) {
1992
+ const cmdArgs = numArgs(cmd);
1993
+ if (cmdArgs === 0) {
1994
+ if (args.length) throw new Error(`${cmd} has no args, ${args.length} invalid`);
1995
+ } else if (args.length % cmdArgs !== 0) throw new Error(`${cmd} has sets of ${cmdArgs} args, ${args.length} invalid`);
1996
+ return cmdArgs;
1997
+ }
1998
+ function cmdCoords(cmd) {
1999
+ const coords = CMD_COORDS[cmd];
2000
+ if (coords === void 0) throw new Error(`Invalid svg command "${cmd}"`);
2001
+ return coords;
2002
+ }
2003
+ function pathSegment(cmd, ...args) {
2004
+ const argsPerCmd = checkCmd(cmd, args);
2005
+ const strArgs = args.map(ntos);
2006
+ const combinedArgs = [];
2007
+ const [xCoords, yCoords] = cmdCoords(cmd);
2008
+ const xyCoords = new Set(xCoords.map((x, i) => `${x},${yCoords[i]}`).slice(0, yCoords.length));
2009
+ if (argsPerCmd) for (let n = 0; n < strArgs.length / argsPerCmd; n++) {
2010
+ const subArgs = strArgs.slice(n * argsPerCmd, (n + 1) * argsPerCmd);
2011
+ let i = 0;
2012
+ while (i < subArgs.length) if (xyCoords.has(`${i},${i + 1}`)) {
2013
+ combinedArgs.push(`${subArgs[i]},${subArgs[i + 1]}`);
2014
+ i += 2;
2015
+ } else {
2016
+ combinedArgs.push(subArgs[i]);
2017
+ i += 1;
2018
+ }
2019
+ }
2020
+ return cmd + combinedArgs.join(" ");
2021
+ }
2022
+ var CMD_RE = /([mzlhvcsqtaMZLHVCSQTA])/;
2023
+ var SEPARATOR_RE = /[, ]+/;
2024
+ var FLOAT_RE = /* @__PURE__ */ new RegExp("^[-+]?(?:(?:0|[1-9][0-9]*)(?:\\.[0-9]+)?|(?:\\.[0-9]+))(?:[eE][-+]?[0-9]+)?");
2025
+ var BOOL_RE = /^[01]/;
2026
+ var ARC_ARGUMENT_TYPES = [
2027
+ FLOAT_RE,
2028
+ FLOAT_RE,
2029
+ FLOAT_RE,
2030
+ BOOL_RE,
2031
+ BOOL_RE,
2032
+ FLOAT_RE,
2033
+ FLOAT_RE
2034
+ ];
2035
+ var IMPLICIT_REPEAT_CMD = {
2036
+ m: "l",
2037
+ M: "L"
2038
+ };
2039
+ function parseArgs(cmd, rawArgsStr) {
2040
+ const rawArgs = rawArgsStr.split(SEPARATOR_RE).filter((s) => s);
2041
+ const result = [];
2042
+ if (!rawArgs.length) return result;
2043
+ const argTypes = cmd.toUpperCase() === "A" ? ARC_ARGUMENT_TYPES : [FLOAT_RE];
2044
+ const n = argTypes.length;
2045
+ let i = 0;
2046
+ let j = 0;
2047
+ while (j < rawArgs.length) {
2048
+ const arg = rawArgs[j];
2049
+ const m = argTypes[i % n].exec(arg);
2050
+ if (!m) throw new Error(`Invalid path data "${cmd}${rawArgsStr.slice(0, 40)}": argument #${i + 1} (${arg}) is not a number`);
2051
+ const end = m[0].length;
2052
+ result.push(parseFloat(arg.slice(0, end)));
2053
+ if (end < arg.length) rawArgs[j] = arg.slice(end);
2054
+ else j += 1;
2055
+ i += 1;
2056
+ }
2057
+ return result;
2058
+ }
2059
+ function explodeCmd(argsPerCmd, cmd, args) {
2060
+ const cmds = [];
2061
+ for (let i = 0; i < Math.floor(args.length / argsPerCmd); i++) {
2062
+ if (i > 0) cmd = IMPLICIT_REPEAT_CMD[cmd] ?? cmd;
2063
+ cmds.push([cmd, args.slice(i * argsPerCmd, (i + 1) * argsPerCmd)]);
2064
+ }
2065
+ return cmds;
2066
+ }
2067
+ function parseSvgPath(svgPath, exploded = false) {
2068
+ const commandTuples = [];
2069
+ const parts = svgPath.split(CMD_RE).slice(1);
2070
+ for (let i = 0; i < parts.length; i += 2) {
2071
+ const cmd = parts[i];
2072
+ const args = parseArgs(cmd, (parts[i + 1] ?? "").trim());
2073
+ const argsPerCmd = checkCmd(cmd, args);
2074
+ if (argsPerCmd === 0 || !exploded) commandTuples.push([cmd, args]);
2075
+ else commandTuples.push(...explodeCmd(argsPerCmd, cmd, args));
2076
+ }
2077
+ return commandTuples;
2078
+ }
2079
+ function addCmdToD(d, cmd, args) {
2080
+ const snippet = pathSegment(cmd, ...args);
2081
+ return d ? `${d} ${snippet}` : snippet;
2082
+ }
2083
+ function buildD(cmds) {
2084
+ let d = "";
2085
+ for (const [cmd, args] of cmds) d = addCmdToD(d, cmd, args);
2086
+ return d;
2087
+ }
2088
+ function nextPos(currPos, cmd, cmdArgs) {
2089
+ const [xCoordIdxs, yCoordIdxs] = cmdCoords(cmd);
2090
+ let newX = currPos.x;
2091
+ let newY = currPos.y;
2092
+ if (cmd === cmd.toUpperCase()) {
2093
+ if (xCoordIdxs.length) newX = 0;
2094
+ if (yCoordIdxs.length) newY = 0;
2095
+ }
2096
+ if (xCoordIdxs.length) newX += cmdArgs[xCoordIdxs[xCoordIdxs.length - 1]];
2097
+ if (yCoordIdxs.length) newY += cmdArgs[yCoordIdxs[yCoordIdxs.length - 1]];
2098
+ return pt(newX, newY);
2099
+ }
2100
+ function explicitLinesCallback(_subpathStart, currPos, cmd, args) {
2101
+ let newArgs;
2102
+ if (cmd === "v") newArgs = [0, args[0]];
2103
+ else if (cmd === "V") newArgs = [currPos.x, args[0]];
2104
+ else if (cmd === "h") newArgs = [args[0], 0];
2105
+ else if (cmd === "H") newArgs = [args[0], currPos.y];
2106
+ else return [[cmd, [...args]]];
2107
+ return [[cmd === cmd.toLowerCase() ? "l" : "L", newArgs]];
2108
+ }
2109
+ function rewriteCoords(cmdConverter, coordConverter, currPos, cmd, args) {
2110
+ const [xCoordIdxs, yCoordIdxs] = cmdCoords(cmd);
2111
+ const desiredCmd = cmdConverter(cmd);
2112
+ const newArgs = [...args];
2113
+ if (cmd !== desiredCmd) {
2114
+ cmd = desiredCmd;
2115
+ for (const xCoordIdx of xCoordIdxs) newArgs[xCoordIdx] += coordConverter(currPos.x);
2116
+ for (const yCoordIdx of yCoordIdxs) newArgs[yCoordIdx] += coordConverter(currPos.y);
2117
+ }
2118
+ return [cmd, newArgs];
2119
+ }
2120
+ function relativeToAbsolute(currPos, cmd, args) {
2121
+ return rewriteCoords((c) => c.toUpperCase(), (scaler) => scaler, currPos, cmd, args);
2122
+ }
2123
+ function moveEndpoint(currPos, cmd, cmdArgs, newEndpoint) {
2124
+ const [[newCmd, newArgs]] = explicitLinesCallback(null, currPos, cmd, cmdArgs);
2125
+ cmd = newCmd;
2126
+ const args = [...newArgs];
2127
+ const [xCoordIdxs, yCoordIdxs] = cmdCoords(cmd);
2128
+ if (xCoordIdxs.length || yCoordIdxs.length) {
2129
+ let { x: newX, y: newY } = newEndpoint;
2130
+ if (cmd === cmd.toLowerCase()) {
2131
+ newX = newX - currPos.x;
2132
+ newY = newY - currPos.y;
2133
+ }
2134
+ args[xCoordIdxs[xCoordIdxs.length - 1]] = newX;
2135
+ args[yCoordIdxs[yCoordIdxs.length - 1]] = newY;
2136
+ }
2137
+ return [cmd, args];
2138
+ }
2139
+ function walkPath(d, callback) {
2140
+ let currPos = pt();
2141
+ let subpathStartPos = currPos;
2142
+ const newCmds = [];
2143
+ const exploded = parseSvgPath(d, true);
2144
+ for (let idx = 0; idx < exploded.length; idx++) {
2145
+ let [cmd, args] = exploded[idx];
2146
+ checkCmd(cmd, args);
2147
+ if (idx === 0 && cmd === "m") cmd = "M";
2148
+ let prev = [
2149
+ null,
2150
+ null,
2151
+ null
2152
+ ];
2153
+ if (newCmds.length) prev = newCmds[newCmds.length - 1];
2154
+ for (const [newCmd, newCmdArgs] of callback(subpathStartPos, currPos, cmd, args, ...prev)) {
2155
+ let next;
2156
+ if (newCmd.toLowerCase() !== "z") next = nextPos(currPos, newCmd, newCmdArgs);
2157
+ else next = subpathStartPos;
2158
+ const prevPos = currPos;
2159
+ currPos = next;
2160
+ if (newCmd.toUpperCase() === "M") subpathStartPos = currPos;
2161
+ newCmds.push([
2162
+ prevPos,
2163
+ newCmd,
2164
+ newCmdArgs
2165
+ ]);
2166
+ }
2167
+ }
2168
+ let newD = "";
2169
+ for (const [, cmd, args] of newCmds) newD = addCmdToD(newD, cmd, args);
2170
+ return newD;
2171
+ }
2172
+ function rewritePath(d, rewriteFn) {
2173
+ return walkPath(d, (subpathStart, currPos, cmd, args) => {
2174
+ let [newCmd, newCmdArgs] = rewriteFn(currPos, cmd, args);
2175
+ const next = nextPos(currPos, newCmd, newCmdArgs);
2176
+ if (!ptEquals(next, subpathStart) && ptAlmostEquals(next, subpathStart)) [newCmd, newCmdArgs] = moveEndpoint(currPos, newCmd, newCmdArgs, subpathStart);
2177
+ return [[newCmd, newCmdArgs]];
2178
+ });
2179
+ }
2180
+ function absolutePath(d) {
2181
+ return rewritePath(d, relativeToAbsolute);
2182
+ }
2183
+ function absoluteMoveto(d) {
2184
+ return rewritePath(d, (currPos, cmd, args) => {
2185
+ if (cmd === "M" || cmd === "m") return relativeToAbsolute(currPos, cmd, args);
2186
+ return [cmd, [...args]];
2187
+ });
2188
+ }
2189
+ function explicitLines(d) {
2190
+ return walkPath(d, explicitLinesCallback);
2191
+ }
2192
+ function expandShorthand(d) {
2193
+ const shortToLong = {
2194
+ S: "C",
2195
+ T: "Q"
2196
+ };
2197
+ return walkPath(d, (_, currPos, cmd, args, prevPos, prevCmd, prevArgs) => {
2198
+ if (!(cmd.toUpperCase() in shortToLong)) return [[cmd, args]];
2199
+ if (cmd === cmd.toLowerCase()) [cmd, args] = relativeToAbsolute(currPos, cmd, args);
2200
+ let newCp = [currPos.x, currPos.y];
2201
+ if (prevCmd) {
2202
+ if (prevCmd === prevCmd.toLowerCase()) [prevCmd, prevArgs] = relativeToAbsolute(prevPos, prevCmd, prevArgs);
2203
+ if (Object.values(shortToLong).includes(prevCmd)) {
2204
+ const prevCpX = prevArgs[prevArgs.length - 4];
2205
+ const prevCpY = prevArgs[prevArgs.length - 3];
2206
+ newCp = [2 * currPos.x - prevCpX, 2 * currPos.y - prevCpY];
2207
+ }
2208
+ }
2209
+ return [[shortToLong[cmd], [...newCp, ...args]]];
2210
+ });
2211
+ }
2212
+ function arcsToCubics(d) {
2213
+ return walkPath(d, (_subpathStart, currPos, cmd, args) => {
2214
+ if (cmd !== "a" && cmd !== "A") return [[cmd, args]];
2215
+ const rx = args[0];
2216
+ const ry = args[1];
2217
+ const xRotation = args[2];
2218
+ const large = args[3];
2219
+ const sweep = args[4];
2220
+ let endX = args[5];
2221
+ let endY = args[6];
2222
+ if (cmd === "a") {
2223
+ endX += currPos.x;
2224
+ endY += currPos.y;
2225
+ }
2226
+ const result = [];
2227
+ for (const [p1, p2, target] of arcToCubic(currPos, rx, ry, xRotation, large, sweep, pt(endX, endY))) {
2228
+ const { x, y } = target;
2229
+ if (p1 !== null) result.push(["C", [
2230
+ p1.x,
2231
+ p1.y,
2232
+ p2.x,
2233
+ p2.y,
2234
+ x,
2235
+ y
2236
+ ]]);
2237
+ else result.push(["L", [x, y]]);
2238
+ }
2239
+ return result;
2240
+ });
2241
+ }
2242
+ function subpaths(d) {
2243
+ const result = [""];
2244
+ walkPath(absoluteMoveto(d), (_subpathStart, _currPos, cmd, args) => {
2245
+ if (cmd.toUpperCase() === "M") result.push("");
2246
+ result[result.length - 1] = addCmdToD(result[result.length - 1], cmd, args);
2247
+ if (cmd.toUpperCase() === "Z") result.push("");
2248
+ return [[cmd, args]];
2249
+ });
2250
+ return result.filter((s) => s);
2251
+ }
2252
+ function roundFloatsD(d, ndigits) {
2253
+ let out = "";
2254
+ for (const [cmd, args] of parseSvgPath(d)) out = addCmdToD(out, cmd, args.map((n) => pythonRound(n, ndigits)));
2255
+ return out;
2256
+ }
2257
+ function asCmdSeq(d) {
2258
+ return parseSvgPath(arcsToCubics(absolutePath(expandShorthand(explicitLines(d)))), true);
2259
+ }
2260
+ //#endregion
2261
+ //#region src/build/core/flatten/shape.ts
2262
+ var f = (name, type, dflt) => ({
2263
+ name,
2264
+ type,
2265
+ default: dflt
2266
+ });
2267
+ var COMMON_FIELDS = [
2268
+ f("id", "str", ""),
2269
+ f("clip_path", "str", attribDefault("clip-path")),
2270
+ f("clip_rule", "str", attribDefault("clip-rule")),
2271
+ f("fill", "str", attribDefault("fill")),
2272
+ f("fill_opacity", "float", attribDefault("fill-opacity")),
2273
+ f("fill_rule", "str", attribDefault("fill-rule")),
2274
+ f("stroke", "str", attribDefault("stroke")),
2275
+ f("stroke_width", "float", attribDefault("stroke-width")),
2276
+ f("stroke_linecap", "str", attribDefault("stroke-linecap")),
2277
+ f("stroke_linejoin", "str", attribDefault("stroke-linejoin")),
2278
+ f("stroke_miterlimit", "float", attribDefault("stroke-miterlimit")),
2279
+ f("stroke_dasharray", "str", attribDefault("stroke-dasharray")),
2280
+ f("stroke_dashoffset", "float", attribDefault("stroke-dashoffset")),
2281
+ f("stroke_opacity", "float", attribDefault("stroke-opacity")),
2282
+ f("opacity", "float", attribDefault("opacity")),
2283
+ f("transform", "str", attribDefault("transform")),
2284
+ f("style", "str", attribDefault("style")),
2285
+ f("display", "str", attribDefault("display"))
2286
+ ];
2287
+ var TAG_FIELDS = {
2288
+ path: [f("d", "str", "")],
2289
+ circle: [
2290
+ f("r", "float", 0),
2291
+ f("cx", "float", 0),
2292
+ f("cy", "float", 0)
2293
+ ],
2294
+ ellipse: [
2295
+ f("rx", "float", 0),
2296
+ f("ry", "float", 0),
2297
+ f("cx", "float", 0),
2298
+ f("cy", "float", 0)
2299
+ ],
2300
+ line: [
2301
+ f("x1", "float", 0),
2302
+ f("y1", "float", 0),
2303
+ f("x2", "float", 0),
2304
+ f("y2", "float", 0)
2305
+ ],
2306
+ polygon: [f("points", "str", "")],
2307
+ polyline: [f("points", "str", "")],
2308
+ rect: [
2309
+ f("x", "float", 0),
2310
+ f("y", "float", 0),
2311
+ f("width", "float", 0),
2312
+ f("height", "float", 0),
2313
+ f("rx", "float", 0),
2314
+ f("ry", "float", 0)
2315
+ ]
2316
+ };
2317
+ var SHAPE_TAGS = new Set(Object.keys(TAG_FIELDS));
2318
+ function isShapeTag(tag) {
2319
+ return SHAPE_TAGS.has(stripNs(tag));
2320
+ }
2321
+ function shapeFields(tag) {
2322
+ const extra = TAG_FIELDS[tag];
2323
+ if (!extra) throw new Error(`Bad tag <${tag}>`);
2324
+ return [...COMMON_FIELDS, ...extra];
2325
+ }
2326
+ function newShape(tag) {
2327
+ const fields = {};
2328
+ for (const field of shapeFields(tag)) fields[field.name] = field.default;
2329
+ return {
2330
+ tag,
2331
+ fields
2332
+ };
2333
+ }
2334
+ function shapeStr(shape, name) {
2335
+ return shape.fields[name];
2336
+ }
2337
+ function shapeNum(shape, name) {
2338
+ return shape.fields[name];
2339
+ }
2340
+ function cloneShape(shape) {
2341
+ return {
2342
+ tag: shape.tag,
2343
+ fields: { ...shape.fields }
2344
+ };
2345
+ }
2346
+ function shapesEqual(a, b) {
2347
+ if (a.tag !== b.tag) return false;
2348
+ for (const field of shapeFields(a.tag)) if (a.fields[field.name] !== b.fields[field.name]) return false;
2349
+ return true;
2350
+ }
2351
+ function parseFloatStrict(raw, tag, attr) {
2352
+ const value = Number(raw.trim());
2353
+ if (Number.isNaN(value)) throw new Error(`${attr}="${raw}" on <${tag}> is not a number (units other than px are not supported)`);
2354
+ return value;
2355
+ }
2356
+ function fromElement(el, inheritedAttrib = {}) {
2357
+ if (!isShapeTag(el.tag)) throw new Error(`Bad tag <${el.tag}>`);
2358
+ const tag = stripNs(el.tag);
2359
+ const attrs = { ...inheritedAttrib };
2360
+ for (const [name, value] of el.attrib) attrs[name] = value;
2361
+ const shape = newShape(tag);
2362
+ for (const field of shapeFields(tag)) {
2363
+ const raw = attrs[attrName(field.name)];
2364
+ if (raw === void 0 || !raw.trim()) continue;
2365
+ shape.fields[field.name] = field.type === "float" ? parseFloatStrict(raw, tag, attrName(field.name)) : raw;
2366
+ }
2367
+ if (tag === "rect") rectPostInit(shape);
2368
+ return shape;
2369
+ }
2370
+ function rectPostInit(shape) {
2371
+ if (!shapeNum(shape, "rx")) shape.fields.rx = shapeNum(shape, "ry");
2372
+ if (!shapeNum(shape, "ry")) shape.fields.ry = shapeNum(shape, "rx");
2373
+ shape.fields.rx = Math.min(shapeNum(shape, "rx"), shapeNum(shape, "width") / 2);
2374
+ shape.fields.ry = Math.min(shapeNum(shape, "ry"), shapeNum(shape, "height") / 2);
2375
+ }
2376
+ function toElement(shape, inheritedAttrib = {}) {
2377
+ const el = new XEl(svgTag(shape.tag));
2378
+ for (const field of shapeFields(shape.tag)) {
2379
+ const attr = attrName(field.name);
2380
+ const fieldValue = shape.fields[field.name];
2381
+ const attribValue = typeof fieldValue === "number" ? ntos(fieldValue) : fieldValue;
2382
+ if (attr in inheritedAttrib) {
2383
+ if (attribValue === inheritedAttrib[attr]) continue;
2384
+ } else if (fieldValue === field.default) continue;
2385
+ el.attrib.set(attr, attribValue);
2386
+ }
2387
+ return el;
2388
+ }
2389
+ function resetFields(shape, pred) {
2390
+ for (const field of shapeFields(shape.tag)) if (pred(field.name)) shape.fields[field.name] = field.default;
2391
+ }
2392
+ function copyCommonFields(target, source) {
2393
+ for (const field of COMMON_FIELDS) target.fields[field.name] = source.fields[field.name];
2394
+ }
2395
+ function pathFromD(d) {
2396
+ const path = newShape("path");
2397
+ path.fields.d = d;
2398
+ return path;
2399
+ }
2400
+ function pathFromCommands(cmds) {
2401
+ return pathFromD(buildD(cmds));
2402
+ }
2403
+ function asPath(shape) {
2404
+ if (shape.tag === "path") return shape;
2405
+ const fields = shape.fields;
2406
+ let d = "";
2407
+ const add = (cmd, ...args) => {
2408
+ d = addCmdToD(d, cmd, args);
2409
+ };
2410
+ const addArc = (rx, ry, x, y, large = 0) => {
2411
+ const snippet = pathSegment("A", rx, ry, 0, large, 1, x, y);
2412
+ d = d ? `${d} ${snippet}` : snippet;
2413
+ };
2414
+ switch (shape.tag) {
2415
+ case "circle":
2416
+ case "ellipse": {
2417
+ const rx = shape.tag === "circle" ? fields.r : fields.rx;
2418
+ const ry = shape.tag === "circle" ? fields.r : fields.ry;
2419
+ const cx = fields.cx;
2420
+ const cy = fields.cy;
2421
+ add("M", cx + rx, cy);
2422
+ addArc(rx, ry, cx - rx, cy, 1);
2423
+ addArc(rx, ry, cx + rx, cy, 1);
2424
+ d = `${d} Z`;
2425
+ break;
2426
+ }
2427
+ case "line":
2428
+ add("M", fields.x1, fields.y1);
2429
+ add("L", fields.x2, fields.y2);
2430
+ break;
2431
+ case "polygon":
2432
+ d = fields.points ? `M${fields.points} Z` : "";
2433
+ break;
2434
+ case "polyline":
2435
+ d = fields.points ? `M${fields.points}` : "";
2436
+ break;
2437
+ case "rect": {
2438
+ const x = fields.x;
2439
+ const y = fields.y;
2440
+ const w = fields.width;
2441
+ const h = fields.height;
2442
+ const rx = fields.rx;
2443
+ const ry = fields.ry;
2444
+ add("M", x + rx, y);
2445
+ add("H", x + w - rx);
2446
+ if (rx > 0) addArc(rx, ry, x + w, y + ry);
2447
+ add("V", y + h - ry);
2448
+ if (rx > 0) addArc(rx, ry, x + w - rx, y + h);
2449
+ add("H", x + rx);
2450
+ if (rx > 0) addArc(rx, ry, x, y + h - ry);
2451
+ add("V", y + ry);
2452
+ if (rx > 0) addArc(rx, ry, x + rx, y);
2453
+ d = `${d} Z`;
2454
+ break;
2455
+ }
2456
+ default: throw new Error(`as_path not implemented for <${shape.tag}>`);
2457
+ }
2458
+ const path = pathFromD(d);
2459
+ copyCommonFields(path, shape);
2460
+ return path;
2461
+ }
2462
+ function shapeCmdSeq(shape) {
2463
+ return asCmdSeq(shapeStr(asPath(shape), "d"));
2464
+ }
2465
+ function absoluteShape(shape) {
2466
+ if (shape.tag !== "path") return shape;
2467
+ const target = cloneShape(shape);
2468
+ target.fields.d = absolutePath(shapeStr(target, "d"));
2469
+ return target;
2470
+ }
2471
+ function explicitLinesExpandShorthand(shape) {
2472
+ if (shape.tag !== "path") return shape;
2473
+ const target = cloneShape(shape);
2474
+ target.fields.d = expandShorthand(explicitLines(shapeStr(target, "d")));
2475
+ return target;
2476
+ }
2477
+ function applyTransform(shape, transform, ops) {
2478
+ let target = asPath(shape);
2479
+ if (target === shape) target = cloneShape(target);
2480
+ let cmds = [["M", [0, 0]]];
2481
+ if (!transform.isDegenerate()) cmds = ops.transformCmds(shapeCmdSeq(shape), transform);
2482
+ target.fields.d = buildD(cmds);
2483
+ return target;
2484
+ }
2485
+ function removeOverlaps(shape, ops) {
2486
+ const cmds = ops.removeOverlaps(shapeCmdSeq(shape), shapeStr(shape, "fill_rule"));
2487
+ const target = cloneShape(asPath(shape));
2488
+ target.fields.fill_rule = "nonzero";
2489
+ target.fields.clip_rule = "nonzero";
2490
+ target.fields.d = buildD(cmds);
2491
+ return target;
2492
+ }
2493
+ function strokeCommands(shape, tolerance, ops) {
2494
+ let dashArray = [];
2495
+ const dasharray = shapeStr(shape, "stroke_dasharray");
2496
+ if (dasharray !== "none") dashArray = dasharray.split(DASHARRAY_SEPARATOR).filter((v) => v).map((v) => parseFloatStrict(v, shape.tag, "stroke-dasharray"));
2497
+ if (dashArray.length % 2 !== 0) dashArray = [...dashArray, ...dashArray];
2498
+ return ops.strokeCmds(shapeCmdSeq(shape), shapeStr(shape, "stroke_linecap"), shapeStr(shape, "stroke_linejoin"), shapeNum(shape, "stroke_width"), shapeNum(shape, "stroke_miterlimit"), tolerance, dashArray, shapeNum(shape, "stroke_dashoffset"));
2499
+ }
2500
+ function parseCssDeclarations(style, output, propertyNames) {
2501
+ const unparsed = [];
2502
+ for (let declaration of style.split(";")) {
2503
+ declaration = declaration.trim();
2504
+ if ((declaration.match(/:/g) ?? []).length === 1) {
2505
+ const [rawName, rawValue] = declaration.split(":");
2506
+ const propertyName = rawName.trim();
2507
+ const value = rawValue.trim();
2508
+ if (propertyNames === void 0 || propertyNames.has(propertyName)) output[propertyName] = value;
2509
+ else unparsed.push(declaration);
2510
+ } else if (declaration) throw new Error(`Invalid CSS declaration syntax: ${declaration}`);
2511
+ }
2512
+ return unparsed.length ? unparsed.join("; ") + ";" : "";
2513
+ }
2514
+ function applyStyleAttribute(shape) {
2515
+ const target = cloneShape(shape);
2516
+ const style = shapeStr(target, "style");
2517
+ if (style) {
2518
+ const fields = shapeFields(target.tag);
2519
+ const attrTypes = new Map(fields.map((fd) => [attrName(fd.name), fd]));
2520
+ const rawAttrs = {};
2521
+ const unparsedStyle = parseCssDeclarations(style, rawAttrs, new Set(attrTypes.keys()));
2522
+ for (const [attr, rawValue] of Object.entries(rawAttrs)) {
2523
+ const field = attrTypes.get(attr);
2524
+ target.fields[field.name] = field.type === "float" ? parseFloatStrict(rawValue, target.tag, attr) : rawValue;
2525
+ }
2526
+ target.fields.style = unparsedStyle;
2527
+ }
2528
+ return target;
2529
+ }
2530
+ function mightPaint(shape, ops) {
2531
+ const styled = applyStyleAttribute(shape);
2532
+ if (shapeStr(styled, "display") === "none") return false;
2533
+ const visible = (fill, opacity) => fill !== "none" && shapeNum(styled, "opacity") * opacity !== 0;
2534
+ if (shapeCmdSeq(shape).every(([c]) => c.toUpperCase() === "M")) return false;
2535
+ if (visible(shapeStr(styled, "stroke"), shapeNum(styled, "stroke_opacity")) && shapeNum(styled, "stroke_width") !== 0) return true;
2536
+ if (!visible(shapeStr(styled, "fill"), shapeNum(styled, "fill_opacity"))) return false;
2537
+ return ops.pathArea(shapeCmdSeq(styled), shapeStr(styled, "fill_rule")) > 0;
2538
+ }
2539
+ function normalizeOpacity(shape) {
2540
+ if (shapeStr(shape, "fill") === "none" && shapeStr(shape, "stroke") === "none") return;
2541
+ for (const [fillAttr, opacityAttr] of [["fill", "stroke_opacity"], ["stroke", "fill_opacity"]]) if (shapeStr(shape, fillAttr) === "none") {
2542
+ shape.fields.opacity = shapeNum(shape, "opacity") * shapeNum(shape, opacityAttr);
2543
+ shape.fields[opacityAttr] = 1;
2544
+ }
2545
+ }
2546
+ function roundShapeFloats(shape, ndigits) {
2547
+ for (const field of shapeFields(shape.tag)) {
2548
+ const value = shape.fields[field.name];
2549
+ if (field.type === "float" && typeof value === "number") shape.fields[field.name] = pythonRound(value, ndigits);
2550
+ }
2551
+ if (shape.tag === "path") shape.fields.d = roundFloatsD(shapeStr(shape, "d"), ndigits);
2552
+ }
2553
+ function removeEmptySubpaths(shape, ops) {
2554
+ if (shape.tag !== "path") return;
2555
+ shape.fields.d = subpaths(shapeStr(shape, "d")).filter((sub) => mightPaint(pathFromD(sub), ops)).join(" ");
2556
+ }
2557
+ //#endregion
2558
+ //#region src/build/core/flatten/element.ts
2559
+ var GRADIENT_TAGS = /* @__PURE__ */ new Set(["linearGradient", "radialGradient"]);
2560
+ function numberAttrib(el, name, fallback) {
2561
+ return parseFloat(el.attrib.get(name) ?? String(fallback));
2562
+ }
2563
+ function isTag(el, local) {
2564
+ return stripNs(el.tag) === local;
2565
+ }
2566
+ function isDefs(tag) {
2567
+ return stripNs(tag) === "defs";
2568
+ }
2569
+ function isGradient(tag) {
2570
+ return GRADIENT_TAGS.has(stripNs(tag));
2571
+ }
2572
+ function isGroup(tag) {
2573
+ return stripNs(tag) === "g";
2574
+ }
2575
+ function clamp(value, minv = 0, maxv = 1) {
2576
+ return Math.max(Math.min(value, maxv), minv);
2577
+ }
2578
+ function elOpacity(el) {
2579
+ return clamp(numberAttrib(el, "opacity", 1));
2580
+ }
2581
+ function isRemovableGroup(el) {
2582
+ if (!isGroup(el.tag)) return false;
2583
+ if (el.attrib.size === 0) return true;
2584
+ const numChildren = el.children.length;
2585
+ const opacity = elOpacity(el);
2586
+ return numChildren <= 1 || opacity === 0 || opacity === 1;
2587
+ }
2588
+ function tryRemoveGroup(groupEl, pushOpacity = true) {
2589
+ const remove = isRemovableGroup(groupEl);
2590
+ const opacity = elOpacity(groupEl);
2591
+ if (remove) {
2592
+ const children = [...groupEl.children];
2593
+ if (groupEl.parent !== null) replaceEl(groupEl, children);
2594
+ if (pushOpacity) for (const child of children) inheritAttrib({ opacity: ntos(opacity) }, child);
2595
+ } else {
2596
+ groupEl.attrib.clear();
2597
+ groupEl.attrib.set("opacity", ntos(opacity));
2598
+ dropDefaultAttrib(groupEl.attrib);
2599
+ }
2600
+ return remove;
2601
+ }
2602
+ function elementTransform(el, currentTransform = Affine2D.identity()) {
2603
+ const attr = isGradient(el.tag) ? "gradientTransform" : "transform";
2604
+ const raw = el.attrib.get(attr);
2605
+ if (raw) return Affine2D.composeLtr([Affine2D.fromString(raw), currentTransform]);
2606
+ return currentTransform;
2607
+ }
2608
+ function idOfTarget(url) {
2609
+ const match = URL_ID_REFERENCE.exec(url);
2610
+ if (!match) throw new Error(`Unrecognized url "${url}"`);
2611
+ return match[1];
2612
+ }
2613
+ function parseViewBox(s) {
2614
+ const box = s.split(VIEWBOX_SEPARATOR).filter((v) => v).map((v) => parseFloat(v));
2615
+ if (box.length !== 4 || box.some((v) => Number.isNaN(v))) throw new Error(`Unable to parse viewBox: ${s}`);
2616
+ return {
2617
+ x: box[0],
2618
+ y: box[1],
2619
+ w: box[2],
2620
+ h: box[3]
2621
+ };
2622
+ }
2623
+ //#endregion
2624
+ //#region src/build/core/flatten/stages/use.ts
2625
+ function expandUses(doc, scopeEl) {
2626
+ const attribNotCopied = /* @__PURE__ */ new Set([
2627
+ "x",
2628
+ "y",
2629
+ "width",
2630
+ "height",
2631
+ "transform",
2632
+ xlinkHrefAttr(),
2633
+ "href"
2634
+ ]);
2635
+ const elById = /* @__PURE__ */ new Map();
2636
+ for (const el of findAll(doc.root, (e) => e.attrib.has("id"))) elById.set(el.attrib.get("id"), el);
2637
+ for (;;) {
2638
+ const useEls = findAll(scopeEl, (e) => isTag(e, "use"));
2639
+ if (!useEls.length) break;
2640
+ const swaps = [];
2641
+ for (const useEl of useEls) {
2642
+ const ref = useEl.attrib.get(xlinkHrefAttr()) ?? useEl.attrib.get("href");
2643
+ if (ref === void 0) throw new Error("<use> has no href attribute to resolve");
2644
+ if (!ref.startsWith("#")) throw new Error(`<use> href "${ref}" is not supported: only same-document #id references are`);
2645
+ const target = elById.get(ref.slice(1));
2646
+ if (target === void 0) throw new Error(`<use> references ${ref}, but no element has id "${ref.slice(1)}"`);
2647
+ const newEl = target.deepClone();
2648
+ for (const el of newEl.iter()) el.attrib.delete("id");
2649
+ const group = new XEl(svgTag("g"));
2650
+ let affine = Affine2D.identity().translate(numberAttrib(useEl, "x", 0), numberAttrib(useEl, "y", 0));
2651
+ const useTransform = useEl.attrib.get("transform");
2652
+ if (useTransform !== void 0) affine = Affine2D.composeLtr([affine, Affine2D.fromString(useTransform)]);
2653
+ if (!affine.equals(Affine2D.identity())) group.attrib.set("transform", affine.toString());
2654
+ for (const [name, value] of useEl.attrib) {
2655
+ if (attribNotCopied.has(name)) continue;
2656
+ group.attrib.set(name, value);
2657
+ }
2658
+ group.append(newEl);
2659
+ if (tryRemoveGroup(group, false)) {
2660
+ inheritAttrib(group.attrib, newEl);
2661
+ swaps.push([useEl, newEl]);
2662
+ } else swaps.push([useEl, group]);
2663
+ }
2664
+ for (const [oldEl, newEl] of swaps) replaceEl(oldEl, [newEl]);
2665
+ }
2666
+ }
2667
+ function resolveUse(doc) {
2668
+ doc.syncTree();
2669
+ expandUses(doc, doc.root);
2670
+ }
2671
+ //#endregion
2672
+ //#region src/build/core/flatten/stages/clip-path.ts
2673
+ function resolveClipPath(doc, clipPathUrl, transform = Affine2D.identity()) {
2674
+ const clipPathEl = doc.resolveUrl(clipPathUrl, "clipPath");
2675
+ expandUses(doc, clipPathEl);
2676
+ const clipTransform = elementTransform(clipPathEl, transform);
2677
+ const clipShapes = clipPathEl.children.map((e) => applyTransform(fromElement(e), elementTransform(e, clipTransform), doc.ops));
2678
+ let clip = pathFromCommands(doc.ops.union(clipShapes.map((s) => shapeCmdSeq(s)), clipShapes.map((s) => shapeStr(s, "clip_rule"))));
2679
+ const nestedClip = clipPathEl.attrib.get("clip-path");
2680
+ if (nestedClip !== void 0) {
2681
+ const clipClop = resolveClipPath(doc, nestedClip, clipTransform);
2682
+ clip = pathFromCommands(doc.ops.intersection([shapeCmdSeq(clip), shapeCmdSeq(clipClop)], [shapeStr(clip, "clip_rule"), shapeStr(clipClop, "clip_rule")]));
2683
+ }
2684
+ return clip;
2685
+ }
2686
+ //#endregion
2687
+ //#region src/build/core/flatten/document.ts
2688
+ var MAX_PCT_ERROR = .1;
2689
+ var DEFAULT_DEFAULT_TOLERANCE = .1;
2690
+ var SvgDocument = class SvgDocument {
2691
+ root;
2692
+ ops;
2693
+ shapeCache = null;
2694
+ constructor(root, ops) {
2695
+ this.root = root;
2696
+ this.ops = ops;
2697
+ }
2698
+ static parse(content, ops) {
2699
+ return new SvgDocument(parseSvgDocument(content), ops);
2700
+ }
2701
+ hasShapes() {
2702
+ return this.shapeCache !== null;
2703
+ }
2704
+ shapeEntries() {
2705
+ if (this.shapeCache) return this.shapeCache;
2706
+ const entries = [];
2707
+ for (const context of this.depthFirst(false)) {
2708
+ const el = context.element;
2709
+ if (!isShapeTag(el.tag)) continue;
2710
+ entries.push([el, [fromElement(el, context.attrib)]]);
2711
+ }
2712
+ this.shapeCache = entries;
2713
+ return this.shapeCache;
2714
+ }
2715
+ invalidateShapes() {
2716
+ this.shapeCache = null;
2717
+ }
2718
+ mapShapes(fn) {
2719
+ const entries = this.shapeEntries();
2720
+ entries.forEach(([el, shapes], idx) => {
2721
+ entries[idx] = [el, shapes.map(fn)];
2722
+ });
2723
+ }
2724
+ forEachShape(fn) {
2725
+ for (const [, shapes] of this.shapeEntries()) for (const shape of shapes) fn(shape);
2726
+ }
2727
+ inheritedAttrib(el) {
2728
+ const parents = [];
2729
+ let current = el.parent;
2730
+ while (current !== null) {
2731
+ parents.push(current);
2732
+ current = current.parent;
2733
+ }
2734
+ let attrib = { ...INHERITABLE_ATTRIB_DEFAULTS };
2735
+ for (const parent of parents.reverse()) attrib = attribToPassOn(attrib, parent);
2736
+ return attrib;
2737
+ }
2738
+ syncTree() {
2739
+ if (!this.shapeCache) return;
2740
+ for (const [oldEl, shapes] of this.shapeCache) {
2741
+ const inherited = this.inheritedAttrib(oldEl);
2742
+ replaceEl(oldEl, shapes.map((s) => toElement(s, inherited)));
2743
+ }
2744
+ this.shapeCache = null;
2745
+ }
2746
+ viewBox() {
2747
+ const raw = this.root.attrib.get("viewBox");
2748
+ if (raw === void 0) {
2749
+ const w = this.root.attrib.get("width");
2750
+ const h = this.root.attrib.get("height");
2751
+ if (w && h) return {
2752
+ x: 0,
2753
+ y: 0,
2754
+ w: parseFloat(w),
2755
+ h: parseFloat(h)
2756
+ };
2757
+ return null;
2758
+ }
2759
+ return parseViewBox(raw);
2760
+ }
2761
+ get tolerance() {
2762
+ const vbox = this.viewBox();
2763
+ if (vbox === null) return DEFAULT_DEFAULT_TOLERANCE;
2764
+ return Math.min(vbox.w, vbox.h) * MAX_PCT_ERROR / 100;
2765
+ }
2766
+ resolveUrl(url, elTag) {
2767
+ const id = idOfTarget(url);
2768
+ const matches = findAll(this.root, (el) => el.attrib.get("id") === id && (elTag === "*" || stripNs(el.tag) === elTag));
2769
+ if (matches.length !== 1) {
2770
+ const what = elTag === "*" ? "element" : `<${elTag}>`;
2771
+ throw new Error(matches.length === 0 ? `${url} references ${what} with id "${id}", but no such element exists` : `${url} matches ${matches.length} ${what} elements with id "${id}", expected exactly one`);
2772
+ }
2773
+ return matches[0];
2774
+ }
2775
+ *traverse(nextFn, appendFn, resolveClipPaths) {
2776
+ const frontier = [{
2777
+ nthOfType: 0,
2778
+ element: this.root,
2779
+ path: "/svg[0]",
2780
+ transform: Affine2D.identity(),
2781
+ clips: [],
2782
+ attrib: attribToPassOn({ ...INHERITABLE_ATTRIB_DEFAULTS }, this.root)
2783
+ }];
2784
+ while (frontier.length) {
2785
+ const context = nextFn(frontier);
2786
+ yield context;
2787
+ const childIdxs = {};
2788
+ const newEntries = [];
2789
+ for (const child of context.element.children) {
2790
+ const transform = elementTransform(child, context.transform);
2791
+ let clips = context.clips;
2792
+ const clipPathAttr = child.attrib.get("clip-path");
2793
+ if (resolveClipPaths && clipPathAttr && clipPathAttr !== "none") clips = [...clips, resolveClipPath(this, clipPathAttr, transform)];
2794
+ const localName = stripNs(child.tag);
2795
+ const nthOfType = childIdxs[localName] ?? 0;
2796
+ childIdxs[localName] = nthOfType + 1;
2797
+ newEntries.push({
2798
+ nthOfType,
2799
+ element: child,
2800
+ path: `${context.path}/${localName}[${nthOfType}]`,
2801
+ transform,
2802
+ clips,
2803
+ attrib: attribToPassOn(context.attrib, child)
2804
+ });
2805
+ }
2806
+ appendFn(frontier, newEntries);
2807
+ }
2808
+ }
2809
+ depthFirst(resolveClipPaths = true) {
2810
+ return this.traverse((f) => f.pop(), (f, e) => f.push(...e.reverse()), resolveClipPaths);
2811
+ }
2812
+ breadthFirst(resolveClipPaths = true) {
2813
+ return this.traverse((f) => f.shift(), (f, e) => f.push(...e), resolveClipPaths);
2814
+ }
2815
+ serialize() {
2816
+ this.syncTree();
2817
+ return serializeSvg(this.root);
2818
+ }
2819
+ };
2820
+ //#endregion
2821
+ //#region src/build/core/pathkit/contours.ts
2822
+ function verbMap(PathKit) {
2823
+ return {
2824
+ MOVE: PathKit.MOVE_VERB ?? 0,
2825
+ LINE: PathKit.LINE_VERB ?? 1,
2826
+ QUAD: PathKit.QUAD_VERB ?? 2,
2827
+ CONIC: PathKit.CONIC_VERB ?? 3,
2828
+ CUBIC: PathKit.CUBIC_VERB ?? 4,
2829
+ CLOSE: PathKit.CLOSE_VERB ?? 5
2830
+ };
2831
+ }
2832
+ function fillTypes(PathKit) {
2833
+ return {
2834
+ EVENODD: PathKit?.FillType?.EVENODD ?? PathKit?.FillType?.EVEN_ODD ?? 1,
2835
+ WINDING: PathKit?.FillType?.WINDING ?? PathKit?.FillType?.NONZERO ?? 0
2836
+ };
2837
+ }
2838
+ var EPS = .01;
2839
+ function roundN(x, ndigits = 3) {
2840
+ const m = 10 ** ndigits;
2841
+ const s = x * m;
2842
+ if (!Number.isFinite(s)) return x;
2843
+ const floor = Math.floor(s);
2844
+ const frac = s - floor;
2845
+ const TIE_EPS = 1e-12;
2846
+ let roundedInt;
2847
+ if (Math.abs(frac - .5) < TIE_EPS) roundedInt = floor % 2 === 0 ? floor : floor + 1;
2848
+ else if (Math.abs(frac + .5) < TIE_EPS) {
2849
+ const ceil = Math.ceil(s);
2850
+ roundedInt = ceil % 2 === 0 ? ceil : ceil - 1;
2851
+ } else roundedInt = Math.round(s);
2852
+ return roundedInt / m;
2853
+ }
2854
+ function normPt(p) {
2855
+ return [Math.round(p[0] * 1e4) / 1e4, Math.round(p[1] * 1e4) / 1e4];
2856
+ }
2857
+ function eqPt(a, b) {
2858
+ const aa = normPt(a);
2859
+ const bb = normPt(b);
2860
+ return Math.abs(aa[0] - bb[0]) < EPS && Math.abs(aa[1] - bb[1]) < EPS;
2861
+ }
2862
+ function signedAreaPolyline(points) {
2863
+ if (points.length < 3) return 0;
2864
+ let a = 0;
2865
+ for (let i = 0; i < points.length; i++) {
2866
+ const [x1, y1] = points[i];
2867
+ const [x2, y2] = points[(i + 1) % points.length];
2868
+ a += x1 * y2 - x2 * y1;
2869
+ }
2870
+ return a / 2;
2871
+ }
2872
+ function approxSignedAreaFromContourCmds(contourCmds, VERB, steps = 24) {
2873
+ let cx = 0, cy = 0;
2874
+ let sx = 0, sy = 0;
2875
+ const pts = [];
2876
+ const add = (x, y) => pts.push([x, y]);
2877
+ for (const cmd of contourCmds) {
2878
+ const v = cmd[0];
2879
+ if (v === VERB.MOVE) {
2880
+ cx = cmd[1];
2881
+ cy = cmd[2];
2882
+ sx = cx;
2883
+ sy = cy;
2884
+ add(cx, cy);
2885
+ } else if (v === VERB.LINE) {
2886
+ cx = cmd[1];
2887
+ cy = cmd[2];
2888
+ add(cx, cy);
2889
+ } else if (v === VERB.QUAD) {
2890
+ const x0 = cx, y0 = cy;
2891
+ const x1 = cmd[1], y1 = cmd[2];
2892
+ const x2 = cmd[3], y2 = cmd[4];
2893
+ for (let i = 1; i <= steps; i++) {
2894
+ const t = i / steps;
2895
+ const mt = 1 - t;
2896
+ add(mt * mt * x0 + 2 * mt * t * x1 + t * t * x2, mt * mt * y0 + 2 * mt * t * y1 + t * t * y2);
2897
+ }
2898
+ cx = x2;
2899
+ cy = y2;
2900
+ } else if (v === VERB.CUBIC) {
2901
+ const x0 = cx, y0 = cy;
2902
+ const x1 = cmd[1], y1 = cmd[2];
2903
+ const x2 = cmd[3], y2 = cmd[4];
2904
+ const x3 = cmd[5], y3 = cmd[6];
2905
+ for (let i = 1; i <= steps; i++) {
2906
+ const t = i / steps;
2907
+ const mt = 1 - t;
2908
+ add(mt * mt * mt * x0 + 3 * mt * mt * t * x1 + 3 * mt * t * t * x2 + t * t * t * x3, mt * mt * mt * y0 + 3 * mt * mt * t * y1 + 3 * mt * t * t * y2 + t * t * t * y3);
2909
+ }
2910
+ cx = x3;
2911
+ cy = y3;
2912
+ } else if (v === VERB.CLOSE) add(sx, sy);
2913
+ }
2914
+ if (pts.length && (pts[0][0] !== pts[pts.length - 1][0] || pts[0][1] !== pts[pts.length - 1][1])) pts.push([pts[0][0], pts[0][1]]);
2915
+ return signedAreaPolyline(pts);
2916
+ }
2917
+ function splitContours(cmds, VERB) {
2918
+ const contours = [];
2919
+ let cur = null;
2920
+ for (const cmd of cmds) if (cmd[0] === VERB.MOVE) {
2921
+ if (cur && cur.length) contours.push(cur);
2922
+ cur = [cmd];
2923
+ } else if (cur) cur.push(cmd);
2924
+ if (cur && cur.length) contours.push(cur);
2925
+ return contours;
2926
+ }
2927
+ function ensureClosed(contourCmds, VERB) {
2928
+ return contourCmds.some((c) => c[0] === VERB.CLOSE) ? [...contourCmds] : [...contourCmds, [VERB.CLOSE]];
2929
+ }
2930
+ function explicitCloseWantedFromCmds(contourCmds, VERB) {
2931
+ if (!contourCmds?.length) return false;
2932
+ const m = contourCmds[0];
2933
+ if (m[0] !== VERB.MOVE) return false;
2934
+ const start = [m[1], m[2]];
2935
+ for (let i = contourCmds.length - 1; i >= 1; i--) {
2936
+ const cmd = contourCmds[i];
2937
+ const v = cmd[0];
2938
+ if (v === VERB.CLOSE) continue;
2939
+ let end = null;
2940
+ if (v === VERB.LINE) end = [cmd[1], cmd[2]];
2941
+ else if (v === VERB.QUAD) end = [cmd[3], cmd[4]];
2942
+ else if (v === VERB.CUBIC) end = [cmd[5], cmd[6]];
2943
+ else continue;
2944
+ return eqPt(end, start);
2945
+ }
2946
+ return false;
2947
+ }
2948
+ function contourToSegments(contourCmds, VERB) {
2949
+ const c = ensureClosed(contourCmds, VERB);
2950
+ const move = c[0];
2951
+ const sx = move[1], sy = move[2];
2952
+ let last = [sx, sy];
2953
+ const segs = [];
2954
+ for (let i = 1; i < c.length; i++) {
2955
+ const cmd = c[i];
2956
+ const v = cmd[0];
2957
+ if (v === VERB.LINE) {
2958
+ const end = [cmd[1], cmd[2]];
2959
+ segs.push({
2960
+ type: "L",
2961
+ start: last,
2962
+ end,
2963
+ synthetic: false
2964
+ });
2965
+ last = end;
2966
+ } else if (v === VERB.QUAD) {
2967
+ const ctrl = [cmd[1], cmd[2]];
2968
+ const end = [cmd[3], cmd[4]];
2969
+ segs.push({
2970
+ type: "Q",
2971
+ start: last,
2972
+ ctrl,
2973
+ end,
2974
+ synthetic: false
2975
+ });
2976
+ last = end;
2977
+ } else if (v === VERB.CUBIC) {
2978
+ const c1 = [cmd[1], cmd[2]];
2979
+ const c2 = [cmd[3], cmd[4]];
2980
+ const end = [cmd[5], cmd[6]];
2981
+ segs.push({
2982
+ type: "C",
2983
+ start: last,
2984
+ c1,
2985
+ c2,
2986
+ end,
2987
+ synthetic: false
2988
+ });
2989
+ last = end;
2990
+ } else if (v === VERB.CLOSE) {
2991
+ const end = [sx, sy];
2992
+ if (!eqPt(last, end)) segs.push({
2993
+ type: "L",
2994
+ start: last,
2995
+ end,
2996
+ synthetic: true
2997
+ });
2998
+ last = end;
2999
+ }
3000
+ }
3001
+ return {
3002
+ start: [sx, sy],
3003
+ segs
3004
+ };
3005
+ }
3006
+ function applyClosePolicy(segs, startPt, explicitCloseWanted) {
3007
+ if (!segs.length) return segs;
3008
+ for (const s of segs) s.synthetic = false;
3009
+ const lastEnd = segs[segs.length - 1].end;
3010
+ if (!eqPt(lastEnd, startPt)) segs.push({
3011
+ type: "L",
3012
+ start: lastEnd,
3013
+ end: startPt,
3014
+ synthetic: false
3015
+ });
3016
+ if (!explicitCloseWanted) segs[segs.length - 1].synthetic = true;
3017
+ return segs;
3018
+ }
3019
+ function segmentsToContourCmds(startPt, segs, VERB) {
3020
+ const out = [[
3021
+ VERB.MOVE,
3022
+ roundN(startPt[0]),
3023
+ roundN(startPt[1])
3024
+ ]];
3025
+ for (const s of segs) {
3026
+ if (s.synthetic) continue;
3027
+ if (s.type === "L") out.push([
3028
+ VERB.LINE,
3029
+ roundN(s.end[0]),
3030
+ roundN(s.end[1])
3031
+ ]);
3032
+ else if (s.type === "Q") out.push([
3033
+ VERB.QUAD,
3034
+ roundN(s.ctrl[0]),
3035
+ roundN(s.ctrl[1]),
3036
+ roundN(s.end[0]),
3037
+ roundN(s.end[1])
3038
+ ]);
3039
+ else out.push([
3040
+ VERB.CUBIC,
3041
+ roundN(s.c1[0]),
3042
+ roundN(s.c1[1]),
3043
+ roundN(s.c2[0]),
3044
+ roundN(s.c2[1]),
3045
+ roundN(s.end[0]),
3046
+ roundN(s.end[1])
3047
+ ]);
3048
+ }
3049
+ out.push([VERB.CLOSE]);
3050
+ return out;
3051
+ }
3052
+ function reverseClosedContourKeepStart(contourCmds, explicitCloseWanted, VERB) {
3053
+ const { start, segs } = contourToSegments(contourCmds, VERB);
3054
+ const reversed = segs.slice().reverse().map((s) => {
3055
+ if (s.type === "L") return {
3056
+ type: "L",
3057
+ start: s.end,
3058
+ end: s.start,
3059
+ synthetic: false
3060
+ };
3061
+ if (s.type === "Q") return {
3062
+ type: "Q",
3063
+ start: s.end,
3064
+ ctrl: s.ctrl,
3065
+ end: s.start,
3066
+ synthetic: false
3067
+ };
3068
+ return {
3069
+ type: "C",
3070
+ start: s.end,
3071
+ c1: s.c2,
3072
+ c2: s.c1,
3073
+ end: s.start,
3074
+ synthetic: false
3075
+ };
3076
+ });
3077
+ applyClosePolicy(reversed, start, explicitCloseWanted);
3078
+ return segmentsToContourCmds(start, reversed, VERB);
3079
+ }
3080
+ function rotateClosedContourToStart(contourCmds, desiredStart, explicitCloseWanted, VERB) {
3081
+ const { segs } = contourToSegments(contourCmds, VERB);
3082
+ let idx = -1;
3083
+ for (let i = 0; i < segs.length; i++) if (eqPt(segs[i].start, desiredStart)) {
3084
+ idx = i;
3085
+ break;
3086
+ }
3087
+ if (idx === -1) {
3088
+ for (let i = 0; i < segs.length; i++) if (eqPt(segs[i].end, desiredStart)) {
3089
+ idx = (i + 1) % segs.length;
3090
+ break;
3091
+ }
3092
+ }
3093
+ if (idx === -1) return [...contourCmds];
3094
+ const rotated = segs.slice(idx).concat(segs.slice(0, idx));
3095
+ applyClosePolicy(rotated, desiredStart, explicitCloseWanted);
3096
+ return segmentsToContourCmds(desiredStart, rotated, VERB);
3097
+ }
3098
+ function mergeMoves(aMoves, bMoves) {
3099
+ const out = [];
3100
+ const pushUnique = (pt) => {
3101
+ for (const existing of out) if (eqPt(existing, pt)) return;
3102
+ out.push(pt);
3103
+ };
3104
+ for (const m of aMoves || []) pushUnique(normPt(m));
3105
+ for (const m of bMoves || []) pushUnique(normPt(m));
3106
+ return out;
3107
+ }
3108
+ function bestStartMinYMinX(contourCmds, VERB) {
3109
+ let best = null;
3110
+ for (const cmd of contourCmds) {
3111
+ const v = cmd[0];
3112
+ const add = (x, y) => {
3113
+ const p = [x, y];
3114
+ if (!best) {
3115
+ best = p;
3116
+ return;
3117
+ }
3118
+ if (p[1] < best[1] - 1e-9) best = p;
3119
+ else if (Math.abs(p[1] - best[1]) < 1e-9 && p[0] < best[0] - 1e-9) best = p;
3120
+ };
3121
+ if (v === VERB.MOVE) add(cmd[1], cmd[2]);
3122
+ else if (v === VERB.LINE) add(cmd[1], cmd[2]);
3123
+ else if (v === VERB.QUAD) add(cmd[3], cmd[4]);
3124
+ else if (v === VERB.CUBIC) add(cmd[5], cmd[6]);
3125
+ }
3126
+ return best;
3127
+ }
3128
+ /**
3129
+ * Convert contour commands to a polyline by sampling curves.
3130
+ * Used for ray-casting containment tests.
3131
+ */
3132
+ function contourToPolyline(contourCmds, V, steps = 8) {
3133
+ let cx = 0, cy = 0;
3134
+ let sx = 0, sy = 0;
3135
+ const pts = [];
3136
+ for (const cmd of contourCmds) {
3137
+ const v = cmd[0];
3138
+ if (v === V.MOVE) {
3139
+ cx = cmd[1];
3140
+ cy = cmd[2];
3141
+ sx = cx;
3142
+ sy = cy;
3143
+ pts.push([cx, cy]);
3144
+ } else if (v === V.LINE) {
3145
+ cx = cmd[1];
3146
+ cy = cmd[2];
3147
+ pts.push([cx, cy]);
3148
+ } else if (v === V.QUAD) {
3149
+ const x0 = cx, y0 = cy;
3150
+ const x1 = cmd[1], y1 = cmd[2];
3151
+ const x2 = cmd[3], y2 = cmd[4];
3152
+ for (let i = 1; i <= steps; i++) {
3153
+ const t = i / steps;
3154
+ const mt = 1 - t;
3155
+ pts.push([mt * mt * x0 + 2 * mt * t * x1 + t * t * x2, mt * mt * y0 + 2 * mt * t * y1 + t * t * y2]);
3156
+ }
3157
+ cx = x2;
3158
+ cy = y2;
3159
+ } else if (v === V.CUBIC) {
3160
+ const x0 = cx, y0 = cy;
3161
+ const x1 = cmd[1], y1 = cmd[2];
3162
+ const x2 = cmd[3], y2 = cmd[4];
3163
+ const x3 = cmd[5], y3 = cmd[6];
3164
+ for (let i = 1; i <= steps; i++) {
3165
+ const t = i / steps;
3166
+ const mt = 1 - t;
3167
+ pts.push([mt * mt * mt * x0 + 3 * mt * mt * t * x1 + 3 * mt * t * t * x2 + t * t * t * x3, mt * mt * mt * y0 + 3 * mt * mt * t * y1 + 3 * mt * t * t * y2 + t * t * t * y3]);
3168
+ }
3169
+ cx = x3;
3170
+ cy = y3;
3171
+ } else if (v === V.CLOSE) {
3172
+ if (cx !== sx || cy !== sy) pts.push([sx, sy]);
3173
+ cx = sx;
3174
+ cy = sy;
3175
+ }
3176
+ }
3177
+ return pts;
3178
+ }
3179
+ /**
3180
+ * Ray-casting point-in-polygon test.
3181
+ */
3182
+ function pointInPolygon(px, py, poly) {
3183
+ let inside = false;
3184
+ for (let i = 0, j = poly.length - 1; i < poly.length; j = i++) {
3185
+ const [xi, yi] = poly[i];
3186
+ const [xj, yj] = poly[j];
3187
+ if (yi > py !== yj > py && px < (xj - xi) * (py - yi) / (yj - yi) + xi) inside = !inside;
3188
+ }
3189
+ return inside;
3190
+ }
3191
+ /**
3192
+ * Get a representative point on the contour boundary (midpoint of first segment).
3193
+ */
3194
+ function getContourSamplePoint(contourCmds, V) {
3195
+ let cx = 0, cy = 0;
3196
+ for (const cmd of contourCmds) {
3197
+ const v = cmd[0];
3198
+ if (v === V.MOVE) {
3199
+ cx = cmd[1];
3200
+ cy = cmd[2];
3201
+ } else if (v === V.LINE) return [(cx + cmd[1]) / 2, (cy + cmd[2]) / 2];
3202
+ else if (v === V.QUAD) {
3203
+ const t = .5, mt = .5;
3204
+ return [mt * mt * cx + 2 * mt * t * cmd[1] + t * t * cmd[3], mt * mt * cy + 2 * mt * t * cmd[2] + t * t * cmd[4]];
3205
+ } else if (v === V.CUBIC) {
3206
+ const t = .5, mt = .5;
3207
+ return [mt ** 3 * cx + 3 * mt ** 2 * t * cmd[1] + 3 * mt * t ** 2 * cmd[3] + t ** 3 * cmd[5], mt ** 3 * cy + 3 * mt ** 2 * t * cmd[2] + 3 * mt * t ** 2 * cmd[4] + t ** 3 * cmd[6]];
3208
+ }
3209
+ }
3210
+ return null;
3211
+ }
3212
+ /**
3213
+ * Apply containment-based winding fix to contour objects.
3214
+ * Even nesting depth = CCW (outer), odd = CW (hole).
3215
+ */
3216
+ function applyContainmentWinding(contourObjs, V) {
3217
+ const n = contourObjs.length;
3218
+ if (n === 0) return;
3219
+ const polylines = contourObjs.map((obj) => contourToPolyline(obj.cmds, V));
3220
+ const samplePts = contourObjs.map((obj) => getContourSamplePoint(obj.cmds, V));
3221
+ const depths = new Array(n).fill(0);
3222
+ for (let i = 0; i < n; i++) {
3223
+ const pt = samplePts[i];
3224
+ if (!pt) continue;
3225
+ for (let j = 0; j < n; j++) {
3226
+ if (i === j) continue;
3227
+ if (pointInPolygon(pt[0], pt[1], polylines[j])) depths[i] = (depths[i] ?? 0) + 1;
3228
+ }
3229
+ }
3230
+ const ensureOrient = (obj, wantCCW) => {
3231
+ if (wantCCW !== approxSignedAreaFromContourCmds(obj.cmds, V) > 0) obj.cmds = reverseClosedContourKeepStart(obj.cmds, obj.explicitCloseWanted, V);
3232
+ };
3233
+ for (let i = 0; i < n; i++) ensureOrient(contourObjs[i], depths[i] % 2 === 0);
3234
+ }
3235
+ function orientedContours(cmds, V) {
3236
+ const contours = splitContours(cmds, V).map((c) => {
3237
+ const explicitCloseWanted = explicitCloseWantedFromCmds(c, V);
3238
+ const cc = ensureClosed(c, V);
3239
+ const a = approxSignedAreaFromContourCmds(cc, V);
3240
+ return {
3241
+ cmds: cc,
3242
+ absA: Math.abs(a),
3243
+ explicitCloseWanted
3244
+ };
3245
+ });
3246
+ contours.sort((x, y) => y.absA - x.absA);
3247
+ applyContainmentWinding(contours, V);
3248
+ return contours;
3249
+ }
3250
+ function canonicalContourCmds(cmds, recordedMoves, preferMinYMinXStart, V) {
3251
+ const moves = recordedMoves.map((m) => normPt([m[0], m[1]]));
3252
+ const used = new Array(moves.length).fill(false);
3253
+ const contours = orientedContours(cmds, V);
3254
+ for (const obj of contours) {
3255
+ let cc = obj.cmds;
3256
+ for (let i = 0; i < moves.length; i++) {
3257
+ if (used[i]) continue;
3258
+ const target = moves[i];
3259
+ const { segs } = contourToSegments(cc, V);
3260
+ if (segs.some((s) => eqPt(s.start, target) || eqPt(s.end, target))) {
3261
+ used[i] = true;
3262
+ cc = rotateClosedContourToStart(cc, target, obj.explicitCloseWanted, V);
3263
+ break;
3264
+ }
3265
+ }
3266
+ if (preferMinYMinXStart) {
3267
+ const best = bestStartMinYMinX(cc, V);
3268
+ if (best) cc = rotateClosedContourToStart(cc, best, obj.explicitCloseWanted, V);
3269
+ }
3270
+ obj.cmds = cc;
3271
+ }
3272
+ return contours.flatMap((x) => x.cmds);
3273
+ }
3274
+ function cmdsToPathData(cmds, V) {
3275
+ const parts = [];
3276
+ for (const cmd of cmds) {
3277
+ const v = cmd[0];
3278
+ if (v === V.MOVE) parts.push(`M${roundN(cmd[1])} ${roundN(cmd[2])}`);
3279
+ else if (v === V.LINE) parts.push(`L${roundN(cmd[1])} ${roundN(cmd[2])}`);
3280
+ else if (v === V.QUAD) parts.push(`Q${roundN(cmd[1])} ${roundN(cmd[2])} ${roundN(cmd[3])} ${roundN(cmd[4])}`);
3281
+ else if (v === V.CUBIC) parts.push(`C${roundN(cmd[1])} ${roundN(cmd[2])} ${roundN(cmd[3])} ${roundN(cmd[4])} ${roundN(cmd[5])} ${roundN(cmd[6])}`);
3282
+ else if (v === V.CLOSE) parts.push("Z");
3283
+ }
3284
+ return parts.join(" ");
3285
+ }
3286
+ function contoursArea(cmds, V) {
3287
+ let total = 0;
3288
+ for (const c of splitContours(cmds, V)) total += Math.abs(approxSignedAreaFromContourCmds(ensureClosed(c, V), V));
3289
+ return total;
3290
+ }
3291
+ //#endregion
3292
+ //#region src/build/core/flatten/pathops.ts
3293
+ var PathOpsError = class extends Error {};
3294
+ function createPathOps(PathKit) {
3295
+ const V = verbMap(PathKit);
3296
+ const FILL = fillTypes(PathKit);
3297
+ const Caps = PathKit.StrokeCap ?? {};
3298
+ const Joins = PathKit.StrokeJoin ?? {};
3299
+ const Ops = PathKit.PathOp ?? {};
3300
+ const LINE_CAP = {
3301
+ butt: Caps.BUTT ?? 0,
3302
+ round: Caps.ROUND ?? 1,
3303
+ square: Caps.SQUARE ?? 2
3304
+ };
3305
+ const LINE_JOIN = {
3306
+ miter: Joins.MITER ?? 0,
3307
+ round: Joins.ROUND ?? 1,
3308
+ bevel: Joins.BEVEL ?? 2
3309
+ };
3310
+ const OP_UNION = Ops.UNION ?? 0;
3311
+ const OP_INTERSECT = Ops.INTERSECT ?? 1;
3312
+ function fillTypeFor(fillRule) {
3313
+ if (fillRule === "nonzero") return FILL.WINDING;
3314
+ if (fillRule === "evenodd") return FILL.EVENODD;
3315
+ throw new Error(`Invalid fill rule: ${fillRule}`);
3316
+ }
3317
+ function dispose(h) {
3318
+ h.p.delete?.();
3319
+ }
3320
+ function fromCommands(cmds, fillRule) {
3321
+ const p = PathKit.NewPath();
3322
+ p.setFillType(fillTypeFor(fillRule));
3323
+ const moves = [];
3324
+ for (const [cmd, args] of cmds) switch (cmd) {
3325
+ case "M":
3326
+ p.moveTo(args[0], args[1]);
3327
+ moves.push(normPt([args[0], args[1]]));
3328
+ break;
3329
+ case "L":
3330
+ p.lineTo(args[0], args[1]);
3331
+ break;
3332
+ case "Q":
3333
+ p.quadTo(args[0], args[1], args[2], args[3]);
3334
+ break;
3335
+ case "C":
3336
+ p.cubicTo(args[0], args[1], args[2], args[3], args[4], args[5]);
3337
+ break;
3338
+ case "Z":
3339
+ p.close();
3340
+ break;
3341
+ default:
3342
+ p.delete?.();
3343
+ throw new Error(`No mapping to Skia for "${cmd} ${args}"`);
3344
+ }
3345
+ return {
3346
+ p,
3347
+ moves
3348
+ };
3349
+ }
3350
+ function normalizedCmds(h) {
3351
+ const p2 = PathKit.FromSVGString(h.p.toSVGString());
3352
+ const cmds = p2 ? p2.toCmds() : [];
3353
+ p2?.delete?.();
3354
+ return cmds;
3355
+ }
3356
+ function simplifyToWinding(h) {
3357
+ try {
3358
+ h.p.simplify();
3359
+ const p2 = PathKit.FromSVGString(h.p.toSVGString());
3360
+ if (p2) {
3361
+ h.p.delete?.();
3362
+ h.p = p2;
3363
+ }
3364
+ } catch {}
3365
+ h.p.setFillType(FILL.WINDING);
3366
+ }
3367
+ function toCommands(h) {
3368
+ const cmds = canonicalContourCmds(normalizedCmds(h), h.moves, h.moves.length === 0, V);
3369
+ const out = [];
3370
+ for (const cmd of cmds) {
3371
+ const v = cmd[0];
3372
+ if (v === V.MOVE) out.push(["M", [cmd[1], cmd[2]]]);
3373
+ else if (v === V.LINE) out.push(["L", [cmd[1], cmd[2]]]);
3374
+ else if (v === V.QUAD) out.push(["Q", [
3375
+ cmd[1],
3376
+ cmd[2],
3377
+ cmd[3],
3378
+ cmd[4]
3379
+ ]]);
3380
+ else if (v === V.CUBIC) out.push(["C", [
3381
+ cmd[1],
3382
+ cmd[2],
3383
+ cmd[3],
3384
+ cmd[4],
3385
+ cmd[5],
3386
+ cmd[6]
3387
+ ]]);
3388
+ else if (v === V.CLOSE) out.push(["Z", []]);
3389
+ else throw new Error(`Unexpected verb in cmds: ${v}`);
3390
+ }
3391
+ return out;
3392
+ }
3393
+ function combine(a, b, op) {
3394
+ try {
3395
+ const out = PathKit.MakeFromOp(a.p, b.p, op);
3396
+ if (!out) return null;
3397
+ return {
3398
+ p: out,
3399
+ moves: mergeMoves(a.moves, b.moves)
3400
+ };
3401
+ } catch {
3402
+ return null;
3403
+ }
3404
+ }
3405
+ function doPathop(op, cmdSeqs, fillRules) {
3406
+ if (!cmdSeqs.length) return [];
3407
+ let skPath = fromCommands(cmdSeqs[0], fillRules[0]);
3408
+ try {
3409
+ for (let i = 1; i < cmdSeqs.length; i++) {
3410
+ const skPath2 = fromCommands(cmdSeqs[i], fillRules[i]);
3411
+ const merged = combine(skPath, skPath2, op);
3412
+ dispose(skPath2);
3413
+ if (merged === null) throw new PathOpsError("operation did not succeed");
3414
+ dispose(skPath);
3415
+ skPath = merged;
3416
+ }
3417
+ simplifyToWinding(skPath);
3418
+ return toCommands(skPath);
3419
+ } finally {
3420
+ dispose(skPath);
3421
+ }
3422
+ }
3423
+ return {
3424
+ union(cmdSeqs, fillRules) {
3425
+ return doPathop(OP_UNION, cmdSeqs, fillRules);
3426
+ },
3427
+ intersection(cmdSeqs, fillRules) {
3428
+ return doPathop(OP_INTERSECT, cmdSeqs, fillRules);
3429
+ },
3430
+ removeOverlaps(cmds, fillRule) {
3431
+ const h = fromCommands(cmds, fillRule);
3432
+ try {
3433
+ simplifyToWinding(h);
3434
+ return toCommands(h);
3435
+ } finally {
3436
+ dispose(h);
3437
+ }
3438
+ },
3439
+ transformCmds(cmds, affine) {
3440
+ const h = fromCommands(cmds, "nonzero");
3441
+ const { a, b, c, d, e, f } = affine;
3442
+ const p = PathKit.NewPath(h.p);
3443
+ p.transform(a, c, e, b, d, f, 0, 0, 1);
3444
+ const transformed = {
3445
+ p,
3446
+ moves: h.moves.map(([x, y]) => normPt([a * x + c * y + e, b * x + d * y + f]))
3447
+ };
3448
+ try {
3449
+ return toCommands(transformed);
3450
+ } finally {
3451
+ dispose(h);
3452
+ dispose(transformed);
3453
+ }
3454
+ },
3455
+ strokeCmds(cmds, svgLinecap, svgLinejoin, strokeWidth, strokeMiterlimit, _tolerance, dashArray = [], dashOffset = 0) {
3456
+ const cap = LINE_CAP[svgLinecap];
3457
+ if (cap === void 0) throw new Error(`Unsupported cap ${svgLinecap}`);
3458
+ const join = LINE_JOIN[svgLinejoin];
3459
+ if (join === void 0) throw new Error(`Unsupported join ${svgLinejoin}`);
3460
+ const h = fromCommands(cmds, "nonzero");
3461
+ const work = PathKit.NewPath(h.p);
3462
+ let strokedPath;
3463
+ try {
3464
+ if (dashArray.length === 2 && typeof work.dash === "function") work.dash(Number(dashArray[0]), Number(dashArray[1]), dashOffset || 0);
3465
+ let stroked = work.stroke({
3466
+ width: strokeWidth,
3467
+ cap,
3468
+ join,
3469
+ miter_limit: strokeMiterlimit
3470
+ });
3471
+ if (!stroked || typeof stroked.toCmds !== "function") stroked = work;
3472
+ if (stroked !== work) work.delete?.();
3473
+ strokedPath = stroked;
3474
+ } catch {
3475
+ strokedPath = work;
3476
+ }
3477
+ const result = {
3478
+ p: strokedPath,
3479
+ moves: []
3480
+ };
3481
+ try {
3482
+ simplifyToWinding(result);
3483
+ return toCommands(result);
3484
+ } finally {
3485
+ dispose(h);
3486
+ dispose(result);
3487
+ }
3488
+ },
3489
+ pathArea(cmds, fillRule) {
3490
+ const h = fromCommands(cmds, fillRule);
3491
+ try {
3492
+ simplifyToWinding(h);
3493
+ try {
3494
+ return contoursArea(normalizedCmds(h), V);
3495
+ } catch {
3496
+ return 0;
3497
+ }
3498
+ } finally {
3499
+ dispose(h);
3500
+ }
3501
+ }
3502
+ };
3503
+ }
3504
+ //#endregion
3505
+ //#region src/build/core/flatten/stages/defs.ts
3506
+ function addToDefs(defs, newEl) {
3507
+ const newId = newEl.attrib.get("id");
3508
+ if (newId === void 0) return;
3509
+ let insertAt = defs.children.length;
3510
+ for (let i = 0; i < defs.children.length; i++) if (newId < (defs.children[i].attrib.get("id") ?? "")) {
3511
+ insertAt = i;
3512
+ break;
3513
+ }
3514
+ defs.insert(insertAt, newEl);
3515
+ }
3516
+ function removeOrphanedGradients(doc) {
3517
+ const usedGradientIds = /* @__PURE__ */ new Set();
3518
+ for (const [, shapes] of doc.shapeEntries()) for (const shape of shapes) {
3519
+ const fill = shapeStr(shape, "fill");
3520
+ if (!fill.startsWith("url(")) continue;
3521
+ let el;
3522
+ try {
3523
+ el = doc.resolveUrl(fill, "*");
3524
+ } catch {
3525
+ continue;
3526
+ }
3527
+ if (!isGradient(el.tag)) continue;
3528
+ const id = el.attrib.get("id");
3529
+ if (id !== void 0) usedGradientIds.add(id);
3530
+ }
3531
+ for (const grad of findAll(doc.root, (e) => isGradient(e.tag))) if (!usedGradientIds.has(grad.attrib.get("id") ?? "")) grad.detach();
3532
+ doc.invalidateShapes();
3533
+ }
3534
+ //#endregion
3535
+ //#region src/build/core/flatten/stages/stroke.ts
3536
+ function strokeToPaths(doc, shape) {
3537
+ const stroke = cloneShape(asPath(shape));
3538
+ stroke.fields.d = pathFromCommands(strokeCommands(shape, doc.tolerance, doc.ops)).fields.d;
3539
+ stroke.fields.fill_rule = "nonzero";
3540
+ stroke.fields.clip_rule = "nonzero";
3541
+ stroke.fields.opacity = stroke.fields.opacity * stroke.fields.stroke_opacity;
3542
+ stroke.fields.fill = stroke.fields.stroke;
3543
+ shape.fields.opacity = shape.fields.opacity * shape.fields.fill_opacity;
3544
+ shape.fields.fill_opacity = 1;
3545
+ stroke.fields.fill_opacity = 1;
3546
+ for (const cleanmeup of [shape, stroke]) resetFields(cleanmeup, (name) => name.startsWith("stroke"));
3547
+ if (!mightPaint(shape, doc.ops)) return [stroke];
3548
+ shape.fields.id = "";
3549
+ stroke.fields.id = "";
3550
+ return [shape, stroke];
3551
+ }
3552
+ //#endregion
3553
+ //#region src/build/core/flatten/stages/collapse.ts
3554
+ function collapse(doc) {
3555
+ doc.syncTree();
3556
+ const toProcess = [...doc.breadthFirst()].reverse();
3557
+ const defs = new XEl(svgTag("defs"));
3558
+ doc.root.insert(0, defs);
3559
+ for (const context of toProcess) {
3560
+ if (context.path.includes("clipPath")) {
3561
+ context.element.detach();
3562
+ continue;
3563
+ }
3564
+ const el = context.element;
3565
+ delAttrs(el, "clip-path", "transform");
3566
+ inheritAttrib(context.attrib, el);
3567
+ if (isShapeTag(el.tag)) {
3568
+ if (el.children.length) throw new Error("Shapes shouldn't have children");
3569
+ const paths = [absoluteShape(asPath(fromElement(el)))];
3570
+ const initialPath = cloneShape(paths[0]);
3571
+ if (shapeStr(paths[0], "stroke") !== "none") paths.splice(0, paths.length, ...strokeToPaths(doc, paths[0]));
3572
+ for (const path of paths) resetFields(path, (name) => name.startsWith("stroke"));
3573
+ if (!context.transform.equals(Affine2D.identity())) paths.forEach((p, i) => {
3574
+ paths[i] = applyTransform(p, context.transform, doc.ops);
3575
+ });
3576
+ if (context.clips.length) for (const p of paths) {
3577
+ const cmds = doc.ops.intersection([shapeCmdSeq(p), ...context.clips.map((c) => shapeCmdSeq(c))], [shapeStr(p, "fill_rule"), ...context.clips.map((c) => shapeStr(c, "clip_rule"))]);
3578
+ p.fields.d = pathFromCommands(cmds).fields.d;
3579
+ p.fields.fill_rule = "nonzero";
3580
+ }
3581
+ if (paths.length !== 1 || !shapesEqual(paths[0], initialPath)) replaceEl(el, paths.map((p) => toElement(p)));
3582
+ } else if (isGradient(el.tag)) {
3583
+ el.detach();
3584
+ addToDefs(defs, el);
3585
+ } else if (isDefs(el.tag)) {
3586
+ for (const childEl of [...el.children]) addToDefs(defs, childEl);
3587
+ el.detach();
3588
+ } else if (isGroup(el.tag)) tryRemoveGroup(el);
3589
+ }
3590
+ delAttrs(doc.root, ...INHERITABLE_ATTRIB);
3591
+ removeOrphanedGradients(doc);
3592
+ for (const unusedEl of [...defs.children]) if (!isGradient(unusedEl.tag)) unusedEl.detach();
3593
+ doc.invalidateShapes();
3594
+ }
3595
+ //#endregion
3596
+ //#region src/build/core/flatten/stages/discard.ts
3597
+ function removeForeignAttrs(el, goodNs) {
3598
+ const attrToRm = [];
3599
+ for (const attr of el.attrib.keys()) if (!goodNs.has(splitNs(attr)[0])) attrToRm.push(attr);
3600
+ delAttrs(el, ...attrToRm);
3601
+ }
3602
+ function removeNonSvgContent(doc) {
3603
+ doc.syncTree();
3604
+ const goodNs = /* @__PURE__ */ new Set([SVG_NS, XLINK_NS]);
3605
+ if (splitNs(doc.root.tag)[0] === "http://www.w3.org/2000/svg") goodNs.add(null);
3606
+ const elToRm = [];
3607
+ for (const el of doc.root.iter()) {
3608
+ if (el === doc.root) continue;
3609
+ if (!goodNs.has(splitNs(el.tag)[0])) {
3610
+ elToRm.push(el);
3611
+ continue;
3612
+ }
3613
+ removeForeignAttrs(el, goodNs);
3614
+ }
3615
+ removeForeignAttrs(doc.root, goodNs);
3616
+ for (const el of elToRm) el.detach();
3617
+ doc.invalidateShapes();
3618
+ }
3619
+ function removeAnonymousSymbols(doc) {
3620
+ doc.syncTree();
3621
+ for (const el of findAll(doc.root, (e) => stripNs(e.tag) === "symbol" && !e.attrib.has("id"))) el.detach();
3622
+ }
3623
+ function removeTitleMetaDesc(doc) {
3624
+ doc.syncTree();
3625
+ const tags = /* @__PURE__ */ new Set([
3626
+ "title",
3627
+ "desc",
3628
+ "metadata",
3629
+ "comment"
3630
+ ]);
3631
+ for (const el of findAll(doc.root, (e) => tags.has(stripNs(e.tag)))) el.detach();
3632
+ }
3633
+ //#endregion
3634
+ //#region src/build/core/flatten/stages/fill-rule.ts
3635
+ function evenoddToNonzeroWinding(doc) {
3636
+ doc.mapShapes((s) => shapeStr(s, "fill_rule") === "evenodd" ? removeOverlaps(s, doc.ops) : s);
3637
+ }
3638
+ //#endregion
3639
+ //#region src/build/core/flatten/stages/nested-svg.ts
3640
+ function rectEquals(a, b) {
3641
+ return a.x === b.x && a.y === b.y && a.w === b.w && a.h === b.h;
3642
+ }
3643
+ function nestedSvgTransform(attrs, parentWidth, parentHeight) {
3644
+ const num = (raw, fallback) => {
3645
+ if (raw === null || raw === void 0 || !raw.trim()) return fallback;
3646
+ const value = parseFloat(raw);
3647
+ return Number.isFinite(value) ? value : fallback;
3648
+ };
3649
+ const x = num(attrs.x, 0);
3650
+ const y = num(attrs.y, 0);
3651
+ const viewport = {
3652
+ x,
3653
+ y,
3654
+ w: num(attrs.width, parentWidth),
3655
+ h: num(attrs.height, parentHeight)
3656
+ };
3657
+ const viewBox = attrs.viewBox === null || attrs.viewBox === void 0 ? viewport : parseViewBox(attrs.viewBox);
3658
+ let transform = rectEquals(viewport, viewBox) ? Affine2D.identity().translate(x, y) : Affine2D.rectToRect(viewBox, viewport, attrs.preserveAspectRatio ?? "xMidYMid");
3659
+ if (attrs.transform !== null && attrs.transform !== void 0) transform = Affine2D.composeLtr([transform, Affine2D.fromString(attrs.transform)]);
3660
+ return {
3661
+ transform,
3662
+ viewport,
3663
+ viewBox
3664
+ };
3665
+ }
3666
+ function viewportRect(x, y, width, height) {
3667
+ return toElement(fromElement(new XEl(svgTag("rect"), [
3668
+ ["x", ntos(x)],
3669
+ ["y", ntos(y)],
3670
+ ["width", ntos(width)],
3671
+ ["height", ntos(height)]
3672
+ ])));
3673
+ }
3674
+ function newId(doc, template) {
3675
+ for (let i = 0; i < 65536; i++) {
3676
+ const candidate = template(i);
3677
+ if (!findAll(doc.root, (el) => el.attrib.get("id") === candidate).length) return candidate;
3678
+ }
3679
+ throw new Error("No free id for nested svg viewport");
3680
+ }
3681
+ function nestedSvgs(root) {
3682
+ const found = [];
3683
+ const frontier = [...root.children];
3684
+ while (frontier.length) {
3685
+ const el = frontier.shift();
3686
+ if (isTag(el, "svg")) found.push(el);
3687
+ else if (el.children.length) frontier.push(...el.children);
3688
+ }
3689
+ return found;
3690
+ }
3691
+ function unnestSvg(doc, svg, parentWidth, parentHeight) {
3692
+ const { transform, viewport, viewBox } = nestedSvgTransform({
3693
+ x: svg.attrib.get("x") ?? null,
3694
+ y: svg.attrib.get("y") ?? null,
3695
+ width: svg.attrib.get("width") ?? null,
3696
+ height: svg.attrib.get("height") ?? null,
3697
+ viewBox: svg.attrib.get("viewBox") ?? null,
3698
+ transform: svg.attrib.get("transform") ?? null,
3699
+ preserveAspectRatio: svg.attrib.get("preserveAspectRatio") ?? null
3700
+ }, parentWidth, parentHeight);
3701
+ const { x, y, w: width, h: height } = viewport;
3702
+ for (const inner of nestedSvgs(svg)) replaceEl(inner, unnestSvg(doc, inner, viewBox.w, viewBox.h));
3703
+ const g = new XEl(svgTag("g"));
3704
+ for (const child of [...svg.children]) g.append(child);
3705
+ if (!transform.equals(Affine2D.identity())) g.attrib.set("transform", transform.toString());
3706
+ const overflow = svg.attrib.get("overflow") ?? "hidden";
3707
+ if (overflow === "visible") return [g];
3708
+ if (overflow !== "hidden") throw new Error(`overflow='${overflow}' is not supported`);
3709
+ const clipId = newId(doc, (i) => `nested-svg-viewport-${i}`);
3710
+ const clipPath = new XEl(svgTag("clipPath"), [["id", clipId]]);
3711
+ clipPath.append(viewportRect(x, y, width, height));
3712
+ const clippedG = new XEl(svgTag("g"), [["clip-path", `url(#${clipId})`]]);
3713
+ clippedG.append(g);
3714
+ return [clipPath, clippedG];
3715
+ }
3716
+ function resolveNestedSvgs(doc) {
3717
+ doc.syncTree();
3718
+ const nested = nestedSvgs(doc.root);
3719
+ if (!nested.length) return;
3720
+ const vbox = doc.viewBox();
3721
+ if (vbox === null) throw new Error("Can't determine root SVG width/height, which is required for resolving nested SVGs");
3722
+ for (const el of nested) replaceEl(el, unnestSvg(doc, el, vbox.w, vbox.h));
3723
+ doc.invalidateShapes();
3724
+ }
3725
+ //#endregion
3726
+ //#region src/build/core/flatten/stages/opacity.ts
3727
+ function normalizeOpacities(doc) {
3728
+ doc.forEachShape((s) => normalizeOpacity(s));
3729
+ }
3730
+ //#endregion
3731
+ //#region src/build/core/flatten/stages/shapes.ts
3732
+ function shapesToPaths(doc) {
3733
+ doc.mapShapes((s) => asPath(s));
3734
+ }
3735
+ function expandShorthandCommands(doc) {
3736
+ doc.mapShapes((s) => s.tag === "path" ? explicitLinesExpandShorthand(s) : s);
3737
+ }
3738
+ function absoluteCoordinates(doc) {
3739
+ doc.mapShapes((s) => absoluteShape(s));
3740
+ }
3741
+ function roundFloats(doc, ndigits) {
3742
+ doc.forEachShape((s) => roundShapeFloats(s, ndigits));
3743
+ }
3744
+ function dropEmptySubpaths(doc) {
3745
+ doc.forEachShape((s) => removeEmptySubpaths(s, doc.ops));
3746
+ }
3747
+ function removeUnpaintedShapes(doc) {
3748
+ doc.syncTree();
3749
+ const remove = [];
3750
+ for (const [el, shapes] of doc.shapeEntries()) if (!mightPaint(shapes[0], doc.ops)) remove.push(el);
3751
+ for (const el of remove) el.detach();
3752
+ doc.invalidateShapes();
3753
+ }
3754
+ //#endregion
3755
+ //#region src/build/core/flatten/stages/styles.ts
3756
+ function applyStyles(el) {
3757
+ const style = el.attrib.get("style") ?? "";
3758
+ el.attrib.delete("style");
3759
+ const parsed = {};
3760
+ parseCssDeclarations(style, parsed);
3761
+ for (const [name, value] of Object.entries(parsed)) if (XML_ATTRIBUTE_NAME.test(name)) el.attrib.set(name, value);
3762
+ }
3763
+ function applyStyleAttributes(doc) {
3764
+ if (doc.hasShapes()) {
3765
+ doc.mapShapes((s) => applyStyleAttribute(s));
3766
+ doc.syncTree();
3767
+ }
3768
+ const styled = [doc.root, ...findAll(doc.root, (e) => e.attrib.has("style"))];
3769
+ for (const el of styled) applyStyles(el);
3770
+ }
3771
+ //#endregion
3772
+ //#region src/build/core/flatten/stages/validate.ts
3773
+ var PATH_ALLOWLIST = [
3774
+ /^\/svg\[0\]$/,
3775
+ /^\/svg\[0\]\/defs\[0\]$/,
3776
+ /^\/svg\[0\]\/defs\[0\]\/(linear|radial)Gradient\[\d+\](\/stop\[\d+\])?$/,
3777
+ /^\/svg\[0\](\/(path|g)\[\d+\])+$/
3778
+ ];
3779
+ function validateFlattened(doc) {
3780
+ doc.syncTree();
3781
+ const errors = [];
3782
+ const badPaths = /* @__PURE__ */ new Set();
3783
+ const pathsRequired = /* @__PURE__ */ new Set(["/svg[0]", "/svg[0]/defs[0]"]);
3784
+ const ids = /* @__PURE__ */ new Map();
3785
+ for (const context of doc.breadthFirst()) {
3786
+ if ([...badPaths].some((bp) => context.path.startsWith(bp))) continue;
3787
+ if (!PATH_ALLOWLIST.some((pat) => pat.test(context.path))) {
3788
+ errors.push(`Unsupported element <${stripNs(context.element.tag)}> at ${context.path}`);
3789
+ badPaths.add(context.path);
3790
+ continue;
3791
+ }
3792
+ pathsRequired.delete(context.path);
3793
+ const elId = context.element.attrib.get("id");
3794
+ if (elId !== void 0) {
3795
+ if (ids.has(elId)) errors.push(`Duplicate id "${elId}" at ${context.path}, first seen at ${ids.get(elId)}`);
3796
+ ids.set(elId, context.path);
3797
+ }
3798
+ }
3799
+ for (const path of pathsRequired) errors.push(`Missing element ${path}`);
3800
+ return errors;
3801
+ }
3802
+ //#endregion
3803
+ //#region src/build/core/flatten/stages.ts
3804
+ function flattenDocument(doc, ndigits = 3) {
3805
+ doc.syncTree();
3806
+ removeNonSvgContent(doc);
3807
+ removeAnonymousSymbols(doc);
3808
+ removeTitleMetaDesc(doc);
3809
+ applyStyleAttributes(doc);
3810
+ resolveNestedSvgs(doc);
3811
+ shapesToPaths(doc);
3812
+ expandShorthandCommands(doc);
3813
+ resolveUse(doc);
3814
+ collapse(doc);
3815
+ evenoddToNonzeroWinding(doc);
3816
+ normalizeOpacities(doc);
3817
+ absoluteCoordinates(doc);
3818
+ roundFloats(doc, ndigits);
3819
+ dropEmptySubpaths(doc);
3820
+ removeUnpaintedShapes(doc);
3821
+ const violations = validateFlattened(doc);
3822
+ if (violations.length) throw new Error(violations.join("; "));
3823
+ }
3824
+ //#endregion
3825
+ //#region src/build/core/flatten/index.ts
3826
+ function flattenSvg(svgContent, pathkit) {
3827
+ const doc = SvgDocument.parse(svgContent, createPathOps(pathkit));
3828
+ flattenDocument(doc);
3829
+ return doc.serialize();
3830
+ }
3831
+ //#endregion
3832
+ //#region src/build/core/glyph/merge.ts
3833
+ /**
3834
+ * Concatenate multiple SVG path `d` strings into a single compound path.
3835
+ * This preserves the exact geometry of each path (no boolean operations)
3836
+ * while combining them into one glyph. Under nonzero winding, this renders
3837
+ * identically to drawing each path separately with the same color.
3838
+ */
3839
+ function concatPathDs(ds) {
3840
+ if (ds.length === 0) return null;
3841
+ if (ds.length === 1) return ds[0];
3842
+ return ds.join(" ");
3843
+ }
3844
+ /**
3845
+ * Merge consecutive same-color paths into single compound paths.
3846
+ * Preserves z-order: only merges runs of adjacent paths with identical fill color.
3847
+ */
3848
+ function mergeSameColorPaths(paths, logger) {
3849
+ if (paths.length <= 1) return paths;
3850
+ const result = [];
3851
+ let i = 0;
3852
+ while (i < paths.length) {
3853
+ const fill = paths[i].fill;
3854
+ const knockout = paths[i].knockout;
3855
+ let j = i + 1;
3856
+ if (!paths[i].noMerge) while (j < paths.length && paths[j].fill === fill && paths[j].knockout === knockout && !paths[j].noMerge) j++;
3857
+ if (j - i === 1) result.push(paths[i]);
3858
+ else {
3859
+ const group = paths.slice(i, j);
3860
+ const merged = concatPathDs(group.map((p) => p.d));
3861
+ if (merged) {
3862
+ logger?.info(` ⊕ Merged ${group.length} same-color paths (fill=${fill})`);
3863
+ result.push(knockout ? {
3864
+ d: merged,
3865
+ fill,
3866
+ knockout
3867
+ } : {
3868
+ d: merged,
3869
+ fill
3870
+ });
3871
+ } else result.push(...group);
3872
+ }
3873
+ i = j;
3874
+ }
3875
+ return result;
3876
+ }
3877
+ //#endregion
3878
+ //#region src/build/core/glyph/validate.ts
3879
+ function validateSvg(content) {
3880
+ if (XML_MASK.test(content)) return {
3881
+ valid: false,
3882
+ reason: "<mask> is not supported yet"
3883
+ };
3884
+ if (XML_FILTER.test(content)) return {
3885
+ valid: false,
3886
+ reason: "<filter> is not supported yet"
3887
+ };
3888
+ if (XML_IMAGE.test(content)) return {
3889
+ valid: false,
3890
+ reason: "embedded raster <image> is not supported yet"
3891
+ };
3892
+ const unconvertible = XML_UNCONVERTIBLE.exec(content);
3893
+ if (unconvertible) return {
3894
+ valid: false,
3895
+ reason: `<${unconvertible[1]}> is not supported yet`
3896
+ };
3897
+ return { valid: true };
3898
+ }
3899
+ function stripPxUnits(content) {
3900
+ return content.replace(PX_ATTRIBUTE_VALUE, "=$1$2$1").replace(PX_STYLE_VALUE, "$1");
3901
+ }
3902
+ function preprocessSvg(content) {
3903
+ const withUnits = stripPxUnits(content);
3904
+ if (XML_XMLNS.test(withUnits)) return withUnits;
3905
+ return withUnits.replace(SVG_OPEN_TAG, `<svg xmlns="${SVG_NS}"`);
3906
+ }
3907
+ //#endregion
3908
+ //#region src/build/core/pathkit/evenodd.ts
3909
+ /**
3910
+ * Convert a path `d` string with evenodd fill semantics to an equivalent
3911
+ * path that renders identically under nonzero winding.
3912
+ *
3913
+ * Steps:
3914
+ * 1. Parse via PathKit, set fill type to EVENODD, simplify (resolve topology)
3915
+ * 2. Split into contours, compute containment depths
3916
+ * 3. Fix winding: even depth = CCW (outer), odd depth = CW (hole)
3917
+ * 4. Reconstruct d string
3918
+ */
3919
+ function convertEvenoddToWinding(PathKit, d) {
3920
+ const V = verbMap(PathKit);
3921
+ const p = PathKit.FromSVGString(d);
3922
+ if (!p) return d;
3923
+ p.setFillType(fillTypes(PathKit).EVENODD);
3924
+ p.simplify();
3925
+ const simplified = p.toSVGString();
3926
+ p.delete?.();
3927
+ const p2 = PathKit.FromSVGString(simplified);
3928
+ if (!p2) return simplified;
3929
+ const cmds = p2.toCmds();
3930
+ p2.delete?.();
3931
+ if (cmds.length === 0) return simplified;
3932
+ return cmdsToPathData(orientedContours(cmds, V).flatMap((x) => x.cmds), V);
3933
+ }
3934
+ //#endregion
3935
+ //#region src/build/core/prepare.ts
3936
+ /**
3937
+ * Fill values that reference a paint server (`url(#…)`) cannot be expressed
3938
+ * as font glyph layers — a solid-color glyph has no gradient or pattern stop
3939
+ * representation. Reject rather than silently painting black.
3940
+ */
3941
+ function assertNoPaintServers(paths, fileLabel) {
3942
+ for (const p of paths) if (p.fill && p.fill.includes("url(")) throw new Error(`${fileLabel} uses a paint-server fill ("${p.fill}"); gradients and patterns cannot be compiled to font glyphs`);
3943
+ }
3944
+ /**
3945
+ * Shared per-file SVG prep: validate → preprocess → flatten → parse →
3946
+ * evenodd convert → same-color merge. Returns null for unsupported SVGs
3947
+ * (logged as a warning); throws for inputs that cannot be converted.
3948
+ */
3949
+ async function prepareSvgLayers(opts) {
3950
+ const { svg, fileLabel, pathkit, logger } = opts;
3951
+ const validation = validateSvg(svg);
3952
+ if (validation.valid === false) {
3953
+ logger?.warn(`${fileLabel} skipped: ${validation.reason}`);
3954
+ return null;
3955
+ }
3956
+ const preprocessed = preprocessSvg(svg);
3957
+ let flattenedSvg;
3958
+ try {
3959
+ flattenedSvg = flattenSvg(preprocessed, pathkit);
3960
+ } catch (err) {
3961
+ throw new Error(`${fileLabel} failed to flatten: ${err instanceof Error ? err.message : String(err)}`, { cause: err });
3962
+ }
3963
+ const parsed = parseFlattenedSvg(flattenedSvg, {
3964
+ onSanitize: (original) => {
3965
+ logger?.info(` ⚠ ${fileLabel} sanitized path: path was missing initial moveto (prepended M from endpoint)`);
3966
+ logger?.info(` Original: ${original.slice(0, 80)}…`);
3967
+ },
3968
+ onMissingViewBox: (assumed) => {
3969
+ logger?.warn(`${fileLabel} has no viewBox; assuming [${assumed.join(" ")}]`);
3970
+ }
3971
+ });
3972
+ assertNoPaintServers(parsed.paths, fileLabel);
3973
+ for (const p of parsed.paths) if (p.fillRule === "evenodd") {
3974
+ logger?.info(` ↻ ${fileLabel} converting evenodd path to nonzero winding`);
3975
+ p.d = convertEvenoddToWinding(pathkit, p.d);
3976
+ delete p.fillRule;
3977
+ p.noMerge = true;
3978
+ }
3979
+ const mergedPaths = mergeSameColorPaths(parsed.paths, logger);
3980
+ return {
3981
+ viewBox: parsed.viewBox,
3982
+ paths: mergedPaths
3983
+ };
3984
+ }
3985
+ //#endregion
3986
+ //#region src/build/core/prepareIcon.ts
3987
+ function collectingLogger(logs) {
3988
+ const push = (level) => (msg) => {
3989
+ logs.push([level, msg]);
3990
+ };
3991
+ return {
3992
+ start: () => {},
3993
+ update: () => {},
3994
+ succeed: () => {},
3995
+ fail: push("fail"),
3996
+ info: push("info"),
3997
+ warn: push("warn")
3998
+ };
3999
+ }
4000
+ async function prepareIcon(task, pathkit) {
4001
+ const logs = [];
4002
+ const logger = collectingLogger(logs);
4003
+ const iconName = task.file.replace(/\.svg$/i, "");
4004
+ const fileLabel = `[${task.fontFamily}: ${task.file}]`;
4005
+ const result = {
4006
+ file: task.file,
4007
+ iconName,
4008
+ adv: 0,
4009
+ layers: [],
4010
+ logs,
4011
+ error: null
4012
+ };
4013
+ logger.info(`Processing ${task.file}`);
4014
+ let prepared;
4015
+ try {
4016
+ prepared = await prepareSvgLayers({
4017
+ svg: task.svg,
4018
+ fileLabel,
4019
+ pathkit,
4020
+ logger
4021
+ });
4022
+ } catch (err) {
4023
+ result.error = err instanceof Error ? err.message : String(err);
4024
+ return result;
4025
+ }
4026
+ if (!prepared) return result;
4027
+ const { vx, vy, scale, xOff, yOff, adv } = computePlacement({
4028
+ upm: task.upm,
4029
+ safeZone: task.safeZone,
4030
+ viewBox: prepared.viewBox
4031
+ });
4032
+ result.adv = adv;
4033
+ for (const p of prepared.paths) {
4034
+ if (shouldSkipPath(p.d, p.fill)) continue;
4035
+ const placed = transformPathForFont(pathkit, p.d, {
4036
+ vx,
4037
+ vy,
4038
+ scale,
4039
+ xOff,
4040
+ yOff,
4041
+ upm: task.upm
4042
+ });
4043
+ result.layers.push({
4044
+ fill: p.fill,
4045
+ d: toQuadraticPath(placed, task.upm * QUADRATIC_ERROR_BOUND_EM)
4046
+ });
4047
+ }
4048
+ if (result.layers.length === 0) logger.warn(`${fileLabel} produced no glyphs: nothing in it paints`);
4049
+ return result;
4050
+ }
4051
+ //#endregion
4052
+ //#region src/build/core/runFontPipeline.ts
4053
+ var SKIP_REASONS = /skipped:|produced no glyphs/;
4054
+ /**
4055
+ * Run the font pipeline: prepared SVG layers → placement → glyphs → TTF +
4056
+ * glyphmap (+ WOFF2 when `config.web`). The PathKit WASM instance is shared
4057
+ * across calls.
4058
+ */
4059
+ async function runFontPipeline(config, paths, inputs, options) {
4060
+ const startTime = Date.now();
4061
+ const logger = options?.logger;
4062
+ logger?.update(`Building "${config.fontFamily}"…`);
4063
+ ensureDir(paths.outputDir);
4064
+ const inputHash = options?.inputHash;
4065
+ const family = inputHash ? buildFontFamily(config.fontFamily, inputHash) : config.fontFamily;
4066
+ const glyphMap = {
4067
+ m: {
4068
+ f: family,
4069
+ u: config.upm,
4070
+ z: config.safeZone,
4071
+ s: config.startUnicode,
4072
+ ...config.web ? { w: true } : {}
4073
+ },
4074
+ i: {}
4075
+ };
4076
+ let currentUnicode = config.startUnicode;
4077
+ const codepointToIcon = /* @__PURE__ */ new Map();
4078
+ const allGlyphs = [];
4079
+ const failed = [];
4080
+ const skipped = [];
4081
+ const pathkit = await loadPathKit();
4082
+ for (const input of inputs) {
4083
+ const result = await prepareIcon({
4084
+ file: input.file,
4085
+ svg: input.svg,
4086
+ fontFamily: config.fontFamily,
4087
+ upm: config.upm,
4088
+ safeZone: config.safeZone
4089
+ }, pathkit);
4090
+ for (const [level, msg] of result.logs) logger?.[level](msg);
4091
+ if (result.error !== null) {
4092
+ logger?.fail(result.error);
4093
+ failed.push(result.file);
4094
+ continue;
4095
+ }
4096
+ if (result.layers.length === 0) {
4097
+ skipped.push({
4098
+ file: result.file,
4099
+ reason: result.logs.filter(([, msg]) => SKIP_REASONS.test(msg)).map(([, msg]) => msg).at(-1) ?? "no paintable layers"
4100
+ });
4101
+ continue;
4102
+ }
4103
+ const layers = [];
4104
+ for (const layer of result.layers) {
4105
+ const cp = currentUnicode++;
4106
+ codepointToIcon.set(cp, result.iconName);
4107
+ allGlyphs.push({
4108
+ codepoint: cp,
4109
+ advanceWidth: result.adv,
4110
+ d: layer.d
4111
+ });
4112
+ layers.push([cp, layer.fill || "black"]);
4113
+ }
4114
+ glyphMap.i[result.iconName] = [result.adv, layers];
4115
+ }
4116
+ if (failed.length) throw new Error(`${failed.length} of ${inputs.length} icons in [${config.fontFamily}] could not be converted: ${failed.join(", ")}`);
4117
+ logger?.info(`Compiling TTF…`);
4118
+ const ttfPath = path.join(paths.outputDir, `${family}.ttf`);
4119
+ let ttfBuffer;
4120
+ try {
4121
+ ttfBuffer = await compileTtfFromGlyphs({
4122
+ glyphs: allGlyphs,
4123
+ outTtfPath: ttfPath,
4124
+ fontName: family,
4125
+ upm: config.upm,
4126
+ ascent: config.upm,
4127
+ descent: 0
4128
+ });
4129
+ } catch (err) {
4130
+ parseCompileTtfFromGlyphsError(err, codepointToIcon);
4131
+ }
4132
+ let woff2Path;
4133
+ if (config.web && ttfBuffer) {
4134
+ logger?.info(`Compiling WOFF2…`);
4135
+ woff2Path = path.join(paths.outputDir, `${family}.woff2`);
4136
+ writeFileAtomic(woff2Path, await compileWoff2FromTtf(ttfBuffer));
4137
+ }
4138
+ const glyphmapPath = path.join(paths.outputDir, `${family}.glyphmap.json`);
4139
+ if (inputHash) glyphMap.m.h = inputHash;
4140
+ writeFileAtomic(glyphmapPath, JSON.stringify(glyphMap));
4141
+ const iconCount = Object.keys(glyphMap.i).length;
4142
+ const elapsed = Date.now() - startTime;
4143
+ const products = woff2Path ? `${family}.ttf + ${family}.woff2` : `${family}.ttf`;
4144
+ logger?.succeed(`Built ${products} [${iconCount} icon${iconCount === 1 ? "" : "s"} in ${elapsed}ms]`);
4145
+ return {
4146
+ ttfPath,
4147
+ glyphmapPath,
4148
+ family,
4149
+ woff2Path,
4150
+ glyphMap,
4151
+ icons: Object.keys(glyphMap.i),
4152
+ skipped
4153
+ };
4154
+ }
4155
+ //#endregion
4156
+ //#region src/build/index.ts
4157
+ var DEFAULT_UPM = 1024;
4158
+ var DEFAULT_SAFE_ZONE = 1020;
4159
+ var DEFAULT_START_UNICODE = 59648;
4160
+ function generatorVersion() {
4161
+ try {
4162
+ const packageJsonPath = createRequire(import.meta.url).resolve("@octane-xplat/nano-icons/package.json");
4163
+ return `@octane-xplat/nano-icons@${JSON.parse(fs$1.readFileSync(packageJsonPath, "utf8")).version ?? "unknown"}`;
4164
+ } catch {
4165
+ return "@octane-xplat/nano-icons@unknown";
4166
+ }
4167
+ }
4168
+ function parseStartUnicode(value) {
4169
+ if (value === void 0) return DEFAULT_START_UNICODE;
4170
+ if (typeof value === "number") return value;
4171
+ const parsed = value.startsWith("0x") ? parseInt(value, 16) : parseInt(value, 10);
4172
+ if (!Number.isInteger(parsed) || parsed < 0 || parsed > 1114111) throw new RangeError(`Invalid startUnicode "${value}"`);
4173
+ return parsed;
4174
+ }
4175
+ /** sha256 over sorted inputs + pipeline settings + generator version. */
4176
+ function fingerprintInputs(inputs, settings) {
4177
+ const hash = crypto.createHash("sha256");
4178
+ for (const input of [...inputs].sort((a, b) => a.file.localeCompare(b.file))) {
4179
+ hash.update(input.file);
4180
+ hash.update(input.svg);
4181
+ }
4182
+ hash.update(JSON.stringify(settings));
4183
+ return hash.digest("hex");
4184
+ }
4185
+ /**
4186
+ * Build a layered icon font. Returns the emitted artifacts; throws if any
4187
+ * icon fails to convert. Icons that pass validation but paint nothing (or
4188
+ * use features skipped by validation) are listed in `skipped`.
4189
+ */
4190
+ async function buildNanoIconFont(options) {
4191
+ const fontFamily = options.fontFamily ?? (options.inputDir ? path.basename(path.resolve(options.inputDir)) : "nano-icons");
4192
+ const web = options.web ?? true;
4193
+ const config = {
4194
+ fontFamily,
4195
+ upm: options.upm ?? 1024,
4196
+ safeZone: options.safeZone ?? 1020,
4197
+ startUnicode: parseStartUnicode(options.startUnicode),
4198
+ web
4199
+ };
4200
+ const inputs = [];
4201
+ const namedIcons = /* @__PURE__ */ new Map();
4202
+ if (options.inputDir) {
4203
+ const dir = path.resolve(options.inputDir);
4204
+ const files = (await fs.readdir(dir)).filter((f) => f.toLowerCase().endsWith(".svg"));
4205
+ for (const file of files.sort()) inputs.push({
4206
+ file,
4207
+ svg: await fs.readFile(path.join(dir, file), "utf8")
4208
+ });
4209
+ }
4210
+ for (const collection of options.iconify ?? []) for (const input of iconifyCollectionToSvgs(collection)) inputs.push({
4211
+ file: input.file,
4212
+ svg: input.svg
4213
+ });
4214
+ for (const icon of options.icons ?? []) {
4215
+ if (namedIcons.has(icon.name)) throw new Error(`Duplicate icon name "${icon.name}"`);
4216
+ namedIcons.set(icon.name, icon.svg);
4217
+ inputs.push({
4218
+ file: `${icon.name}.svg`,
4219
+ svg: icon.svg
4220
+ });
4221
+ }
4222
+ if (inputs.length === 0) throw new Error("buildNanoIconFont needs at least one of inputDir, icons, or iconify");
4223
+ const inputHash = fingerprintInputs(inputs, [
4224
+ config.upm,
4225
+ config.safeZone,
4226
+ config.startUnicode,
4227
+ web,
4228
+ generatorVersion()
4229
+ ]);
4230
+ const family = `${config.fontFamily}-${inputHash.slice(0, 8)}`;
4231
+ const outputDir = path.resolve(options.outputDir);
4232
+ const glyphmapPath = path.join(outputDir, `${family}.glyphmap.json`);
4233
+ const ttfPath = path.join(outputDir, `${family}.ttf`);
4234
+ const woff2Path = path.join(outputDir, `${family}.woff2`);
4235
+ if (fs$1.existsSync(glyphmapPath) && fs$1.existsSync(ttfPath)) {
4236
+ const existing = JSON.parse(fs$1.readFileSync(glyphmapPath, "utf8"));
4237
+ if (existing.m?.h === inputHash && existing.m.f === family) {
4238
+ options.logger?.info?.(`Font "${family}" is up to date — skipping`);
4239
+ const modulePath = options.module === false ? void 0 : emitNanoIconSetModule({
4240
+ outputDir,
4241
+ fontFamily,
4242
+ family,
4243
+ glyphMap: existing,
4244
+ web
4245
+ });
4246
+ return {
4247
+ fontFamily,
4248
+ family,
4249
+ ttfPath,
4250
+ woff2Path: web && fs$1.existsSync(woff2Path) ? woff2Path : void 0,
4251
+ glyphmapPath,
4252
+ modulePath,
4253
+ icons: Object.keys(existing.i),
4254
+ skipped: [],
4255
+ glyphMap: existing,
4256
+ unchanged: true
4257
+ };
4258
+ }
4259
+ }
4260
+ const result = await runFontPipeline(config, { outputDir }, inputs, {
4261
+ logger: options.logger,
4262
+ inputHash
4263
+ });
4264
+ let modulePath;
4265
+ if (options.module !== false) modulePath = emitNanoIconSetModule({
4266
+ outputDir,
4267
+ fontFamily,
4268
+ family: result.family,
4269
+ glyphMap: result.glyphMap,
4270
+ web
4271
+ });
4272
+ if (options.appFontsDir) {
4273
+ ensureDir(options.appFontsDir);
4274
+ writeFileAtomic(path.join(options.appFontsDir, `${result.family}.ttf`), fs$1.readFileSync(result.ttfPath));
4275
+ }
4276
+ return {
4277
+ fontFamily,
4278
+ family: result.family,
4279
+ ttfPath: result.ttfPath,
4280
+ woff2Path: result.woff2Path,
4281
+ glyphmapPath: result.glyphmapPath,
4282
+ modulePath,
4283
+ icons: result.icons,
4284
+ skipped: result.skipped,
4285
+ glyphMap: result.glyphMap,
4286
+ unchanged: false
4287
+ };
4288
+ }
4289
+ function loadNanoIconsConfig(configRoot) {
4290
+ const configPath = path.join(configRoot, ".nanoicons.json");
4291
+ if (!fs$1.existsSync(configPath)) throw new Error(`No .nanoicons.json found at ${configRoot}. Create one with:\n{ "iconSets": [{ "inputDir": "assets/icons", "fontFamily": "MyIcons", "outputDir": "src/generated/icons" }] }`);
4292
+ return JSON.parse(fs$1.readFileSync(configPath, "utf8"));
4293
+ }
4294
+ /** Run every icon set in a config file; returns per-set results. */
4295
+ async function buildAllNanoIconFonts(config, configRoot, logger) {
4296
+ if (!config.iconSets?.length) throw new Error("Config must contain an \"iconSets\" array with at least one entry");
4297
+ const results = [];
4298
+ for (const set of config.iconSets) results.push(await buildNanoIconFont({
4299
+ ...set,
4300
+ outputDir: path.resolve(configRoot, set.outputDir ?? "nano-icons"),
4301
+ inputDir: set.inputDir ? path.resolve(configRoot, set.inputDir) : void 0,
4302
+ appFontsDir: set.appFontsDir ? path.resolve(configRoot, set.appFontsDir) : void 0,
4303
+ logger
4304
+ }));
4305
+ return results;
4306
+ }
4307
+ //#endregion
4308
+ export { buildNanoIconFont as a, loadPathKit as c, buildAllNanoIconFonts as i, iconifyCollectionToSvgs as l, DEFAULT_START_UNICODE as n, loadNanoIconsConfig as o, DEFAULT_UPM as r, runFontPipeline as s, DEFAULT_SAFE_ZONE as t };