@weasel-js/font 1.6.1 → 1.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-QDTJ3CPE.js → chunk-RYN4NYX4.js} +582 -38
- package/dist/chunk-RYN4NYX4.js.map +1 -0
- package/dist/index.d.ts +34 -18
- package/dist/index.js +19 -6
- package/dist/index.js.map +1 -1
- package/dist/{test-seams-BpiGqWz-.d.ts → test-seams-Df2Zqwk8.d.ts} +372 -95
- package/dist/test-seams.d.ts +2 -1
- package/dist/test-seams.js +1 -1
- package/package.json +2 -1
- package/dist/chunk-QDTJ3CPE.js.map +0 -1
|
@@ -1,3 +1,82 @@
|
|
|
1
|
+
import { createReflectable } from '@weasel-js/registry';
|
|
2
|
+
|
|
3
|
+
// src/faceMetrics.ts
|
|
4
|
+
var finite = (n) => typeof n === "number" && Number.isFinite(n);
|
|
5
|
+
var USE_TYPO_METRICS = 1 << 7;
|
|
6
|
+
function verticalMetricsFromTables(t) {
|
|
7
|
+
const upem = t.unitsPerEm;
|
|
8
|
+
if (!finite(upem) || upem <= 0) return void 0;
|
|
9
|
+
const os2 = t.os2;
|
|
10
|
+
const pair = (a, d, down) => finite(a) && finite(d) && a + (down ? d : -d) > 0 ? { ascent: a / upem, descent: (down ? d : -d) / upem } : void 0;
|
|
11
|
+
const typo = pair(os2?.sTypoAscender, os2?.sTypoDescender, false);
|
|
12
|
+
const hhea = pair(t.hhea?.ascender, t.hhea?.descender, false);
|
|
13
|
+
const win = pair(os2?.usWinAscent, os2?.usWinDescent, true);
|
|
14
|
+
const useTypo = finite(os2?.fsSelection) && (os2.fsSelection & USE_TYPO_METRICS) !== 0;
|
|
15
|
+
return (useTypo ? typo : void 0) ?? hhea ?? typo ?? win;
|
|
16
|
+
}
|
|
17
|
+
function faceMetricsFromTables(t) {
|
|
18
|
+
const upem = t.unitsPerEm;
|
|
19
|
+
if (!finite(upem) || upem <= 0) return void 0;
|
|
20
|
+
const out = {};
|
|
21
|
+
const rule = (position, thickness) => finite(position) && finite(thickness) && thickness > 0 ? { offset: -position / upem, thickness: thickness / upem } : void 0;
|
|
22
|
+
const script = (size, shift) => finite(size) && finite(shift) && size > 0 ? { size: size / upem, shift: shift / upem } : void 0;
|
|
23
|
+
const underline = rule(t.post?.underlinePosition, t.post?.underlineThickness);
|
|
24
|
+
const strikethrough = rule(t.os2?.yStrikeoutPosition, t.os2?.yStrikeoutSize);
|
|
25
|
+
const superscript = script(t.os2?.ySuperscriptYSize, t.os2?.ySuperscriptYOffset);
|
|
26
|
+
const sub = t.os2?.ySubscriptYOffset;
|
|
27
|
+
const subscript = script(t.os2?.ySubscriptYSize, finite(sub) ? -sub : sub);
|
|
28
|
+
const vertical = verticalMetricsFromTables(t);
|
|
29
|
+
if (vertical) {
|
|
30
|
+
out.ascent = vertical.ascent;
|
|
31
|
+
out.descent = vertical.descent;
|
|
32
|
+
}
|
|
33
|
+
if (underline) out.underline = underline;
|
|
34
|
+
if (strikethrough) out.strikethrough = strikethrough;
|
|
35
|
+
if (superscript) out.superscript = superscript;
|
|
36
|
+
if (subscript) out.subscript = subscript;
|
|
37
|
+
const xHeight = t.os2?.sxHeight;
|
|
38
|
+
const capHeight = t.os2?.sCapHeight;
|
|
39
|
+
if (finite(xHeight) && xHeight > 0) out.xHeight = xHeight / upem;
|
|
40
|
+
if (finite(capHeight) && capHeight > 0) out.capHeight = capHeight / upem;
|
|
41
|
+
return Object.keys(out).length > 0 ? out : void 0;
|
|
42
|
+
}
|
|
43
|
+
function parseFaceMetrics(raw) {
|
|
44
|
+
if (raw === void 0) return void 0;
|
|
45
|
+
if (typeof raw !== "object" || raw === null) throw new Error("parseBmFont: faceMetrics must be an object");
|
|
46
|
+
const r = raw;
|
|
47
|
+
const out = {};
|
|
48
|
+
if (r.ascent !== void 0 || r.descent !== void 0) {
|
|
49
|
+
if (!finite(r.ascent) || !finite(r.descent) || r.ascent + r.descent <= 0) {
|
|
50
|
+
throw new Error("parseBmFont: malformed faceMetrics.ascent/descent");
|
|
51
|
+
}
|
|
52
|
+
out.ascent = r.ascent;
|
|
53
|
+
out.descent = r.descent;
|
|
54
|
+
}
|
|
55
|
+
const pair = (key, a, b, positive) => {
|
|
56
|
+
const v = r[key];
|
|
57
|
+
if (v === void 0) return;
|
|
58
|
+
const e = v;
|
|
59
|
+
if (typeof e !== "object" || e === null || !finite(e[a]) || !finite(e[b]) || e[positive] <= 0) {
|
|
60
|
+
throw new Error(`parseBmFont: malformed faceMetrics.${key}`);
|
|
61
|
+
}
|
|
62
|
+
out[key] = { [a]: e[a], [b]: e[b] };
|
|
63
|
+
};
|
|
64
|
+
pair("underline", "offset", "thickness", "thickness");
|
|
65
|
+
pair("strikethrough", "offset", "thickness", "thickness");
|
|
66
|
+
pair("superscript", "size", "shift", "size");
|
|
67
|
+
pair("subscript", "size", "shift", "size");
|
|
68
|
+
for (const key of ["xHeight", "capHeight"]) {
|
|
69
|
+
const v = r[key];
|
|
70
|
+
if (v === void 0) continue;
|
|
71
|
+
if (!finite(v) || v <= 0) throw new Error(`parseBmFont: malformed faceMetrics.${key}`);
|
|
72
|
+
out[key] = v;
|
|
73
|
+
}
|
|
74
|
+
return Object.keys(out).length > 0 ? out : void 0;
|
|
75
|
+
}
|
|
76
|
+
function faceMetricsOf(result) {
|
|
77
|
+
return result.outlineFace?.faceMetrics ?? result.entry?.font.faceMetrics;
|
|
78
|
+
}
|
|
79
|
+
|
|
1
80
|
// src/FontAtlas.ts
|
|
2
81
|
var FIXTURE_FONT = {
|
|
3
82
|
info: { face: "Inter", size: 32 },
|
|
@@ -31,7 +110,18 @@ function parseBmFont(raw) {
|
|
|
31
110
|
}
|
|
32
111
|
inner.set(k.second, k.amount);
|
|
33
112
|
}
|
|
34
|
-
|
|
113
|
+
const faceMetrics = parseFaceMetrics(r.faceMetrics);
|
|
114
|
+
const range = r.distanceField?.distanceRange;
|
|
115
|
+
return {
|
|
116
|
+
info,
|
|
117
|
+
common,
|
|
118
|
+
chars,
|
|
119
|
+
kernings,
|
|
120
|
+
charMap,
|
|
121
|
+
kerningMap,
|
|
122
|
+
...faceMetrics ? { faceMetrics } : {},
|
|
123
|
+
...typeof range === "number" && range > 0 ? { distanceRange: range } : {}
|
|
124
|
+
};
|
|
35
125
|
}
|
|
36
126
|
|
|
37
127
|
// src/glyphReady.ts
|
|
@@ -58,13 +148,17 @@ function _clearGlyphReadySubscribers() {
|
|
|
58
148
|
var policy = "substitute";
|
|
59
149
|
var defaultFamily = null;
|
|
60
150
|
function setFontFallbackPolicy(next) {
|
|
151
|
+
if (next === policy) return;
|
|
61
152
|
policy = next;
|
|
153
|
+
notifyGlyphReady();
|
|
62
154
|
}
|
|
63
155
|
function getFontFallbackPolicy() {
|
|
64
156
|
return policy;
|
|
65
157
|
}
|
|
66
158
|
function setDefaultFontFamily(family) {
|
|
159
|
+
if (family === defaultFamily) return;
|
|
67
160
|
defaultFamily = family;
|
|
161
|
+
notifyGlyphReady();
|
|
68
162
|
}
|
|
69
163
|
function getDefaultFontFamily() {
|
|
70
164
|
return defaultFamily;
|
|
@@ -203,8 +297,9 @@ function alphaToSdf(alpha, width, height, radius, cutoff) {
|
|
|
203
297
|
// src/dynamic/glyphRasterizer.ts
|
|
204
298
|
var BAKE_SIZE = 48;
|
|
205
299
|
var PAD = 8;
|
|
206
|
-
|
|
207
|
-
|
|
300
|
+
var METRICS_SIZE = 1e3;
|
|
301
|
+
function cssFontString(weight, style, family, size = BAKE_SIZE) {
|
|
302
|
+
return `${style === "italic" ? "italic " : ""}${weight} ${size}px ${JSON.stringify(family)}`;
|
|
208
303
|
}
|
|
209
304
|
function createCanvasRasterizer() {
|
|
210
305
|
let canvas;
|
|
@@ -219,18 +314,19 @@ function createCanvasRasterizer() {
|
|
|
219
314
|
}
|
|
220
315
|
const ctx = canvas.getContext("2d", { willReadFrequently: true });
|
|
221
316
|
if (!ctx) throw new Error("weasel DynamicGlyphAtlas: 2D context unavailable");
|
|
222
|
-
function setFont(family, weight, style) {
|
|
223
|
-
ctx.font = cssFontString(weight, style, family);
|
|
317
|
+
function setFont(family, weight, style, size = BAKE_SIZE) {
|
|
318
|
+
ctx.font = cssFontString(weight, style, family, size);
|
|
224
319
|
ctx.textBaseline = "alphabetic";
|
|
225
320
|
ctx.textAlign = "left";
|
|
226
321
|
}
|
|
227
322
|
return {
|
|
228
323
|
faceMetrics(family, weight, style) {
|
|
229
|
-
setFont(family, weight, style);
|
|
324
|
+
setFont(family, weight, style, METRICS_SIZE);
|
|
230
325
|
const m = ctx.measureText("Hg");
|
|
326
|
+
const k = BAKE_SIZE / METRICS_SIZE;
|
|
231
327
|
return {
|
|
232
|
-
ascent: m.fontBoundingBoxAscent ??
|
|
233
|
-
descent: m.fontBoundingBoxDescent ??
|
|
328
|
+
ascent: (m.fontBoundingBoxAscent ?? METRICS_SIZE * 0.8) * k,
|
|
329
|
+
descent: (m.fontBoundingBoxDescent ?? METRICS_SIZE * 0.2) * k
|
|
234
330
|
};
|
|
235
331
|
},
|
|
236
332
|
rasterize(family, weight, style, codepoint) {
|
|
@@ -292,8 +388,10 @@ function getRasterizer() {
|
|
|
292
388
|
return rasterizer;
|
|
293
389
|
}
|
|
294
390
|
function registerCanvasFont(family) {
|
|
391
|
+
if (isExplicitCanvasFont(family)) return;
|
|
295
392
|
canvasFamilies.add(family);
|
|
296
393
|
autoEnrolledFamilies.delete(family);
|
|
394
|
+
notifyGlyphReady();
|
|
297
395
|
}
|
|
298
396
|
function isCanvasFont(family) {
|
|
299
397
|
if (!canvasFamilies.has(family)) return false;
|
|
@@ -316,11 +414,13 @@ function isExplicitCanvasFont(family) {
|
|
|
316
414
|
return canvasFamilies.has(family) && !autoEnrolledFamilies.has(family);
|
|
317
415
|
}
|
|
318
416
|
function unregisterCanvasFont(family) {
|
|
417
|
+
if (!canvasFamilies.has(family)) return;
|
|
319
418
|
canvasFamilies.delete(family);
|
|
320
419
|
autoEnrolledFamilies.delete(family);
|
|
321
420
|
for (const key of [...faces.keys()]) {
|
|
322
421
|
if (faces.get(key).family === family) faces.delete(key);
|
|
323
422
|
}
|
|
423
|
+
notifyGlyphReady();
|
|
324
424
|
}
|
|
325
425
|
function getDynamicFace(family, weight, style) {
|
|
326
426
|
const key = `${family}|${weight}|${style}`;
|
|
@@ -341,7 +441,9 @@ function getDynamicFace(family, weight, style) {
|
|
|
341
441
|
kernings: [],
|
|
342
442
|
// no kerning in v1 (measured-pair kerning is future work)
|
|
343
443
|
charMap: /* @__PURE__ */ new Map(),
|
|
344
|
-
kerningMap: /* @__PURE__ */ new Map()
|
|
444
|
+
kerningMap: /* @__PURE__ */ new Map(),
|
|
445
|
+
// Unrounded, unlike `common`: layout places the baseline from these.
|
|
446
|
+
faceMetrics: { ascent: metrics.ascent / BAKE_SIZE, descent: metrics.descent / BAKE_SIZE }
|
|
345
447
|
};
|
|
346
448
|
const face = {
|
|
347
449
|
family,
|
|
@@ -598,6 +700,163 @@ function sfntFromCollection(bytes, postScriptName) {
|
|
|
598
700
|
return { bytes: extractFont(bytes, view, offsets[0]), matched: !postScriptName };
|
|
599
701
|
}
|
|
600
702
|
|
|
703
|
+
// src/outline/gposKerning.ts
|
|
704
|
+
function gposPairKerning(bytes) {
|
|
705
|
+
const v = new DataView(bytes);
|
|
706
|
+
const version = v.byteLength >= 12 ? tag(v, 0) : "";
|
|
707
|
+
if (version !== "\0\0\0" && version !== "true" && version !== "OTTO") return null;
|
|
708
|
+
const gpos = tableOffset(v, "GPOS");
|
|
709
|
+
if (gpos === null) return null;
|
|
710
|
+
const featureList = gpos + v.getUint16(gpos + 6);
|
|
711
|
+
const lookupList = gpos + v.getUint16(gpos + 8);
|
|
712
|
+
const lookupIndexes = /* @__PURE__ */ new Set();
|
|
713
|
+
const featureCount = v.getUint16(featureList);
|
|
714
|
+
for (let i = 0; i < featureCount; i++) {
|
|
715
|
+
const rec = featureList + 2 + i * 6;
|
|
716
|
+
if (tag(v, rec) !== "kern") continue;
|
|
717
|
+
const feature = featureList + v.getUint16(rec + 4);
|
|
718
|
+
const count = v.getUint16(feature + 2);
|
|
719
|
+
for (let j = 0; j < count; j++) lookupIndexes.add(v.getUint16(feature + 4 + j * 2));
|
|
720
|
+
}
|
|
721
|
+
if (lookupIndexes.size === 0) return null;
|
|
722
|
+
const lookups = [];
|
|
723
|
+
for (const index of [...lookupIndexes].sort((a, b) => a - b)) {
|
|
724
|
+
const lookup = lookupList + v.getUint16(lookupList + 2 + index * 2);
|
|
725
|
+
const type = v.getUint16(lookup);
|
|
726
|
+
const subtables = [];
|
|
727
|
+
const count = v.getUint16(lookup + 4);
|
|
728
|
+
for (let j = 0; j < count; j++) {
|
|
729
|
+
let sub = lookup + v.getUint16(lookup + 6 + j * 2);
|
|
730
|
+
let subType = type;
|
|
731
|
+
if (type === 9) {
|
|
732
|
+
subType = v.getUint16(sub + 2);
|
|
733
|
+
sub += v.getUint32(sub + 4);
|
|
734
|
+
}
|
|
735
|
+
if (subType === 2) subtables.push(sub);
|
|
736
|
+
}
|
|
737
|
+
if (subtables.length > 0) lookups.push(subtables);
|
|
738
|
+
}
|
|
739
|
+
const memo = /* @__PURE__ */ new Map();
|
|
740
|
+
return (left, right) => {
|
|
741
|
+
const key = left * 65536 + right;
|
|
742
|
+
const hit = memo.get(key);
|
|
743
|
+
if (hit !== void 0) return hit;
|
|
744
|
+
let total = 0;
|
|
745
|
+
for (const subtables of lookups) {
|
|
746
|
+
for (const sub of subtables) {
|
|
747
|
+
const value = pairValue(v, sub, left, right);
|
|
748
|
+
if (value !== null) {
|
|
749
|
+
total += value;
|
|
750
|
+
break;
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
memo.set(key, total);
|
|
755
|
+
return total;
|
|
756
|
+
};
|
|
757
|
+
}
|
|
758
|
+
function tag(v, at) {
|
|
759
|
+
return String.fromCharCode(v.getUint8(at), v.getUint8(at + 1), v.getUint8(at + 2), v.getUint8(at + 3));
|
|
760
|
+
}
|
|
761
|
+
function tableOffset(v, name) {
|
|
762
|
+
const count = v.getUint16(4);
|
|
763
|
+
for (let i = 0; i < count; i++) {
|
|
764
|
+
const rec = 12 + i * 16;
|
|
765
|
+
if (tag(v, rec) === name) return v.getUint32(rec + 8);
|
|
766
|
+
}
|
|
767
|
+
return null;
|
|
768
|
+
}
|
|
769
|
+
function valueSize(format) {
|
|
770
|
+
let n = 0;
|
|
771
|
+
for (let f = format; f; f >>= 1) n += f & 1;
|
|
772
|
+
return n * 2;
|
|
773
|
+
}
|
|
774
|
+
function xAdvance(v, at, format) {
|
|
775
|
+
if (!(format & 4)) return 0;
|
|
776
|
+
return v.getInt16(at + (format & 1 ? 2 : 0) + (format & 2 ? 2 : 0));
|
|
777
|
+
}
|
|
778
|
+
function pairValue(v, sub, left, right) {
|
|
779
|
+
const format = v.getUint16(sub);
|
|
780
|
+
const covIndex = coverageIndex(v, sub + v.getUint16(sub + 2), left);
|
|
781
|
+
if (covIndex < 0) return null;
|
|
782
|
+
const vf1 = v.getUint16(sub + 4);
|
|
783
|
+
const vf2 = v.getUint16(sub + 6);
|
|
784
|
+
const recordSize = 2 + valueSize(vf1) + valueSize(vf2);
|
|
785
|
+
if (format === 1) {
|
|
786
|
+
const pairSet = sub + v.getUint16(sub + 10 + covIndex * 2);
|
|
787
|
+
let lo = 0;
|
|
788
|
+
let hi = v.getUint16(pairSet) - 1;
|
|
789
|
+
while (lo <= hi) {
|
|
790
|
+
const mid = lo + hi >> 1;
|
|
791
|
+
const rec = pairSet + 2 + mid * recordSize;
|
|
792
|
+
const second = v.getUint16(rec);
|
|
793
|
+
if (second === right) return xAdvance(v, rec + 2, vf1);
|
|
794
|
+
if (second < right) lo = mid + 1;
|
|
795
|
+
else hi = mid - 1;
|
|
796
|
+
}
|
|
797
|
+
return null;
|
|
798
|
+
}
|
|
799
|
+
if (format === 2) {
|
|
800
|
+
const class1 = classOf(v, sub + v.getUint16(sub + 8), left);
|
|
801
|
+
const class2 = classOf(v, sub + v.getUint16(sub + 10), right);
|
|
802
|
+
const class1Count = v.getUint16(sub + 12);
|
|
803
|
+
const class2Count = v.getUint16(sub + 14);
|
|
804
|
+
if (class1 >= class1Count || class2 >= class2Count) return null;
|
|
805
|
+
const rec = sub + 16 + (class1 * class2Count + class2) * (recordSize - 2);
|
|
806
|
+
return xAdvance(v, rec, vf1);
|
|
807
|
+
}
|
|
808
|
+
return null;
|
|
809
|
+
}
|
|
810
|
+
function coverageIndex(v, cov, glyph) {
|
|
811
|
+
const format = v.getUint16(cov);
|
|
812
|
+
const count = v.getUint16(cov + 2);
|
|
813
|
+
let lo = 0;
|
|
814
|
+
let hi = count - 1;
|
|
815
|
+
if (format === 1) {
|
|
816
|
+
while (lo <= hi) {
|
|
817
|
+
const mid = lo + hi >> 1;
|
|
818
|
+
const g = v.getUint16(cov + 4 + mid * 2);
|
|
819
|
+
if (g === glyph) return mid;
|
|
820
|
+
if (g < glyph) lo = mid + 1;
|
|
821
|
+
else hi = mid - 1;
|
|
822
|
+
}
|
|
823
|
+
return -1;
|
|
824
|
+
}
|
|
825
|
+
if (format === 2) {
|
|
826
|
+
while (lo <= hi) {
|
|
827
|
+
const mid = lo + hi >> 1;
|
|
828
|
+
const rec = cov + 4 + mid * 6;
|
|
829
|
+
const start = v.getUint16(rec);
|
|
830
|
+
const end = v.getUint16(rec + 2);
|
|
831
|
+
if (glyph < start) hi = mid - 1;
|
|
832
|
+
else if (glyph > end) lo = mid + 1;
|
|
833
|
+
else return v.getUint16(rec + 4) + glyph - start;
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
return -1;
|
|
837
|
+
}
|
|
838
|
+
function classOf(v, def, glyph) {
|
|
839
|
+
const format = v.getUint16(def);
|
|
840
|
+
if (format === 1) {
|
|
841
|
+
const start = v.getUint16(def + 2);
|
|
842
|
+
const count = v.getUint16(def + 4);
|
|
843
|
+
const i = glyph - start;
|
|
844
|
+
return i >= 0 && i < count ? v.getUint16(def + 6 + i * 2) : 0;
|
|
845
|
+
}
|
|
846
|
+
if (format === 2) {
|
|
847
|
+
let lo = 0;
|
|
848
|
+
let hi = v.getUint16(def + 2) - 1;
|
|
849
|
+
while (lo <= hi) {
|
|
850
|
+
const mid = lo + hi >> 1;
|
|
851
|
+
const rec = def + 4 + mid * 6;
|
|
852
|
+
if (glyph < v.getUint16(rec)) hi = mid - 1;
|
|
853
|
+
else if (glyph > v.getUint16(rec + 2)) lo = mid + 1;
|
|
854
|
+
else return v.getUint16(rec + 4);
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
return 0;
|
|
858
|
+
}
|
|
859
|
+
|
|
601
860
|
// src/outline/opentypeParser.ts
|
|
602
861
|
var modulePromise = null;
|
|
603
862
|
function loadOpenType() {
|
|
@@ -608,11 +867,14 @@ function parserOf(ns) {
|
|
|
608
867
|
const interop = ns;
|
|
609
868
|
return interop.default?.parse ?? ns.parse;
|
|
610
869
|
}
|
|
611
|
-
function faceFor(font) {
|
|
870
|
+
function faceFor(font, gpos) {
|
|
612
871
|
const upem = font.unitsPerEm;
|
|
872
|
+
const { hhea, post, os2 } = font.tables;
|
|
873
|
+
const faceMetrics = faceMetricsFromTables({ unitsPerEm: upem, hhea, post, os2 });
|
|
613
874
|
return {
|
|
614
875
|
unitsPerEm: upem,
|
|
615
|
-
ascender: font.ascender / upem,
|
|
876
|
+
ascender: faceMetrics?.ascent ?? font.ascender / upem,
|
|
877
|
+
...faceMetrics ? { faceMetrics } : {},
|
|
616
878
|
advanceOf(cp) {
|
|
617
879
|
const index = font.charToGlyphIndex(String.fromCodePoint(cp));
|
|
618
880
|
if (!index) return null;
|
|
@@ -622,6 +884,7 @@ function faceFor(font) {
|
|
|
622
884
|
const l = font.charToGlyphIndex(String.fromCodePoint(left));
|
|
623
885
|
const r = font.charToGlyphIndex(String.fromCodePoint(right));
|
|
624
886
|
if (!l || !r) return 0;
|
|
887
|
+
if (gpos) return gpos(l, r) / upem;
|
|
625
888
|
return font.getKerningValue(font.glyphs.get(l), font.glyphs.get(r)) / upem;
|
|
626
889
|
},
|
|
627
890
|
glyphD(cp) {
|
|
@@ -636,13 +899,14 @@ function createOpenTypeParser(postScriptName) {
|
|
|
636
899
|
return async (bytes) => {
|
|
637
900
|
const parse = parserOf(await loadOpenType());
|
|
638
901
|
const { bytes: single } = sfntFromCollection(bytes, postScriptName);
|
|
639
|
-
return faceFor(parse(single));
|
|
902
|
+
return faceFor(parse(single), gposPairKerning(single));
|
|
640
903
|
};
|
|
641
904
|
}
|
|
642
905
|
var openTypeParser = createOpenTypeParser();
|
|
643
906
|
|
|
644
907
|
// src/outline/outlineRegistry.ts
|
|
645
|
-
var slots =
|
|
908
|
+
var slots = createReflectable();
|
|
909
|
+
var fontOutlineRegistry = slots.reflection;
|
|
646
910
|
function slotKey(family, weight, style) {
|
|
647
911
|
return `${family}|${weight}|${style}`;
|
|
648
912
|
}
|
|
@@ -656,12 +920,15 @@ function registerFontOutlines(family, variant, source, opts = {}) {
|
|
|
656
920
|
weight,
|
|
657
921
|
style,
|
|
658
922
|
source,
|
|
923
|
+
cssSrc: opts.cssSrc,
|
|
659
924
|
parser: opts.parser ?? openTypeParser,
|
|
660
925
|
status: "idle",
|
|
661
926
|
face: null,
|
|
662
|
-
glyphs: /* @__PURE__ */ new Map()
|
|
927
|
+
glyphs: /* @__PURE__ */ new Map(),
|
|
928
|
+
load: null
|
|
663
929
|
});
|
|
664
930
|
notifyGlyphReady();
|
|
931
|
+
if (source instanceof ArrayBuffer) announceBytes(slots.get(slotKey(family, weight, style)), source);
|
|
665
932
|
}
|
|
666
933
|
function unregisterFontOutlines(family, variant = {}) {
|
|
667
934
|
const { weight, style } = normalize(variant);
|
|
@@ -674,7 +941,7 @@ function outlineStatus(family, weight = 400, style = "normal") {
|
|
|
674
941
|
return slots.get(slotKey(family, weight, style))?.status ?? null;
|
|
675
942
|
}
|
|
676
943
|
function listFontOutlines() {
|
|
677
|
-
return
|
|
944
|
+
return slots.entries().map(({ value: { family, weight, style, status } }) => ({ family, weight, style, status })).sort((a, b) => a.family.localeCompare(b.family) || a.weight - b.weight || a.style.localeCompare(b.style));
|
|
678
945
|
}
|
|
679
946
|
function closeContours(d) {
|
|
680
947
|
let out = "";
|
|
@@ -725,20 +992,57 @@ function outlineMetrics(family, weight, style) {
|
|
|
725
992
|
}
|
|
726
993
|
return slot.status === "ready" ? slot.face : null;
|
|
727
994
|
}
|
|
728
|
-
async function
|
|
995
|
+
async function loadFontOutlines(family, variant = {}) {
|
|
996
|
+
const { weight, style } = normalize(variant);
|
|
997
|
+
const slot = slots.get(slotKey(family, weight, style));
|
|
998
|
+
if (!slot) return null;
|
|
999
|
+
await beginLoad(slot);
|
|
1000
|
+
return slot.status;
|
|
1001
|
+
}
|
|
1002
|
+
function beginLoad(slot) {
|
|
1003
|
+
slot.load ??= runLoad(slot);
|
|
1004
|
+
return slot.load;
|
|
1005
|
+
}
|
|
1006
|
+
async function runLoad(slot) {
|
|
729
1007
|
slot.status = "loading";
|
|
1008
|
+
slots.bump();
|
|
730
1009
|
try {
|
|
731
|
-
|
|
1010
|
+
const bytes = await readSource(slot.source);
|
|
1011
|
+
announceBytes(slot, bytes);
|
|
1012
|
+
slot.face = await slot.parser(bytes);
|
|
732
1013
|
slot.status = "ready";
|
|
1014
|
+
slots.bump();
|
|
733
1015
|
notifyGlyphReady();
|
|
734
1016
|
} catch (err) {
|
|
735
1017
|
slot.status = "failed";
|
|
1018
|
+
slots.bump();
|
|
1019
|
+
notifyGlyphReady();
|
|
736
1020
|
warnOnce(
|
|
737
1021
|
`load|${slotKey(slot.family, slot.weight, slot.style)}`,
|
|
738
1022
|
`weasel registerFontOutlines("${slot.family}" ${slot.weight}/${slot.style}): ${err instanceof Error ? err.message : String(err)}. Large text in this face keeps rendering from the SDF tier.`
|
|
739
1023
|
);
|
|
740
1024
|
}
|
|
741
1025
|
}
|
|
1026
|
+
function outlineCssSource(family, weight, style) {
|
|
1027
|
+
const slot = slots.get(slotKey(family, weight, style));
|
|
1028
|
+
if (!slot) return null;
|
|
1029
|
+
if (slot.cssSrc !== void 0) return { registration: slot, src: slot.cssSrc };
|
|
1030
|
+
if (typeof slot.source === "string") {
|
|
1031
|
+
return { registration: slot, url: `url(${JSON.stringify(slot.source)})` };
|
|
1032
|
+
}
|
|
1033
|
+
const source = slot.source;
|
|
1034
|
+
return { registration: slot, bytes: () => readSource(source) };
|
|
1035
|
+
}
|
|
1036
|
+
var bytesListener = null;
|
|
1037
|
+
function onOutlineBytes(listener) {
|
|
1038
|
+
bytesListener = listener;
|
|
1039
|
+
}
|
|
1040
|
+
function announceBytes(slot, bytes) {
|
|
1041
|
+
try {
|
|
1042
|
+
bytesListener?.(slot, bytes);
|
|
1043
|
+
} catch {
|
|
1044
|
+
}
|
|
1045
|
+
}
|
|
742
1046
|
async function readSource(source) {
|
|
743
1047
|
const resolved = typeof source === "function" ? await source() : source;
|
|
744
1048
|
if (typeof resolved === "string") {
|
|
@@ -756,12 +1060,11 @@ function warnOnce(key, message) {
|
|
|
756
1060
|
console.warn(message);
|
|
757
1061
|
}
|
|
758
1062
|
function _resetFontOutlinesForTests() {
|
|
759
|
-
slots
|
|
1063
|
+
slots.clear();
|
|
760
1064
|
warned.clear();
|
|
761
1065
|
}
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
var registry = /* @__PURE__ */ new Map();
|
|
1066
|
+
var registry = createReflectable();
|
|
1067
|
+
var fontRegistry = registry.reflection;
|
|
765
1068
|
function variantKey(weight, style) {
|
|
766
1069
|
return `${weight}|${style}`;
|
|
767
1070
|
}
|
|
@@ -771,17 +1074,33 @@ function normalizeVariant(v) {
|
|
|
771
1074
|
style: v.style ?? "normal"
|
|
772
1075
|
};
|
|
773
1076
|
}
|
|
1077
|
+
var inFlight = /* @__PURE__ */ new Map();
|
|
1078
|
+
var loads = /* @__PURE__ */ new Map();
|
|
1079
|
+
var declared = /* @__PURE__ */ new Map();
|
|
1080
|
+
var registeredFamilies = /* @__PURE__ */ new Set();
|
|
774
1081
|
function _resetFontRegistryForTests() {
|
|
775
|
-
registry
|
|
1082
|
+
registry.clear();
|
|
1083
|
+
inFlight.clear();
|
|
1084
|
+
loads.clear();
|
|
1085
|
+
declared.clear();
|
|
1086
|
+
registeredFamilies.clear();
|
|
776
1087
|
_clearFallbackWarnings();
|
|
777
1088
|
}
|
|
778
1089
|
function getFont(family, weight = 400, style = "normal") {
|
|
779
1090
|
return registry.get(family)?.get(variantKey(weight, style)) ?? null;
|
|
780
1091
|
}
|
|
1092
|
+
function faceMetricsFor(family, weight = 400, style = "normal") {
|
|
1093
|
+
return getFont(family, weight, style)?.font.faceMetrics ?? outlineMetrics(family, weight, style === "italic" ? "italic" : "normal")?.faceMetrics;
|
|
1094
|
+
}
|
|
781
1095
|
function listFonts() {
|
|
782
1096
|
const out = [];
|
|
783
|
-
for (const
|
|
784
|
-
const
|
|
1097
|
+
for (const family of registeredFamilies) {
|
|
1098
|
+
const keys = /* @__PURE__ */ new Set([
|
|
1099
|
+
...registry.get(family)?.keys() ?? [],
|
|
1100
|
+
...declared.get(family)?.keys() ?? []
|
|
1101
|
+
]);
|
|
1102
|
+
if (keys.size === 0) continue;
|
|
1103
|
+
const variants = [...keys].map((key) => {
|
|
785
1104
|
const [w, s] = key.split("|");
|
|
786
1105
|
return { weight: Number(w), style: s };
|
|
787
1106
|
}).sort((a, b) => a.weight - b.weight || a.style.localeCompare(b.style));
|
|
@@ -789,10 +1108,60 @@ function listFonts() {
|
|
|
789
1108
|
}
|
|
790
1109
|
return out;
|
|
791
1110
|
}
|
|
792
|
-
|
|
1111
|
+
function listFontWeights(family) {
|
|
1112
|
+
const weights = /* @__PURE__ */ new Set();
|
|
1113
|
+
for (const key of registry.get(family)?.keys() ?? []) weights.add(Number(key.split("|")[0]));
|
|
1114
|
+
for (const key of declared.get(family)?.keys() ?? []) weights.add(Number(key.split("|")[0]));
|
|
1115
|
+
for (const face of listFontOutlines()) {
|
|
1116
|
+
if (face.family === family && face.status !== "failed") weights.add(face.weight);
|
|
1117
|
+
}
|
|
1118
|
+
return [...weights].sort((a, b) => a - b);
|
|
1119
|
+
}
|
|
1120
|
+
function registerFont(family, variant, metricsUrl, atlasUrl, opts = {}) {
|
|
793
1121
|
const { weight, style } = normalizeVariant(variant);
|
|
794
1122
|
const key = variantKey(weight, style);
|
|
795
|
-
|
|
1123
|
+
registeredFamilies.add(family);
|
|
1124
|
+
if (registry.get(family)?.has(key)) return Promise.resolve();
|
|
1125
|
+
if (!opts.lazy) return loadVariant(family, key, metricsUrl, atlasUrl);
|
|
1126
|
+
const running = loads.get(`${family}|${key}`);
|
|
1127
|
+
if (running) return running;
|
|
1128
|
+
const existing = declared.get(family)?.get(key);
|
|
1129
|
+
if (existing) return existing.promise;
|
|
1130
|
+
let settle2;
|
|
1131
|
+
const promise = new Promise((resolve, reject) => {
|
|
1132
|
+
settle2 = (load) => {
|
|
1133
|
+
load.then(resolve, reject);
|
|
1134
|
+
};
|
|
1135
|
+
});
|
|
1136
|
+
const variants = declared.get(family) ?? /* @__PURE__ */ new Map();
|
|
1137
|
+
declared.set(family, variants.set(key, { metricsUrl, atlasUrl, promise, settle: settle2 }));
|
|
1138
|
+
notifyGlyphReady();
|
|
1139
|
+
return promise;
|
|
1140
|
+
}
|
|
1141
|
+
function loadVariant(family, key, metricsUrl, atlasUrl) {
|
|
1142
|
+
const id = `${family}|${key}`;
|
|
1143
|
+
const running = loads.get(id);
|
|
1144
|
+
if (running) return running;
|
|
1145
|
+
const load = fetchVariant(family, key, metricsUrl, atlasUrl);
|
|
1146
|
+
loads.set(id, load);
|
|
1147
|
+
const settleLoad = () => {
|
|
1148
|
+
if (loads.get(id) === load) loads.delete(id);
|
|
1149
|
+
};
|
|
1150
|
+
load.then(settleLoad, settleLoad);
|
|
1151
|
+
const pendingLazy = declared.get(family)?.get(key);
|
|
1152
|
+
if (pendingLazy) {
|
|
1153
|
+
const variants = declared.get(family);
|
|
1154
|
+
variants.delete(key);
|
|
1155
|
+
if (variants.size === 0) declared.delete(family);
|
|
1156
|
+
pendingLazy.settle(load);
|
|
1157
|
+
}
|
|
1158
|
+
return load;
|
|
1159
|
+
}
|
|
1160
|
+
async function fetchVariant(family, key, metricsUrl, atlasUrl) {
|
|
1161
|
+
const [weight, style] = key.split("|");
|
|
1162
|
+
const token = {};
|
|
1163
|
+
const tokens = inFlight.get(family) ?? /* @__PURE__ */ new Set();
|
|
1164
|
+
inFlight.set(family, tokens.add(token));
|
|
796
1165
|
try {
|
|
797
1166
|
const [metricsRes, atlasRes] = await Promise.all([
|
|
798
1167
|
fetch(metricsUrl),
|
|
@@ -810,19 +1179,76 @@ async function registerFont(family, variant, metricsUrl, atlasUrl) {
|
|
|
810
1179
|
]);
|
|
811
1180
|
const font = parseBmFont(rawJson);
|
|
812
1181
|
const bitmap = await createImageBitmap(blob);
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
familyMap = /* @__PURE__ */ new Map();
|
|
816
|
-
registry.set(family, familyMap);
|
|
817
|
-
}
|
|
818
|
-
familyMap.set(key, { font, bitmap });
|
|
1182
|
+
registry.set(family, new Map(registry.get(family)).set(key, { font, bitmap }));
|
|
1183
|
+
settle(family, token);
|
|
819
1184
|
notifyGlyphReady();
|
|
820
1185
|
} catch (err) {
|
|
1186
|
+
settle(family, token);
|
|
1187
|
+
notifyGlyphReady();
|
|
821
1188
|
throw new Error(
|
|
822
1189
|
`weasel registerFont("${family}" ${weight}/${style}): ${err instanceof Error ? err.message : String(err)}`
|
|
823
1190
|
);
|
|
824
1191
|
}
|
|
825
1192
|
}
|
|
1193
|
+
function wakeDeclared(family, weight, style) {
|
|
1194
|
+
const variants = declared.get(family);
|
|
1195
|
+
if (!variants) return;
|
|
1196
|
+
const exact = variantKey(weight, style);
|
|
1197
|
+
const keys = variants.has(exact) ? [exact] : [...variants.keys()];
|
|
1198
|
+
for (const key of keys) {
|
|
1199
|
+
const { metricsUrl, atlasUrl } = variants.get(key);
|
|
1200
|
+
loadVariant(family, key, metricsUrl, atlasUrl).catch(() => {
|
|
1201
|
+
});
|
|
1202
|
+
}
|
|
1203
|
+
}
|
|
1204
|
+
function warmFonts(fonts) {
|
|
1205
|
+
const pending2 = [];
|
|
1206
|
+
for (const font of fonts ?? [...registeredFamilies]) {
|
|
1207
|
+
if (typeof font !== "string") {
|
|
1208
|
+
const { weight, style } = normalizeVariant(font);
|
|
1209
|
+
pending2.push(...variantLoads(font.family, weight, style));
|
|
1210
|
+
continue;
|
|
1211
|
+
}
|
|
1212
|
+
if (!registeredFamilies.has(font)) {
|
|
1213
|
+
return Promise.reject(new Error(`weasel warmFonts: "${font}" was never registered.`));
|
|
1214
|
+
}
|
|
1215
|
+
for (const [key, { metricsUrl, atlasUrl }] of [...declared.get(font) ?? []]) {
|
|
1216
|
+
loadVariant(font, key, metricsUrl, atlasUrl);
|
|
1217
|
+
}
|
|
1218
|
+
pending2.push(...familyLoads(font));
|
|
1219
|
+
}
|
|
1220
|
+
return Promise.all(pending2).then(() => void 0);
|
|
1221
|
+
}
|
|
1222
|
+
function familyLoads(family) {
|
|
1223
|
+
return [...loads].filter(([id]) => id.startsWith(`${family}|`)).map(([, load]) => load);
|
|
1224
|
+
}
|
|
1225
|
+
function variantLoads(family, weight, style) {
|
|
1226
|
+
const out = [];
|
|
1227
|
+
const outlines = hasFontOutlines(family, weight, style);
|
|
1228
|
+
if (outlines) out.push(loadFontOutlines(family, { weight, style }));
|
|
1229
|
+
const key = variantKey(weight, style);
|
|
1230
|
+
if (registry.get(family)?.has(key)) return out;
|
|
1231
|
+
wakeDeclared(family, weight, style);
|
|
1232
|
+
const exact = loads.get(`${family}|${key}`);
|
|
1233
|
+
if (exact) return [...out, exact];
|
|
1234
|
+
if (registry.has(family) || atlasPending(family)) return [...out, ...familyLoads(family)];
|
|
1235
|
+
if (outlines || isExplicitCanvasFont(family) || getFontFallbackPolicy() !== "substitute") return out;
|
|
1236
|
+
const fallback = getDefaultFontFamily() ?? firstRegisteredFamily();
|
|
1237
|
+
return fallback !== null && fallback !== family ? [...out, ...variantLoads(fallback, weight, style)] : out;
|
|
1238
|
+
}
|
|
1239
|
+
function atlasPending(family) {
|
|
1240
|
+
return inFlight.has(family) || declared.has(family);
|
|
1241
|
+
}
|
|
1242
|
+
function settle(family, token) {
|
|
1243
|
+
const tokens = inFlight.get(family);
|
|
1244
|
+
if (!tokens?.delete(token)) return;
|
|
1245
|
+
if (tokens.size === 0) inFlight.delete(family);
|
|
1246
|
+
}
|
|
1247
|
+
function fontPending(family, weight = 400, style = "normal") {
|
|
1248
|
+
if (atlasPending(family)) return true;
|
|
1249
|
+
const outline = outlineStatus(family, weight, style === "italic" ? "italic" : "normal");
|
|
1250
|
+
return outline === "idle" || outline === "loading";
|
|
1251
|
+
}
|
|
826
1252
|
function ensureFontTexture(family, weight, style, textureCache) {
|
|
827
1253
|
const entry = getFont(family, weight, style);
|
|
828
1254
|
if (!entry) return false;
|
|
@@ -868,12 +1294,13 @@ function missResolveResult(family, weight, style, suppressWarn = false) {
|
|
|
868
1294
|
if (policy2 === "substitute") {
|
|
869
1295
|
const fallback = getDefaultFontFamily() ?? firstRegisteredFamily();
|
|
870
1296
|
if (fallback !== null && fallback !== family) {
|
|
871
|
-
if (registry.has(fallback) || isCanvasFont(fallback)) {
|
|
1297
|
+
if (registry.has(fallback) || isCanvasFont(fallback) || atlasPending(fallback)) {
|
|
872
1298
|
const result = resolveFontVariantInternal(fallback, weight, style, true);
|
|
873
1299
|
if (result.entry !== null || result.dynamicFace !== void 0) {
|
|
874
1300
|
if (!suppressWarn) warnMissingFamilyOnce(family, weight, style, fallback);
|
|
875
1301
|
return { ...result, substituted: { requested: family, resolved: fallback } };
|
|
876
1302
|
}
|
|
1303
|
+
if (atlasPending(fallback)) return pendingMiss(family, weight, style);
|
|
877
1304
|
}
|
|
878
1305
|
if (!suppressWarn) warnUnusableDefaultOnce(family, weight, style, fallback);
|
|
879
1306
|
} else if (fallback === family && !suppressWarn) {
|
|
@@ -892,9 +1319,19 @@ function missResolveResult(family, weight, style, suppressWarn = false) {
|
|
|
892
1319
|
};
|
|
893
1320
|
}
|
|
894
1321
|
function firstRegisteredFamily() {
|
|
895
|
-
for (const family of
|
|
1322
|
+
for (const family of registeredFamilies) {
|
|
1323
|
+
if (registry.has(family) || atlasPending(family)) return family;
|
|
1324
|
+
}
|
|
896
1325
|
return null;
|
|
897
1326
|
}
|
|
1327
|
+
function pendingMiss(family, weight, style) {
|
|
1328
|
+
return {
|
|
1329
|
+
entry: null,
|
|
1330
|
+
resolved: { family, weight, style },
|
|
1331
|
+
synthetic: { bold: false, italic: false },
|
|
1332
|
+
source: "atlas"
|
|
1333
|
+
};
|
|
1334
|
+
}
|
|
898
1335
|
function warnMissingFamilyOnce(family, weight, style, resolved) {
|
|
899
1336
|
if (!claimFallbackWarning(`substituted|${family}|${weight}|${style}`)) return;
|
|
900
1337
|
const cause = registry.has(family) ? `has no variant matching ${weight}/${style}, and none of its registered variants are close enough for the within-family chain to substitute \u2014 rendering with "${resolved}" instead. Bake that variant with registerFont("${family}", { weight: ${weight}, style: '${style}' }, \u2026)` : `is not registered \u2014 rendering with "${resolved}" instead. Call registerFont("${family}", \u2026)`;
|
|
@@ -946,11 +1383,17 @@ function resolveGlyphFallback(family, weight, style) {
|
|
|
946
1383
|
}
|
|
947
1384
|
}
|
|
948
1385
|
function resolveFontVariantInternal(family, weight, style, suppressWarn) {
|
|
1386
|
+
wakeDeclared(family, weight, style);
|
|
1387
|
+
const exactKey = variantKey(weight, style);
|
|
1388
|
+
if (loads.has(`${family}|${exactKey}`) && !registry.get(family)?.has(exactKey)) {
|
|
1389
|
+
return pendingMiss(family, weight, style);
|
|
1390
|
+
}
|
|
949
1391
|
const familyMap = registry.get(family);
|
|
950
1392
|
if (!familyMap || familyMap.size === 0) {
|
|
1393
|
+
if (atlasPending(family)) return pendingMiss(family, weight, style);
|
|
951
1394
|
return missResolveResult(family, weight, style, suppressWarn);
|
|
952
1395
|
}
|
|
953
|
-
const exact = familyMap.get(
|
|
1396
|
+
const exact = familyMap.get(exactKey);
|
|
954
1397
|
if (exact) {
|
|
955
1398
|
return {
|
|
956
1399
|
entry: exact,
|
|
@@ -1040,6 +1483,107 @@ function resolveFontVariantInternal(family, weight, style, suppressWarn) {
|
|
|
1040
1483
|
return missResolveResult(family, weight, style, suppressWarn);
|
|
1041
1484
|
}
|
|
1042
1485
|
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1486
|
+
// src/domFace.ts
|
|
1487
|
+
var names = /* @__PURE__ */ new Map();
|
|
1488
|
+
var faces2 = /* @__PURE__ */ new Map();
|
|
1489
|
+
var warned2 = /* @__PURE__ */ new Set();
|
|
1490
|
+
function domFonts() {
|
|
1491
|
+
return typeof FontFace !== "undefined" && typeof document !== "undefined" && !!document.fonts;
|
|
1492
|
+
}
|
|
1493
|
+
function nameFor(family) {
|
|
1494
|
+
let name = names.get(family);
|
|
1495
|
+
if (!name) {
|
|
1496
|
+
name = `weasel-face-${names.size}`;
|
|
1497
|
+
names.set(family, name);
|
|
1498
|
+
}
|
|
1499
|
+
return name;
|
|
1500
|
+
}
|
|
1501
|
+
onOutlineBytes((registration, bytes) => {
|
|
1502
|
+
if (!domFonts()) return;
|
|
1503
|
+
const { family, weight, style } = registration;
|
|
1504
|
+
ensureFace(nameFor(family), family, weight, style, bytes);
|
|
1505
|
+
});
|
|
1506
|
+
function privateFamily(family, variant) {
|
|
1507
|
+
if (!domFonts()) return null;
|
|
1508
|
+
const resolved = resolveFontVariant(family, variant.weight ?? 400, variant.style ?? "normal");
|
|
1509
|
+
if (resolved.source === "canvas") return null;
|
|
1510
|
+
const target = resolved.resolved.family;
|
|
1511
|
+
const variants = listFontOutlines().filter((f) => f.family === target && f.status !== "failed");
|
|
1512
|
+
if (variants.length === 0) {
|
|
1513
|
+
if (resolved.entry && !warned2.has(target)) {
|
|
1514
|
+
warned2.add(target);
|
|
1515
|
+
console.warn(
|
|
1516
|
+
`weasel: "${target}" draws from a baked atlas with no font file registered, so DOM text in it (the text edit overlay) uses the browser's own "${family}" and will not match the canvas. Register the file the atlas was baked from: registerFontOutlines("${target}", \u2026).`
|
|
1517
|
+
);
|
|
1518
|
+
}
|
|
1519
|
+
return null;
|
|
1520
|
+
}
|
|
1521
|
+
return { target, variants };
|
|
1522
|
+
}
|
|
1523
|
+
function cssFontFamily(family, variant = {}) {
|
|
1524
|
+
const priv = privateFamily(family, variant);
|
|
1525
|
+
if (!priv) return family;
|
|
1526
|
+
const name = nameFor(priv.target);
|
|
1527
|
+
for (const v of priv.variants) ensureFace(name, v.family, v.weight, v.style);
|
|
1528
|
+
return `${JSON.stringify(name)}, ${family}`;
|
|
1529
|
+
}
|
|
1530
|
+
function cssFontFamilyLoading(family, variant = {}) {
|
|
1531
|
+
const priv = privateFamily(family, variant);
|
|
1532
|
+
if (!priv) return null;
|
|
1533
|
+
const name = nameFor(priv.target);
|
|
1534
|
+
for (const v of priv.variants) ensureFace(name, v.family, v.weight, v.style);
|
|
1535
|
+
const weight = variant.weight ?? 400;
|
|
1536
|
+
const style = variant.style ?? "normal";
|
|
1537
|
+
const exact = priv.variants.filter((v) => v.weight === weight && v.style === style);
|
|
1538
|
+
const pending2 = (exact.length > 0 ? exact : priv.variants).map((v) => faces2.get(`${v.family}|${v.weight}|${v.style}`)).filter((l) => l !== void 0 && !l.settled).map((l) => l.done);
|
|
1539
|
+
return pending2.length === 0 ? null : Promise.all(pending2).then(() => {
|
|
1540
|
+
});
|
|
1541
|
+
}
|
|
1542
|
+
function ensureFace(name, family, weight, style, bytes) {
|
|
1543
|
+
const key = `${family}|${weight}|${style}`;
|
|
1544
|
+
const css = outlineCssSource(family, weight, style);
|
|
1545
|
+
if (!css) return;
|
|
1546
|
+
const have = faces2.get(key);
|
|
1547
|
+
if (have?.from === css.registration) return;
|
|
1548
|
+
if (have?.face) document.fonts.delete(have.face);
|
|
1549
|
+
let settle2;
|
|
1550
|
+
const entry = {
|
|
1551
|
+
from: css.registration,
|
|
1552
|
+
face: null,
|
|
1553
|
+
settled: false,
|
|
1554
|
+
done: new Promise((r) => {
|
|
1555
|
+
settle2 = r;
|
|
1556
|
+
})
|
|
1557
|
+
};
|
|
1558
|
+
const finish = () => {
|
|
1559
|
+
entry.settled = true;
|
|
1560
|
+
settle2();
|
|
1561
|
+
};
|
|
1562
|
+
faces2.set(key, entry);
|
|
1563
|
+
const descriptors = { weight: String(weight), style, display: "swap" };
|
|
1564
|
+
const add = (face) => {
|
|
1565
|
+
if (faces2.get(key) !== entry) return finish();
|
|
1566
|
+
entry.face = face;
|
|
1567
|
+
document.fonts.add(face);
|
|
1568
|
+
face.load().then(finish, () => {
|
|
1569
|
+
finish();
|
|
1570
|
+
console.warn(`weasel: could not load "${family}" (${weight}/${style}) for DOM text; the text edit overlay falls back to the browser's own face.`);
|
|
1571
|
+
});
|
|
1572
|
+
};
|
|
1573
|
+
if ("src" in css) add(new FontFace(name, css.src, descriptors));
|
|
1574
|
+
else if (bytes !== void 0) add(new FontFace(name, bytes, descriptors));
|
|
1575
|
+
else if ("url" in css) add(new FontFace(name, css.url, descriptors));
|
|
1576
|
+
else css.bytes().then((buf) => add(new FontFace(name, buf, descriptors)), finish);
|
|
1577
|
+
}
|
|
1578
|
+
function _resetDomFacesForTests() {
|
|
1579
|
+
if (typeof document !== "undefined" && document.fonts) {
|
|
1580
|
+
for (const { face } of faces2.values()) if (face) document.fonts.delete(face);
|
|
1581
|
+
}
|
|
1582
|
+
names.clear();
|
|
1583
|
+
faces2.clear();
|
|
1584
|
+
warned2.clear();
|
|
1585
|
+
}
|
|
1586
|
+
|
|
1587
|
+
export { DEFAULT_BAKE_BUDGET, FIXTURE_FONT, PAGE_SIZE, SDF_RADIUS, __setGlyphRasterizerForTests, _getPagesForTests, _resetDomFacesForTests, _resetDynamicFontsForTests, _resetFallbackForTests, _resetFontOutlinesForTests, _resetFontRegistryForTests, createOpenTypeParser, cssFontFamily, cssFontFamilyLoading, dynamicPageTextureId, ensureFontTexture, faceMetricsFor, faceMetricsFromTables, faceMetricsOf, fontOutlineRegistry, fontPending, fontRegistry, getDefaultFontFamily, getFont, getFontFallbackPolicy, glyphGeneration, glyphOutline, hasFontOutlines, isCanvasFont, listCanvasFonts, listFontOutlines, listFontWeights, listFonts, loadFontOutlines, markAllFontsNotUploaded, outlineMetrics, outlineStatus, parseBmFont, registerCanvasFont, registerFont, registerFontOutlines, resetBakeBudget, resolveFontVariant, resolveGlyphFallback, setDefaultFontFamily, setFontFallbackPolicy, subscribeGlyphReady, syncDynamicPageTexture, textureCacheKey, unregisterCanvasFont, unregisterFontOutlines, verticalMetricsFromTables, warmFonts };
|
|
1588
|
+
//# sourceMappingURL=chunk-RYN4NYX4.js.map
|
|
1589
|
+
//# sourceMappingURL=chunk-RYN4NYX4.js.map
|