@web-ppt/fonts 0.5.0-beta.1 → 0.5.0-beta.7

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,10 @@
1
+ import type { FontFaceInfo } from './types';
2
+ export interface FontTable {
3
+ offset: number;
4
+ length: number;
5
+ }
6
+ export interface SfntFont {
7
+ tables: ReadonlyMap<string, FontTable>;
8
+ info: Omit<FontFaceInfo, 'id' | 'family' | 'origin'>;
9
+ }
10
+ export declare function inspectSfnt(bytes: Uint8Array): SfntFont;
@@ -0,0 +1,3 @@
1
+ import type { FontFaceInfo, FontResult, FontShapeOptions, GlyphRun, ShaperGlyph } from './types';
2
+ export declare function checkShapeText(text: string, options: FontShapeOptions, maxLength: number): void;
3
+ export declare function makeGlyphRun(info: FontFaceInfo, text: string, options: FontShapeOptions, raw: ShaperGlyph[]): FontResult<GlyphRun>;
@@ -0,0 +1,8 @@
1
+ import type { FontEmbedding, FontProviderOptions, FontRequestOptions, FontResult, FontShapeOptions, GlyphRun } from './types';
2
+ export declare function abortable<T>(pending: Promise<T>, signal: AbortSignal): Promise<T>;
3
+ export declare function createShaping(options: FontProviderOptions, embedding: (id: string, options: FontRequestOptions) => Promise<FontResult<FontEmbedding>>): {
4
+ release(id: string): void;
5
+ shape(id: string, text: string, request: FontShapeOptions): Promise<FontResult<GlyphRun>>;
6
+ outline(id: string, glyphId: number, request: FontRequestOptions): Promise<FontResult<string>>;
7
+ dispose(): void;
8
+ };
@@ -0,0 +1,15 @@
1
+ import type { FontResult, FontScript } from './types';
2
+ export interface FontTextSegment {
3
+ start: number;
4
+ end: number;
5
+ text: string;
6
+ script: FontScript;
7
+ direction: 'ltr';
8
+ language: string;
9
+ }
10
+ export declare function fontCharacterScript(character: string, start: number): FontScript | undefined;
11
+ /** 此处只分脚本;沿用输入原文,断行和字体选择仍由已有布局及宿主负责。 */
12
+ export declare function segmentFontText(text: string, options: {
13
+ direction: 'ltr';
14
+ language: string;
15
+ }): FontResult<FontTextSegment[]>;
@@ -0,0 +1,126 @@
1
+ export interface FontSource {
2
+ id: string;
3
+ family?: string;
4
+ bytes?: Uint8Array;
5
+ origin: 'explicit' | 'embedded' | 'substitute';
6
+ sourceLabel?: string;
7
+ embeddingEvidence?: string;
8
+ /** 上游容器或使用许可施加的额外限制,只能收紧 sfnt 自身权限。 */
9
+ restrictions?: FontEmbeddingRights;
10
+ }
11
+ export interface FontRequestOptions {
12
+ purpose: 'edit' | 'view-print';
13
+ signal?: AbortSignal;
14
+ }
15
+ export interface FontRequest extends FontRequestOptions {
16
+ family: string;
17
+ weight: number;
18
+ italic: boolean;
19
+ }
20
+ export interface FontFaceInfo {
21
+ id: string;
22
+ family: string;
23
+ sourceFamily: string;
24
+ weight: number;
25
+ italic: boolean;
26
+ format: 'ttf-glyf';
27
+ unitsPerEm: number;
28
+ glyphCount: number;
29
+ bbox: readonly [number, number, number, number];
30
+ fsType: number;
31
+ os2Version: number;
32
+ byteLength: number;
33
+ origin: FontSource['origin'];
34
+ sourceLabel?: string;
35
+ embeddingEvidence?: string;
36
+ embedding: FontEmbeddingRights;
37
+ }
38
+ export interface FontEmbeddingRights {
39
+ usage: 'installable' | 'editable' | 'view-print' | 'restricted';
40
+ subsetAllowed: boolean;
41
+ outlineAllowed: boolean;
42
+ }
43
+ export interface FontEmbedding {
44
+ bytes: Uint8Array;
45
+ info: FontFaceInfo;
46
+ }
47
+ export type FontScript = 'Latn' | 'Hani';
48
+ export interface FontShapeOptions extends FontRequestOptions {
49
+ script: FontScript;
50
+ direction: 'ltr';
51
+ language: string;
52
+ }
53
+ export interface PositionedGlyph {
54
+ id: number;
55
+ xAdvance: number;
56
+ yAdvance: number;
57
+ xOffset: number;
58
+ yOffset: number;
59
+ }
60
+ export interface GlyphCluster {
61
+ /** 原文 UTF-16 区间;字形编号与原文不可互相替代。 */
62
+ start: number;
63
+ end: number;
64
+ text: string;
65
+ glyphStart: number;
66
+ glyphEnd: number;
67
+ }
68
+ export interface GlyphRun {
69
+ faceId: string;
70
+ text: string;
71
+ script: FontScript;
72
+ direction: 'ltr';
73
+ language: string;
74
+ unitsPerEm: number;
75
+ xAdvance: number;
76
+ yAdvance: number;
77
+ glyphs: PositionedGlyph[];
78
+ clusters: GlyphCluster[];
79
+ }
80
+ /** 适配器边界只使用设计单位和 UTF-16 簇,不暴露第三方字体对象。 */
81
+ export interface ShaperGlyph extends PositionedGlyph {
82
+ cluster: number;
83
+ }
84
+ export interface FontShaperFace {
85
+ shape(text: string, options: FontShapeOptions): Promise<ShaperGlyph[]>;
86
+ outline(glyphId: number, options: FontRequestOptions): Promise<string>;
87
+ dispose(): void;
88
+ }
89
+ export interface FontShaper {
90
+ open(font: FontEmbedding, signal: AbortSignal): Promise<FontShaperFace>;
91
+ dispose(): void;
92
+ }
93
+ export interface FontProviderOptions {
94
+ loadShaper?: (signal: AbortSignal) => Promise<FontShaper>;
95
+ limits?: Partial<FontProviderLimits>;
96
+ decodeEot?: (bytes: Uint8Array, options: {
97
+ signal: AbortSignal;
98
+ maxOutputBytes: number;
99
+ }) => Promise<Uint8Array | null>;
100
+ }
101
+ export interface FontProviderLimits {
102
+ maxFontBytes: number;
103
+ maxTotalFontBytes: number;
104
+ maxFaces: number;
105
+ maxGlyphs: number;
106
+ maxTextLength: number;
107
+ }
108
+ export type FontProblem = 'font-bytes-unavailable' | 'provider-disposed' | 'invalid-font' | 'aborted' | 'face-unavailable' | 'duplicate-face-id' | 'ambiguous-face' | 'resource-limit' | 'embedding-restricted' | 'preview-print-only' | 'bitmap-only' | 'cmap-not-supported' | 'missing-glyphs' | 'unsupported-script' | 'unsupported-direction' | 'script-mismatch' | 'invalid-text' | 'shaper-unavailable' | 'shaper-failed' | 'invalid-glyph-id' | 'invalid-request' | 'needs-container-decoder' | 'collection-not-supported' | 'variable-not-supported' | 'cff-not-supported' | 'color-font-not-supported' | 'decoder-unavailable' | 'decode-failed' | 'eot-root-restricted' | 'eot-feature-not-supported' | 'subset-not-permitted' | 'measurement-not-prepared' | 'unsupported-layout' | 'layout-failed' | 'face-style-mismatch' | 'font-install-failed';
109
+ export interface FontFailure {
110
+ ok: false;
111
+ reason: FontProblem;
112
+ faceId?: string;
113
+ missing?: Array<{
114
+ start: number;
115
+ end: number;
116
+ text: string;
117
+ }>;
118
+ range?: {
119
+ start: number;
120
+ end: number;
121
+ };
122
+ }
123
+ export type FontResult<T> = {
124
+ ok: true;
125
+ value: T;
126
+ } | FontFailure;
@@ -0,0 +1,4 @@
1
+ import type { SfntFont } from './sfnt';
2
+ import type { FontValidationPause } from './validation-budget';
3
+ /** 只验证字符映射范围;实际选字仍交给整形器,避免两套 cmap 选择规则。 */
4
+ export declare function validateCmap(bytes: Uint8Array, font: SfntFont, pause: FontValidationPause): Promise<void>;
@@ -0,0 +1,3 @@
1
+ import type { SfntFont } from './sfnt';
2
+ import type { FontValidationPause } from './validation-budget';
3
+ export declare function validateGlyphStorage(bytes: Uint8Array, font: SfntFont, pause: FontValidationPause): Promise<void>;
@@ -0,0 +1,5 @@
1
+ import type { FontProviderLimits } from './types';
2
+ export type FontValidationPause = () => Promise<void> | undefined;
3
+ export declare function fontLimits(input?: Partial<FontProviderLimits>): FontProviderLimits;
4
+ /** 让长校验归还事件循环,AbortSignal 才有机会在注册提交之前生效。 */
5
+ export declare function validationPause(check: () => void): FontValidationPause;
@@ -0,0 +1,18 @@
1
+ import type { FontProviderOptions, FontShaper } from './types';
2
+ import type { FontWorker } from './worker-protocol';
3
+ export interface FontWorkerOptions {
4
+ maxQueuedRequests?: number;
5
+ maxPendingBytes?: number;
6
+ requestTimeoutMs?: number;
7
+ }
8
+ /** 活跃请求取消时结束整个 WASM 实例;未取消请求在新 Worker 按需重建字体。 */
9
+ export declare function createWorkerFontShaper(factory: () => FontWorker, options?: FontWorkerOptions): FontShaper & {
10
+ decodeEot: NonNullable<FontProviderOptions['decodeEot']>;
11
+ state(): {
12
+ disposed: boolean;
13
+ retainedFontBytes: number;
14
+ pendingRequests: number;
15
+ pendingBytes: number;
16
+ workerActive: boolean;
17
+ };
18
+ };
@@ -0,0 +1,35 @@
1
+ import type { FontFaceInfo, FontResult, FontShapeOptions, GlyphRun } from './types';
2
+ export declare const FONT_WORKER_PROTOCOL = "web-ppt-fonts/1";
3
+ export type FontWorkerRequest = {
4
+ protocol: typeof FONT_WORKER_PROTOCOL;
5
+ id: number;
6
+ } & ({
7
+ operation: 'register';
8
+ face: FontFaceInfo;
9
+ bytes: Uint8Array;
10
+ } | {
11
+ operation: 'shape';
12
+ faceId: string;
13
+ text: string;
14
+ options: Omit<FontShapeOptions, 'signal'>;
15
+ } | {
16
+ operation: 'outline';
17
+ faceId: string;
18
+ glyphId: number;
19
+ purpose: 'edit' | 'view-print';
20
+ } | {
21
+ operation: 'decode';
22
+ bytes: Uint8Array;
23
+ maxOutputBytes: number;
24
+ });
25
+ export interface FontWorkerResponse {
26
+ protocol: typeof FONT_WORKER_PROTOCOL;
27
+ id: number;
28
+ result: FontResult<FontFaceInfo | GlyphRun | string | Uint8Array>;
29
+ }
30
+ export interface FontWorkerEndpoint {
31
+ postMessage(message: unknown, transfer?: Transferable[]): void;
32
+ addEventListener(type: 'message', listener: (event: MessageEvent) => void): void;
33
+ removeEventListener(type: 'message', listener: (event: MessageEvent) => void): void;
34
+ }
35
+ export type FontWorker = Pick<Worker, 'postMessage' | 'addEventListener' | 'removeEventListener' | 'terminate'>;
@@ -0,0 +1,3 @@
1
+ import type { FontFaceInfo, FontShapeOptions, ShaperGlyph } from './types';
2
+ /** Worker 也是输入边界;先限制簇范围,再展开,避免错误消息造成无限循环或大分配。 */
3
+ export declare function workerGlyphs(value: unknown, info: FontFaceInfo, text: string, options: FontShapeOptions): ShaperGlyph[];
@@ -0,0 +1,4 @@
1
+ import type { FontProviderOptions } from './types';
2
+ import type { FontWorkerEndpoint } from './worker-protocol';
3
+ /** 在专属 Worker 内安装;入口及 WASM URL 由宿主构建工具决定。 */
4
+ export declare function serveFontWorker(endpoint: FontWorkerEndpoint, options: FontProviderOptions): () => void;
@@ -0,0 +1,4 @@
1
+ export { serveFontWorker } from './worker-server';
2
+ export { createWorkerFontShaper } from './worker-client';
3
+ export type { FontWorkerOptions } from './worker-client';
4
+ export type { FontWorker, FontWorkerEndpoint } from './worker-protocol';
@@ -0,0 +1,78 @@
1
+ function U(b, a, t) {
2
+ return new Promise((f, u) => {
3
+ const n = () => {
4
+ o(), u(new DOMException("字体加载已取消", "AbortError"));
5
+ }, o = () => {
6
+ a.removeEventListener("abort", n), t == null || t.removeEventListener("abort", n);
7
+ };
8
+ if (a.addEventListener("abort", n, { once: !0 }), t == null || t.addEventListener("abort", n, { once: !0 }), a.aborted || t != null && t.aborted) {
9
+ o(), n();
10
+ return;
11
+ }
12
+ Promise.resolve().then(() => b.load()).then(() => {
13
+ o(), f();
14
+ }, (e) => {
15
+ o(), u(e);
16
+ });
17
+ });
18
+ }
19
+ function p(b, a = document) {
20
+ const t = /* @__PURE__ */ new Map(), f = /* @__PURE__ */ new Set(), u = new AbortController();
21
+ let n = !1;
22
+ const o = (e, r) => {
23
+ var s;
24
+ return n || b.state().disposed ? { ok: !1, reason: "provider-disposed", faceId: e } : (s = r.signal) != null && s.aborted ? { ok: !1, reason: "aborted", faceId: e } : void 0;
25
+ };
26
+ return {
27
+ async install(e, r) {
28
+ const s = o(e, r);
29
+ if (s) return s;
30
+ const y = await b.embedding(e, r);
31
+ if (!y.ok) return y;
32
+ const g = o(e, r);
33
+ if (g) return g;
34
+ const { info: i, bytes: m } = y.value, l = r.family ?? i.family;
35
+ if (![400, 700].includes(i.weight)) return { ok: !1, reason: "face-style-mismatch", faceId: e };
36
+ if (!l.trim() || l.length > 256 || /[\u0000-\u001f\u007f]/.test(l)) return { ok: !1, reason: "invalid-request", faceId: e };
37
+ const d = JSON.stringify([e, l]), k = t.get(d);
38
+ if (k) return { ok: !0, value: { ...k.resource } };
39
+ if (f.has(d)) return { ok: !1, reason: "duplicate-face-id", faceId: e };
40
+ f.add(d);
41
+ let c, v;
42
+ try {
43
+ const L = o(e, r);
44
+ if (L) return L;
45
+ c = new FontFace(l, new Uint8Array(m).buffer, { weight: String(i.weight), style: i.italic ? "italic" : "normal" }), await U(c, u.signal, r.signal);
46
+ const w = o(e, r);
47
+ if (w) return w;
48
+ a.fonts.add(c), v = URL.createObjectURL(new Blob([new Uint8Array(m).buffer], { type: "font/ttf" }));
49
+ const h = { family: l, src: v, bold: i.weight === 700, italic: i.italic };
50
+ return t.set(d, { id: e, font: c, resource: h, bytes: m.length }), { ok: !0, value: { ...h } };
51
+ } catch {
52
+ return c && a.fonts.delete(c), v && URL.revokeObjectURL(v), o(e, r) ?? { ok: !1, reason: "font-install-failed", faceId: e };
53
+ } finally {
54
+ f.delete(d);
55
+ }
56
+ },
57
+ resources() {
58
+ return [...t.values()].map((e) => ({ ...e.resource }));
59
+ },
60
+ remove(e) {
61
+ for (const [r, s] of t) s.id === e && (a.fonts.delete(s.font), URL.revokeObjectURL(s.resource.src), t.delete(r));
62
+ },
63
+ state() {
64
+ return { disposed: n, faces: t.size, sourceBytes: [...t.values()].reduce((e, r) => e + r.bytes, 0) };
65
+ },
66
+ dispose() {
67
+ if (!n) {
68
+ n = !0, u.abort();
69
+ for (const e of t.values())
70
+ a.fonts.delete(e.font), URL.revokeObjectURL(e.resource.src);
71
+ t.clear();
72
+ }
73
+ }
74
+ };
75
+ }
76
+ export {
77
+ p as createFontFaceScope
78
+ };
@@ -0,0 +1,38 @@
1
+ import { F as s } from "./fault-HMYM9DhC.js";
2
+ function y(t) {
3
+ let r = !1;
4
+ const a = /* @__PURE__ */ new Set();
5
+ return {
6
+ async open(i, g) {
7
+ if (r || g.aborted) throw new s("aborted");
8
+ let n = new t.Font(new t.Face(new t.Blob(i.bytes)));
9
+ const c = () => {
10
+ n = void 0, a.delete(c);
11
+ };
12
+ return a.add(c), {
13
+ async shape(l, o) {
14
+ var f;
15
+ if (!n || r || (f = o.signal) != null && f.aborted) throw new s("aborted");
16
+ const e = new t.Buffer();
17
+ e.addText(l), e.guessSegmentProperties(), e.setDirection(t.Direction.LTR), e.setScript(o.script), e.setLanguage(o.language), t.shape(n, e);
18
+ const d = e.getGlyphInfos(), p = e.getGlyphPositions();
19
+ if (d.length !== p.length) throw new s("shaper-failed");
20
+ return d.map((u, w) => ({ id: u.codepoint, cluster: u.cluster, ...p[w] }));
21
+ },
22
+ async outline(l, o) {
23
+ var e;
24
+ if (!n || r || (e = o.signal) != null && e.aborted) throw new s("aborted");
25
+ return n.glyphToPath(l);
26
+ },
27
+ dispose: c
28
+ };
29
+ },
30
+ dispose() {
31
+ r = !0;
32
+ for (const i of [...a]) i();
33
+ }
34
+ };
35
+ }
36
+ export {
37
+ y as createHarfBuzzShaper
38
+ };
@@ -0,0 +1,215 @@
1
+ import { c as W, f as C, m as G } from "./provider-9-K8y5p4.js";
2
+ import { F as l } from "./fault-HMYM9DhC.js";
3
+ const B = "web-ppt-fonts/1";
4
+ function z(w, y) {
5
+ const u = W(y), h = new AbortController(), g = C(y.limits);
6
+ let i = !1, d = Promise.resolve();
7
+ const m = (v) => {
8
+ const s = v.data;
9
+ i || !s || s.protocol !== B || !Number.isSafeInteger(s.id) || (d = d.then(async () => {
10
+ if (i) return;
11
+ let t;
12
+ try {
13
+ if (s.operation === "register") t = await u.register({
14
+ id: s.face.id,
15
+ family: s.face.family,
16
+ origin: s.face.origin,
17
+ bytes: s.bytes,
18
+ restrictions: s.face.embedding
19
+ }, { purpose: "view-print" });
20
+ else if (s.operation === "shape") t = await u.shape(s.faceId, s.text, s.options);
21
+ else if (s.operation === "outline") t = await u.outline(s.faceId, s.glyphId, { purpose: s.purpose });
22
+ else if (s.operation === "decode")
23
+ if (!y.decodeEot) t = { ok: !1, reason: "decoder-unavailable" };
24
+ else if (!(s.bytes instanceof Uint8Array) || s.bytes.length > g.maxFontBytes || !Number.isSafeInteger(s.maxOutputBytes) || s.maxOutputBytes < 1) t = { ok: !1, reason: "resource-limit" };
25
+ else {
26
+ const a = Math.min(s.maxOutputBytes, g.maxFontBytes), r = await y.decodeEot(s.bytes, { signal: h.signal, maxOutputBytes: a });
27
+ t = r != null && r.length ? r.length > a ? { ok: !1, reason: "resource-limit" } : { ok: !0, value: new Uint8Array(r) } : { ok: !1, reason: "decode-failed" };
28
+ }
29
+ else t = { ok: !1, reason: "shaper-failed" };
30
+ } catch (a) {
31
+ t = { ok: !1, reason: a instanceof l ? a.reason : s.operation === "decode" ? "decode-failed" : "shaper-failed" };
32
+ }
33
+ i || w.postMessage(
34
+ { protocol: B, id: s.id, result: t },
35
+ t.ok && t.value instanceof Uint8Array ? [t.value.buffer] : []
36
+ );
37
+ }).catch(() => {
38
+ }));
39
+ };
40
+ return w.addEventListener("message", m), () => {
41
+ i || (i = !0, h.abort(), w.removeEventListener("message", m), u.dispose());
42
+ };
43
+ }
44
+ function _(w, y, u, h) {
45
+ const g = () => {
46
+ throw new l("shaper-failed");
47
+ };
48
+ (!w || typeof w != "object") && g();
49
+ const i = w;
50
+ if ((!Array.isArray(i.glyphs) || !Array.isArray(i.clusters) || i.faceId !== y.id || i.text !== u || i.unitsPerEm !== y.unitsPerEm || i.script !== h.script || i.direction !== h.direction || i.language !== h.language) && g(), i.glyphs.length > Math.max(256, u.length * 32) || i.clusters.length > i.glyphs.length)
51
+ throw new l("resource-limit");
52
+ const d = [];
53
+ let m = 0, v = 0;
54
+ for (const t of i.clusters) {
55
+ (!t || !Number.isInteger(t.glyphEnd) || !Number.isInteger(t.end) || t.glyphStart !== m || t.glyphEnd <= m || t.glyphEnd > i.glyphs.length || t.start !== v || t.end <= v || t.end > u.length || t.text !== u.slice(t.start, t.end)) && g();
56
+ for (let a = t.glyphStart; a < t.glyphEnd; a++) {
57
+ const r = i.glyphs[a];
58
+ (!r || typeof r != "object") && g(), d.push({ ...r, cluster: t.start });
59
+ }
60
+ m = t.glyphEnd, v = t.end;
61
+ }
62
+ (m !== i.glyphs.length || v !== u.length) && g();
63
+ const s = G(y, u, h, d);
64
+ return (!s.ok || s.value.xAdvance !== i.xAdvance || s.value.yAdvance !== i.yAdvance) && g(), d;
65
+ }
66
+ function D(w, y = {}) {
67
+ const u = y.maxQueuedRequests ?? 64, h = y.requestTimeoutMs ?? 3e4, g = y.maxPendingBytes ?? 64 * 1024 * 1024;
68
+ if (!Number.isInteger(u) || u < 1 || !Number.isSafeInteger(g) || g < 1 || !Number.isFinite(h) || h < 1) throw new RangeError("Invalid font worker budget");
69
+ const i = /* @__PURE__ */ new Map(), d = [], m = /* @__PURE__ */ new Set(), v = /* @__PURE__ */ new Set(), s = /* @__PURE__ */ new Set();
70
+ let t = !1, a, r, R = 0, S = 0, L = 0, M = "action", k, A;
71
+ const O = () => {
72
+ k !== void 0 && clearTimeout(k), k = void 0, A == null || A(), A = void 0, a == null || a.terminate(), a = void 0, m.clear();
73
+ }, b = (e, o, n) => {
74
+ var E;
75
+ L -= e.cost, (E = e.signal) == null || E.removeEventListener("abort", e.cancel), e === r && (k !== void 0 && clearTimeout(k), k = void 0, r = void 0), n ? e.reject(n) : e.resolve(o);
76
+ }, I = () => {
77
+ O(), r && b(r, void 0, new l("shaper-failed"));
78
+ for (const e of d.splice(0)) b(e, void 0, new l("shaper-failed"));
79
+ }, q = () => {
80
+ if (!r || !a) return;
81
+ const e = r.font;
82
+ M = !e || m.has(e.info.id) ? "action" : "register", S = ++R;
83
+ const o = { protocol: B, id: S };
84
+ if (M === "register" && e) {
85
+ const n = e.bytes.slice();
86
+ a.postMessage({ ...o, operation: "register", face: e.info, bytes: n }, [n.buffer]);
87
+ } else if (r.action.operation === "decode") {
88
+ const n = r.action.bytes.slice();
89
+ a.postMessage({ ...o, ...r.action, bytes: n }, [n.buffer]);
90
+ } else e && a.postMessage({ ...o, ...r.action, faceId: e.info.id });
91
+ }, F = () => {
92
+ if (!(r || t || !d.length)) {
93
+ r = d.shift();
94
+ try {
95
+ if (!a) {
96
+ const e = w();
97
+ a = e;
98
+ const o = (E) => {
99
+ if (a !== e || !r) return;
100
+ const c = E.data;
101
+ if (!(!c || c.protocol !== B || c.id !== S)) {
102
+ if (!c.result || typeof c.result.ok != "boolean") {
103
+ I();
104
+ return;
105
+ }
106
+ if (!c.result.ok) {
107
+ b(r, void 0, new l(c.result.reason, c.result.missing, c.result.range)), F();
108
+ return;
109
+ }
110
+ if (M === "register") {
111
+ m.add(r.font.info.id);
112
+ try {
113
+ q();
114
+ } catch {
115
+ I();
116
+ }
117
+ } else
118
+ b(r, c.result.value), F();
119
+ }
120
+ }, n = () => {
121
+ a === e && I();
122
+ };
123
+ e.addEventListener("message", o), e.addEventListener("error", n), e.addEventListener("messageerror", n), A = () => {
124
+ e.removeEventListener("message", o), e.removeEventListener("error", n), e.removeEventListener("messageerror", n);
125
+ };
126
+ }
127
+ k = setTimeout(I, h), q();
128
+ } catch {
129
+ I();
130
+ }
131
+ }
132
+ }, N = (e, o, n) => new Promise((E, c) => {
133
+ if (t) {
134
+ c(new l("provider-disposed"));
135
+ return;
136
+ }
137
+ if (n != null && n.aborted) {
138
+ c(new l("aborted"));
139
+ return;
140
+ }
141
+ const x = o.operation === "decode" ? o.bytes.length : o.operation === "shape" ? o.text.length * 2 : 0;
142
+ if (d.length + (r ? 1 : 0) >= u || L + x > g) {
143
+ c(new l("resource-limit"));
144
+ return;
145
+ }
146
+ o.operation === "decode" && (o = { ...o, bytes: new Uint8Array(o.bytes) }), L += x;
147
+ const p = { font: e, action: o, cost: x, signal: n, resolve: E, reject: c, cancel: () => {
148
+ if (r === p)
149
+ O(), b(p, void 0, new l("aborted"));
150
+ else {
151
+ const f = d.indexOf(p);
152
+ if (f < 0) return;
153
+ d.splice(f, 1), b(p, void 0, new l("aborted"));
154
+ }
155
+ F();
156
+ } };
157
+ n == null || n.addEventListener("abort", p.cancel, { once: !0 }), d.push(p), F();
158
+ });
159
+ return {
160
+ async decodeEot(e, o) {
161
+ return await N(void 0, { operation: "decode", bytes: e, maxOutputBytes: o.maxOutputBytes }, o.signal);
162
+ },
163
+ async open(e, o) {
164
+ if (t || o.aborted) throw new l(t ? "provider-disposed" : "aborted");
165
+ if (v.has(e.info.id)) throw new l("duplicate-face-id");
166
+ v.add(e.info.id);
167
+ let n = { info: { ...e.info, bbox: [...e.info.bbox], embedding: { ...e.info.embedding } }, bytes: new Uint8Array(e.bytes) };
168
+ i.set(n.info.id, n);
169
+ const E = n.info.id, c = () => {
170
+ if (!n) return;
171
+ const p = n;
172
+ if (n = void 0, i.delete(E), s.delete(c), !t) {
173
+ for (let f = d.length - 1; f >= 0; f--) d[f].font === p && b(d.splice(f, 1)[0], void 0, new l("face-unavailable"));
174
+ (r == null ? void 0 : r.font) === p ? b(r, void 0, new l("face-unavailable")) : r && (d.unshift(r), r = void 0), O(), F();
175
+ }
176
+ };
177
+ s.add(c);
178
+ const x = () => {
179
+ if (!n || t) throw new l("provider-disposed");
180
+ return n;
181
+ };
182
+ return {
183
+ async shape(p, f) {
184
+ const P = x(), T = { purpose: f.purpose, script: f.script, direction: f.direction, language: f.language }, U = await N(P, { operation: "shape", text: p, options: T }, f.signal);
185
+ return _(U, P.info, p, f);
186
+ },
187
+ async outline(p, f) {
188
+ return await N(x(), { operation: "outline", glyphId: p, purpose: f.purpose }, f.signal);
189
+ },
190
+ dispose: c
191
+ };
192
+ },
193
+ state() {
194
+ return {
195
+ disposed: t,
196
+ retainedFontBytes: [...i.values()].reduce((e, o) => e + o.bytes.length, 0),
197
+ pendingRequests: d.length + (r ? 1 : 0),
198
+ pendingBytes: L,
199
+ workerActive: !!a
200
+ };
201
+ },
202
+ dispose() {
203
+ if (!t) {
204
+ t = !0, O(), r && b(r, void 0, new l("provider-disposed"));
205
+ for (const e of d.splice(0)) b(e, void 0, new l("provider-disposed"));
206
+ for (const e of [...s]) e();
207
+ i.clear(), v.clear();
208
+ }
209
+ }
210
+ };
211
+ }
212
+ export {
213
+ D as createWorkerFontShaper,
214
+ z as serveFontWorker
215
+ };
package/dist/glyphs.js ADDED
@@ -0,0 +1,71 @@
1
+ import { s as I } from "./provider-9-K8y5p4.js";
2
+ import { c as P } from "./provider-9-K8y5p4.js";
3
+ import { F as u } from "./fault-HMYM9DhC.js";
4
+ async function x(g, z, a) {
5
+ var F;
6
+ const d = /* @__PURE__ */ new Map(), k = /* @__PURE__ */ new Set(), w = /* @__PURE__ */ new Map(), n = /* @__PURE__ */ new Map(), m = a.maxQueries ?? 8192;
7
+ let h = !1, b = 0;
8
+ const v = (e, t, p) => {
9
+ var i;
10
+ if (!e) return 0;
11
+ if ((i = t.math) != null && i.length) throw new u("unsupported-layout");
12
+ const s = JSON.stringify([e, t.fonts, !!t.b, !!t.i]), o = d.get(s), l = t.size * p * (t.baseline ? 0.65 : 1);
13
+ if (o !== void 0) return o * l + (t.spacing ?? 0) * e.length;
14
+ if (!h) throw new u("measurement-not-prepared");
15
+ if (!n.has(s)) {
16
+ if (b += e.length, d.size + n.size >= m || b > 1e6) throw new u("resource-limit");
17
+ n.set(s, { text: e, style: { fonts: [...t.fonts], b: t.b, i: t.i } });
18
+ }
19
+ return e.length * l;
20
+ };
21
+ try {
22
+ if (!Number.isSafeInteger(m) || m < 1) return { ok: !1, reason: "invalid-request" };
23
+ for (let e = 0; e < 8; e++) {
24
+ if (g.state().disposed) return { ok: !1, reason: "provider-disposed" };
25
+ if ((F = a.signal) != null && F.aborted) return { ok: !1, reason: "aborted" };
26
+ n.clear(), h = !0;
27
+ let t;
28
+ try {
29
+ t = z(v);
30
+ } finally {
31
+ h = !1;
32
+ }
33
+ if (!n.size) return { ok: !0, value: { result: t, measureText: v, faceIds: [...k] } };
34
+ for (const [p, s] of n) {
35
+ const o = I(s.text, { direction: "ltr", language: a.language });
36
+ if (!o.ok) return o;
37
+ let l = 0;
38
+ for (const i of o.value) {
39
+ const r = a.faceForRun(s.style, i.script);
40
+ if (!r) return { ok: !1, reason: "face-unavailable" };
41
+ let f = w.get(r);
42
+ if (!f) {
43
+ const y = await g.faceInfo(r, a);
44
+ if (!y.ok) return y;
45
+ f = y.value, w.set(r, f);
46
+ }
47
+ if (f.weight !== (s.style.b ? 700 : 400) || f.italic !== !!s.style.i)
48
+ return { ok: !1, reason: "face-style-mismatch", faceId: r };
49
+ const c = await g.shape(r, i.text, {
50
+ purpose: a.purpose,
51
+ signal: a.signal,
52
+ language: a.language,
53
+ script: i.script,
54
+ direction: "ltr"
55
+ });
56
+ if (!c.ok) return c;
57
+ l += c.value.xAdvance / c.value.unitsPerEm, k.add(r);
58
+ }
59
+ d.set(p, l);
60
+ }
61
+ }
62
+ return { ok: !1, reason: "resource-limit" };
63
+ } catch (e) {
64
+ return e instanceof u ? { ok: !1, reason: e.reason, range: e.range } : { ok: !1, reason: "layout-failed" };
65
+ }
66
+ }
67
+ export {
68
+ P as createFontProvider,
69
+ I as segmentFontText,
70
+ x as withFontMeasurement
71
+ };