@unmade/text-renderer 1.1.1 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,8 +1,8 @@
1
1
  import { Blob as e, Buffer as t, Face as n, Feature as r, Font as i, shape as a } from "harfbuzzjs";
2
- import { svgPathProperties as o } from "svg-path-properties";
2
+ import o from "@thednp/dommatrix";
3
3
  import { DOMParser as s } from "linkedom";
4
4
  import "node:path";
5
- import c from "@thednp/dommatrix";
5
+ import { svgPathProperties as c } from "svg-path-properties";
6
6
  //#region src/loading/createLoadedFont.ts
7
7
  function l(e) {
8
8
  let t = Infinity, n = Infinity, r = -Infinity, i = -Infinity, a = 0, o = 0;
@@ -237,18 +237,18 @@ var x = async (t, r = !1) => {
237
237
  let a = f.get(t);
238
238
  if (a) {
239
239
  if (r && !a.svgGlyphs) {
240
- let r = new n(new e(await b(t))), o = new i(r), s = r.referenceTable("SVG ");
241
- a.svgGlyphs = s ? await _(s, o, r.upem) : /* @__PURE__ */ new Map();
240
+ let r = await b(t), o = new e(r), s = new n(o), c = new i(s), l = s.referenceTable("SVG ");
241
+ a.svgGlyphs = l ? await _(l, c, s.upem) : /* @__PURE__ */ new Map();
242
242
  }
243
243
  return a;
244
244
  }
245
- let o = new n(new e(await b(t))), s = new i(o), c = o.getName(1, "en") ?? "", l;
245
+ let o = await b(t), s = new e(o), c = new n(s), l = new i(c), u = c.getName(1, "en") ?? "", p;
246
246
  if (r) {
247
- let e = o.referenceTable("SVG ");
248
- l = e ? await _(e, s, o.upem) : /* @__PURE__ */ new Map();
247
+ let e = c.referenceTable("SVG ");
248
+ p = e ? await _(e, l, c.upem) : /* @__PURE__ */ new Map();
249
249
  }
250
- let u = d(o, s, c, l);
251
- return f.set(t, u), u;
250
+ let m = d(c, l, u, p);
251
+ return f.set(t, m), m;
252
252
  }, S = .03125, C = 1.1, w = .2, T = {
253
253
  outlineWidth: S,
254
254
  letterSpacing: 0,
@@ -267,36 +267,44 @@ var x = async (t, r = !1) => {
267
267
  return `${this.x} ${this.y}`;
268
268
  }
269
269
  };
270
- function D(e, t, n = new E(0, 0)) {
270
+ function ee(e, t, n = new E(0, 0)) {
271
271
  let r = e.X - n.X, i = e.Y - n.Y, a = t / 180 * Math.PI, o = Math.cos(a), s = Math.sin(a), c = r * o - i * s, l = r * s + i * o;
272
272
  return new E(c + n.X, l + n.Y);
273
273
  }
274
274
  //#endregion
275
275
  //#region src/baselines/index.ts
276
- var O = (e) => e === void 0 || e === "flat" ? { type: "flat" } : e === "curved" ? { type: "curved" } : e, k = (e) => {
276
+ var D = (e) => e === void 0 || e === "flat" ? { type: "flat" } : e === "curved" ? { type: "curved" } : e, O = (e) => {
277
277
  if (!e || !e.trim()) throw Error("Custom baseline path must be a non-empty SVG path string");
278
278
  if (!e.trim().startsWith("M")) throw Error("Custom baseline path must start with an M (moveto) command");
279
- }, A = ({ boxDimensions: e, totalTextHeight: t, baseline: n, lineHeight: r, verticalAlignment: i = "centre", lineNumber: a = 0 }) => {
279
+ }, k = ({ boxDimensions: e, totalTextHeight: t, baseline: n, lineHeight: r, verticalAlignment: i = "centre", lineNumber: a = 0 }) => {
280
280
  let { width: o, height: s } = e, c = r - n, l = n;
281
281
  switch (i) {
282
282
  case "centre":
283
283
  l = (s - t) / 2 + r - c;
284
284
  break;
285
- case "bottom":
286
- l = s - t + r - c;
287
- break;
285
+ case "bottom": l = s - t + r - c;
288
286
  }
289
287
  return l += r * C * a, `M 0 ${l} L ${o} ${l}`;
290
- }, j = ({ boxDimensions: e, baseline: t, textHeight: n, verticalAlignment: r }) => {
291
- let { width: i, height: a } = e, o = n * w, s = n + o, c = r === "top" ? 0 : a / 2 - s / 2, l = t + c, u = l + o;
292
- if (r !== "top" && (u > a || l - n < 0)) return j({
288
+ }, A = ({ boxDimensions: e, baseline: t, textHeight: n, verticalAlignment: r }) => {
289
+ let { height: i } = e, a = n * w, o = n + a, s = t + (r === "top" ? 0 : i / 2 - o / 2), c = s + a;
290
+ return r !== "top" && (c > i || s - n < 0) ? A({
293
291
  boxDimensions: e,
294
292
  baseline: t,
295
293
  textHeight: n,
296
294
  verticalAlignment: "top"
297
- });
298
- let d = n / 4, f = new E(d, u), p = new E(i / 2, l), m = new E(i - d, u), h = i * .2, g = new E(h, t + c), _ = new E(i - h, t + c);
299
- return `${`M ${f.toSVGString()}`} ${`Q ${g.toSVGString()} ${p.toSVGString()}`} ${`Q ${_.toSVGString()} ${m.toSVGString()}`}`;
295
+ }) : {
296
+ curveTop: s,
297
+ curveBottom: c,
298
+ curveXInset: n / 4
299
+ };
300
+ }, j = ({ boxDimensions: e, baseline: t, textHeight: n, verticalAlignment: r }) => {
301
+ let { width: i } = e, { curveTop: a, curveBottom: o, curveXInset: s } = A({
302
+ boxDimensions: e,
303
+ baseline: t,
304
+ textHeight: n,
305
+ verticalAlignment: r
306
+ }), c = new E(s, o), l = new E(i / 2, a), u = new E(i - s, o), d = i * .2, f = new E(d, a), p = new E(i - d, a);
307
+ return `${`M ${c.toSVGString()}`} ${`Q ${f.toSVGString()} ${l.toSVGString()}`} ${`Q ${p.toSVGString()} ${u.toSVGString()}`}`;
300
308
  }, M = (e) => {
301
309
  for (let t of [
302
310
  "I",
@@ -324,11 +332,11 @@ var O = (e) => e === void 0 || e === "flat" ? { type: "flat" } : e === "curved"
324
332
  height: s.y2 - s.y1 + c,
325
333
  baseline: s.y1 * -1 + c / 2
326
334
  };
327
- }, I = s, ee = c, te = new I().parseFromString("<svg></svg>", "image/svg+xml");
328
- te.readyState = "complete";
335
+ }, I = s, te = o, ne = new I().parseFromString("<svg></svg>", "image/svg+xml");
336
+ ne.readyState = "complete";
329
337
  //#endregion
330
338
  //#region ../svg-helpers/dist/colours/named-colours.mjs
331
- var ne = {
339
+ var re = {
332
340
  aliceblue: "#f0f8ff",
333
341
  antiquewhite: "#faebd7",
334
342
  aqua: "#00ffff",
@@ -477,13 +485,13 @@ var ne = {
477
485
  whitesmoke: "#f5f5f5",
478
486
  yellow: "#ffff00",
479
487
  yellowgreen: "#9acd32"
480
- }, re = (e) => {
488
+ }, ie = (e) => {
481
489
  let t = Array.from(e), n = !1;
482
490
  return t[0] === "#" && (t.shift(), n = !0), t.length === 3 && (t = t.map((e) => `${e}${e}`)), `${n ? "#" : ""}${t.join("").toLowerCase()}`;
483
491
  }, L = (e) => Math.max(0, Math.min(255, Math.round(typeof e == "string" ? Number.parseInt(e, 10) : e))).toString(16).padStart(2, "0"), R = (...e) => {
484
492
  let [t, n, r] = Array.isArray(e[0]) ? e[0] : e;
485
493
  return `#${L(t)}${L(n)}${L(r)}`;
486
- }, ie = (e, t, n) => {
494
+ }, ae = (e, t, n) => {
487
495
  if (t === 0) {
488
496
  let e = Math.round(n * 255);
489
497
  return [
@@ -498,9 +506,9 @@ var ne = {
498
506
  Math.round((c + o) * 255),
499
507
  Math.round((l + o) * 255)
500
508
  ];
501
- }, ae = (e) => /^#?([0-9a-f]{3}|[0-9a-f]{6})$/i.test(e), z = (e) => {
509
+ }, oe = (e) => /^#?([0-9a-f]{3}|[0-9a-f]{6})$/i.test(e), z = (e) => {
502
510
  let t = e.trim().toLowerCase();
503
- if (t.startsWith("#") || /^[0-9a-f]{3}([0-9a-f]{3})?$/.test(t)) return ae(t) ? re(t) : void 0;
511
+ if (t.startsWith("#") || /^[0-9a-f]{3}([0-9a-f]{3})?$/.test(t)) return oe(t) ? ie(t) : void 0;
504
512
  if (t.startsWith("rgb")) {
505
513
  let e = t.match(/rgba?\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(?:,\s*[\d.]+\s*)?\)/);
506
514
  return e ? R(Number.parseInt(e[1], 10), Number.parseInt(e[2], 10), Number.parseInt(e[3], 10)) : void 0;
@@ -508,10 +516,10 @@ var ne = {
508
516
  if (t.startsWith("hsl")) {
509
517
  let e = t.match(/hsla?\(\s*(\d+)\s*,\s*([\d.]+)%\s*,\s*([\d.]+)%\s*(?:,\s*[\d.]+\s*)?\)/);
510
518
  if (!e) return;
511
- let [n, r, i] = ie(Number.parseInt(e[1], 10), Number.parseFloat(e[2]) / 100, Number.parseFloat(e[3]) / 100);
519
+ let [n, r, i] = ae(Number.parseInt(e[1], 10), Number.parseFloat(e[2]) / 100, Number.parseFloat(e[3]) / 100);
512
520
  return R(n, r, i);
513
521
  }
514
- let n = ne[t];
522
+ let n = re[t];
515
523
  if (n) return n;
516
524
  }, B = {
517
525
  svg: "svg",
@@ -527,7 +535,7 @@ var ne = {
527
535
  }, V = {
528
536
  fill: "fill",
529
537
  stroke: "stroke"
530
- }, oe = {
538
+ }, se = {
531
539
  tiling: [
532
540
  B.circle,
533
541
  B.ellipse,
@@ -553,69 +561,69 @@ var ne = {
553
561
  `[${V.fill}]`,
554
562
  `[${V.stroke}]`
555
563
  ]
556
- }, se = (e) => Array.from(e.querySelectorAll(oe.tiling.map((e) => `*:not(clipPath) > ${e}`).join(", "))), H = (e) => {
564
+ }, ce = (e) => Array.from(e.querySelectorAll(se.tiling.map((e) => `*:not(clipPath) > ${e}`).join(", "))), le = (e) => {
557
565
  let t = e.getAttribute(V.stroke);
558
566
  return t === "none" || !t ? !1 : t;
559
- }, U = (e) => {
567
+ }, H = (e) => {
560
568
  if (!(e === "none" || e === "transparent" || e.startsWith("url("))) return z(e);
561
- }, W = (e) => {
562
- let t = se(e).reduce((e, t) => {
563
- let n = t.getAttribute("stop-color"), r = t.getAttribute(V.fill) || "#000", i = H(t);
569
+ }, ue = (e) => {
570
+ let t = ce(e).reduce((e, t) => {
571
+ let n = t.getAttribute("stop-color"), r = t.getAttribute(V.fill) || "#000", i = le(t);
564
572
  if (r && !n) {
565
- let t = U(r);
573
+ let t = H(r);
566
574
  t && e.add(t);
567
575
  }
568
576
  if (n) {
569
- let t = U(n);
577
+ let t = H(n);
570
578
  t && e.add(t);
571
579
  }
572
580
  if (i) {
573
- let t = U(i);
581
+ let t = H(i);
574
582
  t && e.add(t);
575
583
  }
576
584
  return e;
577
585
  }, /* @__PURE__ */ new Set());
578
586
  return Array.from(t);
579
- }, G = (e) => {
587
+ }, de = (e) => {
580
588
  let t = new I().parseFromString(e, "image/svg+xml").querySelector("svg");
581
- return t ? W(t) : [];
589
+ return t ? ue(t) : [];
582
590
  };
583
591
  //#endregion
584
592
  //#region src/glyph/svg-glyph-converter.ts
585
- function K(e) {
593
+ function U(e) {
586
594
  let t = e.trim(), n = t.startsWith("<svg") || t.startsWith("<?xml"), r = n ? t : `<svg>${t}</svg>`;
587
595
  return {
588
596
  doc: new I().parseFromString(r, "image/svg+xml"),
589
597
  wasWrapped: !n
590
598
  };
591
599
  }
592
- function q(e, t) {
600
+ function W(e, t) {
593
601
  let n = e.querySelector("svg");
594
602
  return n ? t ? n.innerHTML : n.outerHTML : "";
595
603
  }
596
- function ce(e) {
604
+ function fe(e) {
597
605
  let { svgGlyphs: t } = e;
598
606
  if (!t || t.size === 0) return [];
599
607
  let n = /* @__PURE__ */ new Set();
600
- for (let [, e] of t) for (let t of G(e.svg)) n.add(t);
608
+ for (let [, e] of t) for (let t of de(e.svg)) n.add(t);
601
609
  return Array.from(n);
602
610
  }
603
- function le(e) {
611
+ function pe(e) {
604
612
  return !!e && e !== "none" && e !== "transparent" && !e.startsWith("url(");
605
613
  }
606
- function ue(e) {
614
+ function me(e) {
607
615
  let t = new I().parseFromString(e, "image/svg+xml").querySelector("svg");
608
616
  return t ? t.innerHTML.trim() : "";
609
617
  }
610
- var de = [
618
+ var he = [
611
619
  "fill",
612
620
  "stroke",
613
621
  "clip-path",
614
622
  "mask",
615
623
  "filter"
616
624
  ];
617
- function fe(e, t) {
618
- let { doc: n, wasWrapped: r } = K(e), i = /* @__PURE__ */ new Map();
625
+ function ge(e, t) {
626
+ let { doc: n, wasWrapped: r } = U(e), i = /* @__PURE__ */ new Map();
619
627
  for (let e of n.querySelectorAll("[id]")) {
620
628
  let n = e.getAttribute("id");
621
629
  if (!n) continue;
@@ -623,7 +631,7 @@ function fe(e, t) {
623
631
  i.set(n, r), e.setAttribute("id", r);
624
632
  }
625
633
  if (i.size === 0) return e;
626
- for (let e of de) for (let t of n.querySelectorAll(`[${e}]`)) {
634
+ for (let e of he) for (let t of n.querySelectorAll(`[${e}]`)) {
627
635
  let n = t.getAttribute(e);
628
636
  if (n) {
629
637
  let r = n.replace(/url\(#([^)]+)\)/g, (e, t) => {
@@ -640,13 +648,13 @@ function fe(e, t) {
640
648
  a && e.setAttribute(t, `#${a}`);
641
649
  }
642
650
  }
643
- return q(n, r);
651
+ return W(n, r);
644
652
  }
645
- var pe = [
653
+ var _e = [
646
654
  "fill",
647
655
  "stroke",
648
656
  "stop-color"
649
- ], me = [
657
+ ], ve = [
650
658
  "path",
651
659
  "rect",
652
660
  "circle",
@@ -655,109 +663,95 @@ var pe = [
655
663
  "polyline",
656
664
  "text"
657
665
  ];
658
- function he(e, t) {
659
- let { doc: n, wasWrapped: r } = K(e), i = {};
666
+ function ye(e, t) {
667
+ let { doc: n, wasWrapped: r } = U(e), i = {};
660
668
  for (let [e, n] of Object.entries(t)) i[z(e) ?? e.toLowerCase()] = n;
661
- for (let e of pe) for (let t of n.querySelectorAll(`[${e}]`)) {
669
+ for (let e of _e) for (let t of n.querySelectorAll(`[${e}]`)) {
662
670
  let n = t.getAttribute(e);
663
- if (!n || !le(n)) continue;
671
+ if (!n || !pe(n)) continue;
664
672
  let r = z(n), a = r ? i[r] : void 0;
665
673
  a && t.setAttribute(e, a.hex);
666
674
  }
667
675
  let a = i["#000000"];
668
676
  if (a) {
669
- let e = me.map((e) => `${e}:not([fill])`).join(", ");
677
+ let e = ve.map((e) => `${e}:not([fill])`).join(", ");
670
678
  for (let t of n.querySelectorAll(e)) t.setAttribute("fill", a.hex);
671
679
  }
672
- return q(n, r);
680
+ return W(n, r);
673
681
  }
674
682
  //#endregion
675
- //#region src/helpers/matrix.ts
676
- var ge = (e = [
677
- 1,
678
- 0,
679
- 0,
680
- 1,
681
- 0,
682
- 0
683
- ]) => {
684
- let t = new ee(`matrix(${e.join(",")})`);
685
- if (!t) throw Error("Your browser does not support matrices");
686
- return t;
687
- };
688
- //#endregion
689
683
  //#region src/helpers/xml.ts
690
- function _e(e) {
684
+ function be(e) {
691
685
  return e.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/'/g, "&apos;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
692
686
  }
693
687
  //#endregion
694
688
  //#region src/rendering/index.ts
695
- var J = (e, t, n) => e / n * t, Y = ({ font: e, baselinePath: t, text: n, fontSize: r, spacing: i = T, horizontalAlignment: a = "centre", colourMap: s }) => {
696
- let { outlineWidth: c, letterSpacing: l, letterSpacingOutline: u } = i, d = new o(t), f = c ? r * c * 2 : 0, p = d.getTotalLength(), m = e.getPaths(n, 0, 0, r, { drawSVG: !1 }), h = e.getPath(n, 0, 0, r, { drawSVG: !1 }).getBoundingBox(), g = h.x1 - f / 2, _ = h.x2 + f / 2 - g, v = c > 0 ? u : l, y = v ? v * r : 0;
689
+ var G = (e, t, n) => e / n * t, xe = ({ font: e, baselinePath: t, text: n, fontSize: r, spacing: i = T, horizontalAlignment: a = "centre", colourMap: o }) => {
690
+ let { outlineWidth: s, letterSpacing: l, letterSpacingOutline: u } = i, d = new c(t), f = s ? r * s * 2 : 0, p = d.getTotalLength(), m = e.getPaths(n, 0, 0, r, { drawSVG: !1 }), h = e.getPath(n, 0, 0, r, { drawSVG: !1 }).getBoundingBox(), g = h.x1 - f / 2, _ = h.x2 + f / 2 - g, v = s > 0 ? u : l, y = v ? v * r : 0;
697
691
  a === "distributed" && (y = n.length === 1 ? 0 : (p - _) / (n.length - 1));
698
692
  let b = y * (n.length - 1), x = _ + b, S = 0 - g;
699
693
  a === "centre" ? S = (p - x) / 2 - g : a === "right" && (S = p - _ - g - b);
700
694
  let C = [];
701
695
  for (let t = 0; t < m.length; t++) {
702
- let i = m[t], a = i.getBoundingBox(), o = e.charToGlyph(n[t]), { advanceWidth: c } = o;
696
+ let i = m[t], a = i.getBoundingBox(), s = e.charToGlyph(n[t]), { advanceWidth: c } = s;
703
697
  if (!c) throw Error(`No advance width for character: ${n[t]} in font ${e.names.fontFamily}`);
704
- let { x1: l, x2: u } = o.getBoundingBox(), f = J(c, r, e.unitsPerEm), h = J(u - l, r, e.unitsPerEm), g = S + a.x1;
698
+ let { x1: l, x2: u } = s.getBoundingBox(), f = G(c, r, e.unitsPerEm), h = G(u - l, r, e.unitsPerEm), g = S + a.x1;
705
699
  if (t > 0 && (g += y * t), g + h > p) throw Error(`TOO_WIDE: character "${n[t]}" at index ${t} exceeds baseline path length (${Math.round(g + h)} > ${Math.round(p)})`);
706
- let _ = g + f / 2, v = d.getTangentAtLength(_), b = Math.atan2(v.y, v.x) * 180 / Math.PI, x = d.getPointAtLength(g), w = x.x, T = x.y, O = a.x2 - a.x1, k = new E(a.x1 + O / 2, 0), A = D(new E(a.x1, 0), b, k), j = w - A.X, M = T - A.Y, N = ge();
707
- N = N.translate(j, M);
708
- let P;
700
+ let _ = g + f / 2, v = d.getTangentAtLength(_), b = Math.atan2(v.y, v.x) * 180 / Math.PI, x = d.getPointAtLength(g), w = x.x, T = x.y, D = a.x2 - a.x1, O = new E(a.x1 + D / 2, 0), k = ee(new E(a.x1, 0), b, O), A = w - k.X, j = T - k.Y, M = new te();
701
+ M = M.translate(A, j);
702
+ let N;
709
703
  if (b) {
710
- let e = a.x1 + O / 2;
711
- N = N.translate(e, 0), N = N.rotate(b), N = N.translate(-e, 0), P = `translate(${j} ${M}) translate(${e} 0) rotate(${b}) translate(${-e} 0)`;
712
- } else P = `translate(${j} ${M})`;
713
- let F = i.toPathData(2);
714
- F.length && !F.endsWith("Z") && (F += "Z", F = F.replace(/M/g, "ZM"), F = F.substring(1));
715
- let I;
704
+ let e = a.x1 + D / 2;
705
+ M = M.translate(e, 0), M = M.rotate(b), M = M.translate(-e, 0), N = `translate(${A} ${j}) translate(${e} 0) rotate(${b}) translate(${-e} 0)`;
706
+ } else N = `translate(${A} ${j})`;
707
+ let P = i.toPathData(2);
708
+ P.length && !P.endsWith("Z") && (P += "Z", P = P.replace(/M/g, "ZM"), P = P.substring(1));
709
+ let F;
716
710
  if (e.svgGlyphs) {
717
- let n = e.svgGlyphs.get(o.index);
711
+ let n = e.svgGlyphs.get(s.index);
718
712
  if (n) {
719
- let e = ue(n.svg);
720
- e = fe(e, `g${o.index}-${t}`), s && (e = he(e, s)), I = {
713
+ let e = me(n.svg);
714
+ e = ge(e, `g${s.index}-${t}`), o && (e = ye(e, o)), F = {
721
715
  content: e,
722
716
  viewBox: n.viewbox,
723
717
  x: a.x1,
724
718
  y: a.y1,
725
719
  width: a.x2 - a.x1,
726
720
  height: a.y2 - a.y1,
727
- glyphIndex: o.index
721
+ glyphIndex: s.index
728
722
  };
729
723
  }
730
724
  }
731
725
  C.push({
732
726
  path: i,
733
- matrix: N,
727
+ matrix: M,
734
728
  attributes: {
735
- d: F,
736
- "data-character": _e(n[t]),
737
- transform: P
729
+ d: P,
730
+ "data-character": be(n[t]),
731
+ transform: N
738
732
  },
739
733
  glyphData: {
740
- index: o.index,
734
+ index: s.index,
741
735
  bbox: a
742
736
  },
743
- ...I ? { svgGlyph: I } : {}
737
+ ...F ? { svgGlyph: F } : {}
744
738
  });
745
739
  }
746
740
  return C;
747
- }, X = { hex: "#FFFFFF" }, Z = (e, t) => {
741
+ }, K = { hex: "#FFFFFF" }, q = (e, t) => {
748
742
  let n = `data-${e}-colour-url`, r = [`${e}="${t.hex}"`];
749
743
  return t.reference && r.push(`${n}="${t.reference}"`), r.join(" ");
750
- }, Q = (e, t) => {
744
+ }, J = (e, t) => {
751
745
  let { attributes: n, svgGlyph: r } = e;
752
- return r ? `<g transform="${n.transform}" data-character="${n["data-character"]}"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="${r.viewBox}" width="${r.width}" height="${r.height}" x="${r.x}" y="${r.y}" data-glyph-index="${r.glyphIndex}">${r.content}</svg></g>` : `<path d="${n.d}" ${Z("fill", t)} transform="${n.transform}" data-character="${n["data-character"]}"/>`;
753
- }, ve = ({ boxDimensions: e, paths: t, spacing: n = T, fontSize: r = 0, fillColour: i = X, strokeColour: a = X }) => {
746
+ return r ? `<g transform="${n.transform}" data-character="${n["data-character"]}"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="${r.viewBox}" width="${r.width}" height="${r.height}" x="${r.x}" y="${r.y}" data-glyph-index="${r.glyphIndex}">${r.content}</svg></g>` : `<path d="${n.d}" ${q("fill", t)} transform="${n.transform}" data-character="${n["data-character"]}"/>`;
747
+ }, Se = ({ boxDimensions: e, paths: t, spacing: n = T, fontSize: r = 0, fillColour: i = K, strokeColour: a = K }) => {
754
748
  let { width: o, height: s } = e, { outlineWidth: c } = n, l = c > 0 && r > 0, u;
755
749
  if (l) {
756
750
  let e = r * c * 2;
757
- u = `${t.filter((e) => !e.svgGlyph).map(({ attributes: t }) => `<path d="${t.d}" ${Z("fill", a)} ${Z("stroke", a)} stroke-width="${e}" stroke-linejoin="round" transform="${t.transform}" data-character="${t["data-character"]}"/>`).join("\n ")}\n ${t.map((e) => Q(e, i)).join("\n ")}`;
758
- } else u = t.map((e) => Q(e, i)).join("\n ");
751
+ u = `${t.filter((e) => !e.svgGlyph).map(({ attributes: t }) => `<path d="${t.d}" ${q("fill", a)} ${q("stroke", a)} stroke-width="${e}" stroke-linejoin="round" transform="${t.transform}" data-character="${t["data-character"]}"/>`).join("\n ")}\n ${t.map((e) => J(e, i)).join("\n ")}`;
752
+ } else u = t.map((e) => J(e, i)).join("\n ");
759
753
  return `<svg xmlns="http://www.w3.org/2000/svg" width="${o}" height="${s}" viewBox="0 0 ${o} ${s}">${u ? `\n ${u}\n` : ""}</svg>`;
760
- }, $ = (e) => {
754
+ }, Y = (e) => {
761
755
  let { text: t, font: n, physicalSize: r, fontSize: i, boxDimensions: a, spacing: o = T, verticalAlignment: s = "centre", baseline: c = "flat", horizontalAlignment: l = "left", fillColour: u = { hex: "#FFFFFF" }, strokeColour: d = { hex: "#FFFFFF" }, colourMap: f } = e, { width: p, height: m, physicalWidth: h, physicalHeight: g } = a;
762
756
  if (p <= 0 || m <= 0) throw Error(`boxDimensions width and height must be positive (got ${p}x${m})`);
763
757
  if (h <= 0 || g <= 0) throw Error(`boxDimensions physicalWidth and physicalHeight must be positive (got ${h}x${g})`);
@@ -767,7 +761,7 @@ var J = (e, t, n) => e / n * t, Y = ({ font: e, baselinePath: t, text: n, fontSi
767
761
  boxDimensions: a,
768
762
  physicalSize: r,
769
763
  spacing: o
770
- }), v = O(c), y = t.split("\n").filter((e) => e.length > 0);
764
+ }), v = D(c), y = t.split("\n").filter((e) => e.length > 0);
771
765
  y.length || (y = [""]), (v.type === "curved" || v.type === "custom") && (y = [y.join("")]);
772
766
  let b = y.map((e) => F({
773
767
  font: n,
@@ -779,7 +773,7 @@ var J = (e, t, n) => e / n * t, Y = ({ font: e, baselinePath: t, text: n, fontSi
779
773
  let t;
780
774
  switch (v.type) {
781
775
  case "custom":
782
- k(v.path), t = v.path;
776
+ O(v.path), t = v.path;
783
777
  break;
784
778
  case "curved": {
785
779
  let e = F({
@@ -796,18 +790,16 @@ var J = (e, t, n) => e / n * t, Y = ({ font: e, baselinePath: t, text: n, fontSi
796
790
  });
797
791
  break;
798
792
  }
799
- case "flat":
800
- t = A({
801
- boxDimensions: a,
802
- totalTextHeight: w,
803
- baseline: x,
804
- lineHeight: S,
805
- verticalAlignment: s,
806
- lineNumber: e
807
- });
808
- break;
793
+ case "flat": t = k({
794
+ boxDimensions: a,
795
+ totalTextHeight: w,
796
+ baseline: x,
797
+ lineHeight: S,
798
+ verticalAlignment: s,
799
+ lineNumber: e
800
+ });
809
801
  }
810
- let r = Y({
802
+ let r = xe({
811
803
  font: n,
812
804
  baselinePath: t,
813
805
  text: y[e],
@@ -818,7 +810,7 @@ var J = (e, t, n) => e / n * t, Y = ({ font: e, baselinePath: t, text: n, fontSi
818
810
  });
819
811
  E.push(...r);
820
812
  }
821
- return ve({
813
+ return Se({
822
814
  boxDimensions: a,
823
815
  paths: E,
824
816
  spacing: o,
@@ -826,15 +818,139 @@ var J = (e, t, n) => e / n * t, Y = ({ font: e, baselinePath: t, text: n, fontSi
826
818
  fillColour: u,
827
819
  strokeColour: d
828
820
  });
829
- }, ye = async (e) => {
821
+ }, X = 100, Ce = (e) => {
822
+ let t = e.split("\n").filter((e) => e.length > 0);
823
+ return t.length ? t : [""];
824
+ }, we = (e) => e.split("\n").filter((e) => e.length > 0).join(""), Te = (e, t, n, r, i, a) => {
825
+ let o = F({
826
+ font: e,
827
+ text: M(e),
828
+ fontSize: n,
829
+ spacing: r
830
+ }), s = {
831
+ boxDimensions: i,
832
+ baseline: o.baseline,
833
+ textHeight: o.height,
834
+ verticalAlignment: a
835
+ }, l = new c(j(s)).getTotalLength(), { curveTop: u, curveBottom: d } = A(s), f = F({
836
+ font: e,
837
+ text: t,
838
+ fontSize: n,
839
+ spacing: r
840
+ }), p = u - f.baseline, m = d + (f.height - f.baseline);
841
+ return {
842
+ width: f.width <= l,
843
+ height: p >= 0 && m <= i.height
844
+ };
845
+ }, Z = (e) => e.width && e.height, Q = (e) => e.width ? "height" : "width", Ee = .01, De = 60, Oe = ({ font: e, text: t, boxDimensions: n, spacing: r, verticalAlignment: i, minFontSize: a, maxFontSize: o }) => {
846
+ let s = we(t);
847
+ if (!s.length) return {
848
+ fontSize: o,
849
+ fits: !0,
850
+ boundBy: "requestedSize"
851
+ };
852
+ let c = (t) => Te(e, s, t, r, n, i), l = c(a);
853
+ if (!Z(l)) return {
854
+ fontSize: a,
855
+ fits: !1,
856
+ boundBy: Q(l)
857
+ };
858
+ if (Z(c(o))) return {
859
+ fontSize: o,
860
+ fits: !0,
861
+ boundBy: "requestedSize"
862
+ };
863
+ let u = a, d = o, f = 0;
864
+ for (; d - u > Ee && f < De;) {
865
+ let e = (u + d) / 2;
866
+ Z(c(e)) ? u = e : d = e, f++;
867
+ }
868
+ return {
869
+ fontSize: u,
870
+ fits: !0,
871
+ boundBy: Q(c(d))
872
+ };
873
+ }, $ = (e) => {
874
+ let { font: t, text: n, boxDimensions: r, size: i, spacing: a = T, baseline: o = "flat", verticalAlignment: s = "centre" } = e, l = i.mode === "fitToBox" ? i.maxFontSize : i.mode === "fontSize" ? i.fontSize : P({
875
+ font: t,
876
+ boxDimensions: r,
877
+ physicalSize: i.physicalSize,
878
+ spacing: a
879
+ });
880
+ if (!n.length) return {
881
+ fontSize: l,
882
+ fits: !0,
883
+ boundBy: "requestedSize"
884
+ };
885
+ let u = D(o);
886
+ if (u.type === "curved") return Oe({
887
+ font: t,
888
+ text: n,
889
+ boxDimensions: r,
890
+ spacing: a,
891
+ verticalAlignment: s,
892
+ minFontSize: i.mode === "fitToBox" ? i.minFontSize : l,
893
+ maxFontSize: l
894
+ });
895
+ let d = Ce(n);
896
+ u.type === "custom" && (d = [d.join("")]);
897
+ let f = d.map((e) => F({
898
+ font: t,
899
+ text: e,
900
+ fontSize: X,
901
+ spacing: a
902
+ })), p = Math.max(...f.map((e) => e.baseline)) + Math.max(...f.map((e) => e.height - e.baseline)), m = d.length === 1 ? p : p + p * (d.length - 1) * C, h = Math.max(...f.map((e) => e.width)), g = (u.type === "custom" ? new c(u.path).getTotalLength() : r.width) / h, _ = r.height / m, v = g <= _ ? "width" : "height", y = X * Math.min(g, _);
903
+ return i.mode === "fitToBox" ? y < i.minFontSize ? {
904
+ fontSize: i.minFontSize,
905
+ fits: !1,
906
+ boundBy: v
907
+ } : y > i.maxFontSize ? {
908
+ fontSize: i.maxFontSize,
909
+ fits: !0,
910
+ boundBy: "requestedSize"
911
+ } : {
912
+ fontSize: y,
913
+ fits: !0,
914
+ boundBy: v
915
+ } : l <= y ? {
916
+ fontSize: l,
917
+ fits: !0,
918
+ boundBy: "requestedSize"
919
+ } : {
920
+ fontSize: l,
921
+ fits: !1,
922
+ boundBy: v
923
+ };
924
+ }, ke = (e) => {
925
+ let t = e.text, n = t, r = 0, i = 0;
926
+ for (;;) {
927
+ let a = $({
928
+ ...e,
929
+ text: n
930
+ });
931
+ if (a.fits) return {
932
+ ...a,
933
+ text: n,
934
+ truncated: n !== t,
935
+ linesDropped: r,
936
+ charactersRemoved: i
937
+ };
938
+ let o = n.split("\n");
939
+ if (o.length > 1) n = o.slice(0, -1).join("\n"), r += 1;
940
+ else {
941
+ let e = n.slice(0, -1).trim();
942
+ i += n.length - e.length, n = e;
943
+ }
944
+ }
945
+ }, Ae = async (e) => {
830
946
  let { fontUrl: t, fontSizeFallback: n, ...r } = e, i = await x(t, !!r.colourMap);
831
947
  try {
832
- return $({
948
+ return Y({
833
949
  ...r,
834
950
  font: i
835
951
  });
836
952
  } catch (e) {
837
- if (n != null && e instanceof Error && e.message.startsWith("TOO_WIDE")) return $({
953
+ if (n != null && e instanceof Error && e.message.startsWith("TOO_WIDE")) return Y({
838
954
  ...r,
839
955
  font: i,
840
956
  fontSize: n
@@ -843,4 +959,4 @@ var J = (e, t, n) => e / n * t, Y = ({ font: e, baselinePath: t, text: n, fontSi
843
959
  }
844
960
  };
845
961
  //#endregion
846
- export { S as DEFAULT_OUTLINE_FACTOR, T as DEFAULT_SPACING, C as LINE_HEIGHT, j as generateCurvedBaseline, A as generateFlatBaseline, p as getFontCache, ce as getFontColours, P as getFontSizeFromPhysicalSize, ye as getRenderedText, F as getTextMetricsAtFontSize, x as loadFont, O as normaliseBaselineOption, $ as renderText, k as validateCustomBaselinePath };
962
+ export { S as DEFAULT_OUTLINE_FACTOR, T as DEFAULT_SPACING, C as LINE_HEIGHT, ke as fitAndTruncateTextToBox, j as generateCurvedBaseline, k as generateFlatBaseline, p as getFontCache, fe as getFontColours, P as getFontSizeFromPhysicalSize, $ as getFontSizeToFitBox, Ae as getRenderedText, F as getTextMetricsAtFontSize, x as loadFont, D as normaliseBaselineOption, Y as renderText, O as validateCustomBaselinePath };
@@ -1,5 +1,5 @@
1
1
  import { LoadedFont } from '../loading';
2
- import { ColourMap, GlyphFill, HorizontalTextAlignmentValues, TextSpacing } from '../types';
2
+ import { ColourMap, GlyphFill, HorizontalTextAlignmentValues, PlatformMatrix, TextSpacing } from '../types';
3
3
  export interface SvgGlyphEntry {
4
4
  content: string;
5
5
  viewBox: string;
@@ -16,7 +16,7 @@ export interface PathEntry {
16
16
  transform: string;
17
17
  };
18
18
  path: unknown;
19
- matrix: DOMMatrix;
19
+ matrix: PlatformMatrix;
20
20
  glyphData: {
21
21
  index: number;
22
22
  bbox: {
@@ -1,4 +1,7 @@
1
+ import { DOMMatrix } from '@unmade/platform';
1
2
  import { LoadedFont } from './loading';
3
+ /** The platform's matrix, which is not the same shape as the DOM's DOMMatrix. */
4
+ export type PlatformMatrix = InstanceType<typeof DOMMatrix>;
2
5
  export type MeasurementUnits = 'mm' | 'in';
3
6
  export type BoxDimensions = {
4
7
  width: number;
@@ -1,5 +1,5 @@
1
1
  import { LoadedFont } from '../loading';
2
- import { ColourMap, GlyphFill, HorizontalTextAlignmentValues, TextSpacing } from '../types';
2
+ import { ColourMap, GlyphFill, HorizontalTextAlignmentValues, PlatformMatrix, TextSpacing } from '../types';
3
3
  export interface SvgGlyphEntry {
4
4
  content: string;
5
5
  viewBox: string;
@@ -16,7 +16,7 @@ export interface PathEntry {
16
16
  transform: string;
17
17
  };
18
18
  path: unknown;
19
- matrix: DOMMatrix;
19
+ matrix: PlatformMatrix;
20
20
  glyphData: {
21
21
  index: number;
22
22
  bbox: {