@weasel-js/svg 1.7.0 → 1.7.2
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 +24 -0
- package/dist/index.d.ts +97 -22
- package/dist/index.js +871 -333
- 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;
|
|
@@ -756,11 +1361,11 @@ function inheritedAttr(el, elements, name, seen = /* @__PURE__ */ new Set()) {
|
|
|
756
1361
|
return inheritedAttr(ref, elements, name, seen);
|
|
757
1362
|
}
|
|
758
1363
|
function inheritedStops(el, elements, onWarn, seen = /* @__PURE__ */ new Set()) {
|
|
759
|
-
const
|
|
760
|
-
if (
|
|
1364
|
+
const own2 = readStops(el, onWarn);
|
|
1365
|
+
if (own2.length > 0) return own2;
|
|
761
1366
|
seen.add(el);
|
|
762
1367
|
const ref = hrefTarget(el, elements);
|
|
763
|
-
if (!ref || seen.has(ref)) return
|
|
1368
|
+
if (!ref || seen.has(ref)) return own2;
|
|
764
1369
|
return inheritedStops(ref, elements, onWarn, seen);
|
|
765
1370
|
}
|
|
766
1371
|
function parseRatio(raw) {
|
|
@@ -776,11 +1381,9 @@ function readStops(el, onWarn) {
|
|
|
776
1381
|
const offsetRaw = parseRatio(c.getAttribute("offset") ?? "0");
|
|
777
1382
|
const offset = Number.isFinite(offsetRaw) ? offsetRaw : 0;
|
|
778
1383
|
const colorAttr = ownProp(c, "stop-color") ?? "#000000";
|
|
779
|
-
const parsed =
|
|
780
|
-
if (parsed
|
|
781
|
-
const
|
|
782
|
-
const own = opacityAttr2 != null ? parseRatio(opacityAttr2) : NaN;
|
|
783
|
-
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;
|
|
784
1387
|
stops.push({ offset, color: alpha < 1 ? applyAlpha(parsed.color, alpha) : parsed.color });
|
|
785
1388
|
} else {
|
|
786
1389
|
onWarn?.(`gradient stop has unrecognized stop-color: ${colorAttr}`);
|
|
@@ -913,6 +1516,10 @@ var PaintServerRegistry = class {
|
|
|
913
1516
|
if (hasNativeSvgForm(paint)) return `url(#${id})`;
|
|
914
1517
|
return `url(#${id}) ${paintFallback(paint)}`;
|
|
915
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
|
+
}
|
|
916
1523
|
/** Whether any registered paint serializes outside SVG's own vocabulary, so
|
|
917
1524
|
* the root has to declare {@link WEASEL_NS}. */
|
|
918
1525
|
usesPrivateNamespace() {
|
|
@@ -995,9 +1602,10 @@ function paintServerXml(id, paint, onWarn) {
|
|
|
995
1602
|
if (paint.fill === "pattern") return patternXml(id, paint, onWarn);
|
|
996
1603
|
const builtin = gradientXml(id, paint);
|
|
997
1604
|
if (builtin) return builtin;
|
|
998
|
-
const
|
|
1605
|
+
const kind = getPaintKind(paint.fill);
|
|
1606
|
+
const custom = kind?.toSvg?.(id, paint);
|
|
999
1607
|
if (custom) return custom;
|
|
1000
|
-
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`);
|
|
1001
1609
|
return "";
|
|
1002
1610
|
}
|
|
1003
1611
|
function gradientUnitsAttr(units) {
|
|
@@ -1076,6 +1684,7 @@ function parseSvg(svg, opts = {}) {
|
|
|
1076
1684
|
if (!root || root.tagName.toLowerCase() !== "svg") {
|
|
1077
1685
|
return { nodes: [], warnings: ["root element is not <svg>"] };
|
|
1078
1686
|
}
|
|
1687
|
+
bindStylesheets(doc, mediaEnvironmentFor(root, opts.media), onWarn);
|
|
1079
1688
|
const namespaces = opts.namespaces ?? {};
|
|
1080
1689
|
const uriToPrefix = /* @__PURE__ */ new Map();
|
|
1081
1690
|
for (const [prefix, uri] of Object.entries(namespaces)) {
|
|
@@ -1093,10 +1702,10 @@ function parseSvg(svg, opts = {}) {
|
|
|
1093
1702
|
if (documentMeta) result.documentMeta = documentMeta;
|
|
1094
1703
|
const viewBox = parseViewBoxAttr(root.getAttribute("viewBox"));
|
|
1095
1704
|
if (viewBox) result.viewBox = viewBox;
|
|
1096
|
-
const
|
|
1097
|
-
if (
|
|
1098
|
-
const
|
|
1099
|
-
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;
|
|
1100
1709
|
for (let i = 0; i < root.children.length; i++) {
|
|
1101
1710
|
const c = root.children[i];
|
|
1102
1711
|
if (c.tagName.toLowerCase() === "title") {
|
|
@@ -1261,7 +1870,7 @@ function parseElement(el, ctm, style, gradients, clips, onWarn, uriToPrefix) {
|
|
|
1261
1870
|
const childCtm = multiply(ctm, local);
|
|
1262
1871
|
const childStyle = deriveStyle(style, el);
|
|
1263
1872
|
const children = parseChildren(el, childCtm, childStyle, gradients, clips, onWarn, uriToPrefix);
|
|
1264
|
-
const opacity2 =
|
|
1873
|
+
const opacity2 = ownValue(el, "opacity");
|
|
1265
1874
|
const group = { kind: "group", children };
|
|
1266
1875
|
const clip = clipFor(el, childCtm, clips, onWarn);
|
|
1267
1876
|
if (clip) group.clip = clip;
|
|
@@ -1323,11 +1932,10 @@ function parseElement(el, ctm, style, gradients, clips, onWarn, uriToPrefix) {
|
|
|
1323
1932
|
}
|
|
1324
1933
|
}
|
|
1325
1934
|
const leafStyle = deriveStyle(style, el);
|
|
1326
|
-
const fill =
|
|
1935
|
+
const fill = readPaint2(leafStyle, "fill", "#000000", gradients, onWarn);
|
|
1327
1936
|
const stroke = readStroke(leafStyle, gradients, onWarn, isNonScalingStroke(el));
|
|
1328
|
-
const opacity =
|
|
1329
|
-
|
|
1330
|
-
if (fillRuleRaw === "evenodd" && path.kind === "polygon") {
|
|
1937
|
+
const opacity = ownValue(el, "opacity");
|
|
1938
|
+
if (styleValue(leafStyle, "fill-rule") === "evenodd" && path.kind === "polygon") {
|
|
1331
1939
|
path = { ...path, fillRule: "evenodd" };
|
|
1332
1940
|
}
|
|
1333
1941
|
const node = { kind: "path", path, fill };
|
|
@@ -1393,10 +2001,10 @@ function polygonToRectIfAxisAligned(p) {
|
|
|
1393
2001
|
if (y0 !== y1 || x1 !== x2 || y2 !== y3 || x3 !== x0) return null;
|
|
1394
2002
|
const x = Math.min(x0, x1);
|
|
1395
2003
|
const y = Math.min(y0, y2);
|
|
1396
|
-
const
|
|
1397
|
-
const
|
|
1398
|
-
if (
|
|
1399
|
-
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 };
|
|
1400
2008
|
}
|
|
1401
2009
|
function pathAabb(path) {
|
|
1402
2010
|
if (path.kind === "rect") {
|
|
@@ -1404,22 +2012,21 @@ function pathAabb(path) {
|
|
|
1404
2012
|
}
|
|
1405
2013
|
return boundsOfPath(path);
|
|
1406
2014
|
}
|
|
1407
|
-
function
|
|
2015
|
+
function readPaint2(style, attr, defaultColor, gradients, onWarn) {
|
|
1408
2016
|
const raw = resolveCurrentColor(style[attr] ?? null, style);
|
|
1409
|
-
const
|
|
1410
|
-
const opacity = opacityRaw != null ? clamp01(parseFloat(opacityRaw)) : void 0;
|
|
2017
|
+
const opacity = styleValue(style, `${attr}-opacity`, onWarn);
|
|
1411
2018
|
if (raw == null) {
|
|
1412
2019
|
if (attr === "stroke") return { kind: "none" };
|
|
1413
2020
|
const out2 = { kind: "solid", color: defaultColor };
|
|
1414
2021
|
if (opacity != null) out2.opacity = opacity;
|
|
1415
2022
|
return out2;
|
|
1416
2023
|
}
|
|
1417
|
-
const parsed =
|
|
2024
|
+
const parsed = readProperty(attr, raw, onWarn);
|
|
1418
2025
|
if (!parsed) {
|
|
1419
2026
|
onWarn(`unrecognized ${attr} value: ${raw}`);
|
|
1420
2027
|
return { kind: "solid", color: defaultColor };
|
|
1421
2028
|
}
|
|
1422
|
-
if (parsed.kind === "none") return { kind: "none" };
|
|
2029
|
+
if (parsed.kind === "none" || parsed.kind === "context") return { kind: "none" };
|
|
1423
2030
|
if (parsed.kind === "ref") {
|
|
1424
2031
|
const paint = gradients.get(parsed.id);
|
|
1425
2032
|
if (!paint) {
|
|
@@ -1443,59 +2050,30 @@ function readStroke(style, gradients, onWarn, nonScaling = false) {
|
|
|
1443
2050
|
const inheritedStroke = style["stroke"] ?? null;
|
|
1444
2051
|
const inheritedWidth = style["stroke-width"] ?? null;
|
|
1445
2052
|
if (inheritedStroke == null && inheritedWidth == null) return void 0;
|
|
1446
|
-
const paint =
|
|
2053
|
+
const paint = readPaint2(style, "stroke", "#000000", gradients, onWarn);
|
|
1447
2054
|
if (paint.kind === "none") return void 0;
|
|
1448
|
-
const n =
|
|
2055
|
+
const n = styleValue(style, "stroke-width", onWarn) ?? 1;
|
|
1449
2056
|
const stroke = { paint, width: nonScaling ? { px: n } : n };
|
|
1450
|
-
const
|
|
1451
|
-
if (
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
}
|
|
1461
|
-
const join = style["stroke-linejoin"] ?? null;
|
|
1462
|
-
if (join === "miter" || join === "round" || join === "bevel") {
|
|
1463
|
-
stroke.join = join;
|
|
1464
|
-
} else if (join === "arcs" || join === "miter-clip") {
|
|
1465
|
-
onWarn(`stroke-linejoin "${join}" not supported; falling back to miter`);
|
|
1466
|
-
stroke.join = "miter";
|
|
1467
|
-
} else if (join != null) {
|
|
1468
|
-
onWarn(`unsupported stroke-linejoin: ${join}`);
|
|
1469
|
-
}
|
|
1470
|
-
const dashAttr = style["stroke-dasharray"] ?? null;
|
|
1471
|
-
if (dashAttr != null && dashAttr.trim() !== "" && dashAttr.trim() !== "none") {
|
|
1472
|
-
const parsed = parseDashArray(dashAttr);
|
|
1473
|
-
if (parsed) stroke.dash = parsed;
|
|
1474
|
-
else onWarn(`unrecognized stroke-dasharray: ${dashAttr}`);
|
|
1475
|
-
}
|
|
1476
|
-
const miterAttr = style["stroke-miterlimit"] ?? null;
|
|
1477
|
-
if (miterAttr != null) {
|
|
1478
|
-
const m = parseFloat(miterAttr);
|
|
1479
|
-
if (Number.isFinite(m) && m >= 1) stroke.miterLimit = m;
|
|
1480
|
-
else onWarn(`unrecognized stroke-miterlimit: ${miterAttr}`);
|
|
1481
|
-
}
|
|
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;
|
|
1482
2067
|
for (const [attr, field] of [
|
|
1483
2068
|
["marker-start", "markerStart"],
|
|
1484
2069
|
["marker-mid", "markerMid"],
|
|
1485
2070
|
["marker-end", "markerEnd"]
|
|
1486
2071
|
]) {
|
|
1487
|
-
const id =
|
|
1488
|
-
if (id
|
|
2072
|
+
const id = styleValue(style, attr, onWarn);
|
|
2073
|
+
if (id) stroke[field] = id;
|
|
1489
2074
|
}
|
|
1490
2075
|
return stroke;
|
|
1491
2076
|
}
|
|
1492
|
-
function parseMarkerRef(raw) {
|
|
1493
|
-
if (!raw) return void 0;
|
|
1494
|
-
const v = raw.trim();
|
|
1495
|
-
if (v === "" || v === "none") return void 0;
|
|
1496
|
-
const m = /^url\(\s*#([^)\s]+)\s*\)/.exec(v);
|
|
1497
|
-
return m ? m[1] : void 0;
|
|
1498
|
-
}
|
|
1499
2077
|
var STROKE_KEYS = [
|
|
1500
2078
|
"stroke",
|
|
1501
2079
|
"stroke-width",
|
|
@@ -1509,7 +2087,7 @@ var STROKE_KEYS = [
|
|
|
1509
2087
|
"marker-end"
|
|
1510
2088
|
];
|
|
1511
2089
|
function isNonScalingStroke(el) {
|
|
1512
|
-
return
|
|
2090
|
+
return ownValue(el, "vector-effect") === "non-scaling-stroke";
|
|
1513
2091
|
}
|
|
1514
2092
|
function ownStrokeStyle(el) {
|
|
1515
2093
|
const out = {};
|
|
@@ -1539,66 +2117,6 @@ function coreStroke(stroke) {
|
|
|
1539
2117
|
if (stroke.markerEnd) out.markerEnd = stroke.markerEnd;
|
|
1540
2118
|
return out;
|
|
1541
2119
|
}
|
|
1542
|
-
function parseDashArray(s) {
|
|
1543
|
-
const tokens = s.trim().split(/[\s,]+/).filter(Boolean);
|
|
1544
|
-
if (tokens.length === 0) return null;
|
|
1545
|
-
const nums = [];
|
|
1546
|
-
for (const t of tokens) {
|
|
1547
|
-
const n = parseFloat(t);
|
|
1548
|
-
if (!Number.isFinite(n) || n < 0) return null;
|
|
1549
|
-
nums.push(n);
|
|
1550
|
-
}
|
|
1551
|
-
return nums.length % 2 === 1 ? [...nums, ...nums] : nums;
|
|
1552
|
-
}
|
|
1553
|
-
function readOpacityAttr(el, name) {
|
|
1554
|
-
const raw = ownProp(el, name);
|
|
1555
|
-
if (raw == null) return void 0;
|
|
1556
|
-
const n = parseFloat(raw);
|
|
1557
|
-
return Number.isFinite(n) ? clamp01(n) : void 0;
|
|
1558
|
-
}
|
|
1559
|
-
function clamp01(n) {
|
|
1560
|
-
return n < 0 ? 0 : n > 1 ? 1 : n;
|
|
1561
|
-
}
|
|
1562
|
-
function parseLetterSpacing(raw, onWarn) {
|
|
1563
|
-
const n = parseFloat(raw);
|
|
1564
|
-
if (!Number.isFinite(n)) return void 0;
|
|
1565
|
-
const unit = /^-?[\d.]+([a-z%]+)$/i.exec(raw.trim())?.[1]?.toLowerCase();
|
|
1566
|
-
if (unit && unit !== "px") {
|
|
1567
|
-
onWarn(`letter-spacing "${raw}" uses unit "${unit}", which is not converted; treated as ${n} world units`);
|
|
1568
|
-
}
|
|
1569
|
-
return n;
|
|
1570
|
-
}
|
|
1571
|
-
function parseTextDecoration(raw) {
|
|
1572
|
-
if (raw == null) return {};
|
|
1573
|
-
const tokens = raw.trim().toLowerCase().split(/\s+/);
|
|
1574
|
-
const out = {};
|
|
1575
|
-
if (tokens.includes("underline")) out.underline = true;
|
|
1576
|
-
if (tokens.includes("line-through")) out.strikethrough = true;
|
|
1577
|
-
if (tokens.includes("overline")) out.overline = true;
|
|
1578
|
-
return out;
|
|
1579
|
-
}
|
|
1580
|
-
function parseBaselineShift(raw, onWarn) {
|
|
1581
|
-
const keyword = raw.trim().toLowerCase();
|
|
1582
|
-
if (keyword === "super" || keyword === "sub") return { script: keyword };
|
|
1583
|
-
const n = parseFloat(raw);
|
|
1584
|
-
if (!Number.isFinite(n)) return {};
|
|
1585
|
-
const unit = /^-?[\d.]+([a-z%]+)$/i.exec(raw.trim())?.[1]?.toLowerCase();
|
|
1586
|
-
if (unit === "%") return { baselineShift: n / 100 };
|
|
1587
|
-
if (unit && unit !== "em") {
|
|
1588
|
-
onWarn(`baseline-shift "${raw}" uses unit "${unit}", which is not converted; treated as ${n} em`);
|
|
1589
|
-
}
|
|
1590
|
-
return n === 0 ? {} : { baselineShift: n };
|
|
1591
|
-
}
|
|
1592
|
-
function parseRunFontSize(raw, onWarn) {
|
|
1593
|
-
const n = parseFloat(raw);
|
|
1594
|
-
if (!Number.isFinite(n)) return {};
|
|
1595
|
-
const unit = /^-?[\d.]+([a-z%]+)$/i.exec(raw.trim())?.[1]?.toLowerCase();
|
|
1596
|
-
if (unit === "%") return { fontScale: n / 100 };
|
|
1597
|
-
if (unit && unit !== "px") {
|
|
1598
|
-
onWarn(`font-size "${raw}" uses unit "${unit}", which is not converted; treated as ${n} world units`);
|
|
1599
|
-
}
|
|
1600
|
-
return { fontSize: n };
|
|
1601
|
-
}
|
|
1602
2120
|
var XLINK_NS2 = "http://www.w3.org/1999/xlink";
|
|
1603
2121
|
function parseImageElement(el, ctm, onWarn) {
|
|
1604
2122
|
const href = imageHref(el);
|
|
@@ -1636,7 +2154,7 @@ function imageNodeAt(boxEl, ownerEl, href, ctm, onWarn) {
|
|
|
1636
2154
|
width: Math.abs(x1 - x0),
|
|
1637
2155
|
height: Math.abs(y1 - y0)
|
|
1638
2156
|
};
|
|
1639
|
-
const opacity =
|
|
2157
|
+
const opacity = ownValue(ownerEl, "opacity");
|
|
1640
2158
|
if (opacity != null) node.opacity = opacity;
|
|
1641
2159
|
const localTransform = parseTransform(ownerEl.getAttribute("transform"), onWarn);
|
|
1642
2160
|
if (!isIdentity(localTransform)) {
|
|
@@ -1741,8 +2259,7 @@ function parseTextElement(el, ctm, style, gradients, onWarn) {
|
|
|
1741
2259
|
const dominantBaseline = el.getAttribute("dominant-baseline");
|
|
1742
2260
|
const explicitTopAnchor = dominantBaseline === "text-before-edge" || dominantBaseline === "hanging";
|
|
1743
2261
|
const topY = explicitTopAnchor ? ay : ay - fontSize;
|
|
1744
|
-
const
|
|
1745
|
-
const inheritedShift = ownShift != null ? parseBaselineShift(ownShift, onWarn) : {};
|
|
2262
|
+
const inheritedShift = ownValue(el, "baseline-shift", onWarn) ?? {};
|
|
1746
2263
|
const runs = [];
|
|
1747
2264
|
for (let i = 0; i < el.childNodes.length; i++) {
|
|
1748
2265
|
const child = el.childNodes[i];
|
|
@@ -1766,16 +2283,16 @@ function parseTextElement(el, ctm, style, gradients, onWarn) {
|
|
|
1766
2283
|
const plain = runs.map((r) => r.text).join("");
|
|
1767
2284
|
const dataW = num2(el.getAttribute("data-weasel-width"), NaN);
|
|
1768
2285
|
const dataH = num2(el.getAttribute("data-weasel-height"), NaN);
|
|
1769
|
-
const
|
|
2286
|
+
const width2 = Number.isFinite(dataW) ? dataW : laidOutWidth(runs, textStyle, plain);
|
|
1770
2287
|
const lines = (plain.match(/\n/g)?.length ?? 0) + 1;
|
|
1771
|
-
const
|
|
1772
|
-
const opacity =
|
|
2288
|
+
const height2 = Number.isFinite(dataH) ? dataH : fontSize * lineHeight * lines;
|
|
2289
|
+
const opacity = ownValue(el, "opacity");
|
|
1773
2290
|
const node = {
|
|
1774
2291
|
kind: "text",
|
|
1775
|
-
x: ax - anchorOffset(textStyle,
|
|
2292
|
+
x: ax - anchorOffset(textStyle, width2),
|
|
1776
2293
|
y: topY,
|
|
1777
|
-
width,
|
|
1778
|
-
height,
|
|
2294
|
+
width: width2,
|
|
2295
|
+
height: height2,
|
|
1779
2296
|
text: plain
|
|
1780
2297
|
};
|
|
1781
2298
|
const hasStyling = runs.some(
|
|
@@ -1783,6 +2300,7 @@ function parseTextElement(el, ctm, style, gradients, onWarn) {
|
|
|
1783
2300
|
);
|
|
1784
2301
|
if (hasStyling) node.runs = runs;
|
|
1785
2302
|
if (el.getAttribute("data-weasel-wrap") === "true") textStyle.wrap = true;
|
|
2303
|
+
if (el.getAttribute("data-weasel-align") === "justify") textStyle.align = "justify";
|
|
1786
2304
|
const va = el.getAttribute("data-weasel-vertical-align");
|
|
1787
2305
|
if (va === "center" || va === "bottom") node.verticalAlign = va;
|
|
1788
2306
|
if (Object.keys(textStyle).length > 0) node.style = textStyle;
|
|
@@ -1807,47 +2325,36 @@ function parseTextElement(el, ctm, style, gradients, onWarn) {
|
|
|
1807
2325
|
}
|
|
1808
2326
|
return node;
|
|
1809
2327
|
}
|
|
1810
|
-
var TEXT_TRANSFORMS = /* @__PURE__ */ new Set(["none", "uppercase", "lowercase", "capitalize"]);
|
|
1811
|
-
function parseTextTransform(raw) {
|
|
1812
|
-
const v = raw?.trim().toLowerCase();
|
|
1813
|
-
return v !== void 0 && TEXT_TRANSFORMS.has(v) ? v : void 0;
|
|
1814
|
-
}
|
|
1815
2328
|
function readTspanRun(el, gradients, style, onWarn) {
|
|
1816
2329
|
const text = el.textContent ?? "";
|
|
1817
2330
|
const run = { text };
|
|
1818
|
-
const fw =
|
|
1819
|
-
if (fw === "bold" || fw === "
|
|
1820
|
-
|
|
1821
|
-
if (
|
|
1822
|
-
|
|
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);
|
|
1823
2337
|
if (ff) run.fontFamily = ff;
|
|
1824
|
-
const
|
|
1825
|
-
if (
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
if (
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
const shift = parseBaselineShift(bs, onWarn);
|
|
1838
|
-
if (shift.script != null) run.script = shift.script;
|
|
1839
|
-
if (shift.baselineShift != null) run.baselineShift = shift.baselineShift;
|
|
1840
|
-
}
|
|
1841
|
-
const decoration = parseTextDecoration(ownProp(el, "text-decoration"));
|
|
1842
|
-
if (decoration.underline) run.underline = true;
|
|
1843
|
-
if (decoration.strikethrough) run.strikethrough = true;
|
|
1844
|
-
if (decoration.overline) run.overline = true;
|
|
1845
|
-
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);
|
|
1846
2351
|
if (transform) run.textTransform = transform;
|
|
2352
|
+
const variant = ownValue(el, "font-variant", onWarn);
|
|
2353
|
+
if (variant) run.fontVariantCaps = variant;
|
|
1847
2354
|
const tspanStyle = deriveStyle(style, el);
|
|
1848
2355
|
const fillAttr = resolveCurrentColor(ownProp(el, "fill"), tspanStyle);
|
|
1849
2356
|
if (fillAttr) {
|
|
1850
|
-
const parsed =
|
|
2357
|
+
const parsed = readProperty("fill", fillAttr);
|
|
1851
2358
|
if (parsed?.kind === "solid") {
|
|
1852
2359
|
run.fill = { fill: "solid", color: parsed.color };
|
|
1853
2360
|
} else if (parsed?.kind === "ref") {
|
|
@@ -1862,44 +2369,41 @@ function readTspanRun(el, gradients, style, onWarn) {
|
|
|
1862
2369
|
}
|
|
1863
2370
|
function readTextStyle(style, onWarn) {
|
|
1864
2371
|
const out = {};
|
|
1865
|
-
const
|
|
1866
|
-
if (
|
|
1867
|
-
|
|
1868
|
-
|
|
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;
|
|
1869
2377
|
}
|
|
1870
|
-
const ff = style
|
|
2378
|
+
const ff = styleValue(style, "font-family", onWarn);
|
|
1871
2379
|
if (ff) out.fontFamily = ff;
|
|
1872
|
-
const fw = style
|
|
1873
|
-
if (fw != null)
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
const fs = style["font-style"];
|
|
1878
|
-
if (fs === "italic" || fs === "normal") out.fontStyle = fs;
|
|
1879
|
-
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";
|
|
1880
2385
|
const ltr = out.direction !== "rtl";
|
|
1881
|
-
const anchor = style
|
|
2386
|
+
const anchor = styleValue(style, "text-anchor") ?? (ltr ? void 0 : "start");
|
|
1882
2387
|
if (anchor === "start") out.align = ltr ? "left" : "right";
|
|
1883
2388
|
else if (anchor === "middle") out.align = "center";
|
|
1884
2389
|
else if (anchor === "end") out.align = ltr ? "right" : "left";
|
|
1885
|
-
const ls = style
|
|
1886
|
-
if (ls != null)
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
if (decoration.strikethrough) out.strikethrough = true;
|
|
1893
|
-
if (decoration.overline) out.overline = true;
|
|
1894
|
-
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);
|
|
1895
2397
|
if (transform && transform !== "none") out.textTransform = transform;
|
|
2398
|
+
const variant = styleValue(style, "font-variant", onWarn);
|
|
2399
|
+
if (variant === "small-caps") out.fontVariantCaps = variant;
|
|
1896
2400
|
return out;
|
|
1897
2401
|
}
|
|
1898
2402
|
function readTextPaint(style, gradients, onWarn, nonScalingStroke = false) {
|
|
1899
2403
|
const out = {};
|
|
1900
2404
|
const fillRaw = resolveCurrentColor(style["fill"] ?? null, style);
|
|
1901
2405
|
if (fillRaw) {
|
|
1902
|
-
const parsed =
|
|
2406
|
+
const parsed = readProperty("fill", fillRaw);
|
|
1903
2407
|
if (parsed?.kind === "solid") {
|
|
1904
2408
|
out.fill = { fill: "solid", color: parsed.color };
|
|
1905
2409
|
} else if (parsed?.kind === "ref") {
|
|
@@ -1929,8 +2433,8 @@ function ingestMarkers(root, nodes, gradients, onWarn) {
|
|
|
1929
2433
|
if (id && !defs.has(id)) defs.set(id, all[i]);
|
|
1930
2434
|
}
|
|
1931
2435
|
const minted = /* @__PURE__ */ new Map();
|
|
1932
|
-
const resolve = (stroke,
|
|
1933
|
-
const w =
|
|
2436
|
+
const resolve = (stroke, width2) => {
|
|
2437
|
+
const w = width2 === void 0 ? 1 : typeof width2 === "number" ? width2 : width2.px;
|
|
1934
2438
|
for (const [field, role, attr] of MARKER_FIELDS) {
|
|
1935
2439
|
const id = stroke[field];
|
|
1936
2440
|
if (typeof id !== "string" || getMarker(id) !== void 0) continue;
|
|
@@ -2013,13 +2517,12 @@ function markerOrient(raw) {
|
|
|
2013
2517
|
return { orient: rad, reverseAtStart: false };
|
|
2014
2518
|
}
|
|
2015
2519
|
function markerPaint(style, attr, gradients, onWarn) {
|
|
2016
|
-
const
|
|
2017
|
-
if (
|
|
2018
|
-
|
|
2019
|
-
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`);
|
|
2020
2523
|
return "line";
|
|
2021
2524
|
}
|
|
2022
|
-
const paint =
|
|
2525
|
+
const paint = readPaint2(style, attr, "#000000", gradients, onWarn);
|
|
2023
2526
|
if (paint.kind === "none") return "none";
|
|
2024
2527
|
if (paint.kind === "gradient") return paint.paint;
|
|
2025
2528
|
return paint.opacity != null ? { ...solid(paint.color), opacity: paint.opacity } : solid(paint.color);
|
|
@@ -2347,19 +2850,19 @@ function svgPaintOpacity(paint) {
|
|
|
2347
2850
|
function opacityAttr(name, v) {
|
|
2348
2851
|
return v != null && v !== 1 ? [`${name}="${trimNumber(v)}"`] : [];
|
|
2349
2852
|
}
|
|
2350
|
-
function strokeWidthAttrs(
|
|
2351
|
-
if (typeof
|
|
2352
|
-
return [`stroke-width="${trimNumber(
|
|
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"'];
|
|
2353
2856
|
}
|
|
2354
2857
|
function coreStrokeAttrs(stroke, registry, warn) {
|
|
2355
2858
|
const paint = stroke?.paint;
|
|
2356
2859
|
if (!stroke || !paint) return [];
|
|
2357
|
-
const
|
|
2358
|
-
if (!((typeof
|
|
2860
|
+
const width2 = stroke.width ?? 1;
|
|
2861
|
+
if (!((typeof width2 === "object" ? width2.px : width2) > 0)) return [];
|
|
2359
2862
|
warnStrokeAlign(stroke.align, warn);
|
|
2360
2863
|
const attrs = [`stroke="${"color" in paint ? paint.color : registry.ref(paint)}"`];
|
|
2361
2864
|
attrs.push(...opacityAttr("stroke-opacity", paint.opacity));
|
|
2362
|
-
attrs.push(...strokeWidthAttrs(
|
|
2865
|
+
attrs.push(...strokeWidthAttrs(width2));
|
|
2363
2866
|
if (stroke.cap) attrs.push(`stroke-linecap="${stroke.cap}"`);
|
|
2364
2867
|
if (stroke.join) attrs.push(`stroke-linejoin="${stroke.join}"`);
|
|
2365
2868
|
if (stroke.dash && stroke.dash.length > 0) {
|
|
@@ -2460,12 +2963,14 @@ function textXml(node, registry, namespaces, warn) {
|
|
|
2460
2963
|
if (style?.fontFamily) attrs.push(`font-family="${escapeAttr2(style.fontFamily)}"`);
|
|
2461
2964
|
if (style?.fontWeight != null) attrs.push(`font-weight="${String(style.fontWeight)}"`);
|
|
2462
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"');
|
|
2463
2967
|
const direction = style?.direction ?? "ltr";
|
|
2464
2968
|
if (direction === "rtl") attrs.push('direction="rtl"');
|
|
2465
2969
|
if (style?.align != null || direction === "rtl") {
|
|
2466
2970
|
const align = style?.align ?? "left";
|
|
2467
|
-
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";
|
|
2468
2972
|
if (anchor !== "start") attrs.push(`text-anchor="${anchor}"`);
|
|
2973
|
+
if (align === "justify") attrs.push('data-weasel-align="justify"');
|
|
2469
2974
|
}
|
|
2470
2975
|
if (style?.letterSpacing != null && style.letterSpacing !== 0) {
|
|
2471
2976
|
attrs.push(`letter-spacing="${trimNumber(resolveScreenLength(style.letterSpacing, 1))}"`);
|
|
@@ -2500,17 +3005,25 @@ function textXml(node, registry, namespaces, warn) {
|
|
|
2500
3005
|
const deg = node.rotation * 180 / Math.PI;
|
|
2501
3006
|
attrs.push(`transform="rotate(${trimNumber(deg)} ${trimNumber(cx)} ${trimNumber(cy)})"`);
|
|
2502
3007
|
}
|
|
2503
|
-
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);
|
|
2504
3009
|
const metaAttrs = metaAttrsXml(node.meta, namespaces);
|
|
2505
3010
|
const metaEls = metaElementsXml(node.meta, namespaces);
|
|
2506
3011
|
return `<text ${attrs.join(" ")}${metaAttrs}>${body}${metaEls}</text>`;
|
|
2507
3012
|
}
|
|
2508
|
-
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) {
|
|
2509
3019
|
const attrs = [];
|
|
2510
|
-
if (run.
|
|
3020
|
+
if (run.fontWeight != null) attrs.push(`font-weight="${run.fontWeight}"`);
|
|
3021
|
+
else if (run.bold) attrs.push(`font-weight="700"`);
|
|
2511
3022
|
if (run.italic) attrs.push(`font-style="italic"`);
|
|
3023
|
+
if (run.fontVariantCaps) attrs.push(`font-variant="${run.fontVariantCaps}"`);
|
|
2512
3024
|
if (run.fontFamily) attrs.push(`font-family="${escapeAttr2(run.fontFamily)}"`);
|
|
2513
|
-
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;
|
|
2514
3027
|
const scale = run.fontScale ?? presetScale;
|
|
2515
3028
|
if (run.fontSize != null) attrs.push(`font-size="${trimNumber(resolveScreenLength(run.fontSize, 1))}"`);
|
|
2516
3029
|
else if (scale != null) attrs.push(`font-size="${trimNumber(scale * 100)}%"`);
|
|
@@ -2561,6 +3074,31 @@ function pathBounds(path) {
|
|
|
2561
3074
|
const b = boundsOfPath(path);
|
|
2562
3075
|
return { minX: b.x, minY: b.y, maxX: b.x + b.width, maxY: b.y + b.height };
|
|
2563
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
|
+
}
|
|
2564
3102
|
var CASCADE_OFFSET_PX = 24;
|
|
2565
3103
|
var VIEWPORT_FIT = 0.9;
|
|
2566
3104
|
function readFileText(file) {
|
|
@@ -2641,8 +3179,8 @@ function svgNodesToKitDrafts(input, nextId, options = {}) {
|
|
|
2641
3179
|
drafts.push({ kind: "leaf", id, parentId, pose, data: out });
|
|
2642
3180
|
return pose;
|
|
2643
3181
|
};
|
|
2644
|
-
const visit = (n, parentId,
|
|
2645
|
-
const k =
|
|
3182
|
+
const visit = (n, parentId, inherited2) => {
|
|
3183
|
+
const k = inherited2 * (n.opacity ?? 1);
|
|
2646
3184
|
if (n.kind === "group") {
|
|
2647
3185
|
const draft = {
|
|
2648
3186
|
kind: "container",
|
|
@@ -2800,12 +3338,12 @@ function svgPaintFromKit(fill, box) {
|
|
|
2800
3338
|
}
|
|
2801
3339
|
function svgStrokeFromKit(stroke, box) {
|
|
2802
3340
|
if (!stroke?.paint) return void 0;
|
|
2803
|
-
const
|
|
2804
|
-
if (!((typeof
|
|
3341
|
+
const width2 = stroke.width ?? 1;
|
|
3342
|
+
if (!((typeof width2 === "object" ? width2.px : width2) > 0)) return void 0;
|
|
2805
3343
|
const opacity = stroke.paint.opacity;
|
|
2806
3344
|
return {
|
|
2807
3345
|
paint: svgPaintFromKit(stroke.paint, box),
|
|
2808
|
-
width,
|
|
3346
|
+
width: width2,
|
|
2809
3347
|
...opacity != null ? { opacity } : {},
|
|
2810
3348
|
...stroke.cap !== void 0 ? { cap: stroke.cap } : {},
|
|
2811
3349
|
...stroke.join !== void 0 ? { join: stroke.join } : {},
|
|
@@ -2900,6 +3438,6 @@ function svgNodesFromKit(tree, options = {}) {
|
|
|
2900
3438
|
return out;
|
|
2901
3439
|
}
|
|
2902
3440
|
|
|
2903
|
-
export { IDENTITY_MATRIX, fillDataFromSvg, nativeSvgKind, nativeSvgSpace, parseSvg, serializeSvg, strokeDataFromSvg, svgImageFromKit, svgLeafFromKit, svgNodesFromKit, svgNodesToKitDrafts, svgPaintFromKit, svgStrokeFromKit, 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 };
|
|
2904
3442
|
//# sourceMappingURL=index.js.map
|
|
2905
3443
|
//# sourceMappingURL=index.js.map
|