@weasel-js/svg 1.6.1 → 1.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +71 -0
- package/dist/index.d.ts +217 -16
- package/dist/index.js +1135 -482
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { pathFromD, PATH_M, PATH_L, PATH_Z, boundsOfPath, resolveScreenLength,
|
|
1
|
+
import { pathFromD, PATH_M, PATH_L, PATH_Z, boundsOfPath, resolveScreenLength, scriptMetricsFor, warmFonts, warmPaintKinds, isPaintKindKnown, solid, fillToBoundsFrame, getMarker, registerMarker, dwarn, unionAABB, createInsertOp, fillInPoseFrame, DEFAULT_SHAPE_FILL, pathInPoseFrame, DEFAULT_TEXT_STYLE, PathBuilder, resolveTextStyle, layoutRuns, resolveRuns, resolveAlign, PATH_C, PATH_Q, resolveRunFace, parseColor, rgbaToHex, resolveMarkerSize, getPaintKind } from '@weasel-js/core';
|
|
2
2
|
import { identity, multiply, invert } from '@weasel-js/geom';
|
|
3
3
|
import { tileGeometry } from '@weasel-js/core/patterns-builtin';
|
|
4
4
|
|
|
@@ -145,16 +145,16 @@ function rectToPolygon(r) {
|
|
|
145
145
|
b.close();
|
|
146
146
|
return b.build();
|
|
147
147
|
}
|
|
148
|
-
function rectElementToPath(x, y,
|
|
148
|
+
function rectElementToPath(x, y, width2, height2, rx, ry, m = IDENTITY_MATRIX) {
|
|
149
149
|
if (rx <= 0 && ry <= 0) {
|
|
150
|
-
return transformPath({ kind: "rect", x, y, width, height }, m);
|
|
150
|
+
return transformPath({ kind: "rect", x, y, width: width2, height: height2 }, m);
|
|
151
151
|
}
|
|
152
|
-
const r = Math.min(Math.max(rx, ry), Math.min(
|
|
153
|
-
const r2 = Math.min(rx > 0 ? rx : r, ry > 0 ? ry : r, Math.min(
|
|
152
|
+
const r = Math.min(Math.max(rx, ry), Math.min(width2, height2) / 2);
|
|
153
|
+
const r2 = Math.min(rx > 0 ? rx : r, ry > 0 ? ry : r, Math.min(width2, height2) / 2);
|
|
154
154
|
const rad = r2;
|
|
155
155
|
const k = KAPPA * rad;
|
|
156
|
-
const x2 = x +
|
|
157
|
-
const y2 = y +
|
|
156
|
+
const x2 = x + width2;
|
|
157
|
+
const y2 = y + height2;
|
|
158
158
|
const b = new PathBuilder();
|
|
159
159
|
b.moveTo(x + rad, y);
|
|
160
160
|
b.lineTo(x2 - rad, y);
|
|
@@ -217,65 +217,12 @@ function polygonToPath(points, m = IDENTITY_MATRIX) {
|
|
|
217
217
|
b.close();
|
|
218
218
|
return transformPath(b.build(), m);
|
|
219
219
|
}
|
|
220
|
-
function anchorOffset(style,
|
|
220
|
+
function anchorOffset(style, width2) {
|
|
221
221
|
const edge = resolveAlign(style?.align ?? "left", style?.direction ?? "ltr");
|
|
222
|
-
return edge === "center" ?
|
|
223
|
-
}
|
|
224
|
-
function parsePaintAttr(raw) {
|
|
225
|
-
if (raw == null) return null;
|
|
226
|
-
const s = raw.trim();
|
|
227
|
-
if (s === "") return null;
|
|
228
|
-
if (s === "none") return { kind: "none" };
|
|
229
|
-
if (s === "currentColor" || s === "currentcolor") {
|
|
230
|
-
return { kind: "solid", color: "#000000", alpha: 1 };
|
|
231
|
-
}
|
|
232
|
-
const urlMatch = /^url\(\s*#([^)\s]+)\s*\)(.*)$/.exec(s);
|
|
233
|
-
if (urlMatch) {
|
|
234
|
-
const out = { kind: "ref", id: urlMatch[1] };
|
|
235
|
-
const rest = urlMatch[2].trim();
|
|
236
|
-
if (rest !== "" && rest !== "none") {
|
|
237
|
-
const fallback = parsePaintAttr(rest);
|
|
238
|
-
if (fallback?.kind === "solid") {
|
|
239
|
-
out.fallback = { color: fallback.color, alpha: fallback.alpha };
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
return out;
|
|
243
|
-
}
|
|
244
|
-
try {
|
|
245
|
-
const [r, g, b, a] = parseColor(s);
|
|
246
|
-
return { kind: "solid", color: rgbaToHex([r, g, b]), alpha: a };
|
|
247
|
-
} catch {
|
|
248
|
-
return null;
|
|
249
|
-
}
|
|
222
|
+
return edge === "center" ? width2 / 2 : edge === "right" ? width2 : 0;
|
|
250
223
|
}
|
|
251
224
|
|
|
252
|
-
// src/
|
|
253
|
-
var EMPTY_STYLE = {};
|
|
254
|
-
var INHERITABLE = [
|
|
255
|
-
"fill",
|
|
256
|
-
"fill-opacity",
|
|
257
|
-
"fill-rule",
|
|
258
|
-
"stroke",
|
|
259
|
-
"stroke-width",
|
|
260
|
-
"stroke-opacity",
|
|
261
|
-
"stroke-linecap",
|
|
262
|
-
"stroke-linejoin",
|
|
263
|
-
"stroke-dasharray",
|
|
264
|
-
"stroke-miterlimit",
|
|
265
|
-
"marker-start",
|
|
266
|
-
"marker-mid",
|
|
267
|
-
"marker-end",
|
|
268
|
-
"color",
|
|
269
|
-
"font-size",
|
|
270
|
-
"font-family",
|
|
271
|
-
"font-weight",
|
|
272
|
-
"font-style",
|
|
273
|
-
"text-anchor",
|
|
274
|
-
"letter-spacing",
|
|
275
|
-
"text-decoration",
|
|
276
|
-
"direction",
|
|
277
|
-
"text-transform"
|
|
278
|
-
];
|
|
225
|
+
// src/cssScan.ts
|
|
279
226
|
function skipString(s, i) {
|
|
280
227
|
const q = s[i];
|
|
281
228
|
for (let j = i + 1; j < s.length; j++) {
|
|
@@ -350,26 +297,21 @@ var IMPORTANT = /!\s*important\s*$/i;
|
|
|
350
297
|
function parseDeclarations(block) {
|
|
351
298
|
const out = [];
|
|
352
299
|
for (const part of splitTopLevel(stripComments(block), ";", true)) {
|
|
353
|
-
const
|
|
354
|
-
if (
|
|
355
|
-
const prop = part.slice(0, colon).trim().toLowerCase();
|
|
356
|
-
if (!PROP_NAME.test(prop)) continue;
|
|
357
|
-
let value = part.slice(colon + 1).trim();
|
|
358
|
-
const important = IMPORTANT.test(value);
|
|
359
|
-
if (important) value = value.replace(IMPORTANT, "").trim();
|
|
360
|
-
if (!value) continue;
|
|
361
|
-
out.push({ prop, value, important });
|
|
300
|
+
const d = parseDeclaration(part);
|
|
301
|
+
if (d) out.push(d);
|
|
362
302
|
}
|
|
363
303
|
return out;
|
|
364
304
|
}
|
|
365
|
-
function
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
305
|
+
function parseDeclaration(text) {
|
|
306
|
+
const colon = text.indexOf(":");
|
|
307
|
+
if (colon < 0) return null;
|
|
308
|
+
const prop = text.slice(0, colon).trim().toLowerCase();
|
|
309
|
+
if (!PROP_NAME.test(prop)) return null;
|
|
310
|
+
let value = text.slice(colon + 1).trim();
|
|
311
|
+
const important = IMPORTANT.test(value);
|
|
312
|
+
if (important) value = value.replace(IMPORTANT, "").trim();
|
|
313
|
+
if (!value) return null;
|
|
314
|
+
return { prop, value, important };
|
|
373
315
|
}
|
|
374
316
|
function parenEnd(s, i) {
|
|
375
317
|
let depth = 0;
|
|
@@ -385,6 +327,561 @@ function parenEnd(s, i) {
|
|
|
385
327
|
}
|
|
386
328
|
return s.length;
|
|
387
329
|
}
|
|
330
|
+
|
|
331
|
+
// src/condition.ts
|
|
332
|
+
var ConditionSyntaxError = class extends Error {
|
|
333
|
+
};
|
|
334
|
+
var triNot = (a) => a == null ? null : !a;
|
|
335
|
+
var triAnd = (a, b) => a === false || b === false ? false : a == null || b == null ? null : true;
|
|
336
|
+
var triOr = (a, b) => a === true || b === true ? true : a == null || b == null ? null : false;
|
|
337
|
+
var WS = /\s/;
|
|
338
|
+
var IDENT = /^-?[a-zA-Z_][\w-]*/;
|
|
339
|
+
function skipWs(p) {
|
|
340
|
+
while (p.i < p.s.length && WS.test(p.s[p.i])) p.i++;
|
|
341
|
+
}
|
|
342
|
+
function keyword(p, word) {
|
|
343
|
+
const end = p.i + word.length;
|
|
344
|
+
if (p.s.slice(p.i, end).toLowerCase() !== word) return false;
|
|
345
|
+
if (end >= p.s.length || !WS.test(p.s[end])) return false;
|
|
346
|
+
p.i = end;
|
|
347
|
+
return true;
|
|
348
|
+
}
|
|
349
|
+
function evaluateCondition(s, leaves, allowOr = true) {
|
|
350
|
+
const p = { s, i: 0 };
|
|
351
|
+
const r = condition(p, leaves, allowOr);
|
|
352
|
+
skipWs(p);
|
|
353
|
+
if (p.i < s.length) throw new ConditionSyntaxError(`unexpected "${s.slice(p.i)}"`);
|
|
354
|
+
return r;
|
|
355
|
+
}
|
|
356
|
+
function condition(p, leaves, allowOr) {
|
|
357
|
+
skipWs(p);
|
|
358
|
+
if (keyword(p, "not")) return triNot(inParens(p, leaves));
|
|
359
|
+
let r = inParens(p, leaves);
|
|
360
|
+
let op = null;
|
|
361
|
+
for (; ; ) {
|
|
362
|
+
const save = p.i;
|
|
363
|
+
skipWs(p);
|
|
364
|
+
if (p.i >= p.s.length) {
|
|
365
|
+
p.i = save;
|
|
366
|
+
return r;
|
|
367
|
+
}
|
|
368
|
+
const next = keyword(p, "and") ? "and" : keyword(p, "or") ? "or" : null;
|
|
369
|
+
if (!next || op && next !== op || next === "or" && !allowOr) {
|
|
370
|
+
throw new ConditionSyntaxError(`unexpected "${p.s.slice(p.i)}"`);
|
|
371
|
+
}
|
|
372
|
+
op = next;
|
|
373
|
+
const rhs = inParens(p, leaves);
|
|
374
|
+
r = op === "and" ? triAnd(r, rhs) : triOr(r, rhs);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
function looksLikeCondition(inner) {
|
|
378
|
+
const t = inner.trimStart();
|
|
379
|
+
return t.startsWith("(") || /^not\s/i.test(t) || /^-?[a-zA-Z_][\w-]*\(/.test(t);
|
|
380
|
+
}
|
|
381
|
+
function inParens(p, leaves) {
|
|
382
|
+
skipWs(p);
|
|
383
|
+
const start = p.i;
|
|
384
|
+
let name = null;
|
|
385
|
+
if (p.s[p.i] !== "(") {
|
|
386
|
+
const m = IDENT.exec(p.s.slice(p.i));
|
|
387
|
+
if (!m || p.s[p.i + m[0].length] !== "(") throw new ConditionSyntaxError(`expected "(" at "${p.s.slice(p.i)}"`);
|
|
388
|
+
name = m[0].toLowerCase();
|
|
389
|
+
p.i += m[0].length;
|
|
390
|
+
}
|
|
391
|
+
const end = parenEnd(p.s, p.i);
|
|
392
|
+
if (p.s[end - 1] !== ")") throw new ConditionSyntaxError(`unbalanced "${p.s.slice(start)}"`);
|
|
393
|
+
const inner = p.s.slice(p.i + 1, end - 1);
|
|
394
|
+
p.i = end;
|
|
395
|
+
if (name != null) return leaves.fn(name, inner);
|
|
396
|
+
if (looksLikeCondition(inner)) {
|
|
397
|
+
try {
|
|
398
|
+
return evaluateCondition(inner, leaves);
|
|
399
|
+
} catch (e) {
|
|
400
|
+
if (!(e instanceof ConditionSyntaxError)) throw e;
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
return leaves.paren(inner);
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
// src/media.ts
|
|
407
|
+
var DEFAULT_MEDIA_ENVIRONMENT = {
|
|
408
|
+
type: "screen",
|
|
409
|
+
width: 300,
|
|
410
|
+
height: 150,
|
|
411
|
+
resolution: 1,
|
|
412
|
+
prefersColorScheme: "light",
|
|
413
|
+
prefersReducedMotion: "no-preference",
|
|
414
|
+
hover: "none",
|
|
415
|
+
pointer: "none",
|
|
416
|
+
update: "none",
|
|
417
|
+
color: 8,
|
|
418
|
+
monochrome: 0
|
|
419
|
+
};
|
|
420
|
+
var ABSOLUTE_PX = {
|
|
421
|
+
px: 1,
|
|
422
|
+
in: 96,
|
|
423
|
+
cm: 96 / 2.54,
|
|
424
|
+
mm: 96 / 25.4,
|
|
425
|
+
q: 96 / 101.6,
|
|
426
|
+
pt: 96 / 72,
|
|
427
|
+
pc: 16
|
|
428
|
+
};
|
|
429
|
+
function absoluteLength(raw) {
|
|
430
|
+
const m = raw == null ? null : /^\s*([+-]?(?:\d+\.?\d*|\.\d+)(?:e[+-]?\d+)?)\s*([a-z]*)\s*$/i.exec(raw);
|
|
431
|
+
if (!m) return void 0;
|
|
432
|
+
const scale = ABSOLUTE_PX[m[2].toLowerCase() || "px"];
|
|
433
|
+
const n = parseFloat(m[1]) * (scale ?? NaN);
|
|
434
|
+
return Number.isFinite(n) && n >= 0 ? n : void 0;
|
|
435
|
+
}
|
|
436
|
+
function mediaEnvironmentFor(root, overrides = {}) {
|
|
437
|
+
let width2 = absoluteLength(root.getAttribute("width"));
|
|
438
|
+
let height2 = absoluteLength(root.getAttribute("height"));
|
|
439
|
+
const vb = (root.getAttribute("viewBox") ?? "").trim().split(/[\s,]+/).map(Number);
|
|
440
|
+
if (vb.length === 4 && vb.every(Number.isFinite) && vb[2] > 0 && vb[3] > 0) {
|
|
441
|
+
const [, , w, h] = vb;
|
|
442
|
+
if (width2 == null && height2 == null) {
|
|
443
|
+
width2 = w;
|
|
444
|
+
height2 = h;
|
|
445
|
+
} else if (width2 == null) width2 = height2 * (w / h);
|
|
446
|
+
else if (height2 == null) height2 = width2 * (h / w);
|
|
447
|
+
}
|
|
448
|
+
return {
|
|
449
|
+
...DEFAULT_MEDIA_ENVIRONMENT,
|
|
450
|
+
width: width2 ?? DEFAULT_MEDIA_ENVIRONMENT.width,
|
|
451
|
+
height: height2 ?? DEFAULT_MEDIA_ENVIRONMENT.height,
|
|
452
|
+
...overrides
|
|
453
|
+
};
|
|
454
|
+
}
|
|
455
|
+
var TOKEN = /\s*(?:([+-]?(?:\d+\.?\d*|\.\d+)(?:e[+-]?\d+)?)([a-z%]*)|(-?[a-z_][\w-]*)|(<=|>=|<|>|=)|(:)|(\/))/iy;
|
|
456
|
+
function tokenize(s) {
|
|
457
|
+
const out = [];
|
|
458
|
+
TOKEN.lastIndex = 0;
|
|
459
|
+
while (TOKEN.lastIndex < s.length) {
|
|
460
|
+
if (/^\s*$/.test(s.slice(TOKEN.lastIndex))) break;
|
|
461
|
+
const m = TOKEN.exec(s);
|
|
462
|
+
if (!m) return null;
|
|
463
|
+
if (m[1] != null) out.push({ k: "num", v: parseFloat(m[1]), unit: m[2].toLowerCase() });
|
|
464
|
+
else if (m[3] != null) out.push({ k: "ident", v: m[3].toLowerCase() });
|
|
465
|
+
else if (m[4] != null) out.push({ k: "op", v: m[4] });
|
|
466
|
+
else if (m[5] != null) out.push({ k: ":" });
|
|
467
|
+
else out.push({ k: "/" });
|
|
468
|
+
}
|
|
469
|
+
return out;
|
|
470
|
+
}
|
|
471
|
+
var width = (e) => e.width;
|
|
472
|
+
var height = (e) => e.height;
|
|
473
|
+
var aspect = (e) => e.width / e.height;
|
|
474
|
+
var hover = { kind: "discrete", values: ["none", "hover"], off: "none", get: (e) => e.hover };
|
|
475
|
+
var pointer = { kind: "discrete", values: ["none", "coarse", "fine"], off: "none", get: (e) => e.pointer };
|
|
476
|
+
var FEATURES = {
|
|
477
|
+
"width": { kind: "range", type: "length", get: width },
|
|
478
|
+
"height": { kind: "range", type: "length", get: height },
|
|
479
|
+
"device-width": { kind: "range", type: "length", get: width },
|
|
480
|
+
"device-height": { kind: "range", type: "length", get: height },
|
|
481
|
+
"aspect-ratio": { kind: "range", type: "ratio", get: aspect },
|
|
482
|
+
"device-aspect-ratio": { kind: "range", type: "ratio", get: aspect },
|
|
483
|
+
"resolution": { kind: "range", type: "resolution", get: (e) => e.resolution },
|
|
484
|
+
"color": { kind: "range", type: "integer", get: (e) => e.color },
|
|
485
|
+
"monochrome": { kind: "range", type: "integer", get: (e) => e.monochrome },
|
|
486
|
+
"orientation": {
|
|
487
|
+
kind: "discrete",
|
|
488
|
+
values: ["portrait", "landscape"],
|
|
489
|
+
get: (e) => e.height >= e.width ? "portrait" : "landscape"
|
|
490
|
+
},
|
|
491
|
+
"prefers-color-scheme": { kind: "discrete", values: ["light", "dark"], get: (e) => e.prefersColorScheme },
|
|
492
|
+
"prefers-reduced-motion": {
|
|
493
|
+
kind: "discrete",
|
|
494
|
+
values: ["no-preference", "reduce"],
|
|
495
|
+
off: "no-preference",
|
|
496
|
+
get: (e) => e.prefersReducedMotion
|
|
497
|
+
},
|
|
498
|
+
"update": { kind: "discrete", values: ["none", "slow", "fast"], off: "none", get: (e) => e.update },
|
|
499
|
+
"hover": hover,
|
|
500
|
+
"any-hover": hover,
|
|
501
|
+
"pointer": pointer,
|
|
502
|
+
"any-pointer": pointer
|
|
503
|
+
};
|
|
504
|
+
var FONT_PX = 16;
|
|
505
|
+
function lengthPx(v, unit, env) {
|
|
506
|
+
if (unit === "") return v === 0 ? 0 : null;
|
|
507
|
+
const abs = ABSOLUTE_PX[unit];
|
|
508
|
+
if (abs != null) return v * abs;
|
|
509
|
+
switch (unit) {
|
|
510
|
+
case "em":
|
|
511
|
+
case "rem":
|
|
512
|
+
return v * FONT_PX;
|
|
513
|
+
case "ex":
|
|
514
|
+
case "ch":
|
|
515
|
+
return v * FONT_PX / 2;
|
|
516
|
+
case "vw":
|
|
517
|
+
return v * env.width / 100;
|
|
518
|
+
case "vh":
|
|
519
|
+
return v * env.height / 100;
|
|
520
|
+
case "vmin":
|
|
521
|
+
return v * Math.min(env.width, env.height) / 100;
|
|
522
|
+
case "vmax":
|
|
523
|
+
return v * Math.max(env.width, env.height) / 100;
|
|
524
|
+
default:
|
|
525
|
+
return null;
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
var DPPX = { dppx: 1, x: 1, dpi: 1 / 96, dpcm: 2.54 / 96 };
|
|
529
|
+
function rangeValue(toks, type, env) {
|
|
530
|
+
const [a, b, c] = toks;
|
|
531
|
+
if (type === "ratio") {
|
|
532
|
+
if (toks.length === 1 && a.k === "num" && a.unit === "" && a.v >= 0) return a.v;
|
|
533
|
+
if (toks.length === 3 && a.k === "num" && b.k === "/" && c.k === "num" && a.unit === "" && c.unit === "" && a.v >= 0 && c.v > 0) return a.v / c.v;
|
|
534
|
+
return null;
|
|
535
|
+
}
|
|
536
|
+
if (toks.length !== 1 || a.k !== "num") return null;
|
|
537
|
+
if (type === "length") return lengthPx(a.v, a.unit, env);
|
|
538
|
+
if (type === "resolution") return DPPX[a.unit] != null ? a.v * DPPX[a.unit] : null;
|
|
539
|
+
return a.unit === "" && Number.isInteger(a.v) ? a.v : null;
|
|
540
|
+
}
|
|
541
|
+
function close(a, b) {
|
|
542
|
+
return Math.abs(a - b) <= 1e-9 * Math.max(1, Math.abs(a), Math.abs(b));
|
|
543
|
+
}
|
|
544
|
+
function compare(a, op, b) {
|
|
545
|
+
switch (op) {
|
|
546
|
+
case "=":
|
|
547
|
+
return close(a, b);
|
|
548
|
+
case "<":
|
|
549
|
+
return a < b && !close(a, b);
|
|
550
|
+
case "<=":
|
|
551
|
+
return a < b || close(a, b);
|
|
552
|
+
case ">":
|
|
553
|
+
return a > b && !close(a, b);
|
|
554
|
+
default:
|
|
555
|
+
return a > b || close(a, b);
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
var FLIP = { "<": ">", "<=": ">=", ">": "<", ">=": "<=", "=": "=" };
|
|
559
|
+
function evaluateFeature(inner, env) {
|
|
560
|
+
const toks = tokenize(inner);
|
|
561
|
+
if (!toks || toks.length === 0) return null;
|
|
562
|
+
const first = toks[0];
|
|
563
|
+
if (toks.length === 1) {
|
|
564
|
+
if (first.k !== "ident") return null;
|
|
565
|
+
const f = FEATURES[first.v];
|
|
566
|
+
if (!f) return null;
|
|
567
|
+
return f.kind === "range" ? f.get(env) !== 0 : f.get(env) !== f.off;
|
|
568
|
+
}
|
|
569
|
+
if (first.k === "ident" && toks[1].k === ":") {
|
|
570
|
+
const prefix = /^(min|max)-/.exec(first.v)?.[1];
|
|
571
|
+
const f = FEATURES[prefix ? first.v.slice(4) : first.v];
|
|
572
|
+
if (!f) return null;
|
|
573
|
+
const rest = toks.slice(2);
|
|
574
|
+
if (f.kind === "discrete") {
|
|
575
|
+
if (prefix || rest.length !== 1 || rest[0].k !== "ident" || !f.values.includes(rest[0].v)) return null;
|
|
576
|
+
return f.get(env) === rest[0].v;
|
|
577
|
+
}
|
|
578
|
+
const v = rangeValue(rest, f.type, env);
|
|
579
|
+
if (v == null) return null;
|
|
580
|
+
return compare(f.get(env), prefix === "min" ? ">=" : prefix === "max" ? "<=" : "=", v);
|
|
581
|
+
}
|
|
582
|
+
const segments = [[]];
|
|
583
|
+
const ops = [];
|
|
584
|
+
for (const t of toks) {
|
|
585
|
+
if (t.k === "op") {
|
|
586
|
+
ops.push(t.v);
|
|
587
|
+
segments.push([]);
|
|
588
|
+
} else segments[segments.length - 1].push(t);
|
|
589
|
+
}
|
|
590
|
+
if (segments.some((s) => s.length === 0)) return null;
|
|
591
|
+
const featureAt = (s) => s.length === 1 && s[0].k === "ident" ? FEATURES[s[0].v] : void 0;
|
|
592
|
+
if (ops.length === 1) {
|
|
593
|
+
const [lhs, rhs] = segments;
|
|
594
|
+
const named = featureAt(lhs) ?? featureAt(rhs);
|
|
595
|
+
if (!named) return null;
|
|
596
|
+
if (named.kind !== "range") return null;
|
|
597
|
+
const nameFirst = featureAt(lhs) != null;
|
|
598
|
+
const v = rangeValue(nameFirst ? rhs : lhs, named.type, env);
|
|
599
|
+
if (v == null) return null;
|
|
600
|
+
return nameFirst ? compare(named.get(env), ops[0], v) : compare(named.get(env), FLIP[ops[0]], v);
|
|
601
|
+
}
|
|
602
|
+
if (ops.length === 2) {
|
|
603
|
+
const f = featureAt(segments[1]);
|
|
604
|
+
if (!f || f.kind !== "range") return null;
|
|
605
|
+
const lt = ops.every((o) => o === "<" || o === "<=");
|
|
606
|
+
const gt = ops.every((o) => o === ">" || o === ">=");
|
|
607
|
+
if (!lt && !gt) return null;
|
|
608
|
+
const lo = rangeValue(segments[0], f.type, env);
|
|
609
|
+
const hi = rangeValue(segments[2], f.type, env);
|
|
610
|
+
if (lo == null || hi == null) return null;
|
|
611
|
+
const x = f.get(env);
|
|
612
|
+
return compare(lo, ops[0], x) && compare(x, ops[1], hi);
|
|
613
|
+
}
|
|
614
|
+
return null;
|
|
615
|
+
}
|
|
616
|
+
var RESERVED_TYPES = /* @__PURE__ */ new Set(["not", "and", "or", "only", "layer"]);
|
|
617
|
+
var TYPED_QUERY = /^(?:(not|only)\s+)?(-?[a-z_][\w-]*)(?:\s+and\s+([\s\S]+))?$/;
|
|
618
|
+
function evaluateQuery(query, env) {
|
|
619
|
+
const leaves = {
|
|
620
|
+
paren: (inner) => evaluateFeature(inner, env),
|
|
621
|
+
fn: () => null
|
|
622
|
+
};
|
|
623
|
+
if (/^(?:\(|not\s+\(|-?[a-z_][\w-]*\()/.test(query)) return evaluateCondition(query, leaves);
|
|
624
|
+
const m = TYPED_QUERY.exec(query);
|
|
625
|
+
if (!m || RESERVED_TYPES.has(m[2])) throw new ConditionSyntaxError(`invalid media query "${query}"`);
|
|
626
|
+
const typed = triAnd(m[2] === "all" || m[2] === env.type, m[3] ? evaluateCondition(m[3], leaves, false) : true);
|
|
627
|
+
return m[1] === "not" ? triNot(typed) : typed;
|
|
628
|
+
}
|
|
629
|
+
function evaluateMediaQuery(list, env) {
|
|
630
|
+
const src = list.trim().toLowerCase();
|
|
631
|
+
if (src === "") return true;
|
|
632
|
+
return splitTopLevel(src, ",").some((raw) => {
|
|
633
|
+
const query = raw.trim();
|
|
634
|
+
if (query === "") return false;
|
|
635
|
+
try {
|
|
636
|
+
return evaluateQuery(query, env) === true;
|
|
637
|
+
} catch (e) {
|
|
638
|
+
if (e instanceof ConditionSyntaxError) return false;
|
|
639
|
+
throw e;
|
|
640
|
+
}
|
|
641
|
+
});
|
|
642
|
+
}
|
|
643
|
+
function parsePaintAttr(raw) {
|
|
644
|
+
if (raw == null) return null;
|
|
645
|
+
const s = raw.trim();
|
|
646
|
+
if (s === "") return null;
|
|
647
|
+
if (s === "none") return { kind: "none" };
|
|
648
|
+
if (s.toLowerCase() === "currentcolor") {
|
|
649
|
+
return { kind: "solid", color: "#000000", alpha: 1 };
|
|
650
|
+
}
|
|
651
|
+
const urlMatch = /^url\(\s*#([^)\s]+)\s*\)(.*)$/.exec(s);
|
|
652
|
+
if (urlMatch) {
|
|
653
|
+
const out = { kind: "ref", id: urlMatch[1] };
|
|
654
|
+
const rest = urlMatch[2].trim();
|
|
655
|
+
if (rest !== "" && rest !== "none") {
|
|
656
|
+
const fallback = parsePaintAttr(rest);
|
|
657
|
+
if (fallback?.kind === "solid") {
|
|
658
|
+
out.fallback = { color: fallback.color, alpha: fallback.alpha };
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
return out;
|
|
662
|
+
}
|
|
663
|
+
try {
|
|
664
|
+
const [r, g, b, a] = parseColor(s);
|
|
665
|
+
return { kind: "solid", color: rgbaToHex([r, g, b]), alpha: a };
|
|
666
|
+
} catch {
|
|
667
|
+
return null;
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
// src/properties.ts
|
|
672
|
+
var TEXT_TRANSFORMS = /* @__PURE__ */ new Set(["none", "uppercase", "lowercase", "capitalize"]);
|
|
673
|
+
var DIMENSION = /^([+-]?(?:\d+\.?\d*|\.\d+)(?:e[+-]?\d+)?)([a-z%]*)$/i;
|
|
674
|
+
function dimension(v) {
|
|
675
|
+
const m = DIMENSION.exec(v);
|
|
676
|
+
return m ? { n: Number(m[1]), unit: m[2].toLowerCase() } : void 0;
|
|
677
|
+
}
|
|
678
|
+
var clamp01 = (n) => n < 0 ? 0 : n > 1 ? 1 : n;
|
|
679
|
+
var length = (native, coercedAs) => (v, warn, prop) => {
|
|
680
|
+
const d = dimension(v);
|
|
681
|
+
if (!d) return void 0;
|
|
682
|
+
if (d.unit && !native.includes(d.unit)) {
|
|
683
|
+
warn(`${prop} "${v}" uses unit "${d.unit}", which is not converted; treated as ${d.n} ${coercedAs}`);
|
|
684
|
+
}
|
|
685
|
+
return d.n;
|
|
686
|
+
};
|
|
687
|
+
var worldLength = length(["px"], "world units");
|
|
688
|
+
var keyword2 = (...words) => (v) => words.includes(v) ? v : void 0;
|
|
689
|
+
var isCurrentColor = (v) => v.toLowerCase() === "currentcolor";
|
|
690
|
+
function readPaint(v) {
|
|
691
|
+
if (v === "context-fill") return { kind: "context", of: "fill" };
|
|
692
|
+
if (v === "context-stroke") return { kind: "context", of: "stroke" };
|
|
693
|
+
return parsePaintAttr(v) ?? void 0;
|
|
694
|
+
}
|
|
695
|
+
function readRatio(v) {
|
|
696
|
+
const d = dimension(v);
|
|
697
|
+
if (!d || d.unit !== "" && d.unit !== "%") return void 0;
|
|
698
|
+
return clamp01(d.unit === "%" ? d.n / 100 : d.n);
|
|
699
|
+
}
|
|
700
|
+
function readSolidColor(v, warn, prop) {
|
|
701
|
+
const parsed = parsePaintAttr(v);
|
|
702
|
+
if (parsed?.kind !== "solid") return void 0;
|
|
703
|
+
if (isCurrentColor(v)) warn(`${prop}="${v}" is read as black; it is not resolved against color`);
|
|
704
|
+
return parsed;
|
|
705
|
+
}
|
|
706
|
+
function readMarkerRef(v) {
|
|
707
|
+
if (v === "none") return null;
|
|
708
|
+
return /^url\(\s*#([^)\s]+)\s*\)$/.exec(v)?.[1];
|
|
709
|
+
}
|
|
710
|
+
function readLinecap(v, warn) {
|
|
711
|
+
const cap = keyword2("butt", "round", "square")(v);
|
|
712
|
+
if (!cap) warn(`unsupported stroke-linecap: ${v}`);
|
|
713
|
+
return cap;
|
|
714
|
+
}
|
|
715
|
+
function readLinejoin(v, warn) {
|
|
716
|
+
const join = keyword2("miter", "round", "bevel")(v);
|
|
717
|
+
if (join) return join;
|
|
718
|
+
if (v === "arcs" || v === "miter-clip") {
|
|
719
|
+
warn(`stroke-linejoin "${v}" not supported; falling back to miter`);
|
|
720
|
+
return "miter";
|
|
721
|
+
}
|
|
722
|
+
warn(`unsupported stroke-linejoin: ${v}`);
|
|
723
|
+
return void 0;
|
|
724
|
+
}
|
|
725
|
+
function readDashArray(v, warn) {
|
|
726
|
+
if (v === "none" || v === "") return null;
|
|
727
|
+
const nums = [];
|
|
728
|
+
for (const t of v.split(/[\s,]+/).filter(Boolean)) {
|
|
729
|
+
const d = dimension(t);
|
|
730
|
+
if (!d || d.unit !== "" && d.unit !== "px" || d.n < 0) {
|
|
731
|
+
warn(`unrecognized stroke-dasharray: ${v}`);
|
|
732
|
+
return void 0;
|
|
733
|
+
}
|
|
734
|
+
nums.push(d.n);
|
|
735
|
+
}
|
|
736
|
+
return nums.length % 2 === 1 ? [...nums, ...nums] : nums;
|
|
737
|
+
}
|
|
738
|
+
function readMiterLimit(v, warn) {
|
|
739
|
+
const d = dimension(v);
|
|
740
|
+
if (d && d.unit === "" && d.n >= 1) return d.n;
|
|
741
|
+
warn(`unrecognized stroke-miterlimit: ${v}`);
|
|
742
|
+
return void 0;
|
|
743
|
+
}
|
|
744
|
+
function readColor(v) {
|
|
745
|
+
return isCurrentColor(v) || parsePaintAttr(v)?.kind === "solid" ? v : void 0;
|
|
746
|
+
}
|
|
747
|
+
function readFontSize(v, warn, prop) {
|
|
748
|
+
const d = dimension(v);
|
|
749
|
+
if (d?.unit === "%") return { fontScale: d.n / 100 };
|
|
750
|
+
const n = worldLength(v, warn, prop);
|
|
751
|
+
return n == null ? void 0 : { fontSize: n };
|
|
752
|
+
}
|
|
753
|
+
function readFontWeight(v) {
|
|
754
|
+
if (v === "normal" || v === "bold" || v === "bolder") return v;
|
|
755
|
+
const d = dimension(v);
|
|
756
|
+
return d && d.unit === "" && d.n >= 1 && d.n <= 1e3 ? d.n : void 0;
|
|
757
|
+
}
|
|
758
|
+
function readFontStyle(v, warn) {
|
|
759
|
+
if (v === "normal" || v === "italic") return v;
|
|
760
|
+
if (v === "oblique") {
|
|
761
|
+
warn('font-style "oblique" is read as italic');
|
|
762
|
+
return "italic";
|
|
763
|
+
}
|
|
764
|
+
return void 0;
|
|
765
|
+
}
|
|
766
|
+
function readLetterSpacing(v, warn, prop) {
|
|
767
|
+
return v === "normal" ? null : worldLength(v, warn, prop);
|
|
768
|
+
}
|
|
769
|
+
function readDecoration(v, warn) {
|
|
770
|
+
const out = {};
|
|
771
|
+
for (const t of v.toLowerCase().split(/\s+/)) {
|
|
772
|
+
if (t === "underline") out.underline = true;
|
|
773
|
+
else if (t === "line-through") out.strikethrough = true;
|
|
774
|
+
else if (t === "overline") out.overline = true;
|
|
775
|
+
else if (t !== "none" && t !== "blink") warn(`text-decoration "${v}": "${t}" is not modeled; dropped`);
|
|
776
|
+
}
|
|
777
|
+
return out;
|
|
778
|
+
}
|
|
779
|
+
function readFontVariant(v, warn) {
|
|
780
|
+
let out;
|
|
781
|
+
for (const t of v.toLowerCase().split(/\s+/)) {
|
|
782
|
+
if (t === "small-caps" || t === "normal") out = t;
|
|
783
|
+
else if (t) warn(`font-variant "${v}": "${t}" is not modeled; dropped`);
|
|
784
|
+
}
|
|
785
|
+
return out;
|
|
786
|
+
}
|
|
787
|
+
function readTextTransform(v) {
|
|
788
|
+
const t = v.toLowerCase();
|
|
789
|
+
return TEXT_TRANSFORMS.has(t) ? t : void 0;
|
|
790
|
+
}
|
|
791
|
+
var emLength = length(["em"], "em");
|
|
792
|
+
function readBaselineShift(v, warn, prop) {
|
|
793
|
+
const k = v.toLowerCase();
|
|
794
|
+
if (k === "super" || k === "sub") return { script: k };
|
|
795
|
+
if (k === "baseline") return {};
|
|
796
|
+
const d = dimension(v);
|
|
797
|
+
if (d?.unit === "%") return d.n === 0 ? {} : { baselineShift: d.n / 100 };
|
|
798
|
+
const n = emLength(v, warn, prop);
|
|
799
|
+
if (n == null) return void 0;
|
|
800
|
+
return n === 0 ? {} : { baselineShift: n };
|
|
801
|
+
}
|
|
802
|
+
var inherited = (read) => ({ inherits: true, read });
|
|
803
|
+
var own = (read) => ({ inherits: false, read });
|
|
804
|
+
var PROPERTIES = {
|
|
805
|
+
"fill": inherited(readPaint),
|
|
806
|
+
"fill-opacity": inherited(readRatio),
|
|
807
|
+
"fill-rule": inherited(keyword2("nonzero", "evenodd")),
|
|
808
|
+
"stroke": inherited(readPaint),
|
|
809
|
+
"stroke-width": inherited(worldLength),
|
|
810
|
+
"stroke-opacity": inherited(readRatio),
|
|
811
|
+
"stroke-linecap": inherited(readLinecap),
|
|
812
|
+
"stroke-linejoin": inherited(readLinejoin),
|
|
813
|
+
"stroke-dasharray": inherited(readDashArray),
|
|
814
|
+
"stroke-miterlimit": inherited(readMiterLimit),
|
|
815
|
+
"marker-start": inherited(readMarkerRef),
|
|
816
|
+
"marker-mid": inherited(readMarkerRef),
|
|
817
|
+
"marker-end": inherited(readMarkerRef),
|
|
818
|
+
"color": inherited(readColor),
|
|
819
|
+
"font-size": inherited(readFontSize),
|
|
820
|
+
"font-family": inherited((v) => v.length > 0 ? v : void 0),
|
|
821
|
+
"font-weight": inherited(readFontWeight),
|
|
822
|
+
"font-style": inherited(readFontStyle),
|
|
823
|
+
"text-anchor": inherited(keyword2("start", "middle", "end")),
|
|
824
|
+
"letter-spacing": inherited(readLetterSpacing),
|
|
825
|
+
"text-decoration": inherited(readDecoration),
|
|
826
|
+
"direction": inherited(keyword2("ltr", "rtl")),
|
|
827
|
+
"text-transform": inherited(readTextTransform),
|
|
828
|
+
"font-variant": inherited(readFontVariant),
|
|
829
|
+
"opacity": own(readRatio),
|
|
830
|
+
"stop-color": own(readSolidColor),
|
|
831
|
+
"stop-opacity": own(readRatio),
|
|
832
|
+
"baseline-shift": own(readBaselineShift),
|
|
833
|
+
"vector-effect": own(keyword2("none", "non-scaling-stroke"))
|
|
834
|
+
};
|
|
835
|
+
var INHERITED_PROPERTIES = Object.keys(PROPERTIES).filter((p) => PROPERTIES[p].inherits);
|
|
836
|
+
var silent = () => {
|
|
837
|
+
};
|
|
838
|
+
function readProperty(prop, raw, warn = silent) {
|
|
839
|
+
if (raw == null) return void 0;
|
|
840
|
+
const v = raw.trim();
|
|
841
|
+
if (v === "inherit") return void 0;
|
|
842
|
+
return PROPERTIES[prop].read(v, warn, prop);
|
|
843
|
+
}
|
|
844
|
+
function isPropertyName(prop) {
|
|
845
|
+
return Object.prototype.hasOwnProperty.call(PROPERTIES, prop);
|
|
846
|
+
}
|
|
847
|
+
function isDeclarationHonored(prop, value) {
|
|
848
|
+
const name = prop.toLowerCase();
|
|
849
|
+
if (!isPropertyName(name)) return false;
|
|
850
|
+
const v = value.trim();
|
|
851
|
+
if (v === "inherit") return PROPERTIES[name].inherits;
|
|
852
|
+
let warned = false;
|
|
853
|
+
return readProperty(name, v, () => {
|
|
854
|
+
warned = true;
|
|
855
|
+
}) !== void 0 && !warned;
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
// src/supports.ts
|
|
859
|
+
function evaluateSupports(condition2, opts = {}) {
|
|
860
|
+
try {
|
|
861
|
+
return evaluateCondition(condition2, {
|
|
862
|
+
paren: (inner) => {
|
|
863
|
+
const d = parseDeclaration(inner);
|
|
864
|
+
return d != null && isDeclarationHonored(d.prop, d.value);
|
|
865
|
+
},
|
|
866
|
+
fn: (name, args) => name === "selector" && (opts.selector?.(args) ?? false)
|
|
867
|
+
}) === true;
|
|
868
|
+
} catch (e) {
|
|
869
|
+
if (e instanceof ConditionSyntaxError) return false;
|
|
870
|
+
throw e;
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
// src/cascade.ts
|
|
875
|
+
var EMPTY_STYLE = {};
|
|
876
|
+
function identEnd(s, i) {
|
|
877
|
+
while (i < s.length) {
|
|
878
|
+
const ch = s[i];
|
|
879
|
+
if (ch === "\\") i += 2;
|
|
880
|
+
else if (/[\w-]/.test(ch) || ch.charCodeAt(0) > 127) i++;
|
|
881
|
+
else break;
|
|
882
|
+
}
|
|
883
|
+
return i;
|
|
884
|
+
}
|
|
388
885
|
var LEGACY_PSEUDO_ELEMENTS = /* @__PURE__ */ new Set(["before", "after", "first-line", "first-letter"]);
|
|
389
886
|
var MAX_ARG_PSEUDOS = /* @__PURE__ */ new Set(["not", "is", "matches", "-webkit-any", "has"]);
|
|
390
887
|
function compareSpecificity(a, b) {
|
|
@@ -444,19 +941,76 @@ function specificity(selector) {
|
|
|
444
941
|
}
|
|
445
942
|
return [a, b, c];
|
|
446
943
|
}
|
|
447
|
-
|
|
944
|
+
var CascadeLayers = class {
|
|
945
|
+
children = /* @__PURE__ */ new Map();
|
|
946
|
+
anonymous = 0;
|
|
947
|
+
/** Declare `name` (possibly dotted) inside `parent`, keeping any earlier position; returns its full name. */
|
|
948
|
+
declare(parent, name) {
|
|
949
|
+
let full = parent;
|
|
950
|
+
for (const segment of name.split(".")) {
|
|
951
|
+
const child = full ? `${full}.${segment}` : segment;
|
|
952
|
+
const siblings = this.children.get(full) ?? [];
|
|
953
|
+
if (!siblings.includes(child)) {
|
|
954
|
+
siblings.push(child);
|
|
955
|
+
this.children.set(full, siblings);
|
|
956
|
+
}
|
|
957
|
+
full = child;
|
|
958
|
+
}
|
|
959
|
+
return full;
|
|
960
|
+
}
|
|
961
|
+
/** A fresh layer inside `parent` that nothing can reopen. */
|
|
962
|
+
declareAnonymous(parent) {
|
|
963
|
+
return this.declare(parent, `\0${this.anonymous++}`);
|
|
964
|
+
}
|
|
965
|
+
/**
|
|
966
|
+
* Each layer's rank for normal declarations, higher winning: a layer's
|
|
967
|
+
* sublayers rank below its own rules, in declaration order, and unlayered
|
|
968
|
+
* rules (`''`) rank above every layer.
|
|
969
|
+
*/
|
|
970
|
+
ranks() {
|
|
971
|
+
const out = /* @__PURE__ */ new Map();
|
|
972
|
+
const visit = (name) => {
|
|
973
|
+
for (const child of this.children.get(name) ?? []) visit(child);
|
|
974
|
+
out.set(name, out.size);
|
|
975
|
+
};
|
|
976
|
+
visit("");
|
|
977
|
+
return out;
|
|
978
|
+
}
|
|
979
|
+
};
|
|
980
|
+
function parseStylesheet(text, firstOrder = 0, ctx = {}, layers = new CascadeLayers()) {
|
|
448
981
|
const src = stripComments(text).replace(/<!\[CDATA\[|\]\]>|<!--|-->/g, " ");
|
|
449
982
|
const rules = [];
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
983
|
+
parseRuleList(src, 0, src.length, "", ctx, rules, { order: firstOrder, importsAllowed: true, layers });
|
|
984
|
+
return rules;
|
|
985
|
+
}
|
|
986
|
+
var IDENT2 = "-?[a-zA-Z_\\u0080-\\uffff][\\w\\u0080-\\uffff-]*";
|
|
987
|
+
var LAYER_NAME = new RegExp(`^${IDENT2}(?:\\.${IDENT2})*$`);
|
|
988
|
+
function layerNames(prelude) {
|
|
989
|
+
if (!prelude.trim()) return [];
|
|
990
|
+
const names = splitTopLevel(prelude, ",").map((n) => n.trim());
|
|
991
|
+
return names.every((n) => LAYER_NAME.test(n)) ? names : null;
|
|
992
|
+
}
|
|
993
|
+
var AT_RULE = /^@([\w-]+)\s*([\s\S]*)$/;
|
|
994
|
+
function parseRuleList(src, from, to, layer, ctx, rules, state) {
|
|
995
|
+
let i = from;
|
|
996
|
+
let preludeStart = from;
|
|
997
|
+
while (i < to) {
|
|
454
998
|
const ch = src[i];
|
|
455
999
|
if (ch === '"' || ch === "'") {
|
|
456
1000
|
i = skipString(src, i);
|
|
457
1001
|
continue;
|
|
458
1002
|
}
|
|
459
1003
|
if (ch === ";") {
|
|
1004
|
+
const text = src.slice(preludeStart, i).trim();
|
|
1005
|
+
const statement = AT_RULE.exec(text);
|
|
1006
|
+
const name = statement?.[1].toLowerCase();
|
|
1007
|
+
if (name === "import" && state.importsAllowed) {
|
|
1008
|
+
ctx.onWarn?.(`@import ${statement?.[2]} is not fetched; its rules do not apply`);
|
|
1009
|
+
} else if (name === "layer") {
|
|
1010
|
+
for (const n of layerNames(statement?.[2] ?? "") ?? []) state.layers.declare(layer, n);
|
|
1011
|
+
} else if (text && name !== "charset") {
|
|
1012
|
+
state.importsAllowed = false;
|
|
1013
|
+
}
|
|
460
1014
|
i++;
|
|
461
1015
|
preludeStart = i;
|
|
462
1016
|
continue;
|
|
@@ -466,41 +1020,77 @@ function parseStylesheet(text, firstOrder = 0) {
|
|
|
466
1020
|
continue;
|
|
467
1021
|
}
|
|
468
1022
|
const prelude = src.slice(preludeStart, i).trim();
|
|
469
|
-
const end = skipBlock(src, i);
|
|
470
|
-
|
|
1023
|
+
const end = Math.min(skipBlock(src, i), to);
|
|
1024
|
+
const at = AT_RULE.exec(prelude);
|
|
1025
|
+
state.importsAllowed = false;
|
|
1026
|
+
if (at) {
|
|
1027
|
+
const name = at[1].toLowerCase();
|
|
1028
|
+
const names = name === "layer" ? layerNames(at[2]) : null;
|
|
1029
|
+
if (names && names.length <= 1) {
|
|
1030
|
+
const inner = names.length ? state.layers.declare(layer, names[0]) : state.layers.declareAnonymous(layer);
|
|
1031
|
+
parseRuleList(src, i + 1, end - 1, inner, ctx, rules, state);
|
|
1032
|
+
} else if (conditionHolds(name, at[2], ctx)) {
|
|
1033
|
+
parseRuleList(src, i + 1, end - 1, layer, ctx, rules, state);
|
|
1034
|
+
}
|
|
1035
|
+
} else if (prelude) {
|
|
471
1036
|
const declarations = parseDeclarations(src.slice(i + 1, end - 1));
|
|
472
1037
|
for (const raw of splitTopLevel(prelude, ",")) {
|
|
473
1038
|
const selector = raw.trim();
|
|
474
1039
|
if (selector) {
|
|
475
|
-
rules.push({ selector, declarations, specificity: specificity(selector), order: order
|
|
1040
|
+
rules.push({ selector, declarations, specificity: specificity(selector), order: state.order++, layer });
|
|
476
1041
|
}
|
|
477
1042
|
}
|
|
478
1043
|
}
|
|
479
1044
|
i = end;
|
|
480
1045
|
preludeStart = end;
|
|
481
1046
|
}
|
|
482
|
-
return rules;
|
|
483
1047
|
}
|
|
484
|
-
function
|
|
1048
|
+
function conditionHolds(name, condition2, ctx) {
|
|
1049
|
+
if (name === "media") return evaluateMediaQuery(condition2, ctx.media ?? DEFAULT_MEDIA_ENVIRONMENT);
|
|
1050
|
+
if (name === "supports") return evaluateSupports(condition2, { selector: ctx.selector });
|
|
1051
|
+
return false;
|
|
1052
|
+
}
|
|
1053
|
+
function appliesAsCss(style, ctx) {
|
|
485
1054
|
const type = style.getAttribute("type")?.trim().toLowerCase();
|
|
486
1055
|
if (type && type !== "text/css") return false;
|
|
487
|
-
|
|
488
|
-
if (!media) return true;
|
|
489
|
-
return media.split(",").some((m) => m.trim() === "all" || m.trim() === "screen");
|
|
1056
|
+
return evaluateMediaQuery(style.getAttribute("media") ?? "", ctx.media ?? DEFAULT_MEDIA_ENVIRONMENT);
|
|
490
1057
|
}
|
|
491
1058
|
var sheetCache = /* @__PURE__ */ new WeakMap();
|
|
1059
|
+
function bindStylesheets(doc, media, onWarn) {
|
|
1060
|
+
sheetCache.set(doc, collectRules(doc, media, onWarn));
|
|
1061
|
+
}
|
|
492
1062
|
function documentRules(doc) {
|
|
493
1063
|
const cached = sheetCache.get(doc);
|
|
494
1064
|
if (cached) return cached;
|
|
1065
|
+
const root = doc.documentElement;
|
|
1066
|
+
const rules = collectRules(doc, root ? mediaEnvironmentFor(root) : DEFAULT_MEDIA_ENVIRONMENT);
|
|
1067
|
+
sheetCache.set(doc, rules);
|
|
1068
|
+
return rules;
|
|
1069
|
+
}
|
|
1070
|
+
function collectRules(doc, media, onWarn) {
|
|
1071
|
+
const ctx = { media, onWarn, selector: (s) => isValidSelector(doc, s) };
|
|
1072
|
+
const layers = new CascadeLayers();
|
|
495
1073
|
const rules = [];
|
|
496
1074
|
const styles = doc.getElementsByTagNameNS("*", "style");
|
|
497
1075
|
for (let i = 0; i < styles.length; i++) {
|
|
498
|
-
if (!appliesAsCss(styles[i])) continue;
|
|
499
|
-
rules.push(...parseStylesheet(styles[i].textContent ?? "", rules.length));
|
|
1076
|
+
if (!appliesAsCss(styles[i], ctx)) continue;
|
|
1077
|
+
rules.push(...parseStylesheet(styles[i].textContent ?? "", rules.length, ctx, layers));
|
|
1078
|
+
}
|
|
1079
|
+
const ranks = layers.ranks();
|
|
1080
|
+
return rules.map((r) => ({ ...r, rank: ranks.get(r.layer) ?? 0 })).sort((x, y) => x.rank - y.rank || compareCascade(x, y));
|
|
1081
|
+
}
|
|
1082
|
+
function compareCascade(x, y) {
|
|
1083
|
+
return compareSpecificity(x.specificity, y.specificity) || x.order - y.order;
|
|
1084
|
+
}
|
|
1085
|
+
function isValidSelector(doc, selector) {
|
|
1086
|
+
const root = doc.documentElement;
|
|
1087
|
+
if (!root || !selector.trim()) return false;
|
|
1088
|
+
try {
|
|
1089
|
+
root.matches(selector);
|
|
1090
|
+
return true;
|
|
1091
|
+
} catch {
|
|
1092
|
+
return false;
|
|
500
1093
|
}
|
|
501
|
-
rules.sort((x, y) => compareSpecificity(x.specificity, y.specificity) || x.order - y.order);
|
|
502
|
-
sheetCache.set(doc, rules);
|
|
503
|
-
return rules;
|
|
504
1094
|
}
|
|
505
1095
|
function matches(el, selector) {
|
|
506
1096
|
try {
|
|
@@ -517,8 +1107,9 @@ function authorDeclarations(el) {
|
|
|
517
1107
|
const matched = rules.filter((r) => matches(el, r.selector));
|
|
518
1108
|
const inline = parseDeclarations(el.getAttribute("style") ?? "");
|
|
519
1109
|
const out = /* @__PURE__ */ new Map();
|
|
520
|
-
|
|
521
|
-
|
|
1110
|
+
const importantOrder = [...matched].sort((x, y) => y.rank - x.rank || compareCascade(x, y));
|
|
1111
|
+
for (const [important, ordered] of [[false, matched], [true, importantOrder]]) {
|
|
1112
|
+
for (const r of ordered) {
|
|
522
1113
|
for (const d of r.declarations) if (d.important === important) out.set(d.prop, d.value);
|
|
523
1114
|
}
|
|
524
1115
|
for (const d of inline) if (d.important === important) out.set(d.prop, d.value);
|
|
@@ -531,18 +1122,25 @@ function ownProp(el, prop) {
|
|
|
531
1122
|
}
|
|
532
1123
|
function deriveStyle(parent, el) {
|
|
533
1124
|
let next = null;
|
|
534
|
-
for (const prop of
|
|
535
|
-
const
|
|
536
|
-
if (
|
|
1125
|
+
for (const prop of INHERITED_PROPERTIES) {
|
|
1126
|
+
const own2 = ownProp(el, prop);
|
|
1127
|
+
if (own2 == null || own2 === "inherit" || prop === "color" && own2.trim().toLowerCase() === "currentcolor") continue;
|
|
537
1128
|
if (!next) next = { ...parent };
|
|
538
|
-
next[prop] =
|
|
1129
|
+
next[prop] = own2;
|
|
539
1130
|
}
|
|
540
1131
|
return next ?? parent;
|
|
541
1132
|
}
|
|
542
1133
|
function resolveCurrentColor(raw, style) {
|
|
543
1134
|
if (raw == null) return null;
|
|
544
|
-
if (raw.trim().toLowerCase()
|
|
545
|
-
|
|
1135
|
+
if (raw.trim().toLowerCase() !== "currentcolor") return raw;
|
|
1136
|
+
const color = readProperty("color", style["color"]);
|
|
1137
|
+
return color == null || color.toLowerCase() === "currentcolor" ? "#000000" : color;
|
|
1138
|
+
}
|
|
1139
|
+
function ownValue(el, prop, warn) {
|
|
1140
|
+
return readProperty(prop, ownProp(el, prop), warn);
|
|
1141
|
+
}
|
|
1142
|
+
function styleValue(style, prop, warn) {
|
|
1143
|
+
return readProperty(prop, style[prop], warn);
|
|
546
1144
|
}
|
|
547
1145
|
|
|
548
1146
|
// src/elements.ts
|
|
@@ -578,7 +1176,11 @@ function patternXml(id, paint, onWarn) {
|
|
|
578
1176
|
`width="${trimNumber(geometry.size)}"`,
|
|
579
1177
|
`height="${trimNumber(geometry.size)}"`
|
|
580
1178
|
];
|
|
581
|
-
|
|
1179
|
+
const [a, b, c, d] = paint.transform ?? [1, 0, 0, 1];
|
|
1180
|
+
const placement = formatMatrix([a, b, c, d, origin.x, origin.y]);
|
|
1181
|
+
if (paint.transform && !isIdentity([a, b, c, d, 0, 0]) && placement) {
|
|
1182
|
+
attrs.push(`patternTransform="${placement}"`);
|
|
1183
|
+
} else if (origin.x !== 0 || origin.y !== 0) {
|
|
582
1184
|
attrs.push(`x="${trimNumber(origin.x)}"`, `y="${trimNumber(origin.y)}"`);
|
|
583
1185
|
}
|
|
584
1186
|
attrs.push(`data-weasel-tile="${escapeAttr(JSON.stringify(spec))}"`);
|
|
@@ -638,8 +1240,11 @@ function readPattern(el, onWarn) {
|
|
|
638
1240
|
}
|
|
639
1241
|
const x = parseFloat(el.getAttribute("x") ?? "0");
|
|
640
1242
|
const y = parseFloat(el.getAttribute("y") ?? "0");
|
|
1243
|
+
const m = parseTransform(el.getAttribute("patternTransform"), onWarn);
|
|
1244
|
+
const origin = applyMatrix(m, x, y);
|
|
641
1245
|
const paint = { fill: "pattern", pattern: spec };
|
|
642
|
-
if (x !== 0 || y !== 0) paint.origin =
|
|
1246
|
+
if (origin.x !== 0 || origin.y !== 0) paint.origin = origin;
|
|
1247
|
+
if (!isIdentity([m[0], m[1], m[2], m[3], 0, 0])) paint.transform = [m[0], m[1], m[2], m[3]];
|
|
643
1248
|
return paint;
|
|
644
1249
|
}
|
|
645
1250
|
function escapeAttr(s) {
|
|
@@ -647,8 +1252,8 @@ function escapeAttr(s) {
|
|
|
647
1252
|
}
|
|
648
1253
|
function serializePathD(path) {
|
|
649
1254
|
if (path.kind === "rect") {
|
|
650
|
-
const { x, y, width, height } = path;
|
|
651
|
-
return `M${trimNumber(x)} ${trimNumber(y)}h${trimNumber(
|
|
1255
|
+
const { x, y, width: width2, height: height2 } = path;
|
|
1256
|
+
return `M${trimNumber(x)} ${trimNumber(y)}h${trimNumber(width2)}v${trimNumber(height2)}h${trimNumber(-width2)}Z`;
|
|
652
1257
|
}
|
|
653
1258
|
const cmds = path.commands;
|
|
654
1259
|
const xs = path.coords;
|
|
@@ -682,13 +1287,18 @@ function serializePathD(path) {
|
|
|
682
1287
|
// src/gradients.ts
|
|
683
1288
|
var WEASEL_NS = "urn:weasel-js:svg";
|
|
684
1289
|
var WEASEL_NS_PREFIX = "wzl";
|
|
685
|
-
function
|
|
686
|
-
const kind = paint.fill ?? "solid";
|
|
1290
|
+
function nativeSvgKind(kind) {
|
|
687
1291
|
return kind === "solid" || kind === "linear-gradient" || kind === "radial-gradient" || kind === "pattern";
|
|
688
1292
|
}
|
|
1293
|
+
function hasNativeSvgForm(paint) {
|
|
1294
|
+
return nativeSvgKind(paint.fill ?? "solid");
|
|
1295
|
+
}
|
|
689
1296
|
var INTERPOLATE_SPACES = /* @__PURE__ */ new Set(["rgb", "oklab", "oklch"]);
|
|
1297
|
+
function nativeSvgSpace(space) {
|
|
1298
|
+
return !space || space === "rgb";
|
|
1299
|
+
}
|
|
690
1300
|
function interpolateAttr(space) {
|
|
691
|
-
if (
|
|
1301
|
+
if (nativeSvgSpace(space)) return "";
|
|
692
1302
|
return ` ${WEASEL_NS_PREFIX}:interpolate="${space}"`;
|
|
693
1303
|
}
|
|
694
1304
|
function readInterpolate(el, elements) {
|
|
@@ -698,7 +1308,7 @@ function readInterpolate(el, elements) {
|
|
|
698
1308
|
}
|
|
699
1309
|
function usesInterpolateAttr(paint) {
|
|
700
1310
|
const space = paint.interpolate;
|
|
701
|
-
return space
|
|
1311
|
+
return !nativeSvgSpace(space) && INTERPOLATE_SPACES.has(space);
|
|
702
1312
|
}
|
|
703
1313
|
function localName(tag) {
|
|
704
1314
|
const i = tag.indexOf(":");
|
|
@@ -751,11 +1361,11 @@ function inheritedAttr(el, elements, name, seen = /* @__PURE__ */ new Set()) {
|
|
|
751
1361
|
return inheritedAttr(ref, elements, name, seen);
|
|
752
1362
|
}
|
|
753
1363
|
function inheritedStops(el, elements, onWarn, seen = /* @__PURE__ */ new Set()) {
|
|
754
|
-
const
|
|
755
|
-
if (
|
|
1364
|
+
const own2 = readStops(el, onWarn);
|
|
1365
|
+
if (own2.length > 0) return own2;
|
|
756
1366
|
seen.add(el);
|
|
757
1367
|
const ref = hrefTarget(el, elements);
|
|
758
|
-
if (!ref || seen.has(ref)) return
|
|
1368
|
+
if (!ref || seen.has(ref)) return own2;
|
|
759
1369
|
return inheritedStops(ref, elements, onWarn, seen);
|
|
760
1370
|
}
|
|
761
1371
|
function parseRatio(raw) {
|
|
@@ -771,11 +1381,9 @@ function readStops(el, onWarn) {
|
|
|
771
1381
|
const offsetRaw = parseRatio(c.getAttribute("offset") ?? "0");
|
|
772
1382
|
const offset = Number.isFinite(offsetRaw) ? offsetRaw : 0;
|
|
773
1383
|
const colorAttr = ownProp(c, "stop-color") ?? "#000000";
|
|
774
|
-
const parsed =
|
|
775
|
-
if (parsed
|
|
776
|
-
const
|
|
777
|
-
const own = opacityAttr != null ? parseRatio(opacityAttr) : NaN;
|
|
778
|
-
const alpha = Number.isFinite(own) ? own : parsed.alpha;
|
|
1384
|
+
const parsed = readProperty("stop-color", colorAttr, onWarn);
|
|
1385
|
+
if (parsed) {
|
|
1386
|
+
const alpha = ownValue(c, "stop-opacity") ?? parsed.alpha;
|
|
779
1387
|
stops.push({ offset, color: alpha < 1 ? applyAlpha(parsed.color, alpha) : parsed.color });
|
|
780
1388
|
} else {
|
|
781
1389
|
onWarn?.(`gradient stop has unrecognized stop-color: ${colorAttr}`);
|
|
@@ -877,12 +1485,13 @@ var PaintServerRegistry = class {
|
|
|
877
1485
|
byPaint = /* @__PURE__ */ new Map();
|
|
878
1486
|
order = [];
|
|
879
1487
|
counter = 0;
|
|
880
|
-
// Marker
|
|
881
|
-
// id is the marker key itself, not a minted counter
|
|
882
|
-
// `marker-end="url(#id)"` fragment back as the marker
|
|
883
|
-
// registry knows it, so a synthetic id would come
|
|
884
|
-
|
|
885
|
-
|
|
1488
|
+
// Marker defs referenced by any stroke, by `<defs>` id in first-use order.
|
|
1489
|
+
// An unsized reference's id is the marker key itself, not a minted counter
|
|
1490
|
+
// id: `parseSvg` reads a `marker-end="url(#id)"` fragment back as the marker
|
|
1491
|
+
// key directly when the registry knows it, so a synthetic id would come
|
|
1492
|
+
// back as a stranger. A sized one gets a def of its own, stamped with its
|
|
1493
|
+
// key and size so the parser can read the reference back whole.
|
|
1494
|
+
markers = /* @__PURE__ */ new Map();
|
|
886
1495
|
// Clip paths, in first-use order. Identity, not value: two groups clipped by
|
|
887
1496
|
// equal outlines get a def each, which costs bytes and misleads nobody.
|
|
888
1497
|
clips = [];
|
|
@@ -907,22 +1516,26 @@ var PaintServerRegistry = class {
|
|
|
907
1516
|
if (hasNativeSvgForm(paint)) return `url(#${id})`;
|
|
908
1517
|
return `url(#${id}) ${paintFallback(paint)}`;
|
|
909
1518
|
}
|
|
1519
|
+
/** The paint kind of each registered paint, once, in first-use order. */
|
|
1520
|
+
paintKinds() {
|
|
1521
|
+
return [...new Set(this.order.map((paint) => paint.fill ?? "solid"))];
|
|
1522
|
+
}
|
|
910
1523
|
/** Whether any registered paint serializes outside SVG's own vocabulary, so
|
|
911
1524
|
* the root has to declare {@link WEASEL_NS}. */
|
|
912
1525
|
usesPrivateNamespace() {
|
|
913
|
-
return this.order.some((paint) => !hasNativeSvgForm(paint) || usesInterpolateAttr(paint));
|
|
1526
|
+
return this.order.some((paint) => !hasNativeSvgForm(paint) || usesInterpolateAttr(paint)) || [...this.markers.values()].some((m) => m.size !== void 0 || writesInset(getMarker(m.key)));
|
|
914
1527
|
}
|
|
915
|
-
/** The `<defs>` id for a marker
|
|
916
|
-
*
|
|
917
|
-
*
|
|
918
|
-
* never be written. */
|
|
919
|
-
markerId(
|
|
1528
|
+
/** The `<defs>` id for a marker reference — the key itself when it has no
|
|
1529
|
+
* size, else the key suffixed with the size. `undefined` when nothing is
|
|
1530
|
+
* registered under the key, so a caller emits no attribute at all rather
|
|
1531
|
+
* than a `url(#…)` pointing at a def that will never be written. */
|
|
1532
|
+
markerId(ref) {
|
|
1533
|
+
const key = typeof ref === "string" ? ref : ref.key;
|
|
1534
|
+
const size = typeof ref === "string" ? void 0 : ref.size;
|
|
920
1535
|
if (getMarker(key) === void 0) return void 0;
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
}
|
|
925
|
-
return key;
|
|
1536
|
+
const id = size === void 0 ? key : `${key}-s${typeof size === "number" ? trimNumber(size) : `${trimNumber(size.px)}px`}`;
|
|
1537
|
+
if (!this.markers.has(id)) this.markers.set(id, size === void 0 ? { key } : { key, size });
|
|
1538
|
+
return id;
|
|
926
1539
|
}
|
|
927
1540
|
/** Mint (or recall) the `<defs>` id for a clip outline. */
|
|
928
1541
|
clipId(path) {
|
|
@@ -936,17 +1549,22 @@ var PaintServerRegistry = class {
|
|
|
936
1549
|
/** Emit `<defs>...</defs>` XML for every registered paint server, marker
|
|
937
1550
|
* and clip path. */
|
|
938
1551
|
toDefsXml(onWarn) {
|
|
939
|
-
if (this.order.length === 0 && this.
|
|
1552
|
+
if (this.order.length === 0 && this.markers.size === 0 && this.clips.length === 0) {
|
|
940
1553
|
return "";
|
|
941
1554
|
}
|
|
942
1555
|
const parts = ["<defs>"];
|
|
943
1556
|
for (const paint of this.order) {
|
|
944
1557
|
parts.push(paintServerXml(this.byPaint.get(paint), paint, onWarn));
|
|
945
1558
|
}
|
|
946
|
-
for (const key of this.
|
|
1559
|
+
for (const [id, { key, size }] of this.markers) {
|
|
947
1560
|
const entry = getMarker(key);
|
|
948
1561
|
if (!entry) continue;
|
|
949
|
-
|
|
1562
|
+
if (entry.toSvg) {
|
|
1563
|
+
if (size !== void 0) onWarn?.(`marker "${key}" writes its own <marker> def, which cannot carry a reference's size; written at its own size`);
|
|
1564
|
+
parts.push(entry.toSvg(id, entry));
|
|
1565
|
+
} else {
|
|
1566
|
+
parts.push(defaultMarkerXml(id, entry, onWarn, key, size));
|
|
1567
|
+
}
|
|
950
1568
|
}
|
|
951
1569
|
for (const path of this.clips) {
|
|
952
1570
|
parts.push(
|
|
@@ -957,13 +1575,19 @@ var PaintServerRegistry = class {
|
|
|
957
1575
|
return parts.join("");
|
|
958
1576
|
}
|
|
959
1577
|
};
|
|
960
|
-
function
|
|
961
|
-
|
|
1578
|
+
function writesInset(entry) {
|
|
1579
|
+
return entry !== void 0 && !entry.toSvg && (entry.inset ?? 0) !== 0;
|
|
1580
|
+
}
|
|
1581
|
+
function defaultMarkerXml(id, entry, onWarn, key, size) {
|
|
1582
|
+
const unit = size === void 0 ? 1 : resolveMarkerSize({ key: key ?? id, size }, 1);
|
|
1583
|
+
const path = entry.path({ size: unit, stroke: { paint: { fill: "solid", color: "#000" } } });
|
|
962
1584
|
const d = serializePathD(path);
|
|
963
1585
|
const fill = markerPaintAttrs("fill", entry.fill, id, onWarn);
|
|
964
|
-
const outline = entry.outline ? ` ${markerPaintAttrs("stroke", entry.outline.paint, id, onWarn)} stroke-width="${trimNumber(entry.outline.width)}" stroke-linecap="round" stroke-linejoin="round"` : "";
|
|
1586
|
+
const outline = entry.outline ? ` ${markerPaintAttrs("stroke", entry.outline.paint, id, onWarn)} stroke-width="${trimNumber(entry.outline.width * unit)}" stroke-linecap="round" stroke-linejoin="round"` : "";
|
|
965
1587
|
const orient = typeof entry.orient === "number" ? trimNumber(entry.orient * 180 / Math.PI) : "auto-start-reverse";
|
|
966
|
-
|
|
1588
|
+
const units = size === void 0 ? 'markerUnits="strokeWidth" markerWidth="8" markerHeight="8"' : `markerUnits="userSpaceOnUse" markerWidth="${trimNumber(8 * unit)}" markerHeight="${trimNumber(8 * unit)}" ${WEASEL_NS_PREFIX}:key="${key}" ${WEASEL_NS_PREFIX}:size="${typeof size === "number" ? trimNumber(size) : `${trimNumber(size.px)}px`}"`;
|
|
1589
|
+
const inset = writesInset(entry) ? ` ${WEASEL_NS_PREFIX}:inset="${trimNumber(entry.inset)}"` : "";
|
|
1590
|
+
return `<marker id="${id}" ${units}${inset} refX="0" refY="0" orient="${orient}" overflow="visible"><path d="${d}" ${fill}${outline}/></marker>`;
|
|
967
1591
|
}
|
|
968
1592
|
function markerPaintAttrs(attr, paint, id, onWarn) {
|
|
969
1593
|
if (paint === "none") return `${attr}="none"`;
|
|
@@ -978,9 +1602,10 @@ function paintServerXml(id, paint, onWarn) {
|
|
|
978
1602
|
if (paint.fill === "pattern") return patternXml(id, paint, onWarn);
|
|
979
1603
|
const builtin = gradientXml(id, paint);
|
|
980
1604
|
if (builtin) return builtin;
|
|
981
|
-
const
|
|
1605
|
+
const kind = getPaintKind(paint.fill);
|
|
1606
|
+
const custom = kind?.toSvg?.(id, paint);
|
|
982
1607
|
if (custom) return custom;
|
|
983
|
-
onWarn?.(`${paint.fill} fill has no vector form \u2014 omitted from <defs>`);
|
|
1608
|
+
onWarn?.(kind ? `${paint.fill} fill has no vector form \u2014 omitted from <defs>` : `${paint.fill} fill is not registered \u2014 omitted from <defs>; a kind loaded on demand needs \`await warmSvg(nodes)\` before a synchronous export`);
|
|
984
1609
|
return "";
|
|
985
1610
|
}
|
|
986
1611
|
function gradientUnitsAttr(units) {
|
|
@@ -1059,6 +1684,7 @@ function parseSvg(svg, opts = {}) {
|
|
|
1059
1684
|
if (!root || root.tagName.toLowerCase() !== "svg") {
|
|
1060
1685
|
return { nodes: [], warnings: ["root element is not <svg>"] };
|
|
1061
1686
|
}
|
|
1687
|
+
bindStylesheets(doc, mediaEnvironmentFor(root, opts.media), onWarn);
|
|
1062
1688
|
const namespaces = opts.namespaces ?? {};
|
|
1063
1689
|
const uriToPrefix = /* @__PURE__ */ new Map();
|
|
1064
1690
|
for (const [prefix, uri] of Object.entries(namespaces)) {
|
|
@@ -1076,10 +1702,10 @@ function parseSvg(svg, opts = {}) {
|
|
|
1076
1702
|
if (documentMeta) result.documentMeta = documentMeta;
|
|
1077
1703
|
const viewBox = parseViewBoxAttr(root.getAttribute("viewBox"));
|
|
1078
1704
|
if (viewBox) result.viewBox = viewBox;
|
|
1079
|
-
const
|
|
1080
|
-
if (
|
|
1081
|
-
const
|
|
1082
|
-
if (
|
|
1705
|
+
const width2 = parseRootLength(root.getAttribute("width"), "width", onWarn);
|
|
1706
|
+
if (width2 != null) result.width = width2;
|
|
1707
|
+
const height2 = parseRootLength(root.getAttribute("height"), "height", onWarn);
|
|
1708
|
+
if (height2 != null) result.height = height2;
|
|
1083
1709
|
for (let i = 0; i < root.children.length; i++) {
|
|
1084
1710
|
const c = root.children[i];
|
|
1085
1711
|
if (c.tagName.toLowerCase() === "title") {
|
|
@@ -1244,7 +1870,7 @@ function parseElement(el, ctm, style, gradients, clips, onWarn, uriToPrefix) {
|
|
|
1244
1870
|
const childCtm = multiply(ctm, local);
|
|
1245
1871
|
const childStyle = deriveStyle(style, el);
|
|
1246
1872
|
const children = parseChildren(el, childCtm, childStyle, gradients, clips, onWarn, uriToPrefix);
|
|
1247
|
-
const opacity2 =
|
|
1873
|
+
const opacity2 = ownValue(el, "opacity");
|
|
1248
1874
|
const group = { kind: "group", children };
|
|
1249
1875
|
const clip = clipFor(el, childCtm, clips, onWarn);
|
|
1250
1876
|
if (clip) group.clip = clip;
|
|
@@ -1306,11 +1932,10 @@ function parseElement(el, ctm, style, gradients, clips, onWarn, uriToPrefix) {
|
|
|
1306
1932
|
}
|
|
1307
1933
|
}
|
|
1308
1934
|
const leafStyle = deriveStyle(style, el);
|
|
1309
|
-
const fill =
|
|
1935
|
+
const fill = readPaint2(leafStyle, "fill", "#000000", gradients, onWarn);
|
|
1310
1936
|
const stroke = readStroke(leafStyle, gradients, onWarn, isNonScalingStroke(el));
|
|
1311
|
-
const opacity =
|
|
1312
|
-
|
|
1313
|
-
if (fillRuleRaw === "evenodd" && path.kind === "polygon") {
|
|
1937
|
+
const opacity = ownValue(el, "opacity");
|
|
1938
|
+
if (styleValue(leafStyle, "fill-rule") === "evenodd" && path.kind === "polygon") {
|
|
1314
1939
|
path = { ...path, fillRule: "evenodd" };
|
|
1315
1940
|
}
|
|
1316
1941
|
const node = { kind: "path", path, fill };
|
|
@@ -1376,10 +2001,10 @@ function polygonToRectIfAxisAligned(p) {
|
|
|
1376
2001
|
if (y0 !== y1 || x1 !== x2 || y2 !== y3 || x3 !== x0) return null;
|
|
1377
2002
|
const x = Math.min(x0, x1);
|
|
1378
2003
|
const y = Math.min(y0, y2);
|
|
1379
|
-
const
|
|
1380
|
-
const
|
|
1381
|
-
if (
|
|
1382
|
-
return { kind: "rect", x, y, width, height };
|
|
2004
|
+
const width2 = Math.abs(x1 - x0);
|
|
2005
|
+
const height2 = Math.abs(y2 - y1);
|
|
2006
|
+
if (width2 === 0 || height2 === 0) return null;
|
|
2007
|
+
return { kind: "rect", x, y, width: width2, height: height2 };
|
|
1383
2008
|
}
|
|
1384
2009
|
function pathAabb(path) {
|
|
1385
2010
|
if (path.kind === "rect") {
|
|
@@ -1387,22 +2012,21 @@ function pathAabb(path) {
|
|
|
1387
2012
|
}
|
|
1388
2013
|
return boundsOfPath(path);
|
|
1389
2014
|
}
|
|
1390
|
-
function
|
|
2015
|
+
function readPaint2(style, attr, defaultColor, gradients, onWarn) {
|
|
1391
2016
|
const raw = resolveCurrentColor(style[attr] ?? null, style);
|
|
1392
|
-
const
|
|
1393
|
-
const opacity = opacityRaw != null ? clamp01(parseFloat(opacityRaw)) : void 0;
|
|
2017
|
+
const opacity = styleValue(style, `${attr}-opacity`, onWarn);
|
|
1394
2018
|
if (raw == null) {
|
|
1395
2019
|
if (attr === "stroke") return { kind: "none" };
|
|
1396
2020
|
const out2 = { kind: "solid", color: defaultColor };
|
|
1397
2021
|
if (opacity != null) out2.opacity = opacity;
|
|
1398
2022
|
return out2;
|
|
1399
2023
|
}
|
|
1400
|
-
const parsed =
|
|
2024
|
+
const parsed = readProperty(attr, raw, onWarn);
|
|
1401
2025
|
if (!parsed) {
|
|
1402
2026
|
onWarn(`unrecognized ${attr} value: ${raw}`);
|
|
1403
2027
|
return { kind: "solid", color: defaultColor };
|
|
1404
2028
|
}
|
|
1405
|
-
if (parsed.kind === "none") return { kind: "none" };
|
|
2029
|
+
if (parsed.kind === "none" || parsed.kind === "context") return { kind: "none" };
|
|
1406
2030
|
if (parsed.kind === "ref") {
|
|
1407
2031
|
const paint = gradients.get(parsed.id);
|
|
1408
2032
|
if (!paint) {
|
|
@@ -1415,7 +2039,7 @@ function readPaint(style, attr, defaultColor, gradients, onWarn) {
|
|
|
1415
2039
|
onWarn(`${attr} references unknown gradient #${parsed.id}`);
|
|
1416
2040
|
return { kind: "solid", color: defaultColor };
|
|
1417
2041
|
}
|
|
1418
|
-
return { kind: "gradient", paint };
|
|
2042
|
+
return { kind: "gradient", paint: opacity != null ? { ...paint, opacity } : paint };
|
|
1419
2043
|
}
|
|
1420
2044
|
const out = { kind: "solid", color: parsed.color };
|
|
1421
2045
|
const a = opacity ?? (parsed.alpha < 1 ? parsed.alpha : void 0);
|
|
@@ -1426,59 +2050,30 @@ function readStroke(style, gradients, onWarn, nonScaling = false) {
|
|
|
1426
2050
|
const inheritedStroke = style["stroke"] ?? null;
|
|
1427
2051
|
const inheritedWidth = style["stroke-width"] ?? null;
|
|
1428
2052
|
if (inheritedStroke == null && inheritedWidth == null) return void 0;
|
|
1429
|
-
const paint =
|
|
2053
|
+
const paint = readPaint2(style, "stroke", "#000000", gradients, onWarn);
|
|
1430
2054
|
if (paint.kind === "none") return void 0;
|
|
1431
|
-
const n =
|
|
2055
|
+
const n = styleValue(style, "stroke-width", onWarn) ?? 1;
|
|
1432
2056
|
const stroke = { paint, width: nonScaling ? { px: n } : n };
|
|
1433
|
-
const
|
|
1434
|
-
if (
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
}
|
|
1444
|
-
const join = style["stroke-linejoin"] ?? null;
|
|
1445
|
-
if (join === "miter" || join === "round" || join === "bevel") {
|
|
1446
|
-
stroke.join = join;
|
|
1447
|
-
} else if (join === "arcs" || join === "miter-clip") {
|
|
1448
|
-
onWarn(`stroke-linejoin "${join}" not supported; falling back to miter`);
|
|
1449
|
-
stroke.join = "miter";
|
|
1450
|
-
} else if (join != null) {
|
|
1451
|
-
onWarn(`unsupported stroke-linejoin: ${join}`);
|
|
1452
|
-
}
|
|
1453
|
-
const dashAttr = style["stroke-dasharray"] ?? null;
|
|
1454
|
-
if (dashAttr != null && dashAttr.trim() !== "" && dashAttr.trim() !== "none") {
|
|
1455
|
-
const parsed = parseDashArray(dashAttr);
|
|
1456
|
-
if (parsed) stroke.dash = parsed;
|
|
1457
|
-
else onWarn(`unrecognized stroke-dasharray: ${dashAttr}`);
|
|
1458
|
-
}
|
|
1459
|
-
const miterAttr = style["stroke-miterlimit"] ?? null;
|
|
1460
|
-
if (miterAttr != null) {
|
|
1461
|
-
const m = parseFloat(miterAttr);
|
|
1462
|
-
if (Number.isFinite(m) && m >= 1) stroke.miterLimit = m;
|
|
1463
|
-
else onWarn(`unrecognized stroke-miterlimit: ${miterAttr}`);
|
|
1464
|
-
}
|
|
2057
|
+
const opacity = styleValue(style, "stroke-opacity", onWarn);
|
|
2058
|
+
if (opacity != null) stroke.opacity = opacity;
|
|
2059
|
+
const cap = styleValue(style, "stroke-linecap", onWarn);
|
|
2060
|
+
if (cap) stroke.cap = cap;
|
|
2061
|
+
const join = styleValue(style, "stroke-linejoin", onWarn);
|
|
2062
|
+
if (join) stroke.join = join;
|
|
2063
|
+
const dash = styleValue(style, "stroke-dasharray", onWarn);
|
|
2064
|
+
if (dash) stroke.dash = dash;
|
|
2065
|
+
const miterLimit = styleValue(style, "stroke-miterlimit", onWarn);
|
|
2066
|
+
if (miterLimit != null) stroke.miterLimit = miterLimit;
|
|
1465
2067
|
for (const [attr, field] of [
|
|
1466
2068
|
["marker-start", "markerStart"],
|
|
1467
2069
|
["marker-mid", "markerMid"],
|
|
1468
2070
|
["marker-end", "markerEnd"]
|
|
1469
2071
|
]) {
|
|
1470
|
-
const id =
|
|
1471
|
-
if (id
|
|
2072
|
+
const id = styleValue(style, attr, onWarn);
|
|
2073
|
+
if (id) stroke[field] = id;
|
|
1472
2074
|
}
|
|
1473
2075
|
return stroke;
|
|
1474
2076
|
}
|
|
1475
|
-
function parseMarkerRef(raw) {
|
|
1476
|
-
if (!raw) return void 0;
|
|
1477
|
-
const v = raw.trim();
|
|
1478
|
-
if (v === "" || v === "none") return void 0;
|
|
1479
|
-
const m = /^url\(\s*#([^)\s]+)\s*\)/.exec(v);
|
|
1480
|
-
return m ? m[1] : void 0;
|
|
1481
|
-
}
|
|
1482
2077
|
var STROKE_KEYS = [
|
|
1483
2078
|
"stroke",
|
|
1484
2079
|
"stroke-width",
|
|
@@ -1492,7 +2087,7 @@ var STROKE_KEYS = [
|
|
|
1492
2087
|
"marker-end"
|
|
1493
2088
|
];
|
|
1494
2089
|
function isNonScalingStroke(el) {
|
|
1495
|
-
return
|
|
2090
|
+
return ownValue(el, "vector-effect") === "non-scaling-stroke";
|
|
1496
2091
|
}
|
|
1497
2092
|
function ownStrokeStyle(el) {
|
|
1498
2093
|
const out = {};
|
|
@@ -1522,66 +2117,6 @@ function coreStroke(stroke) {
|
|
|
1522
2117
|
if (stroke.markerEnd) out.markerEnd = stroke.markerEnd;
|
|
1523
2118
|
return out;
|
|
1524
2119
|
}
|
|
1525
|
-
function parseDashArray(s) {
|
|
1526
|
-
const tokens = s.trim().split(/[\s,]+/).filter(Boolean);
|
|
1527
|
-
if (tokens.length === 0) return null;
|
|
1528
|
-
const nums = [];
|
|
1529
|
-
for (const t of tokens) {
|
|
1530
|
-
const n = parseFloat(t);
|
|
1531
|
-
if (!Number.isFinite(n) || n < 0) return null;
|
|
1532
|
-
nums.push(n);
|
|
1533
|
-
}
|
|
1534
|
-
return nums.length % 2 === 1 ? [...nums, ...nums] : nums;
|
|
1535
|
-
}
|
|
1536
|
-
function readOpacityAttr(el, name) {
|
|
1537
|
-
const raw = ownProp(el, name);
|
|
1538
|
-
if (raw == null) return void 0;
|
|
1539
|
-
const n = parseFloat(raw);
|
|
1540
|
-
return Number.isFinite(n) ? clamp01(n) : void 0;
|
|
1541
|
-
}
|
|
1542
|
-
function clamp01(n) {
|
|
1543
|
-
return n < 0 ? 0 : n > 1 ? 1 : n;
|
|
1544
|
-
}
|
|
1545
|
-
function parseLetterSpacing(raw, onWarn) {
|
|
1546
|
-
const n = parseFloat(raw);
|
|
1547
|
-
if (!Number.isFinite(n)) return void 0;
|
|
1548
|
-
const unit = /^-?[\d.]+([a-z%]+)$/i.exec(raw.trim())?.[1]?.toLowerCase();
|
|
1549
|
-
if (unit && unit !== "px") {
|
|
1550
|
-
onWarn(`letter-spacing "${raw}" uses unit "${unit}", which is not converted; treated as ${n} world units`);
|
|
1551
|
-
}
|
|
1552
|
-
return n;
|
|
1553
|
-
}
|
|
1554
|
-
function parseTextDecoration(raw) {
|
|
1555
|
-
if (raw == null) return {};
|
|
1556
|
-
const tokens = raw.trim().toLowerCase().split(/\s+/);
|
|
1557
|
-
const out = {};
|
|
1558
|
-
if (tokens.includes("underline")) out.underline = true;
|
|
1559
|
-
if (tokens.includes("line-through")) out.strikethrough = true;
|
|
1560
|
-
if (tokens.includes("overline")) out.overline = true;
|
|
1561
|
-
return out;
|
|
1562
|
-
}
|
|
1563
|
-
function parseBaselineShift(raw, onWarn) {
|
|
1564
|
-
const keyword = raw.trim().toLowerCase();
|
|
1565
|
-
if (keyword === "super" || keyword === "sub") return { script: keyword };
|
|
1566
|
-
const n = parseFloat(raw);
|
|
1567
|
-
if (!Number.isFinite(n)) return {};
|
|
1568
|
-
const unit = /^-?[\d.]+([a-z%]+)$/i.exec(raw.trim())?.[1]?.toLowerCase();
|
|
1569
|
-
if (unit === "%") return { baselineShift: n / 100 };
|
|
1570
|
-
if (unit && unit !== "em") {
|
|
1571
|
-
onWarn(`baseline-shift "${raw}" uses unit "${unit}", which is not converted; treated as ${n} em`);
|
|
1572
|
-
}
|
|
1573
|
-
return n === 0 ? {} : { baselineShift: n };
|
|
1574
|
-
}
|
|
1575
|
-
function parseRunFontSize(raw, onWarn) {
|
|
1576
|
-
const n = parseFloat(raw);
|
|
1577
|
-
if (!Number.isFinite(n)) return {};
|
|
1578
|
-
const unit = /^-?[\d.]+([a-z%]+)$/i.exec(raw.trim())?.[1]?.toLowerCase();
|
|
1579
|
-
if (unit === "%") return { fontScale: n / 100 };
|
|
1580
|
-
if (unit && unit !== "px") {
|
|
1581
|
-
onWarn(`font-size "${raw}" uses unit "${unit}", which is not converted; treated as ${n} world units`);
|
|
1582
|
-
}
|
|
1583
|
-
return { fontSize: n };
|
|
1584
|
-
}
|
|
1585
2120
|
var XLINK_NS2 = "http://www.w3.org/1999/xlink";
|
|
1586
2121
|
function parseImageElement(el, ctm, onWarn) {
|
|
1587
2122
|
const href = imageHref(el);
|
|
@@ -1619,7 +2154,7 @@ function imageNodeAt(boxEl, ownerEl, href, ctm, onWarn) {
|
|
|
1619
2154
|
width: Math.abs(x1 - x0),
|
|
1620
2155
|
height: Math.abs(y1 - y0)
|
|
1621
2156
|
};
|
|
1622
|
-
const opacity =
|
|
2157
|
+
const opacity = ownValue(ownerEl, "opacity");
|
|
1623
2158
|
if (opacity != null) node.opacity = opacity;
|
|
1624
2159
|
const localTransform = parseTransform(ownerEl.getAttribute("transform"), onWarn);
|
|
1625
2160
|
if (!isIdentity(localTransform)) {
|
|
@@ -1724,8 +2259,7 @@ function parseTextElement(el, ctm, style, gradients, onWarn) {
|
|
|
1724
2259
|
const dominantBaseline = el.getAttribute("dominant-baseline");
|
|
1725
2260
|
const explicitTopAnchor = dominantBaseline === "text-before-edge" || dominantBaseline === "hanging";
|
|
1726
2261
|
const topY = explicitTopAnchor ? ay : ay - fontSize;
|
|
1727
|
-
const
|
|
1728
|
-
const inheritedShift = ownShift != null ? parseBaselineShift(ownShift, onWarn) : {};
|
|
2262
|
+
const inheritedShift = ownValue(el, "baseline-shift", onWarn) ?? {};
|
|
1729
2263
|
const runs = [];
|
|
1730
2264
|
for (let i = 0; i < el.childNodes.length; i++) {
|
|
1731
2265
|
const child = el.childNodes[i];
|
|
@@ -1749,16 +2283,16 @@ function parseTextElement(el, ctm, style, gradients, onWarn) {
|
|
|
1749
2283
|
const plain = runs.map((r) => r.text).join("");
|
|
1750
2284
|
const dataW = num2(el.getAttribute("data-weasel-width"), NaN);
|
|
1751
2285
|
const dataH = num2(el.getAttribute("data-weasel-height"), NaN);
|
|
1752
|
-
const
|
|
2286
|
+
const width2 = Number.isFinite(dataW) ? dataW : laidOutWidth(runs, textStyle, plain);
|
|
1753
2287
|
const lines = (plain.match(/\n/g)?.length ?? 0) + 1;
|
|
1754
|
-
const
|
|
1755
|
-
const opacity =
|
|
2288
|
+
const height2 = Number.isFinite(dataH) ? dataH : fontSize * lineHeight * lines;
|
|
2289
|
+
const opacity = ownValue(el, "opacity");
|
|
1756
2290
|
const node = {
|
|
1757
2291
|
kind: "text",
|
|
1758
|
-
x: ax - anchorOffset(textStyle,
|
|
2292
|
+
x: ax - anchorOffset(textStyle, width2),
|
|
1759
2293
|
y: topY,
|
|
1760
|
-
width,
|
|
1761
|
-
height,
|
|
2294
|
+
width: width2,
|
|
2295
|
+
height: height2,
|
|
1762
2296
|
text: plain
|
|
1763
2297
|
};
|
|
1764
2298
|
const hasStyling = runs.some(
|
|
@@ -1766,6 +2300,7 @@ function parseTextElement(el, ctm, style, gradients, onWarn) {
|
|
|
1766
2300
|
);
|
|
1767
2301
|
if (hasStyling) node.runs = runs;
|
|
1768
2302
|
if (el.getAttribute("data-weasel-wrap") === "true") textStyle.wrap = true;
|
|
2303
|
+
if (el.getAttribute("data-weasel-align") === "justify") textStyle.align = "justify";
|
|
1769
2304
|
const va = el.getAttribute("data-weasel-vertical-align");
|
|
1770
2305
|
if (va === "center" || va === "bottom") node.verticalAlign = va;
|
|
1771
2306
|
if (Object.keys(textStyle).length > 0) node.style = textStyle;
|
|
@@ -1790,47 +2325,36 @@ function parseTextElement(el, ctm, style, gradients, onWarn) {
|
|
|
1790
2325
|
}
|
|
1791
2326
|
return node;
|
|
1792
2327
|
}
|
|
1793
|
-
var TEXT_TRANSFORMS = /* @__PURE__ */ new Set(["none", "uppercase", "lowercase", "capitalize"]);
|
|
1794
|
-
function parseTextTransform(raw) {
|
|
1795
|
-
const v = raw?.trim().toLowerCase();
|
|
1796
|
-
return v !== void 0 && TEXT_TRANSFORMS.has(v) ? v : void 0;
|
|
1797
|
-
}
|
|
1798
2328
|
function readTspanRun(el, gradients, style, onWarn) {
|
|
1799
2329
|
const text = el.textContent ?? "";
|
|
1800
2330
|
const run = { text };
|
|
1801
|
-
const fw =
|
|
1802
|
-
if (fw === "bold" || fw === "
|
|
1803
|
-
|
|
1804
|
-
if (
|
|
1805
|
-
|
|
2331
|
+
const fw = ownValue(el, "font-weight", onWarn);
|
|
2332
|
+
if (fw === "bold" || fw === "bolder" || fw === 700) run.bold = true;
|
|
2333
|
+
else if (fw === "normal") run.fontWeight = 400;
|
|
2334
|
+
else if (typeof fw === "number") run.fontWeight = fw;
|
|
2335
|
+
if (ownValue(el, "font-style", onWarn) === "italic") run.italic = true;
|
|
2336
|
+
const ff = ownValue(el, "font-family", onWarn);
|
|
1806
2337
|
if (ff) run.fontFamily = ff;
|
|
1807
|
-
const
|
|
1808
|
-
if (
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
if (
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
const shift = parseBaselineShift(bs, onWarn);
|
|
1821
|
-
if (shift.script != null) run.script = shift.script;
|
|
1822
|
-
if (shift.baselineShift != null) run.baselineShift = shift.baselineShift;
|
|
1823
|
-
}
|
|
1824
|
-
const decoration = parseTextDecoration(ownProp(el, "text-decoration"));
|
|
1825
|
-
if (decoration.underline) run.underline = true;
|
|
1826
|
-
if (decoration.strikethrough) run.strikethrough = true;
|
|
1827
|
-
if (decoration.overline) run.overline = true;
|
|
1828
|
-
const transform = parseTextTransform(ownProp(el, "text-transform"));
|
|
2338
|
+
const size = ownValue(el, "font-size", onWarn);
|
|
2339
|
+
if (size?.fontSize != null) run.fontSize = size.fontSize;
|
|
2340
|
+
if (size?.fontScale != null) run.fontScale = size.fontScale;
|
|
2341
|
+
const ls = ownValue(el, "letter-spacing", onWarn);
|
|
2342
|
+
if (ls != null) run.letterSpacing = ls;
|
|
2343
|
+
const shift = ownValue(el, "baseline-shift", onWarn);
|
|
2344
|
+
if (shift?.script != null) run.script = shift.script;
|
|
2345
|
+
if (shift?.baselineShift != null) run.baselineShift = shift.baselineShift;
|
|
2346
|
+
const decoration = ownValue(el, "text-decoration", onWarn);
|
|
2347
|
+
if (decoration?.underline) run.underline = true;
|
|
2348
|
+
if (decoration?.strikethrough) run.strikethrough = true;
|
|
2349
|
+
if (decoration?.overline) run.overline = true;
|
|
2350
|
+
const transform = ownValue(el, "text-transform", onWarn);
|
|
1829
2351
|
if (transform) run.textTransform = transform;
|
|
2352
|
+
const variant = ownValue(el, "font-variant", onWarn);
|
|
2353
|
+
if (variant) run.fontVariantCaps = variant;
|
|
1830
2354
|
const tspanStyle = deriveStyle(style, el);
|
|
1831
2355
|
const fillAttr = resolveCurrentColor(ownProp(el, "fill"), tspanStyle);
|
|
1832
2356
|
if (fillAttr) {
|
|
1833
|
-
const parsed =
|
|
2357
|
+
const parsed = readProperty("fill", fillAttr);
|
|
1834
2358
|
if (parsed?.kind === "solid") {
|
|
1835
2359
|
run.fill = { fill: "solid", color: parsed.color };
|
|
1836
2360
|
} else if (parsed?.kind === "ref") {
|
|
@@ -1845,44 +2369,41 @@ function readTspanRun(el, gradients, style, onWarn) {
|
|
|
1845
2369
|
}
|
|
1846
2370
|
function readTextStyle(style, onWarn) {
|
|
1847
2371
|
const out = {};
|
|
1848
|
-
const
|
|
1849
|
-
if (
|
|
1850
|
-
|
|
1851
|
-
|
|
2372
|
+
const size = styleValue(style, "font-size", onWarn);
|
|
2373
|
+
if (size?.fontSize != null) out.fontSize = size.fontSize;
|
|
2374
|
+
else if (size?.fontScale != null) {
|
|
2375
|
+
onWarn(`font-size "${style["font-size"]}" on <text> is resolved against the 16px default`);
|
|
2376
|
+
out.fontSize = 16 * size.fontScale;
|
|
1852
2377
|
}
|
|
1853
|
-
const ff = style
|
|
2378
|
+
const ff = styleValue(style, "font-family", onWarn);
|
|
1854
2379
|
if (ff) out.fontFamily = ff;
|
|
1855
|
-
const fw = style
|
|
1856
|
-
if (fw != null)
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
const fs = style["font-style"];
|
|
1861
|
-
if (fs === "italic" || fs === "normal") out.fontStyle = fs;
|
|
1862
|
-
if (style["direction"] === "rtl") out.direction = "rtl";
|
|
2380
|
+
const fw = styleValue(style, "font-weight", onWarn);
|
|
2381
|
+
if (fw != null) out.fontWeight = fw;
|
|
2382
|
+
const fs = styleValue(style, "font-style", onWarn);
|
|
2383
|
+
if (fs) out.fontStyle = fs;
|
|
2384
|
+
if (styleValue(style, "direction") === "rtl") out.direction = "rtl";
|
|
1863
2385
|
const ltr = out.direction !== "rtl";
|
|
1864
|
-
const anchor = style
|
|
2386
|
+
const anchor = styleValue(style, "text-anchor") ?? (ltr ? void 0 : "start");
|
|
1865
2387
|
if (anchor === "start") out.align = ltr ? "left" : "right";
|
|
1866
2388
|
else if (anchor === "middle") out.align = "center";
|
|
1867
2389
|
else if (anchor === "end") out.align = ltr ? "right" : "left";
|
|
1868
|
-
const ls = style
|
|
1869
|
-
if (ls != null)
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
if (decoration.strikethrough) out.strikethrough = true;
|
|
1876
|
-
if (decoration.overline) out.overline = true;
|
|
1877
|
-
const transform = parseTextTransform(style["text-transform"] ?? null);
|
|
2390
|
+
const ls = styleValue(style, "letter-spacing", onWarn);
|
|
2391
|
+
if (ls != null) out.letterSpacing = ls;
|
|
2392
|
+
const decoration = styleValue(style, "text-decoration", onWarn);
|
|
2393
|
+
if (decoration?.underline) out.underline = true;
|
|
2394
|
+
if (decoration?.strikethrough) out.strikethrough = true;
|
|
2395
|
+
if (decoration?.overline) out.overline = true;
|
|
2396
|
+
const transform = styleValue(style, "text-transform", onWarn);
|
|
1878
2397
|
if (transform && transform !== "none") out.textTransform = transform;
|
|
2398
|
+
const variant = styleValue(style, "font-variant", onWarn);
|
|
2399
|
+
if (variant === "small-caps") out.fontVariantCaps = variant;
|
|
1879
2400
|
return out;
|
|
1880
2401
|
}
|
|
1881
2402
|
function readTextPaint(style, gradients, onWarn, nonScalingStroke = false) {
|
|
1882
2403
|
const out = {};
|
|
1883
2404
|
const fillRaw = resolveCurrentColor(style["fill"] ?? null, style);
|
|
1884
2405
|
if (fillRaw) {
|
|
1885
|
-
const parsed =
|
|
2406
|
+
const parsed = readProperty("fill", fillRaw);
|
|
1886
2407
|
if (parsed?.kind === "solid") {
|
|
1887
2408
|
out.fill = { fill: "solid", color: parsed.color };
|
|
1888
2409
|
} else if (parsed?.kind === "ref") {
|
|
@@ -1912,8 +2433,8 @@ function ingestMarkers(root, nodes, gradients, onWarn) {
|
|
|
1912
2433
|
if (id && !defs.has(id)) defs.set(id, all[i]);
|
|
1913
2434
|
}
|
|
1914
2435
|
const minted = /* @__PURE__ */ new Map();
|
|
1915
|
-
const resolve = (stroke,
|
|
1916
|
-
const w =
|
|
2436
|
+
const resolve = (stroke, width2) => {
|
|
2437
|
+
const w = width2 === void 0 ? 1 : typeof width2 === "number" ? width2 : width2.px;
|
|
1917
2438
|
for (const [field, role, attr] of MARKER_FIELDS) {
|
|
1918
2439
|
const id = stroke[field];
|
|
1919
2440
|
if (typeof id !== "string" || getMarker(id) !== void 0) continue;
|
|
@@ -1923,6 +2444,16 @@ function ingestMarkers(root, nodes, gradients, onWarn) {
|
|
|
1923
2444
|
delete stroke[field];
|
|
1924
2445
|
continue;
|
|
1925
2446
|
}
|
|
2447
|
+
const sized = sizedMarkerRef(el);
|
|
2448
|
+
if (sized) {
|
|
2449
|
+
if (getMarker(sized.key) === void 0 && !minted.has(sized.key)) {
|
|
2450
|
+
const entry2 = markerEntryFrom(el, id, role, resolveMarkerSize(sized, 1), gradients, onWarn);
|
|
2451
|
+
minted.set(sized.key, entry2 && { ...entry2, id: sized.key });
|
|
2452
|
+
}
|
|
2453
|
+
if (minted.get(sized.key) === null) delete stroke[field];
|
|
2454
|
+
else stroke[field] = sized;
|
|
2455
|
+
continue;
|
|
2456
|
+
}
|
|
1926
2457
|
const cacheKey = `${id}\0${role}\0${w}`;
|
|
1927
2458
|
if (!minted.has(cacheKey)) minted.set(cacheKey, markerEntryFrom(el, id, role, w, gradients, onWarn));
|
|
1928
2459
|
const entry = minted.get(cacheKey);
|
|
@@ -1945,6 +2476,22 @@ function ingestMarkers(root, nodes, gradients, onWarn) {
|
|
|
1945
2476
|
for (const entry of minted.values()) if (entry) out.set(entry.id, entry);
|
|
1946
2477
|
return [...out.values()];
|
|
1947
2478
|
}
|
|
2479
|
+
function weaselAttr(el, name) {
|
|
2480
|
+
return el.getAttributeNS(WEASEL_NS, name) ?? el.getAttribute(`${WEASEL_NS_PREFIX}:${name}`);
|
|
2481
|
+
}
|
|
2482
|
+
function sizedMarkerRef(el) {
|
|
2483
|
+
const key = weaselAttr(el, "key");
|
|
2484
|
+
const raw = weaselAttr(el, "size")?.trim();
|
|
2485
|
+
if (!key || !raw) return void 0;
|
|
2486
|
+
const px = raw.endsWith("px");
|
|
2487
|
+
const n = parseFloat(px ? raw.slice(0, -2) : raw);
|
|
2488
|
+
if (!Number.isFinite(n) || n <= 0) return void 0;
|
|
2489
|
+
return { key, size: px ? { px: n } : n };
|
|
2490
|
+
}
|
|
2491
|
+
function markerInsetAttr(el) {
|
|
2492
|
+
const n = parseFloat(weaselAttr(el, "inset") ?? "");
|
|
2493
|
+
return Number.isFinite(n) && n > 0 ? n : 0;
|
|
2494
|
+
}
|
|
1948
2495
|
function styleAt(el) {
|
|
1949
2496
|
const chain = [];
|
|
1950
2497
|
for (let e = el; e; e = e.parentElement) chain.unshift(e);
|
|
@@ -1970,13 +2517,12 @@ function markerOrient(raw) {
|
|
|
1970
2517
|
return { orient: rad, reverseAtStart: false };
|
|
1971
2518
|
}
|
|
1972
2519
|
function markerPaint(style, attr, gradients, onWarn) {
|
|
1973
|
-
const
|
|
1974
|
-
if (
|
|
1975
|
-
|
|
1976
|
-
onWarn(`a marker's ${attr}="context-fill" is read as the line's stroke paint`);
|
|
2520
|
+
const context = readProperty(attr, style[attr]);
|
|
2521
|
+
if (context?.kind === "context") {
|
|
2522
|
+
if (context.of === "fill") onWarn(`a marker's ${attr}="context-fill" is read as the line's stroke paint`);
|
|
1977
2523
|
return "line";
|
|
1978
2524
|
}
|
|
1979
|
-
const paint =
|
|
2525
|
+
const paint = readPaint2(style, attr, "#000000", gradients, onWarn);
|
|
1980
2526
|
if (paint.kind === "none") return "none";
|
|
1981
2527
|
if (paint.kind === "gradient") return paint.paint;
|
|
1982
2528
|
return paint.opacity != null ? { ...solid(paint.color), opacity: paint.opacity } : solid(paint.color);
|
|
@@ -2066,6 +2612,7 @@ function markerEntryFrom(el, id, role, strokeWidth, gradients, onWarn) {
|
|
|
2066
2612
|
pi += p.path.coords.length;
|
|
2067
2613
|
}
|
|
2068
2614
|
const fillRule = parts[0].path.fillRule ?? "nonzero";
|
|
2615
|
+
const inset = markerInsetAttr(el);
|
|
2069
2616
|
const round = (v) => Math.round(v * 1e4) / 1e4 + 0;
|
|
2070
2617
|
const hash = shortHash(JSON.stringify([
|
|
2071
2618
|
Array.from(commands),
|
|
@@ -2073,11 +2620,13 @@ function markerEntryFrom(el, id, role, strokeWidth, gradients, onWarn) {
|
|
|
2073
2620
|
fillRule,
|
|
2074
2621
|
fill,
|
|
2075
2622
|
outline,
|
|
2076
|
-
typeof orient === "number" ? round(orient) : orient
|
|
2623
|
+
typeof orient === "number" ? round(orient) : orient,
|
|
2624
|
+
...inset !== 0 ? [round(inset)] : []
|
|
2077
2625
|
]));
|
|
2078
2626
|
const key = id.endsWith(`-${hash}`) ? id : `${id}-${hash}`;
|
|
2079
2627
|
return {
|
|
2080
2628
|
id: key,
|
|
2629
|
+
reads: [],
|
|
2081
2630
|
path: ({ size }) => {
|
|
2082
2631
|
const scaled = new Float32Array(coords.length);
|
|
2083
2632
|
for (let i = 0; i < coords.length; i++) scaled[i] = coords[i] * size;
|
|
@@ -2086,7 +2635,7 @@ function markerEntryFrom(el, id, role, strokeWidth, gradients, onWarn) {
|
|
|
2086
2635
|
fill,
|
|
2087
2636
|
outline,
|
|
2088
2637
|
orient,
|
|
2089
|
-
inset
|
|
2638
|
+
inset
|
|
2090
2639
|
};
|
|
2091
2640
|
}
|
|
2092
2641
|
function serializeSvg(nodes, opts = {}) {
|
|
@@ -2198,16 +2747,10 @@ function registerPaintServers(nodes, registry) {
|
|
|
2198
2747
|
function registerTextPaint(paint, registry) {
|
|
2199
2748
|
if (paint && !("color" in paint)) registry.register(paint);
|
|
2200
2749
|
}
|
|
2201
|
-
function markerKeyOfRef(ref) {
|
|
2202
|
-
if (typeof ref === "string") return ref;
|
|
2203
|
-
if (ref && typeof ref === "object" && "key" in ref) return String(ref.key);
|
|
2204
|
-
return void 0;
|
|
2205
|
-
}
|
|
2206
2750
|
function registerMarkers(stroke, registry) {
|
|
2207
2751
|
if (!stroke) return;
|
|
2208
2752
|
for (const ref of [stroke.markerStart, stroke.markerMid, stroke.markerEnd]) {
|
|
2209
|
-
|
|
2210
|
-
if (key) registry.markerId(key);
|
|
2753
|
+
if (ref !== void 0) registry.markerId(ref);
|
|
2211
2754
|
}
|
|
2212
2755
|
}
|
|
2213
2756
|
function nodeXml(node, registry, namespaces, warn) {
|
|
@@ -2296,35 +2839,30 @@ function pathXml(node, registry, namespaces, warn) {
|
|
|
2296
2839
|
}
|
|
2297
2840
|
function paintAttrs(paint, name, registry, includeOpacity = true) {
|
|
2298
2841
|
if (paint.kind === "none") return [`${name}="none"`];
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
return [`${name}="${registry.ref(paint.paint)}"`];
|
|
2842
|
+
const out = [`${name}="${paint.kind === "solid" ? paint.color : registry.ref(paint.paint)}"`];
|
|
2843
|
+
if (includeOpacity) out.push(...opacityAttr(`${name}-opacity`, svgPaintOpacity(paint)));
|
|
2844
|
+
return out;
|
|
2845
|
+
}
|
|
2846
|
+
function svgPaintOpacity(paint) {
|
|
2847
|
+
if (paint.kind === "solid") return paint.opacity;
|
|
2848
|
+
return paint.kind === "gradient" ? paint.paint.opacity : void 0;
|
|
2307
2849
|
}
|
|
2308
|
-
function
|
|
2309
|
-
|
|
2310
|
-
|
|
2850
|
+
function opacityAttr(name, v) {
|
|
2851
|
+
return v != null && v !== 1 ? [`${name}="${trimNumber(v)}"`] : [];
|
|
2852
|
+
}
|
|
2853
|
+
function strokeWidthAttrs(width2) {
|
|
2854
|
+
if (typeof width2 === "number") return [`stroke-width="${trimNumber(width2)}"`];
|
|
2855
|
+
return [`stroke-width="${trimNumber(width2.px)}"`, 'vector-effect="non-scaling-stroke"'];
|
|
2311
2856
|
}
|
|
2312
2857
|
function coreStrokeAttrs(stroke, registry, warn) {
|
|
2313
2858
|
const paint = stroke?.paint;
|
|
2314
2859
|
if (!stroke || !paint) return [];
|
|
2315
|
-
const
|
|
2316
|
-
if (!((typeof
|
|
2860
|
+
const width2 = stroke.width ?? 1;
|
|
2861
|
+
if (!((typeof width2 === "object" ? width2.px : width2) > 0)) return [];
|
|
2317
2862
|
warnStrokeAlign(stroke.align, warn);
|
|
2318
|
-
const attrs = [];
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
if (paint.opacity != null && paint.opacity !== 1) {
|
|
2322
|
-
attrs.push(`stroke-opacity="${trimNumber(paint.opacity)}"`);
|
|
2323
|
-
}
|
|
2324
|
-
} else {
|
|
2325
|
-
attrs.push(`stroke="${registry.ref(paint)}"`);
|
|
2326
|
-
}
|
|
2327
|
-
attrs.push(...strokeWidthAttrs(width));
|
|
2863
|
+
const attrs = [`stroke="${"color" in paint ? paint.color : registry.ref(paint)}"`];
|
|
2864
|
+
attrs.push(...opacityAttr("stroke-opacity", paint.opacity));
|
|
2865
|
+
attrs.push(...strokeWidthAttrs(width2));
|
|
2328
2866
|
if (stroke.cap) attrs.push(`stroke-linecap="${stroke.cap}"`);
|
|
2329
2867
|
if (stroke.join) attrs.push(`stroke-linejoin="${stroke.join}"`);
|
|
2330
2868
|
if (stroke.dash && stroke.dash.length > 0) {
|
|
@@ -2338,8 +2876,7 @@ function coreStrokeAttrs(stroke, registry, warn) {
|
|
|
2338
2876
|
]) {
|
|
2339
2877
|
const ref = stroke[field];
|
|
2340
2878
|
if (ref === void 0) continue;
|
|
2341
|
-
const
|
|
2342
|
-
const id = registry.markerId(key);
|
|
2879
|
+
const id = registry.markerId(ref);
|
|
2343
2880
|
if (id === void 0) continue;
|
|
2344
2881
|
attrs.push(`${attr}="url(#${id})"`);
|
|
2345
2882
|
}
|
|
@@ -2349,10 +2886,7 @@ function strokeAttrsFor(stroke, registry, warn) {
|
|
|
2349
2886
|
warnStrokeAlign(stroke.align, warn);
|
|
2350
2887
|
const attrs = paintAttrs(stroke.paint, "stroke", registry, false);
|
|
2351
2888
|
attrs.push(...strokeWidthAttrs(stroke.width));
|
|
2352
|
-
|
|
2353
|
-
if (opacity != null && opacity !== 1) {
|
|
2354
|
-
attrs.push(`stroke-opacity="${trimNumber(opacity)}"`);
|
|
2355
|
-
}
|
|
2889
|
+
attrs.push(...opacityAttr("stroke-opacity", stroke.opacity ?? svgPaintOpacity(stroke.paint)));
|
|
2356
2890
|
if (stroke.cap) {
|
|
2357
2891
|
attrs.push(`stroke-linecap="${stroke.cap}"`);
|
|
2358
2892
|
}
|
|
@@ -2429,18 +2963,21 @@ function textXml(node, registry, namespaces, warn) {
|
|
|
2429
2963
|
if (style?.fontFamily) attrs.push(`font-family="${escapeAttr2(style.fontFamily)}"`);
|
|
2430
2964
|
if (style?.fontWeight != null) attrs.push(`font-weight="${String(style.fontWeight)}"`);
|
|
2431
2965
|
if (style?.fontStyle && style.fontStyle !== "normal") attrs.push(`font-style="${style.fontStyle}"`);
|
|
2966
|
+
if (style?.fontVariantCaps === "small-caps") attrs.push('font-variant="small-caps"');
|
|
2432
2967
|
const direction = style?.direction ?? "ltr";
|
|
2433
2968
|
if (direction === "rtl") attrs.push('direction="rtl"');
|
|
2434
2969
|
if (style?.align != null || direction === "rtl") {
|
|
2435
2970
|
const align = style?.align ?? "left";
|
|
2436
|
-
const anchor = align === "center" ? "middle" : align === "start" ? "start" : align === "end" ? "end" : align === "left" === (direction === "ltr") ? "start" : "end";
|
|
2971
|
+
const anchor = align === "center" ? "middle" : align === "start" || align === "justify" ? "start" : align === "end" ? "end" : align === "left" === (direction === "ltr") ? "start" : "end";
|
|
2437
2972
|
if (anchor !== "start") attrs.push(`text-anchor="${anchor}"`);
|
|
2973
|
+
if (align === "justify") attrs.push('data-weasel-align="justify"');
|
|
2438
2974
|
}
|
|
2439
2975
|
if (style?.letterSpacing != null && style.letterSpacing !== 0) {
|
|
2440
2976
|
attrs.push(`letter-spacing="${trimNumber(resolveScreenLength(style.letterSpacing, 1))}"`);
|
|
2441
2977
|
}
|
|
2442
2978
|
const decoration = textDecorationValue(style?.underline, style?.strikethrough, style?.overline);
|
|
2443
2979
|
if (decoration) attrs.push(`text-decoration="${decoration}"`);
|
|
2980
|
+
if (style?.script) attrs.push(`baseline-shift="${style.script}"`);
|
|
2444
2981
|
if (style?.textTransform && style.textTransform !== "none") {
|
|
2445
2982
|
attrs.push(`style="text-transform:${style.textTransform}"`);
|
|
2446
2983
|
}
|
|
@@ -2455,14 +2992,8 @@ function textXml(node, registry, namespaces, warn) {
|
|
|
2455
2992
|
if (node.fill === null) {
|
|
2456
2993
|
attrs.push('fill="none"');
|
|
2457
2994
|
} else if (node.fill) {
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
if (node.fill.opacity != null && node.fill.opacity !== 1) {
|
|
2461
|
-
attrs.push(`fill-opacity="${trimNumber(node.fill.opacity)}"`);
|
|
2462
|
-
}
|
|
2463
|
-
} else {
|
|
2464
|
-
attrs.push(`fill="${registry.ref(node.fill)}"`);
|
|
2465
|
-
}
|
|
2995
|
+
attrs.push(`fill="${"color" in node.fill ? node.fill.color : registry.ref(node.fill)}"`);
|
|
2996
|
+
attrs.push(...opacityAttr("fill-opacity", node.fill.opacity));
|
|
2466
2997
|
}
|
|
2467
2998
|
for (const a of coreStrokeAttrs(node.stroke, registry, warn)) attrs.push(a);
|
|
2468
2999
|
if (node.opacity != null && node.opacity !== 1) {
|
|
@@ -2474,17 +3005,25 @@ function textXml(node, registry, namespaces, warn) {
|
|
|
2474
3005
|
const deg = node.rotation * 180 / Math.PI;
|
|
2475
3006
|
attrs.push(`transform="rotate(${trimNumber(deg)} ${trimNumber(cx)} ${trimNumber(cy)})"`);
|
|
2476
3007
|
}
|
|
2477
|
-
const body = node.runs && node.runs.length > 0 ? node.runs.map((r) => runXml(r, registry, warn)).join("") : escapeText(node.text);
|
|
3008
|
+
const body = node.runs && node.runs.length > 0 ? node.runs.map((r) => runXml(r, style, registry, warn)).join("") : escapeText(node.text);
|
|
2478
3009
|
const metaAttrs = metaAttrsXml(node.meta, namespaces);
|
|
2479
3010
|
const metaEls = metaElementsXml(node.meta, namespaces);
|
|
2480
3011
|
return `<text ${attrs.join(" ")}${metaAttrs}>${body}${metaEls}</text>`;
|
|
2481
3012
|
}
|
|
2482
|
-
function
|
|
3013
|
+
function scriptSizeFace(run, style) {
|
|
3014
|
+
if (run.script == null || run.baselineShift == null) return void 0;
|
|
3015
|
+
const face = resolveRunFace(run, resolveTextStyle(style));
|
|
3016
|
+
return { family: face.fontFamily, weight: face.fontWeight, style: face.fontStyle };
|
|
3017
|
+
}
|
|
3018
|
+
function runXml(run, style, registry, warn) {
|
|
2483
3019
|
const attrs = [];
|
|
2484
|
-
if (run.
|
|
3020
|
+
if (run.fontWeight != null) attrs.push(`font-weight="${run.fontWeight}"`);
|
|
3021
|
+
else if (run.bold) attrs.push(`font-weight="700"`);
|
|
2485
3022
|
if (run.italic) attrs.push(`font-style="italic"`);
|
|
3023
|
+
if (run.fontVariantCaps) attrs.push(`font-variant="${run.fontVariantCaps}"`);
|
|
2486
3024
|
if (run.fontFamily) attrs.push(`font-family="${escapeAttr2(run.fontFamily)}"`);
|
|
2487
|
-
const
|
|
3025
|
+
const face = scriptSizeFace(run, style);
|
|
3026
|
+
const presetScale = face && run.script ? scriptMetricsFor(face.family, face.weight, face.style)[run.script].size : void 0;
|
|
2488
3027
|
const scale = run.fontScale ?? presetScale;
|
|
2489
3028
|
if (run.fontSize != null) attrs.push(`font-size="${trimNumber(resolveScreenLength(run.fontSize, 1))}"`);
|
|
2490
3029
|
else if (scale != null) attrs.push(`font-size="${trimNumber(scale * 100)}%"`);
|
|
@@ -2535,6 +3074,31 @@ function pathBounds(path) {
|
|
|
2535
3074
|
const b = boundsOfPath(path);
|
|
2536
3075
|
return { minX: b.x, minY: b.y, maxX: b.x + b.width, maxY: b.y + b.height };
|
|
2537
3076
|
}
|
|
3077
|
+
function svgNeeds(nodes) {
|
|
3078
|
+
const registry = new PaintServerRegistry();
|
|
3079
|
+
registerPaintServers(nodes, registry);
|
|
3080
|
+
const fonts = /* @__PURE__ */ new Map();
|
|
3081
|
+
const walk = (list) => {
|
|
3082
|
+
for (const n of list) {
|
|
3083
|
+
if (n.kind === "group") walk(n.children);
|
|
3084
|
+
else if (n.kind === "text") {
|
|
3085
|
+
for (const run of n.runs ?? []) {
|
|
3086
|
+
const face = scriptSizeFace(run, n.style);
|
|
3087
|
+
if (face) fonts.set(`${face.family}|${face.weight}|${face.style}`, face);
|
|
3088
|
+
}
|
|
3089
|
+
}
|
|
3090
|
+
}
|
|
3091
|
+
};
|
|
3092
|
+
walk(nodes);
|
|
3093
|
+
return { fonts: [...fonts.values()], paintKinds: registry.paintKinds() };
|
|
3094
|
+
}
|
|
3095
|
+
function warmSvg(nodes) {
|
|
3096
|
+
const needs = svgNeeds(nodes);
|
|
3097
|
+
return Promise.all([
|
|
3098
|
+
warmFonts(needs.fonts),
|
|
3099
|
+
warmPaintKinds(needs.paintKinds.filter(isPaintKindKnown))
|
|
3100
|
+
]).then(() => void 0);
|
|
3101
|
+
}
|
|
2538
3102
|
var CASCADE_OFFSET_PX = 24;
|
|
2539
3103
|
var VIEWPORT_FIT = 0.9;
|
|
2540
3104
|
function readFileText(file) {
|
|
@@ -2549,17 +3113,31 @@ var svgIdCounter = 0;
|
|
|
2549
3113
|
function freshSvgNodeId() {
|
|
2550
3114
|
return `n${(svgIdCounter++).toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
|
|
2551
3115
|
}
|
|
2552
|
-
function
|
|
3116
|
+
function fillDataFromSvg(paint, box) {
|
|
2553
3117
|
if (!paint) return void 0;
|
|
2554
3118
|
if (paint.kind === "none") return null;
|
|
2555
|
-
if (paint.kind === "solid")
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
return
|
|
3119
|
+
if (paint.kind === "solid") {
|
|
3120
|
+
return paint.opacity != null ? { ...solid(paint.color), opacity: paint.opacity } : solid(paint.color);
|
|
3121
|
+
}
|
|
3122
|
+
return fillInBoxFrame(paint.paint, box);
|
|
3123
|
+
}
|
|
3124
|
+
function fadeFill(fill, k) {
|
|
3125
|
+
return k === 1 ? fill : { ...fill, opacity: (fill.opacity ?? 1) * k };
|
|
3126
|
+
}
|
|
3127
|
+
function fadeStroke(stroke, k) {
|
|
3128
|
+
return k === 1 || !stroke.paint ? stroke : { ...stroke, paint: fadeFill(stroke.paint, k) };
|
|
3129
|
+
}
|
|
3130
|
+
function fadeRun(run, k) {
|
|
3131
|
+
if (k === 1 || !run.fill && !run.stroke) return run;
|
|
3132
|
+
return {
|
|
3133
|
+
...run,
|
|
3134
|
+
...run.fill ? { fill: fadeFill(run.fill, k) } : {},
|
|
3135
|
+
...run.stroke ? { stroke: fadeStroke(run.stroke, k) } : {}
|
|
3136
|
+
};
|
|
2559
3137
|
}
|
|
2560
3138
|
function strokeDataFromSvg(stroke, box) {
|
|
2561
3139
|
if (!stroke || stroke.paint.kind === "none") return void 0;
|
|
2562
|
-
const paint =
|
|
3140
|
+
const paint = fillDataFromSvg(stroke.paint, box);
|
|
2563
3141
|
if (paint === void 0 || paint === null) return void 0;
|
|
2564
3142
|
return {
|
|
2565
3143
|
paint: stroke.opacity !== void 0 ? { ...paint, opacity: stroke.opacity } : paint,
|
|
@@ -2573,22 +3151,6 @@ function strokeDataFromSvg(stroke, box) {
|
|
|
2573
3151
|
...stroke.markerEnd !== void 0 ? { markerEnd: stroke.markerEnd } : {}
|
|
2574
3152
|
};
|
|
2575
3153
|
}
|
|
2576
|
-
function svgImageFromKit(image, pose) {
|
|
2577
|
-
const node = {
|
|
2578
|
-
kind: "image",
|
|
2579
|
-
href: image.src,
|
|
2580
|
-
x: pose.x,
|
|
2581
|
-
y: pose.y,
|
|
2582
|
-
width: pose.width,
|
|
2583
|
-
height: pose.height
|
|
2584
|
-
};
|
|
2585
|
-
if (image.source) node.source = { ...image.source };
|
|
2586
|
-
if (image.flipX) node.flipX = true;
|
|
2587
|
-
if (image.flipY) node.flipY = true;
|
|
2588
|
-
if (image.opacity != null) node.opacity = image.opacity;
|
|
2589
|
-
if (pose.rotation) node.rotation = pose.rotation;
|
|
2590
|
-
return node;
|
|
2591
|
-
}
|
|
2592
3154
|
function fillInBoxFrame(fill, box) {
|
|
2593
3155
|
const units = "units" in fill ? fill.units : void 0;
|
|
2594
3156
|
return units === "world" ? fillToBoundsFrame(fill, box) : fill;
|
|
@@ -2604,28 +3166,36 @@ function strokeInBoxFrame(stroke, box) {
|
|
|
2604
3166
|
const paint = fillInBoxFrame(stroke.paint, box);
|
|
2605
3167
|
return paint === stroke.paint ? stroke : { ...stroke, paint };
|
|
2606
3168
|
}
|
|
2607
|
-
function svgNodesToKitDrafts(
|
|
3169
|
+
function svgNodesToKitDrafts(input, nextId, options = {}) {
|
|
3170
|
+
const nodes = isNodeList(input) ? input : input.nodes;
|
|
3171
|
+
if (!isNodeList(input)) {
|
|
3172
|
+
for (const m of input.markers ?? []) if (getMarker(m.id) === void 0) registerMarker(m);
|
|
3173
|
+
}
|
|
2608
3174
|
const drafts = [];
|
|
2609
|
-
const
|
|
3175
|
+
const pushLeaf = (source, parentId, pose, data) => {
|
|
3176
|
+
const id = nextId(source);
|
|
3177
|
+
const out = options.leaf ? options.leaf(id, { pose, data }, source) : data;
|
|
3178
|
+
if (out === null) return null;
|
|
3179
|
+
drafts.push({ kind: "leaf", id, parentId, pose, data: out });
|
|
3180
|
+
return pose;
|
|
3181
|
+
};
|
|
3182
|
+
const visit = (n, parentId, inherited2) => {
|
|
3183
|
+
const k = inherited2 * (n.opacity ?? 1);
|
|
2610
3184
|
if (n.kind === "group") {
|
|
2611
3185
|
const draft = {
|
|
2612
3186
|
kind: "container",
|
|
2613
|
-
id: nextId(),
|
|
3187
|
+
id: nextId(n),
|
|
2614
3188
|
parentId,
|
|
2615
3189
|
pose: { x: 0, y: 0, width: 0, height: 0 }
|
|
2616
3190
|
};
|
|
2617
3191
|
drafts.push(draft);
|
|
2618
|
-
|
|
2619
|
-
for (const c of n.children) {
|
|
2620
|
-
const b2 = visit(c, draft.id);
|
|
2621
|
-
if (b2) acc = acc ? unionRect(acc, b2) : b2;
|
|
2622
|
-
}
|
|
3192
|
+
const acc = unionAABB(n.children.map((c) => visit(c, draft.id, k)));
|
|
2623
3193
|
if (!acc) {
|
|
2624
3194
|
drafts.splice(drafts.indexOf(draft), 1);
|
|
2625
3195
|
return null;
|
|
2626
3196
|
}
|
|
2627
3197
|
draft.pose = acc;
|
|
2628
|
-
return
|
|
3198
|
+
return draft.pose;
|
|
2629
3199
|
}
|
|
2630
3200
|
if (n.kind === "text") {
|
|
2631
3201
|
const pose2 = {
|
|
@@ -2641,65 +3211,48 @@ function svgNodesToKitDrafts(nodes, nextId) {
|
|
|
2641
3211
|
width: pose2.width,
|
|
2642
3212
|
height: pose2.height
|
|
2643
3213
|
};
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
// `!== undefined`, not a truthiness test: `null` is the document
|
|
2655
|
-
// saying `fill="none"`, and absent takes the painter's default.
|
|
2656
|
-
...n.fill !== void 0 ? { fill: n.fill === null ? null : fillInBoxFrame(n.fill, box) } : {},
|
|
2657
|
-
...n.stroke !== void 0 ? { stroke: strokeInBoxFrame(n.stroke, box) } : {}
|
|
2658
|
-
}
|
|
3214
|
+
const fill2 = n.fill === void 0 && k !== 1 ? DEFAULT_TEXT_STYLE.fill : n.fill;
|
|
3215
|
+
return pushLeaf(n, parentId, pose2, {
|
|
3216
|
+
text: n.text,
|
|
3217
|
+
...n.style ? { style: n.style } : {},
|
|
3218
|
+
...n.verticalAlign ? { verticalAlign: n.verticalAlign } : {},
|
|
3219
|
+
...n.runs ? { runs: n.runs.map((r) => fadeRun(runInBoxFrame(r, box), k)) } : {},
|
|
3220
|
+
// `!== undefined`, not a truthiness test: `null` is the document
|
|
3221
|
+
// saying `fill="none"`, and absent takes the painter's default.
|
|
3222
|
+
...fill2 !== void 0 ? { fill: fill2 === null ? null : fadeFill(fillInBoxFrame(fill2, box), k) } : {},
|
|
3223
|
+
...n.stroke !== void 0 ? { stroke: fadeStroke(strokeInBoxFrame(n.stroke, box), k) } : {}
|
|
2659
3224
|
});
|
|
2660
|
-
return pose2;
|
|
2661
3225
|
}
|
|
2662
3226
|
if (n.kind === "image") {
|
|
2663
3227
|
const pose2 = { x: n.x, y: n.y, width: n.width, height: n.height };
|
|
2664
3228
|
if (n.rotation) pose2.rotation = n.rotation;
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
src: n.href,
|
|
2673
|
-
...n.opacity != null ? { opacity: n.opacity } : {},
|
|
2674
|
-
...n.source ? { source: { ...n.source } } : {},
|
|
2675
|
-
...n.flipX ? { flipX: true } : {},
|
|
2676
|
-
...n.flipY ? { flipY: true } : {}
|
|
2677
|
-
}
|
|
3229
|
+
return pushLeaf(n, parentId, pose2, {
|
|
3230
|
+
image: {
|
|
3231
|
+
src: n.href,
|
|
3232
|
+
...k !== 1 ? { opacity: k } : {},
|
|
3233
|
+
...n.source ? { source: { ...n.source } } : {},
|
|
3234
|
+
...n.flipX ? { flipX: true } : {},
|
|
3235
|
+
...n.flipY ? { flipY: true } : {}
|
|
2678
3236
|
}
|
|
2679
3237
|
});
|
|
2680
|
-
return pose2;
|
|
2681
3238
|
}
|
|
2682
3239
|
const b = n.path.kind === "rect" ? { x: n.path.x, y: n.path.y, width: n.path.width, height: n.path.height } : boundsOfPath(n.path);
|
|
2683
3240
|
const pose = { x: b.x, y: b.y, width: b.width, height: b.height };
|
|
2684
3241
|
if (n.rotation) pose.rotation = n.rotation;
|
|
2685
|
-
const fill =
|
|
3242
|
+
const fill = fillDataFromSvg(n.fill, b);
|
|
2686
3243
|
const strokeFromSvg = strokeDataFromSvg(n.stroke, b);
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
pose,
|
|
2692
|
-
data: {
|
|
2693
|
-
path: n.path,
|
|
2694
|
-
...fill !== void 0 ? { fill } : {},
|
|
2695
|
-
...strokeFromSvg !== void 0 ? { stroke: strokeFromSvg } : {}
|
|
2696
|
-
}
|
|
3244
|
+
return pushLeaf(n, parentId, pose, {
|
|
3245
|
+
path: n.path,
|
|
3246
|
+
...fill !== void 0 ? { fill: fill && fadeFill(fill, k) } : {},
|
|
3247
|
+
...strokeFromSvg !== void 0 ? { stroke: fadeStroke(strokeFromSvg, k) } : {}
|
|
2697
3248
|
});
|
|
2698
|
-
return pose;
|
|
2699
3249
|
};
|
|
2700
|
-
for (const n of nodes) visit(n, null);
|
|
3250
|
+
for (const n of nodes) visit(n, null, 1);
|
|
2701
3251
|
return drafts;
|
|
2702
3252
|
}
|
|
3253
|
+
function isNodeList(input) {
|
|
3254
|
+
return Array.isArray(input);
|
|
3255
|
+
}
|
|
2703
3256
|
function scaleTextData(data, scale) {
|
|
2704
3257
|
if (scale === 1 || typeof data.text !== "string") return data;
|
|
2705
3258
|
const style = data.style ?? {};
|
|
@@ -2713,13 +3266,6 @@ function scaleTextData(data, scale) {
|
|
|
2713
3266
|
style: { ...style, fontSize: scaleSize(style.fontSize ?? resolveTextStyle(style).fontSize) }
|
|
2714
3267
|
};
|
|
2715
3268
|
}
|
|
2716
|
-
function unionRect(a, b) {
|
|
2717
|
-
const minX = Math.min(a.x, b.x);
|
|
2718
|
-
const minY = Math.min(a.y, b.y);
|
|
2719
|
-
const maxX = Math.max(a.x + a.width, b.x + b.width);
|
|
2720
|
-
const maxY = Math.max(a.y + a.height, b.y + b.height);
|
|
2721
|
-
return { x: minX, y: minY, width: maxX - minX, height: maxY - minY };
|
|
2722
|
-
}
|
|
2723
3269
|
async function unpackSvgFiles(files, ctx) {
|
|
2724
3270
|
const layer = ctx.scene.layers[0]?.id ?? "default";
|
|
2725
3271
|
let index = 0;
|
|
@@ -2728,8 +3274,7 @@ async function unpackSvgFiles(files, ctx) {
|
|
|
2728
3274
|
const text = await readFileText(file);
|
|
2729
3275
|
const parsed = parseSvg(text);
|
|
2730
3276
|
for (const w of parsed.warnings) dwarn("ingest", `svg "${file.name}":`, w);
|
|
2731
|
-
|
|
2732
|
-
let drafts = svgNodesToKitDrafts(parsed.nodes, freshSvgNodeId);
|
|
3277
|
+
let drafts = svgNodesToKitDrafts(parsed, freshSvgNodeId);
|
|
2733
3278
|
if (drafts.length === 0) {
|
|
2734
3279
|
console.warn(`weasel ingest: svg "${file.name}" parsed to no drawable nodes`);
|
|
2735
3280
|
continue;
|
|
@@ -2737,13 +3282,13 @@ async function unpackSvgFiles(files, ctx) {
|
|
|
2737
3282
|
const roots = drafts.filter((d) => d.parentId === null);
|
|
2738
3283
|
if (roots.length > 1) {
|
|
2739
3284
|
const wrapperId = freshSvgNodeId();
|
|
2740
|
-
const union2 = roots.map((d) => d.pose)
|
|
3285
|
+
const union2 = unionAABB(roots.map((d) => d.pose));
|
|
2741
3286
|
drafts = [
|
|
2742
3287
|
{ kind: "container", id: wrapperId, parentId: null, pose: union2 },
|
|
2743
3288
|
...drafts.map((d) => d.parentId === null ? { ...d, parentId: wrapperId } : d)
|
|
2744
3289
|
];
|
|
2745
3290
|
}
|
|
2746
|
-
const union = drafts.filter((d) => d.parentId === null).map((d) => d.pose)
|
|
3291
|
+
const union = unionAABB(drafts.filter((d) => d.parentId === null).map((d) => d.pose));
|
|
2747
3292
|
const view = ctx.viewportWorldRect();
|
|
2748
3293
|
const scale = Math.min(
|
|
2749
3294
|
1,
|
|
@@ -2784,7 +3329,115 @@ async function unpackSvgFiles(files, ctx) {
|
|
|
2784
3329
|
}
|
|
2785
3330
|
}
|
|
2786
3331
|
}
|
|
3332
|
+
function svgPaintFromKit(fill, box) {
|
|
3333
|
+
if (fill === null) return { kind: "none" };
|
|
3334
|
+
if (fill.fill === void 0 || fill.fill === "solid") {
|
|
3335
|
+
return { kind: "solid", color: fill.color, ...fill.opacity != null ? { opacity: fill.opacity } : {} };
|
|
3336
|
+
}
|
|
3337
|
+
return { kind: "gradient", paint: fillInPoseFrame(fill, box) };
|
|
3338
|
+
}
|
|
3339
|
+
function svgStrokeFromKit(stroke, box) {
|
|
3340
|
+
if (!stroke?.paint) return void 0;
|
|
3341
|
+
const width2 = stroke.width ?? 1;
|
|
3342
|
+
if (!((typeof width2 === "object" ? width2.px : width2) > 0)) return void 0;
|
|
3343
|
+
const opacity = stroke.paint.opacity;
|
|
3344
|
+
return {
|
|
3345
|
+
paint: svgPaintFromKit(stroke.paint, box),
|
|
3346
|
+
width: width2,
|
|
3347
|
+
...opacity != null ? { opacity } : {},
|
|
3348
|
+
...stroke.cap !== void 0 ? { cap: stroke.cap } : {},
|
|
3349
|
+
...stroke.join !== void 0 ? { join: stroke.join } : {},
|
|
3350
|
+
...stroke.dash !== void 0 ? { dash: stroke.dash } : {},
|
|
3351
|
+
...stroke.miterLimit !== void 0 ? { miterLimit: stroke.miterLimit } : {},
|
|
3352
|
+
...stroke.align !== void 0 ? { align: stroke.align } : {},
|
|
3353
|
+
...stroke.markerStart !== void 0 ? { markerStart: stroke.markerStart } : {},
|
|
3354
|
+
...stroke.markerMid !== void 0 ? { markerMid: stroke.markerMid } : {},
|
|
3355
|
+
...stroke.markerEnd !== void 0 ? { markerEnd: stroke.markerEnd } : {}
|
|
3356
|
+
};
|
|
3357
|
+
}
|
|
3358
|
+
function svgImageFromKit(image, pose) {
|
|
3359
|
+
const node = {
|
|
3360
|
+
kind: "image",
|
|
3361
|
+
href: image.src,
|
|
3362
|
+
x: pose.x,
|
|
3363
|
+
y: pose.y,
|
|
3364
|
+
width: pose.width,
|
|
3365
|
+
height: pose.height
|
|
3366
|
+
};
|
|
3367
|
+
if (image.source) node.source = { ...image.source };
|
|
3368
|
+
if (image.flipX) node.flipX = true;
|
|
3369
|
+
if (image.flipY) node.flipY = true;
|
|
3370
|
+
if (image.opacity != null) node.opacity = image.opacity;
|
|
3371
|
+
if (pose.rotation) node.rotation = pose.rotation;
|
|
3372
|
+
return node;
|
|
3373
|
+
}
|
|
3374
|
+
function strokeInPoseFrame(stroke, box) {
|
|
3375
|
+
return stroke.paint ? { ...stroke, paint: fillInPoseFrame(stroke.paint, box) } : stroke;
|
|
3376
|
+
}
|
|
3377
|
+
function runInPoseFrame(run, box) {
|
|
3378
|
+
if (!run.fill && !run.stroke) return run;
|
|
3379
|
+
return {
|
|
3380
|
+
...run,
|
|
3381
|
+
...run.fill ? { fill: fillInPoseFrame(run.fill, box) } : {},
|
|
3382
|
+
...run.stroke ? { stroke: strokeInPoseFrame(run.stroke, box) } : {}
|
|
3383
|
+
};
|
|
3384
|
+
}
|
|
3385
|
+
function svgLeafFromKit(data, pose) {
|
|
3386
|
+
if (data.image) return svgImageFromKit(data.image, pose);
|
|
3387
|
+
if (data.text != null) {
|
|
3388
|
+
const node2 = {
|
|
3389
|
+
kind: "text",
|
|
3390
|
+
x: pose.x,
|
|
3391
|
+
y: pose.y,
|
|
3392
|
+
width: pose.width,
|
|
3393
|
+
height: pose.height,
|
|
3394
|
+
text: data.text
|
|
3395
|
+
};
|
|
3396
|
+
if (data.style) node2.style = data.style;
|
|
3397
|
+
if (data.runs) node2.runs = data.runs.map((r) => runInPoseFrame(r, pose));
|
|
3398
|
+
if (data.verticalAlign) node2.verticalAlign = data.verticalAlign;
|
|
3399
|
+
if (data.fill !== void 0) node2.fill = data.fill && fillInPoseFrame(data.fill, pose);
|
|
3400
|
+
if (data.stroke) node2.stroke = strokeInPoseFrame(data.stroke, pose);
|
|
3401
|
+
if (pose.rotation) node2.rotation = pose.rotation;
|
|
3402
|
+
return node2;
|
|
3403
|
+
}
|
|
3404
|
+
if (!data.path) return null;
|
|
3405
|
+
const stroke = svgStrokeFromKit(data.stroke, pose);
|
|
3406
|
+
const fill = data.fill !== void 0 ? data.fill : stroke ? null : DEFAULT_SHAPE_FILL;
|
|
3407
|
+
const node = {
|
|
3408
|
+
kind: "path",
|
|
3409
|
+
path: pathInPoseFrame(data.path, pose),
|
|
3410
|
+
fill: svgPaintFromKit(fill, pose)
|
|
3411
|
+
};
|
|
3412
|
+
if (stroke) node.stroke = stroke;
|
|
3413
|
+
if (pose.rotation) node.rotation = pose.rotation;
|
|
3414
|
+
return node;
|
|
3415
|
+
}
|
|
3416
|
+
function svgNodesFromKit(tree, options = {}) {
|
|
3417
|
+
const { include, leaf, group } = options;
|
|
3418
|
+
const emit = (id) => {
|
|
3419
|
+
if (include && !include(id)) return null;
|
|
3420
|
+
const node = tree.get(id);
|
|
3421
|
+
if (!node) return null;
|
|
3422
|
+
if (node.kind === "container") {
|
|
3423
|
+
const children = [];
|
|
3424
|
+
for (const c of tree.childrenOf(id)) {
|
|
3425
|
+
const child = emit(c);
|
|
3426
|
+
if (child) children.push(child);
|
|
3427
|
+
}
|
|
3428
|
+
const g = { kind: "group", children };
|
|
3429
|
+
return group ? group(id, g) : g;
|
|
3430
|
+
}
|
|
3431
|
+
return leaf ? leaf(id, node) : svgLeafFromKit(node.data, node.pose);
|
|
3432
|
+
};
|
|
3433
|
+
const out = [];
|
|
3434
|
+
for (const id of options.roots ?? tree.roots) {
|
|
3435
|
+
const n = emit(id);
|
|
3436
|
+
if (n) out.push(n);
|
|
3437
|
+
}
|
|
3438
|
+
return out;
|
|
3439
|
+
}
|
|
2787
3440
|
|
|
2788
|
-
export { IDENTITY_MATRIX, parseSvg, serializeSvg, strokeDataFromSvg, svgImageFromKit, svgNodesToKitDrafts, tilePreviewCssUrl, tilePreviewSvg, unpackSvgFiles };
|
|
3441
|
+
export { DEFAULT_MEDIA_ENVIRONMENT, IDENTITY_MATRIX, evaluateMediaQuery, evaluateSupports, fillDataFromSvg, nativeSvgKind, nativeSvgSpace, parseSvg, serializeSvg, strokeDataFromSvg, svgImageFromKit, svgLeafFromKit, svgNeeds, svgNodesFromKit, svgNodesToKitDrafts, svgPaintFromKit, svgStrokeFromKit, tilePreviewCssUrl, tilePreviewSvg, unpackSvgFiles, warmSvg };
|
|
2789
3442
|
//# sourceMappingURL=index.js.map
|
|
2790
3443
|
//# sourceMappingURL=index.js.map
|