@arcblock/did-connect-service 4.1.58 → 4.1.60

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.
@@ -0,0 +1,105 @@
1
+ /**
2
+ * Cross-script CJK fallback fonts (ArcBlock/arc#8832).
3
+ *
4
+ * A card's primary font is the viewer locale's regional face (Noto Sans SC /
5
+ * TC / JP, or Noto Sans for Latin locales) so a shared character keeps that
6
+ * region's glyph form. The card's TEXT is whatever the author wrote, so it can
7
+ * hold characters the primary font does not have at all (a Simplified title on
8
+ * a zh-TW card, 歩 on a zh-TW card, kana on an en card). For those, the render
9
+ * adds other CJK families after the primary: satori draws each character with
10
+ * the first font in its `fonts` list that maps it (per glyph, see
11
+ * og-font-fallback.test.ts), so the primary still draws everything it has.
12
+ *
13
+ * The rule (deterministic, from the text alone):
14
+ * 1. `missing` = codepoints of the render that the primary's FULL font does not
15
+ * map (default-ignorable / control codepoints excluded: they draw nothing).
16
+ * 2. Every order of the fallback families (the CJK families other than the
17
+ * primary's; Latin is never a fallback) is evaluated: each family in turn
18
+ * takes the `missing` codepoints its full font maps and that no earlier
19
+ * family took. A family that takes nothing is not loaded. A family is
20
+ * loaded as its subset when the subset maps everything it takes, else as
21
+ * its full font (identical glyphs either way).
22
+ * 3. The order that loads the fewest font bytes wins (bytes ≈ parse CPU and
23
+ * memory). Ties go to the preference order {@link OG_CJK_FALLBACK_PREFERENCE}
24
+ * (zh-CN → ja → zh-TW); when `missing` holds kana, ja must come first (kana
25
+ * drawn with Japanese forms).
26
+ * 4. The whole font set of the render must fit the budget: its raw bytes
27
+ * (primary + fallbacks, every weight loaded) within
28
+ * {@link OG_FONT_RENDER_BUDGET_BYTES}, at most ONE full CJK family, and —
29
+ * when one is full — at most two CJK families (the Latin primary does not
30
+ * count). Each full family costs ~20 MB of parse heap whatever its size, so
31
+ * bytes alone under-count it (measurements in the PR, arc#8832). The
32
+ * cheapest order that fits wins. When no order fits, the fallback whose cut
33
+ * leaves the fewest characters undrawn is cut back and the plan recomputed:
34
+ * a full font is first limited to its subset (it then takes only what its
35
+ * subset maps), a subset is dropped. Either way the family is reported in `overBudget`; the
36
+ * characters it no longer takes go to a later family that maps them, or
37
+ * stay `.notdef`. The primary itself is never refused.
38
+ * Codepoints no family maps (emoji, Hangul — no Korean face) stay `.notdef`,
39
+ * as before. A tree whose text cannot be read reliably gets no fallbacks.
40
+ */
41
+ import { type OgSubsetFontSet } from "./font-subset.js";
42
+ /** A CJK family that can serve as a fallback (key in the subset table / font maps). */
43
+ export type OgCjkFamily = "zh-CN" | "ja" | "zh-TW";
44
+ /** Tie-break order among fallback families (see the module comment). */
45
+ export declare const OG_CJK_FALLBACK_PREFERENCE: readonly OgCjkFamily[];
46
+ /**
47
+ * Upper bound on the raw bytes of one render's whole font set (primary +
48
+ * fallbacks, Regular + Bold as loaded) once it has fallbacks. Satori parses
49
+ * every font of the set per render; a render holds the raw bytes, a copy, the
50
+ * parse (≈ 20 MB of V8 heap per full CJK family, ≈ 6–8 MB per subset) and
51
+ * ~20 MB of wasm. The budget is the heaviest set renders already use — full
52
+ * Noto Sans SC Regular + Bold, 16.1 MiB — plus a little, so together with the
53
+ * family limits of rule 4 a fallback never makes a render heavier than the
54
+ * heaviest card today (measured, see the PR for arc#8832).
55
+ */
56
+ export declare const OG_FONT_RENDER_BUDGET_BYTES: number;
57
+ export interface OgFallbackCandidate {
58
+ family: OgCjkFamily;
59
+ set: OgSubsetFontSet;
60
+ }
61
+ export interface OgFallbackChoice extends OgFallbackCandidate {
62
+ /** Load the family's subset (true) or its full font (false). */
63
+ subset: boolean;
64
+ /** Raw bytes this choice loads (Regular, + Bold when bold is wanted). */
65
+ bytes: number;
66
+ /** The codepoints this family draws. */
67
+ codepoints: number[];
68
+ }
69
+ export interface OgFontPlan {
70
+ /** Fallback families in satori `fonts` order (after the primary). */
71
+ fallbacks: OgFallbackChoice[];
72
+ /** Families cut back (limited to their subset, or dropped) to stay within the budget. */
73
+ overBudget: OgCjkFamily[];
74
+ }
75
+ /** Codepoints that draw nothing by themselves — never a reason to load a font. */
76
+ export declare function ogFallbackIgnorable(cp: number): boolean;
77
+ /** Raw bytes of `set`'s subset (or full) objects for the weights a render loads. */
78
+ export declare function ogFontSetBytes(set: OgSubsetFontSet, subset: boolean, bold: boolean): number;
79
+ /** Rule 4: does the primary plus `fallbacks` fit the budget? */
80
+ export declare function ogFontSetFits(primary: {
81
+ primaryBytes: number;
82
+ primaryFull?: boolean;
83
+ primaryCjk?: boolean;
84
+ }, fallbacks: ReadonlyArray<{
85
+ subset: boolean;
86
+ bytes: number;
87
+ }>, budgetBytes?: number): boolean;
88
+ /**
89
+ * Plans the fallback fonts of one render (see the module comment for the rule).
90
+ * `primaryBytes` is what the primary loads (subset or full, every weight);
91
+ * `candidates` are the fallback families available to this renderer.
92
+ */
93
+ export declare function planOgFontFallbacks(input: {
94
+ primary: OgSubsetFontSet;
95
+ primaryBytes: number;
96
+ /** The primary loads its full font (not its subset). */
97
+ primaryFull?: boolean;
98
+ /** The primary is a CJK family (not the Latin face). */
99
+ primaryCjk?: boolean;
100
+ codepoints: ReadonlySet<number>;
101
+ candidates: readonly OgFallbackCandidate[];
102
+ bold: boolean;
103
+ budgetBytes?: number;
104
+ }): OgFontPlan;
105
+ //# sourceMappingURL=font-fallback.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"font-fallback.d.ts","sourceRoot":"","sources":["../../src/og/font-fallback.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AAEH,OAAO,EAAE,KAAK,eAAe,EAAoC,MAAM,kBAAkB,CAAC;AAE1F,uFAAuF;AACvF,MAAM,MAAM,WAAW,GAAG,OAAO,GAAG,IAAI,GAAG,OAAO,CAAC;AAEnD,wEAAwE;AACxE,eAAO,MAAM,0BAA0B,EAAE,SAAS,WAAW,EAA6B,CAAC;AAE3F;;;;;;;;;GASG;AACH,eAAO,MAAM,2BAA2B,QAAmB,CAAC;AAE5D,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,WAAW,CAAC;IACpB,GAAG,EAAE,eAAe,CAAC;CACtB;AAOD,MAAM,WAAW,gBAAiB,SAAQ,mBAAmB;IAC3D,gEAAgE;IAChE,MAAM,EAAE,OAAO,CAAC;IAChB,yEAAyE;IACzE,KAAK,EAAE,MAAM,CAAC;IACd,wCAAwC;IACxC,UAAU,EAAE,MAAM,EAAE,CAAC;CACtB;AAED,MAAM,WAAW,UAAU;IACzB,qEAAqE;IACrE,SAAS,EAAE,gBAAgB,EAAE,CAAC;IAC9B,yFAAyF;IACzF,UAAU,EAAE,WAAW,EAAE,CAAC;CAC3B;AAID,kFAAkF;AAClF,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAYvD;AAMD,oFAAoF;AACpF,wBAAgB,cAAc,CAAC,GAAG,EAAE,eAAe,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,GAAG,MAAM,CAG3F;AAQD,gEAAgE;AAChE,wBAAgB,aAAa,CAC3B,OAAO,EAAE;IAAE,YAAY,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,OAAO,CAAC;IAAC,UAAU,CAAC,EAAE,OAAO,CAAA;CAAE,EAC9E,SAAS,EAAE,aAAa,CAAC;IAAE,MAAM,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,EAC5D,WAAW,GAAE,MAAoC,GAChD,OAAO,CAKT;AA0BD;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE;IACzC,OAAO,EAAE,eAAe,CAAC;IACzB,YAAY,EAAE,MAAM,CAAC;IACrB,wDAAwD;IACxD,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,wDAAwD;IACxD,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,UAAU,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAChC,UAAU,EAAE,SAAS,mBAAmB,EAAE,CAAC;IAC3C,IAAI,EAAE,OAAO,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,GAAG,UAAU,CA2Cb"}
@@ -0,0 +1,170 @@
1
+ /**
2
+ * Cross-script CJK fallback fonts (ArcBlock/arc#8832).
3
+ *
4
+ * A card's primary font is the viewer locale's regional face (Noto Sans SC /
5
+ * TC / JP, or Noto Sans for Latin locales) so a shared character keeps that
6
+ * region's glyph form. The card's TEXT is whatever the author wrote, so it can
7
+ * hold characters the primary font does not have at all (a Simplified title on
8
+ * a zh-TW card, 歩 on a zh-TW card, kana on an en card). For those, the render
9
+ * adds other CJK families after the primary: satori draws each character with
10
+ * the first font in its `fonts` list that maps it (per glyph, see
11
+ * og-font-fallback.test.ts), so the primary still draws everything it has.
12
+ *
13
+ * The rule (deterministic, from the text alone):
14
+ * 1. `missing` = codepoints of the render that the primary's FULL font does not
15
+ * map (default-ignorable / control codepoints excluded: they draw nothing).
16
+ * 2. Every order of the fallback families (the CJK families other than the
17
+ * primary's; Latin is never a fallback) is evaluated: each family in turn
18
+ * takes the `missing` codepoints its full font maps and that no earlier
19
+ * family took. A family that takes nothing is not loaded. A family is
20
+ * loaded as its subset when the subset maps everything it takes, else as
21
+ * its full font (identical glyphs either way).
22
+ * 3. The order that loads the fewest font bytes wins (bytes ≈ parse CPU and
23
+ * memory). Ties go to the preference order {@link OG_CJK_FALLBACK_PREFERENCE}
24
+ * (zh-CN → ja → zh-TW); when `missing` holds kana, ja must come first (kana
25
+ * drawn with Japanese forms).
26
+ * 4. The whole font set of the render must fit the budget: its raw bytes
27
+ * (primary + fallbacks, every weight loaded) within
28
+ * {@link OG_FONT_RENDER_BUDGET_BYTES}, at most ONE full CJK family, and —
29
+ * when one is full — at most two CJK families (the Latin primary does not
30
+ * count). Each full family costs ~20 MB of parse heap whatever its size, so
31
+ * bytes alone under-count it (measurements in the PR, arc#8832). The
32
+ * cheapest order that fits wins. When no order fits, the fallback whose cut
33
+ * leaves the fewest characters undrawn is cut back and the plan recomputed:
34
+ * a full font is first limited to its subset (it then takes only what its
35
+ * subset maps), a subset is dropped. Either way the family is reported in `overBudget`; the
36
+ * characters it no longer takes go to a later family that maps them, or
37
+ * stay `.notdef`. The primary itself is never refused.
38
+ * Codepoints no family maps (emoji, Hangul — no Korean face) stay `.notdef`,
39
+ * as before. A tree whose text cannot be read reliably gets no fallbacks.
40
+ */
41
+ import { ogFullFontMaps, ogSubsetFontMaps } from "./font-subset.js";
42
+ /** Tie-break order among fallback families (see the module comment). */
43
+ export const OG_CJK_FALLBACK_PREFERENCE = ["zh-CN", "ja", "zh-TW"];
44
+ /**
45
+ * Upper bound on the raw bytes of one render's whole font set (primary +
46
+ * fallbacks, Regular + Bold as loaded) once it has fallbacks. Satori parses
47
+ * every font of the set per render; a render holds the raw bytes, a copy, the
48
+ * parse (≈ 20 MB of V8 heap per full CJK family, ≈ 6–8 MB per subset) and
49
+ * ~20 MB of wasm. The budget is the heaviest set renders already use — full
50
+ * Noto Sans SC Regular + Bold, 16.1 MiB — plus a little, so together with the
51
+ * family limits of rule 4 a fallback never makes a render heavier than the
52
+ * heaviest card today (measured, see the PR for arc#8832).
53
+ */
54
+ export const OG_FONT_RENDER_BUDGET_BYTES = 17 * 1024 * 1024;
55
+ const NO_FALLBACKS = { fallbacks: [], overBudget: [] };
56
+ /** Codepoints that draw nothing by themselves — never a reason to load a font. */
57
+ export function ogFallbackIgnorable(cp) {
58
+ return (cp < 0x20 ||
59
+ (cp >= 0x7f && cp <= 0x9f) ||
60
+ cp === 0xad ||
61
+ (cp >= 0x200b && cp <= 0x200f) ||
62
+ (cp >= 0x2028 && cp <= 0x202e) ||
63
+ (cp >= 0x2060 && cp <= 0x206f) ||
64
+ (cp >= 0xfe00 && cp <= 0xfe0f) ||
65
+ cp === 0xfeff ||
66
+ (cp >= 0xe0000 && cp <= 0xe0fff));
67
+ }
68
+ function isKana(cp) {
69
+ return (cp >= 0x3040 && cp <= 0x30ff) || (cp >= 0x31f0 && cp <= 0x31ff) || (cp >= 0xff66 && cp <= 0xff9f);
70
+ }
71
+ /** Raw bytes of `set`'s subset (or full) objects for the weights a render loads. */
72
+ export function ogFontSetBytes(set, subset, bold) {
73
+ if (subset)
74
+ return set.regular.bytes + (bold && set.bold ? set.bold.bytes : 0);
75
+ return set.fullBytes.regular + (bold ? (set.fullBytes.bold ?? 0) : 0);
76
+ }
77
+ /** True when `set`'s subset can stand in for its full font for `codepoints`, in every weight loaded. */
78
+ function subsetServes(set, codepoints, bold) {
79
+ if (bold && !set.bold)
80
+ return false;
81
+ return codepoints.every((cp) => ogSubsetFontMaps(set, cp));
82
+ }
83
+ /** Rule 4: does the primary plus `fallbacks` fit the budget? */
84
+ export function ogFontSetFits(primary, fallbacks, budgetBytes = OG_FONT_RENDER_BUDGET_BYTES) {
85
+ const bytes = fallbacks.reduce((sum, f) => sum + f.bytes, primary.primaryBytes);
86
+ const fulls = fallbacks.filter((f) => !f.subset).length + (primary.primaryFull && primary.primaryCjk ? 1 : 0);
87
+ const families = fallbacks.length + (primary.primaryCjk ? 1 : 0);
88
+ return bytes <= budgetBytes && fulls <= 1 && (fulls === 0 || families <= 2);
89
+ }
90
+ function permutations(items) {
91
+ if (items.length <= 1)
92
+ return [items.slice()];
93
+ return items.flatMap((item, i) => permutations([...items.slice(0, i), ...items.slice(i + 1)]).map((rest) => [item, ...rest]));
94
+ }
95
+ function assign(order, missing, bold) {
96
+ let remaining = missing;
97
+ const choices = [];
98
+ let bytes = 0;
99
+ for (const { subsetOnly, ...candidate } of order) {
100
+ const maps = subsetOnly
101
+ ? (cp) => ogFullFontMaps(candidate.set, cp) && ogSubsetFontMaps(candidate.set, cp)
102
+ : (cp) => ogFullFontMaps(candidate.set, cp);
103
+ const taken = remaining.filter(maps);
104
+ if (!taken.length)
105
+ continue;
106
+ remaining = remaining.filter((cp) => !maps(cp));
107
+ const subset = subsetOnly || subsetServes(candidate.set, taken, bold);
108
+ const choiceBytes = ogFontSetBytes(candidate.set, subset, bold);
109
+ choices.push({ ...candidate, subset, bytes: choiceBytes, codepoints: taken });
110
+ bytes += choiceBytes;
111
+ }
112
+ return { choices, bytes };
113
+ }
114
+ /**
115
+ * Plans the fallback fonts of one render (see the module comment for the rule).
116
+ * `primaryBytes` is what the primary loads (subset or full, every weight);
117
+ * `candidates` are the fallback families available to this renderer.
118
+ */
119
+ export function planOgFontFallbacks(input) {
120
+ const { primary, codepoints, bold } = input;
121
+ const budget = input.budgetBytes ?? OG_FONT_RENDER_BUDGET_BYTES;
122
+ const missing = [];
123
+ for (const cp of codepoints) {
124
+ // Unreadable text (the collector's sentinel): no reliable plan, no fallbacks.
125
+ if (cp < 0)
126
+ return NO_FALLBACKS;
127
+ if (!ogFallbackIgnorable(cp) && !ogFullFontMaps(primary, cp))
128
+ missing.push(cp);
129
+ }
130
+ if (!missing.length)
131
+ return NO_FALLBACKS;
132
+ missing.sort((a, b) => a - b);
133
+ const rank = (family) => OG_CJK_FALLBACK_PREFERENCE.indexOf(family);
134
+ let candidates = input.candidates.filter((c) => c.set !== primary).sort((a, b) => rank(a.family) - rank(b.family));
135
+ const kanaFirst = missing.some(isKana) && candidates.some((c) => c.family === "ja");
136
+ const overBudget = [];
137
+ for (;;) {
138
+ // The cheapest order overall, and the cheapest that fits the budget. Every
139
+ // order covers the same characters, so a fitting order loses nothing.
140
+ // permutations() keeps the preference order lexicographically, so the first
141
+ // cheapest order found is the tie-break winner.
142
+ let best;
143
+ let bestFit;
144
+ for (const order of permutations(candidates)) {
145
+ if (kanaFirst && candidates.some((c) => c.family === "ja") && order[0].family !== "ja")
146
+ continue;
147
+ const plan = assign(order, missing, bold);
148
+ if (!best || plan.bytes < best.bytes)
149
+ best = plan;
150
+ if (ogFontSetFits(input, plan.choices, budget) && (!bestFit || plan.bytes < bestFit.bytes))
151
+ bestFit = plan;
152
+ }
153
+ if (!best?.choices.length)
154
+ return { fallbacks: [], overBudget };
155
+ if (bestFit)
156
+ return { fallbacks: bestFit.choices, overBudget };
157
+ // Cut back the fallback whose cut leaves the fewest characters undrawn
158
+ // (ties: the heavier one): a full font ⇒ its subset (when it has every
159
+ // weight), a subset ⇒ dropped.
160
+ const canLimit = (c) => !c.subset && (!bold || !!c.set.bold);
161
+ const loss = (c) => (canLimit(c) ? c.codepoints.filter((cp) => !ogSubsetFontMaps(c.set, cp)).length : c.codepoints.length);
162
+ const victim = best.choices.reduce((a, b) => (loss(b) < loss(a) || (loss(b) === loss(a) && b.bytes > a.bytes) ? b : a));
163
+ if (!overBudget.includes(victim.family))
164
+ overBudget.push(victim.family);
165
+ candidates = canLimit(victim)
166
+ ? candidates.map((c) => (c.family === victim.family ? { ...c, subsetOnly: true } : c))
167
+ : candidates.filter((c) => c.family !== victim.family);
168
+ }
169
+ }
170
+ //# sourceMappingURL=font-fallback.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"font-fallback.js","sourceRoot":"","sources":["../../src/og/font-fallback.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AAEH,OAAO,EAAwB,cAAc,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAK1F,wEAAwE;AACxE,MAAM,CAAC,MAAM,0BAA0B,GAA2B,CAAC,OAAO,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;AAE3F;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,2BAA2B,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC;AA4B5D,MAAM,YAAY,GAAe,EAAE,SAAS,EAAE,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE,CAAC;AAEnE,kFAAkF;AAClF,MAAM,UAAU,mBAAmB,CAAC,EAAU;IAC5C,OAAO,CACL,EAAE,GAAG,IAAI;QACT,CAAC,EAAE,IAAI,IAAI,IAAI,EAAE,IAAI,IAAI,CAAC;QAC1B,EAAE,KAAK,IAAI;QACX,CAAC,EAAE,IAAI,MAAM,IAAI,EAAE,IAAI,MAAM,CAAC;QAC9B,CAAC,EAAE,IAAI,MAAM,IAAI,EAAE,IAAI,MAAM,CAAC;QAC9B,CAAC,EAAE,IAAI,MAAM,IAAI,EAAE,IAAI,MAAM,CAAC;QAC9B,CAAC,EAAE,IAAI,MAAM,IAAI,EAAE,IAAI,MAAM,CAAC;QAC9B,EAAE,KAAK,MAAM;QACb,CAAC,EAAE,IAAI,OAAO,IAAI,EAAE,IAAI,OAAO,CAAC,CACjC,CAAC;AACJ,CAAC;AAED,SAAS,MAAM,CAAC,EAAU;IACxB,OAAO,CAAC,EAAE,IAAI,MAAM,IAAI,EAAE,IAAI,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,MAAM,IAAI,EAAE,IAAI,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,MAAM,IAAI,EAAE,IAAI,MAAM,CAAC,CAAC;AAC5G,CAAC;AAED,oFAAoF;AACpF,MAAM,UAAU,cAAc,CAAC,GAAoB,EAAE,MAAe,EAAE,IAAa;IACjF,IAAI,MAAM;QAAE,OAAO,GAAG,CAAC,OAAO,CAAC,KAAK,GAAG,CAAC,IAAI,IAAI,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC/E,OAAO,GAAG,CAAC,SAAS,CAAC,OAAO,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACxE,CAAC;AAED,wGAAwG;AACxG,SAAS,YAAY,CAAC,GAAoB,EAAE,UAA6B,EAAE,IAAa;IACtF,IAAI,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI;QAAE,OAAO,KAAK,CAAC;IACpC,OAAO,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,gBAAgB,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,CAAC;AAC7D,CAAC;AAED,gEAAgE;AAChE,MAAM,UAAU,aAAa,CAC3B,OAA8E,EAC9E,SAA4D,EAC5D,cAAsB,2BAA2B;IAEjD,MAAM,KAAK,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,KAAK,EAAE,OAAO,CAAC,YAAY,CAAC,CAAC;IAChF,MAAM,KAAK,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,MAAM,GAAG,CAAC,OAAO,CAAC,WAAW,IAAI,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC9G,MAAM,QAAQ,GAAG,SAAS,CAAC,MAAM,GAAG,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACjE,OAAO,KAAK,IAAI,WAAW,IAAI,KAAK,IAAI,CAAC,IAAI,CAAC,KAAK,KAAK,CAAC,IAAI,QAAQ,IAAI,CAAC,CAAC,CAAC;AAC9E,CAAC;AAED,SAAS,YAAY,CAAI,KAAmB;IAC1C,IAAI,KAAK,CAAC,MAAM,IAAI,CAAC;QAAE,OAAO,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC;IAC9C,OAAO,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC,YAAY,CAAC,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;AAChI,CAAC;AAED,SAAS,MAAM,CAAC,KAA+B,EAAE,OAA0B,EAAE,IAAa;IACxF,IAAI,SAAS,GAAG,OAAO,CAAC;IACxB,MAAM,OAAO,GAAuB,EAAE,CAAC;IACvC,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,KAAK,MAAM,EAAE,UAAU,EAAE,GAAG,SAAS,EAAE,IAAI,KAAK,EAAE,CAAC;QACjD,MAAM,IAAI,GAAG,UAAU;YACrB,CAAC,CAAC,CAAC,EAAU,EAAE,EAAE,CAAC,cAAc,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,gBAAgB,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,CAAC;YAC1F,CAAC,CAAC,CAAC,EAAU,EAAE,EAAE,CAAC,cAAc,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;QACtD,MAAM,KAAK,GAAG,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QACrC,IAAI,CAAC,KAAK,CAAC,MAAM;YAAE,SAAS;QAC5B,SAAS,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;QAChD,MAAM,MAAM,GAAG,UAAU,IAAI,YAAY,CAAC,SAAS,CAAC,GAAG,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC;QACtE,MAAM,WAAW,GAAG,cAAc,CAAC,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC;QAChE,OAAO,CAAC,IAAI,CAAC,EAAE,GAAG,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC,CAAC;QAC9E,KAAK,IAAI,WAAW,CAAC;IACvB,CAAC;IACD,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;AAC5B,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CAAC,KAWnC;IACC,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,GAAG,KAAK,CAAC;IAC5C,MAAM,MAAM,GAAG,KAAK,CAAC,WAAW,IAAI,2BAA2B,CAAC;IAChE,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,KAAK,MAAM,EAAE,IAAI,UAAU,EAAE,CAAC;QAC5B,8EAA8E;QAC9E,IAAI,EAAE,GAAG,CAAC;YAAE,OAAO,YAAY,CAAC;QAChC,IAAI,CAAC,mBAAmB,CAAC,EAAE,CAAC,IAAI,CAAC,cAAc,CAAC,OAAO,EAAE,EAAE,CAAC;YAAE,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACjF,CAAC;IACD,IAAI,CAAC,OAAO,CAAC,MAAM;QAAE,OAAO,YAAY,CAAC;IACzC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IAE9B,MAAM,IAAI,GAAG,CAAC,MAAmB,EAAE,EAAE,CAAC,0BAA0B,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IACjF,IAAI,UAAU,GAAoB,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,KAAK,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;IACpI,MAAM,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,IAAI,CAAC,CAAC;IACpF,MAAM,UAAU,GAAkB,EAAE,CAAC;IAErC,SAAS,CAAC;QACR,2EAA2E;QAC3E,sEAAsE;QACtE,4EAA4E;QAC5E,gDAAgD;QAChD,IAAI,IAAgE,CAAC;QACrE,IAAI,OAAoB,CAAC;QACzB,KAAK,MAAM,KAAK,IAAI,YAAY,CAAC,UAAU,CAAC,EAAE,CAAC;YAC7C,IAAI,SAAS,IAAI,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,IAAI,CAAC,IAAI,KAAK,CAAC,CAAC,CAAE,CAAC,MAAM,KAAK,IAAI;gBAAE,SAAS;YAClG,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;YAC1C,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK;gBAAE,IAAI,GAAG,IAAI,CAAC;YAClD,IAAI,aAAa,CAAC,KAAK,EAAE,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,OAAO,IAAI,IAAI,CAAC,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC;gBAAE,OAAO,GAAG,IAAI,CAAC;QAC7G,CAAC;QACD,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,MAAM;YAAE,OAAO,EAAE,SAAS,EAAE,EAAE,EAAE,UAAU,EAAE,CAAC;QAChE,IAAI,OAAO;YAAE,OAAO,EAAE,SAAS,EAAE,OAAO,CAAC,OAAO,EAAE,UAAU,EAAE,CAAC;QAC/D,uEAAuE;QACvE,uEAAuE;QACvE,+BAA+B;QAC/B,MAAM,QAAQ,GAAG,CAAC,CAAmB,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC/E,MAAM,IAAI,GAAG,CAAC,CAAmB,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;QAC7I,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACxH,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC;YAAE,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QACxE,UAAU,GAAG,QAAQ,CAAC,MAAM,CAAC;YAC3B,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YACtF,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,CAAC;IAC3D,CAAC;AACH,CAAC"}
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Per-locale subset fonts for the tree renderer (ArcBlock/arc#8800).
3
+ *
4
+ * Satori parses every font it is handed on every render, and the full CJK
5
+ * faces (Noto Sans SC/TC/JP, Regular + Bold ≈ 9–16 MB) cost ~50–60 ms of CPU
6
+ * per render to parse, plus a 16 MB R2 read on a cold isolate. Static subsets
7
+ * (common characters only, every OpenType layout feature kept — built by
8
+ * `scripts/build-og-subset-fonts.py`) parse ~8× faster and draw the SAME
9
+ * pixels for any text they cover.
10
+ *
11
+ * A renderer uses a subset only when EVERY character the render can draw is
12
+ * covered ({@link ogTreeCoveredBySubset}); otherwise it falls back to the full
13
+ * font (the pre-subset behavior). Coverage comes from the generated
14
+ * {@link OG_SUBSET_FONTS} table, and each subset object is checked against its
15
+ * sha256 before use, so a stale or foreign upload is treated as missing rather
16
+ * than drawing the wrong glyphs.
17
+ *
18
+ * Parsed subsets are NOT kept between renders: opentype.js caches every glyph
19
+ * it draws on the parsed font object, so a resident parse grows with each new
20
+ * character (measured: SC subset 12 MB after one card, 82 MB once every glyph
21
+ * was drawn; TC 122 MB) — unbounded for a 128 MB isolate. Only raw bytes stay.
22
+ */
23
+ import { OG_SUBSET_FONTS, type OgSubsetFontSet } from "./subset-fonts.generated.js";
24
+ export type { OgSubsetFontFile, OgSubsetFontSet } from "./subset-fonts.generated.js";
25
+ export { OG_SUBSET_FONTS, OG_SUBSET_PREFIX } from "./subset-fonts.generated.js";
26
+ /**
27
+ * How a render picked its fonts, reported on the `og-render` event:
28
+ * - `"hit"`: every character was covered and the subset fonts were used;
29
+ * - `"miss"`: some character is outside the subset, so the full fonts were used;
30
+ * - `"absent"`: the text was covered, but no usable subset was available (object
31
+ * missing, sha256 mismatch, no subset for this font) — full fonts were used.
32
+ */
33
+ export type OgFontSubsetStatus = "hit" | "miss" | "absent";
34
+ export type OgSubsetFontSetId = keyof typeof OG_SUBSET_FONTS;
35
+ /** The subset table in effect (the generated one unless a test replaced it). */
36
+ export declare function ogSubsetFontSets(): Readonly<Record<string, OgSubsetFontSet>>;
37
+ /** Test isolation: replace the subset table (`undefined` restores the generated one). */
38
+ export declare function setOgSubsetFontsForTesting(table: Readonly<Record<string, OgSubsetFontSet>> | undefined): void;
39
+ /** Bucket key of a subset object. */
40
+ export declare function ogSubsetFontKey(file: string): string;
41
+ /** Decodes the generated page encoding: `"<hex>"` = whole page, `"<hex>:<base64 bitmap>"` = some of it. */
42
+ export declare function decodeOgCodepointPages(encoded: string): Map<number, true | Uint8Array>;
43
+ /** True when the FULL fonts of `set` map `codepoint` (every weight). */
44
+ export declare function ogFullFontMaps(set: OgSubsetFontSet, codepoint: number): boolean;
45
+ /** True when the SUBSET of `set` maps `codepoint` (a page requested whole counts, see the generated table). */
46
+ export declare function ogSubsetFontMaps(set: OgSubsetFontSet, codepoint: number): boolean;
47
+ /** Every codepoint in `codepoints` is drawn identically by the subset and the full font. */
48
+ export declare function ogSubsetCovers(set: OgSubsetFontSet, codepoints: Iterable<number>): boolean;
49
+ /**
50
+ * Every codepoint a satori + resvg render of `tree` may draw: text children
51
+ * (with case-mapped forms for `textTransform`), custom `lineClamp` ellipses,
52
+ * and the text of SVG data-URI images. A value the collector cannot read
53
+ * adds a sentinel that no subset covers, so the render uses the full font.
54
+ */
55
+ export declare function collectOgTreeCodepoints(tree: unknown): Set<number>;
56
+ /** True when the subset `set` draws every character of `tree` exactly like the full font. */
57
+ export declare function ogTreeCoveredBySubset(set: OgSubsetFontSet | undefined, tree: unknown): boolean;
58
+ /** Lowercase hex sha256 (WebCrypto: Workers and Node ≥ 19). */
59
+ export declare function ogFontSha256(bytes: Uint8Array): Promise<string>;
60
+ //# sourceMappingURL=font-subset.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"font-subset.d.ts","sourceRoot":"","sources":["../../src/og/font-subset.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAE,eAAe,EAAoB,KAAK,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAEtG,YAAY,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AACrF,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAEhF;;;;;;GAMG;AACH,MAAM,MAAM,kBAAkB,GAAG,KAAK,GAAG,MAAM,GAAG,QAAQ,CAAC;AAE3D,MAAM,MAAM,iBAAiB,GAAG,MAAM,OAAO,eAAe,CAAC;AAI7D,gFAAgF;AAChF,wBAAgB,gBAAgB,IAAI,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC,CAE5E;AAED,yFAAyF;AACzF,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC,GAAG,SAAS,GAAG,IAAI,CAG7G;AAED,qCAAqC;AACrC,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEpD;AAkBD,2GAA2G;AAC3G,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,IAAI,GAAG,UAAU,CAAC,CAetF;AAwBD,wEAAwE;AACxE,wBAAgB,cAAc,CAAC,GAAG,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAE/E;AAED,+GAA+G;AAC/G,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAEjF;AAED,4FAA4F;AAC5F,wBAAgB,cAAc,CAAC,GAAG,EAAE,eAAe,EAAE,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,GAAG,OAAO,CAM1F;AAiHD;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC,CAoClE;AAED,6FAA6F;AAC7F,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,eAAe,GAAG,SAAS,EAAE,IAAI,EAAE,OAAO,GAAG,OAAO,CAG9F;AAED,+DAA+D;AAC/D,wBAAsB,YAAY,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAGrE"}
@@ -0,0 +1,271 @@
1
+ /**
2
+ * Per-locale subset fonts for the tree renderer (ArcBlock/arc#8800).
3
+ *
4
+ * Satori parses every font it is handed on every render, and the full CJK
5
+ * faces (Noto Sans SC/TC/JP, Regular + Bold ≈ 9–16 MB) cost ~50–60 ms of CPU
6
+ * per render to parse, plus a 16 MB R2 read on a cold isolate. Static subsets
7
+ * (common characters only, every OpenType layout feature kept — built by
8
+ * `scripts/build-og-subset-fonts.py`) parse ~8× faster and draw the SAME
9
+ * pixels for any text they cover.
10
+ *
11
+ * A renderer uses a subset only when EVERY character the render can draw is
12
+ * covered ({@link ogTreeCoveredBySubset}); otherwise it falls back to the full
13
+ * font (the pre-subset behavior). Coverage comes from the generated
14
+ * {@link OG_SUBSET_FONTS} table, and each subset object is checked against its
15
+ * sha256 before use, so a stale or foreign upload is treated as missing rather
16
+ * than drawing the wrong glyphs.
17
+ *
18
+ * Parsed subsets are NOT kept between renders: opentype.js caches every glyph
19
+ * it draws on the parsed font object, so a resident parse grows with each new
20
+ * character (measured: SC subset 12 MB after one card, 82 MB once every glyph
21
+ * was drawn; TC 122 MB) — unbounded for a 128 MB isolate. Only raw bytes stay.
22
+ */
23
+ import { OG_SUBSET_FONTS, OG_SUBSET_PREFIX } from "./subset-fonts.generated.js";
24
+ export { OG_SUBSET_FONTS, OG_SUBSET_PREFIX } from "./subset-fonts.generated.js";
25
+ let tableOverride;
26
+ /** The subset table in effect (the generated one unless a test replaced it). */
27
+ export function ogSubsetFontSets() {
28
+ return tableOverride ?? OG_SUBSET_FONTS;
29
+ }
30
+ /** Test isolation: replace the subset table (`undefined` restores the generated one). */
31
+ export function setOgSubsetFontsForTesting(table) {
32
+ tableOverride = table;
33
+ decoded.clear();
34
+ }
35
+ /** Bucket key of a subset object. */
36
+ export function ogSubsetFontKey(file) {
37
+ return `${OG_SUBSET_PREFIX}/${file}`;
38
+ }
39
+ const decoded = new Map();
40
+ /** Decodes the generated page encoding: `"<hex>"` = whole page, `"<hex>:<base64 bitmap>"` = some of it. */
41
+ export function decodeOgCodepointPages(encoded) {
42
+ const pages = new Map();
43
+ if (!encoded)
44
+ return pages;
45
+ for (const entry of encoded.split(",")) {
46
+ const colon = entry.indexOf(":");
47
+ if (colon < 0) {
48
+ pages.set(Number.parseInt(entry, 16), true);
49
+ continue;
50
+ }
51
+ const binary = atob(entry.slice(colon + 1));
52
+ const bitmap = new Uint8Array(32);
53
+ for (let i = 0; i < 32; i++)
54
+ bitmap[i] = binary.charCodeAt(i);
55
+ pages.set(Number.parseInt(entry.slice(0, colon), 16), bitmap);
56
+ }
57
+ return pages;
58
+ }
59
+ function coverageOf(set) {
60
+ let coverage = decoded.get(set);
61
+ if (!coverage) {
62
+ coverage = { pages: decodeOgCodepointPages(set.subset), cmap: decodeOgCodepointPages(set.cmap) };
63
+ decoded.set(set, coverage);
64
+ }
65
+ return coverage;
66
+ }
67
+ function inPages(pages, codepoint) {
68
+ const entry = pages.get(codepoint >> 8);
69
+ if (entry === true)
70
+ return true;
71
+ return !!entry && (entry[(codepoint & 0xff) >> 3] & (1 << (codepoint & 7))) !== 0;
72
+ }
73
+ /** True when the subset draws `codepoint` exactly like the full font. */
74
+ function covers(coverage, codepoint) {
75
+ if (codepoint < 0)
76
+ return false;
77
+ // Not in the full font ⇒ .notdef (or a fallback font) either way.
78
+ return inPages(coverage.pages, codepoint) || !inPages(coverage.cmap, codepoint);
79
+ }
80
+ /** True when the FULL fonts of `set` map `codepoint` (every weight). */
81
+ export function ogFullFontMaps(set, codepoint) {
82
+ return codepoint >= 0 && inPages(coverageOf(set).cmap, codepoint);
83
+ }
84
+ /** True when the SUBSET of `set` maps `codepoint` (a page requested whole counts, see the generated table). */
85
+ export function ogSubsetFontMaps(set, codepoint) {
86
+ return codepoint >= 0 && inPages(coverageOf(set).pages, codepoint);
87
+ }
88
+ /** Every codepoint in `codepoints` is drawn identically by the subset and the full font. */
89
+ export function ogSubsetCovers(set, codepoints) {
90
+ const coverage = coverageOf(set);
91
+ for (const codepoint of codepoints) {
92
+ if (!covers(coverage, codepoint))
93
+ return false;
94
+ }
95
+ return true;
96
+ }
97
+ /** Forces a miss: text the collector could not read reliably. */
98
+ const UNREADABLE = -1;
99
+ function addText(out, text) {
100
+ for (const ch of text)
101
+ out.add(ch.codePointAt(0));
102
+ }
103
+ /** The text itself plus its case-mapped forms (satori's `textTransform`). */
104
+ function addLayoutText(out, text) {
105
+ addText(out, text);
106
+ const upper = text.toUpperCase();
107
+ if (upper !== text)
108
+ addText(out, upper);
109
+ const lower = text.toLowerCase();
110
+ if (lower !== text)
111
+ addText(out, lower);
112
+ }
113
+ const SVG_DATA_PREFIX = "data:image/svg+xml";
114
+ /**
115
+ * Characters resvg may draw from an SVG data URI: everything outside markup
116
+ * (a superset of `<text>` content), with numeric character references and
117
+ * CDATA unwrapped. Anything that could define text indirectly (entities) or
118
+ * cannot be decoded forces a miss.
119
+ */
120
+ function addSvgDataUriText(out, uri) {
121
+ const comma = uri.indexOf(",");
122
+ if (comma < 0) {
123
+ out.add(UNREADABLE);
124
+ return;
125
+ }
126
+ const header = uri.slice(0, comma);
127
+ const payload = uri.slice(comma + 1);
128
+ let svg;
129
+ try {
130
+ if (/;base64/i.test(header)) {
131
+ const binary = atob(payload.trim());
132
+ const bytes = new Uint8Array(binary.length);
133
+ for (let i = 0; i < binary.length; i++)
134
+ bytes[i] = binary.charCodeAt(i);
135
+ svg = new TextDecoder().decode(bytes);
136
+ }
137
+ else {
138
+ try {
139
+ svg = decodeURIComponent(payload);
140
+ }
141
+ catch {
142
+ svg = payload;
143
+ }
144
+ }
145
+ }
146
+ catch {
147
+ out.add(UNREADABLE);
148
+ return;
149
+ }
150
+ if (/<!ENTITY/i.test(svg)) {
151
+ out.add(UNREADABLE);
152
+ return;
153
+ }
154
+ const text = svg
155
+ .replace(/<!\[CDATA\[([\s\S]*?)\]\]>/g, " $1 ")
156
+ .replace(/<[^>]*>/g, " ")
157
+ .replace(/&#x([0-9a-f]+);?/gi, (_, hex) => codepointText(Number.parseInt(hex, 16)))
158
+ .replace(/&#(\d+);?/g, (_, dec) => codepointText(Number.parseInt(dec, 10)));
159
+ addText(out, text);
160
+ }
161
+ function codepointText(codepoint) {
162
+ return codepoint >= 0 && codepoint <= 0x10ffff ? String.fromCodePoint(codepoint) : "�";
163
+ }
164
+ /** SVG data URIs inside a prop / style value (whole value, or inside `url(...)`). */
165
+ function addSvgUrisIn(out, value) {
166
+ if (!value.includes(SVG_DATA_PREFIX))
167
+ return;
168
+ if (value.startsWith(SVG_DATA_PREFIX)) {
169
+ addSvgDataUriText(out, value);
170
+ return;
171
+ }
172
+ let from = 0;
173
+ for (;;) {
174
+ const start = value.indexOf(SVG_DATA_PREFIX, from);
175
+ if (start < 0)
176
+ return;
177
+ const comma = value.indexOf(",", start);
178
+ // Only a base64 payload has a reliable end inside url(...): quotes and
179
+ // parentheses cannot occur in it. Anything else forces a miss.
180
+ if (comma < 0 || !/;base64/i.test(value.slice(start, comma))) {
181
+ out.add(UNREADABLE);
182
+ return;
183
+ }
184
+ const end = value.slice(comma + 1).search(/[^A-Za-z0-9+/=\s]/);
185
+ const stop = end < 0 ? value.length : comma + 1 + end;
186
+ addSvgDataUriText(out, value.slice(start, stop));
187
+ from = stop;
188
+ }
189
+ }
190
+ function collectProps(out, value, key, depth) {
191
+ if (depth > 64) {
192
+ out.add(UNREADABLE);
193
+ return;
194
+ }
195
+ if (typeof value === "string") {
196
+ addSvgUrisIn(out, value);
197
+ // `lineClamp: '2 "…more"'` draws its custom ellipsis string.
198
+ if (/lineclamp/i.test(key))
199
+ addLayoutText(out, value);
200
+ return;
201
+ }
202
+ if (Array.isArray(value)) {
203
+ for (const item of value)
204
+ collectProps(out, item, key, depth + 1);
205
+ return;
206
+ }
207
+ if (value && typeof value === "object") {
208
+ for (const [childKey, item] of Object.entries(value))
209
+ collectProps(out, item, childKey, depth + 1);
210
+ }
211
+ }
212
+ /**
213
+ * Every codepoint a satori + resvg render of `tree` may draw: text children
214
+ * (with case-mapped forms for `textTransform`), custom `lineClamp` ellipses,
215
+ * and the text of SVG data-URI images. A value the collector cannot read
216
+ * adds a sentinel that no subset covers, so the render uses the full font.
217
+ */
218
+ export function collectOgTreeCodepoints(tree) {
219
+ const out = new Set();
220
+ // "…" is drawn by textOverflow / lineClamp truncation.
221
+ out.add(0x2026);
222
+ const stack = [{ node: tree, depth: 0 }];
223
+ while (stack.length) {
224
+ const { node, depth } = stack.pop();
225
+ if (node === null || node === undefined || typeof node === "boolean")
226
+ continue;
227
+ if (typeof node === "string") {
228
+ addLayoutText(out, node);
229
+ continue;
230
+ }
231
+ if (typeof node === "number") {
232
+ addText(out, String(node));
233
+ continue;
234
+ }
235
+ if (depth > 256) {
236
+ out.add(UNREADABLE);
237
+ break;
238
+ }
239
+ if (Array.isArray(node)) {
240
+ for (const child of node)
241
+ stack.push({ node: child, depth: depth + 1 });
242
+ continue;
243
+ }
244
+ if (typeof node !== "object") {
245
+ out.add(UNREADABLE);
246
+ continue;
247
+ }
248
+ const props = node.props;
249
+ if (!props || typeof props !== "object")
250
+ continue;
251
+ for (const [key, value] of Object.entries(props)) {
252
+ if (key === "children")
253
+ stack.push({ node: value, depth: depth + 1 });
254
+ else
255
+ collectProps(out, value, key, 0);
256
+ }
257
+ }
258
+ return out;
259
+ }
260
+ /** True when the subset `set` draws every character of `tree` exactly like the full font. */
261
+ export function ogTreeCoveredBySubset(set, tree) {
262
+ if (!set)
263
+ return false;
264
+ return ogSubsetCovers(set, collectOgTreeCodepoints(tree));
265
+ }
266
+ /** Lowercase hex sha256 (WebCrypto: Workers and Node ≥ 19). */
267
+ export async function ogFontSha256(bytes) {
268
+ const digest = await crypto.subtle.digest("SHA-256", bytes);
269
+ return [...new Uint8Array(digest)].map((byte) => byte.toString(16).padStart(2, "0")).join("");
270
+ }
271
+ //# sourceMappingURL=font-subset.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"font-subset.js","sourceRoot":"","sources":["../../src/og/font-subset.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAwB,MAAM,6BAA6B,CAAC;AAGtG,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAahF,IAAI,aAAoE,CAAC;AAEzE,gFAAgF;AAChF,MAAM,UAAU,gBAAgB;IAC9B,OAAO,aAAa,IAAI,eAAe,CAAC;AAC1C,CAAC;AAED,yFAAyF;AACzF,MAAM,UAAU,0BAA0B,CAAC,KAA4D;IACrG,aAAa,GAAG,KAAK,CAAC;IACtB,OAAO,CAAC,KAAK,EAAE,CAAC;AAClB,CAAC;AAED,qCAAqC;AACrC,MAAM,UAAU,eAAe,CAAC,IAAY;IAC1C,OAAO,GAAG,gBAAgB,IAAI,IAAI,EAAE,CAAC;AACvC,CAAC;AAgBD,MAAM,OAAO,GAAG,IAAI,GAAG,EAA6B,CAAC;AAErD,2GAA2G;AAC3G,MAAM,UAAU,sBAAsB,CAAC,OAAe;IACpD,MAAM,KAAK,GAAG,IAAI,GAAG,EAA6B,CAAC;IACnD,IAAI,CAAC,OAAO;QAAE,OAAO,KAAK,CAAC;IAC3B,KAAK,MAAM,KAAK,IAAI,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;QACvC,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACjC,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;YACd,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,EAAE,EAAE,CAAC,EAAE,IAAI,CAAC,CAAC;YAC5C,SAAS;QACX,CAAC;QACD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC;QAC5C,MAAM,MAAM,GAAG,IAAI,UAAU,CAAC,EAAE,CAAC,CAAC;QAClC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,EAAE;YAAE,MAAM,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;QAC9D,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC;IAChE,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,UAAU,CAAC,GAAoB;IACtC,IAAI,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAChC,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,QAAQ,GAAG,EAAE,KAAK,EAAE,sBAAsB,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,sBAAsB,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;QACjG,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;IAC7B,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,SAAS,OAAO,CAAC,KAAqB,EAAE,SAAiB;IACvD,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,SAAS,IAAI,CAAC,CAAC,CAAC;IACxC,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IAChC,OAAO,CAAC,CAAC,KAAK,IAAI,CAAC,KAAK,CAAC,CAAC,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,CAAE,GAAG,CAAC,CAAC,IAAI,CAAC,SAAS,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;AACrF,CAAC;AAED,yEAAyE;AACzE,SAAS,MAAM,CAAC,QAAkB,EAAE,SAAiB;IACnD,IAAI,SAAS,GAAG,CAAC;QAAE,OAAO,KAAK,CAAC;IAChC,kEAAkE;IAClE,OAAO,OAAO,CAAC,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;AAClF,CAAC;AAED,wEAAwE;AACxE,MAAM,UAAU,cAAc,CAAC,GAAoB,EAAE,SAAiB;IACpE,OAAO,SAAS,IAAI,CAAC,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;AACpE,CAAC;AAED,+GAA+G;AAC/G,MAAM,UAAU,gBAAgB,CAAC,GAAoB,EAAE,SAAiB;IACtE,OAAO,SAAS,IAAI,CAAC,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;AACrE,CAAC;AAED,4FAA4F;AAC5F,MAAM,UAAU,cAAc,CAAC,GAAoB,EAAE,UAA4B;IAC/E,MAAM,QAAQ,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC;IACjC,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;QACnC,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,SAAS,CAAC;YAAE,OAAO,KAAK,CAAC;IACjD,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,iEAAiE;AACjE,MAAM,UAAU,GAAG,CAAC,CAAC,CAAC;AAEtB,SAAS,OAAO,CAAC,GAAgB,EAAE,IAAY;IAC7C,KAAK,MAAM,EAAE,IAAI,IAAI;QAAE,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,WAAW,CAAC,CAAC,CAAE,CAAC,CAAC;AACrD,CAAC;AAED,6EAA6E;AAC7E,SAAS,aAAa,CAAC,GAAgB,EAAE,IAAY;IACnD,OAAO,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IACnB,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;IACjC,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IACxC,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;IACjC,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;AAC1C,CAAC;AAED,MAAM,eAAe,GAAG,oBAAoB,CAAC;AAE7C;;;;;GAKG;AACH,SAAS,iBAAiB,CAAC,GAAgB,EAAE,GAAW;IACtD,MAAM,KAAK,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAC/B,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;QACd,GAAG,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QACpB,OAAO;IACT,CAAC;IACD,MAAM,MAAM,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;IACnC,MAAM,OAAO,GAAG,GAAG,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;IACrC,IAAI,GAAW,CAAC;IAChB,IAAI,CAAC;QACH,IAAI,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;YAC5B,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;YACpC,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YAC5C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE;gBAAE,KAAK,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;YACxE,GAAG,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACxC,CAAC;aAAM,CAAC;YACN,IAAI,CAAC;gBACH,GAAG,GAAG,kBAAkB,CAAC,OAAO,CAAC,CAAC;YACpC,CAAC;YAAC,MAAM,CAAC;gBACP,GAAG,GAAG,OAAO,CAAC;YAChB,CAAC;QACH,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,GAAG,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QACpB,OAAO;IACT,CAAC;IACD,IAAI,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QAC1B,GAAG,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QACpB,OAAO;IACT,CAAC;IACD,MAAM,IAAI,GAAG,GAAG;SACb,OAAO,CAAC,6BAA6B,EAAE,MAAM,CAAC;SAC9C,OAAO,CAAC,UAAU,EAAE,GAAG,CAAC;SACxB,OAAO,CAAC,oBAAoB,EAAE,CAAC,CAAC,EAAE,GAAW,EAAE,EAAE,CAAC,aAAa,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,CAAC;SAC1F,OAAO,CAAC,YAAY,EAAE,CAAC,CAAC,EAAE,GAAW,EAAE,EAAE,CAAC,aAAa,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC;IACtF,OAAO,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;AACrB,CAAC;AAED,SAAS,aAAa,CAAC,SAAiB;IACtC,OAAO,SAAS,IAAI,CAAC,IAAI,SAAS,IAAI,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;AACzF,CAAC;AAED,qFAAqF;AACrF,SAAS,YAAY,CAAC,GAAgB,EAAE,KAAa;IACnD,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,eAAe,CAAC;QAAE,OAAO;IAC7C,IAAI,KAAK,CAAC,UAAU,CAAC,eAAe,CAAC,EAAE,CAAC;QACtC,iBAAiB,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QAC9B,OAAO;IACT,CAAC;IACD,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,SAAS,CAAC;QACR,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,eAAe,EAAE,IAAI,CAAC,CAAC;QACnD,IAAI,KAAK,GAAG,CAAC;YAAE,OAAO;QACtB,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QACxC,uEAAuE;QACvE,+DAA+D;QAC/D,IAAI,KAAK,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC;YAC7D,GAAG,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;YACpB,OAAO;QACT,CAAC;QACD,MAAM,GAAG,GAAG,KAAK,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,mBAAmB,CAAC,CAAC;QAC/D,MAAM,IAAI,GAAG,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,GAAG,GAAG,CAAC;QACtD,iBAAiB,CAAC,GAAG,EAAE,KAAK,CAAC,KAAK,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC;QACjD,IAAI,GAAG,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,SAAS,YAAY,CAAC,GAAgB,EAAE,KAAc,EAAE,GAAW,EAAE,KAAa;IAChF,IAAI,KAAK,GAAG,EAAE,EAAE,CAAC;QACf,GAAG,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QACpB,OAAO;IACT,CAAC;IACD,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,YAAY,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QACzB,6DAA6D;QAC7D,IAAI,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC;YAAE,aAAa,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QACtD,OAAO;IACT,CAAC;IACD,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,KAAK,MAAM,IAAI,IAAI,KAAK;YAAE,YAAY,CAAC,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC;QAClE,OAAO;IACT,CAAC;IACD,IAAI,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QACvC,KAAK,MAAM,CAAC,QAAQ,EAAE,IAAI,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC;YAAE,YAAY,CAAC,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC;IACrG,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,uBAAuB,CAAC,IAAa;IACnD,MAAM,GAAG,GAAG,IAAI,GAAG,EAAU,CAAC;IAC9B,uDAAuD;IACvD,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAChB,MAAM,KAAK,GAA4C,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC;IAClF,OAAO,KAAK,CAAC,MAAM,EAAE,CAAC;QACpB,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,KAAK,CAAC,GAAG,EAAG,CAAC;QACrC,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,KAAK,SAAS,IAAI,OAAO,IAAI,KAAK,SAAS;YAAE,SAAS;QAC/E,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;YAC7B,aAAa,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;YACzB,SAAS;QACX,CAAC;QACD,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;YAC7B,OAAO,CAAC,GAAG,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;YAC3B,SAAS;QACX,CAAC;QACD,IAAI,KAAK,GAAG,GAAG,EAAE,CAAC;YAChB,GAAG,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;YACpB,MAAM;QACR,CAAC;QACD,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;YACxB,KAAK,MAAM,KAAK,IAAI,IAAI;gBAAE,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,GAAG,CAAC,EAAE,CAAC,CAAC;YACxE,SAAS;QACX,CAAC;QACD,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;YAC7B,GAAG,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;YACpB,SAAS;QACX,CAAC;QACD,MAAM,KAAK,GAAI,IAA4B,CAAC,KAAK,CAAC;QAClD,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ;YAAE,SAAS;QAClD,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAgC,CAAC,EAAE,CAAC;YAC5E,IAAI,GAAG,KAAK,UAAU;gBAAE,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,GAAG,CAAC,EAAE,CAAC,CAAC;;gBACjE,YAAY,CAAC,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC;QACxC,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,6FAA6F;AAC7F,MAAM,UAAU,qBAAqB,CAAC,GAAgC,EAAE,IAAa;IACnF,IAAI,CAAC,GAAG;QAAE,OAAO,KAAK,CAAC;IACvB,OAAO,cAAc,CAAC,GAAG,EAAE,uBAAuB,CAAC,IAAI,CAAC,CAAC,CAAC;AAC5D,CAAC;AAED,+DAA+D;AAC/D,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,KAAiB;IAClD,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,KAAgC,CAAC,CAAC;IACvF,OAAO,CAAC,GAAG,IAAI,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AAChG,CAAC"}