@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.
- package/CHANGELOG.md +188 -0
- package/README.md +4 -2
- package/dist/fault-HMYM9DhC.js +16 -0
- package/dist/fonts.js +97 -70
- package/dist/glyphs/browser.d.ts +17 -0
- package/dist/glyphs/eot-container.d.ts +10 -0
- package/dist/glyphs/fault.d.ts +9 -0
- package/dist/glyphs/glyph-data.d.ts +3 -0
- package/dist/glyphs/harfbuzz.d.ts +32 -0
- package/dist/glyphs/index.d.ts +7 -0
- package/dist/glyphs/measurement.d.ts +16 -0
- package/dist/glyphs/permissions.d.ts +4 -0
- package/dist/glyphs/provider.d.ts +19 -0
- package/dist/glyphs/sfnt.d.ts +10 -0
- package/dist/glyphs/shape-run.d.ts +3 -0
- package/dist/glyphs/shaping.d.ts +8 -0
- package/dist/glyphs/text-scripts.d.ts +15 -0
- package/dist/glyphs/types.d.ts +126 -0
- package/dist/glyphs/validate-cmap.d.ts +4 -0
- package/dist/glyphs/validate-glyphs.d.ts +3 -0
- package/dist/glyphs/validation-budget.d.ts +5 -0
- package/dist/glyphs/worker-client.d.ts +18 -0
- package/dist/glyphs/worker-protocol.d.ts +35 -0
- package/dist/glyphs/worker-result.d.ts +3 -0
- package/dist/glyphs/worker-server.d.ts +4 -0
- package/dist/glyphs/worker.d.ts +4 -0
- package/dist/glyphs-browser.js +78 -0
- package/dist/glyphs-harfbuzz.js +38 -0
- package/dist/glyphs-worker.js +215 -0
- package/dist/glyphs.js +71 -0
- package/dist/index.d.ts +2 -2
- package/dist/load.d.ts +3 -1
- package/dist/provider-9-K8y5p4.js +617 -0
- package/dist/substitute.d.ts +7 -0
- package/package.json +18 -2
|
@@ -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,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,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,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
|
+
};
|