@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
package/dist/index.d.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @web-ppt/fonts —— 字体替换与按需加载。
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* 这里解决的是**文件没带字体、本机也没装**的情况:拉丁用度量兼容的免费字体,
|
|
4
|
+
* 默认入口解决的是**文件没带字体、本机也没装**的情况:拉丁用度量兼容的免费字体,
|
|
6
5
|
* 断行与 PowerPoint 逐字对齐;中文换成思源系,行高靠 @font-face 描述符拉近。
|
|
7
6
|
*
|
|
8
7
|
* 包里**一个字节的字体都没有**:切片指向 fontsource 的已发布版本,
|
|
9
8
|
* 由 jsDelivr 分发,用不到就不下载。
|
|
9
|
+
* 显式字体字节与字形能力见独立的 ./glyphs 按需入口。
|
|
10
10
|
*/
|
|
11
11
|
export { SUBSTITUTIONS, substituteFor } from './substitute';
|
|
12
12
|
export type { Substitution } from './substitute';
|
package/dist/load.d.ts
CHANGED
|
@@ -56,7 +56,9 @@ export declare function isInstalled(family: string, doc?: Document): boolean;
|
|
|
56
56
|
* (我们是把 CSS 文本注入到页面里,相对路径会按页面地址解析,必然 404)。
|
|
57
57
|
* `unicode-range` 原样保留——按需下载全靠它。
|
|
58
58
|
*/
|
|
59
|
-
export declare function rewriteFontFaceCss(css: string, original: string, cssHref: string
|
|
59
|
+
export declare function rewriteFontFaceCss(css: string, original: string, cssHref: string, opts?: {
|
|
60
|
+
readonly preferLocal?: boolean;
|
|
61
|
+
}): string;
|
|
60
62
|
/**
|
|
61
63
|
* 为一份演示文稿的字体用量准备好网络字体。
|
|
62
64
|
*
|
|
@@ -0,0 +1,617 @@
|
|
|
1
|
+
import { F as v, r as M, i as F } from "./fault-HMYM9DhC.js";
|
|
2
|
+
function Q(n, e) {
|
|
3
|
+
if (!e) return n;
|
|
4
|
+
const o = ["installable", "editable", "view-print", "restricted"];
|
|
5
|
+
if (!o.includes(e.usage) || typeof e.subsetAllowed != "boolean" || typeof e.outlineAllowed != "boolean")
|
|
6
|
+
throw new Error("invalid-font");
|
|
7
|
+
return {
|
|
8
|
+
usage: o[Math.max(o.indexOf(n.usage), o.indexOf(e.usage))],
|
|
9
|
+
subsetAllowed: n.subsetAllowed && e.subsetAllowed,
|
|
10
|
+
outlineAllowed: n.outlineAllowed && e.outlineAllowed
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
function _(n, e) {
|
|
14
|
+
const o = n & 15;
|
|
15
|
+
if (e > 5 || o & 1 || e >= 3 && ![0, 2, 4, 8].includes(o) || e >= 2 && n & 64752) throw new Error("invalid-font");
|
|
16
|
+
return {
|
|
17
|
+
usage: o & 8 ? "editable" : o & 4 ? "view-print" : o & 2 ? "restricted" : "installable",
|
|
18
|
+
subsetAllowed: e < 2 || !(n & 256),
|
|
19
|
+
outlineAllowed: e < 2 || !(n & 512)
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
function N(n, e) {
|
|
23
|
+
if (!n.outlineAllowed) return "bitmap-only";
|
|
24
|
+
if (n.usage === "restricted") return "embedding-restricted";
|
|
25
|
+
if (n.usage === "view-print" && e !== "view-print") return "preview-print-only";
|
|
26
|
+
}
|
|
27
|
+
function K(n) {
|
|
28
|
+
var C;
|
|
29
|
+
const e = new DataView(n.buffer, n.byteOffset, n.byteLength), o = F;
|
|
30
|
+
n.length < 4 && o();
|
|
31
|
+
const c = e.getUint32(0);
|
|
32
|
+
if ([2001684038, 2001684018].includes(c)) throw new v("needs-container-decoder");
|
|
33
|
+
if (c === 1953784678) throw new v("collection-not-supported");
|
|
34
|
+
(n.length < 12 || ![65536, 1953658213, 1330926671].includes(c)) && o();
|
|
35
|
+
const a = e.getUint16(4), g = 12 + a * 16;
|
|
36
|
+
(!a || a > 256 || g > n.length) && o();
|
|
37
|
+
const w = /* @__PURE__ */ new Map();
|
|
38
|
+
for (let f = 0; f < a; f++) {
|
|
39
|
+
const p = 12 + f * 16, x = String.fromCharCode(...n.subarray(p, p + 4)), S = e.getUint32(p + 8), B = e.getUint32(p + 12);
|
|
40
|
+
(w.has(x) || S < g || S % 4 || S + B > n.length) && o(), w.set(x, { offset: S, length: B });
|
|
41
|
+
}
|
|
42
|
+
const k = [...w.values()].filter((f) => f.length).sort((f, p) => f.offset - p.offset);
|
|
43
|
+
k.some((f, p) => p > 0 && k[p - 1].offset + k[p - 1].length > f.offset) && o();
|
|
44
|
+
const b = (f, p) => {
|
|
45
|
+
const x = w.get(f);
|
|
46
|
+
return !x || x.length < p ? o() : x.offset;
|
|
47
|
+
};
|
|
48
|
+
if (w.has("fvar")) throw new v("variable-not-supported");
|
|
49
|
+
if (w.has("CFF ") || w.has("CFF2")) throw new v("cff-not-supported");
|
|
50
|
+
if (["COLR", "CPAL", "CBDT", "CBLC", "sbix", "SVG "].some((f) => w.has(f))) throw new v("color-font-not-supported");
|
|
51
|
+
c === 1330926671 && o(), b("glyf", 0), b("loca", 4), b("cmap", 4), b("hmtx", 4);
|
|
52
|
+
const U = b("head", 54), i = b("OS/2", 78), u = b("maxp", 32), r = e.getUint16(i);
|
|
53
|
+
b("OS/2", r === 0 ? 78 : r === 1 ? 86 : r < 5 ? 96 : 100), b("hhea", 36);
|
|
54
|
+
const t = e.getUint16(U + 18), s = e.getUint16(u + 4), l = e.getUint16(i + 4), d = e.getUint16(i + 8);
|
|
55
|
+
(e.getUint32(U + 12) !== 1594834165 || t < 16 || t > 16384 || !s || l < 1 || l > 1e3 || e.getUint32(u) !== 65536) && o();
|
|
56
|
+
const y = b("name", 6), A = w.get("name").length, O = e.getUint16(y + 2), m = e.getUint16(y + 4);
|
|
57
|
+
(6 + O * 12 > A || m < 6 + O * 12 || m > A) && o(), O > 1024 && M();
|
|
58
|
+
const h = [];
|
|
59
|
+
let L = 65536;
|
|
60
|
+
for (let f = 0; f < O; f++) {
|
|
61
|
+
const p = y + 6 + f * 12, x = e.getUint16(p), S = e.getUint16(p + 4), B = e.getUint16(p + 6), I = e.getUint16(p + 8), z = m + e.getUint16(p + 10);
|
|
62
|
+
if (z + I > A && o(), B !== 16 && B !== 1 || ![0, 3].includes(x) || I % 2 || I > 512) continue;
|
|
63
|
+
L -= I, L < 0 && M();
|
|
64
|
+
const P = new TextDecoder("utf-16be", { fatal: !0 }).decode(n.subarray(y + z, y + z + I)).trim();
|
|
65
|
+
P && P.length <= 256 && !/[\u0000-\u001f\u007f]/.test(P) && h.push({ value: P, rank: (B === 16 ? 10 : 0) + (S === 1033 ? 2 : 0) });
|
|
66
|
+
}
|
|
67
|
+
const E = (C = h.sort((f, p) => p.rank - f.rank)[0]) == null ? void 0 : C.value;
|
|
68
|
+
return E || o(), { tables: w, info: {
|
|
69
|
+
sourceFamily: E,
|
|
70
|
+
weight: l,
|
|
71
|
+
italic: !!(e.getUint16(i + 62) & 1),
|
|
72
|
+
format: "ttf-glyf",
|
|
73
|
+
unitsPerEm: t,
|
|
74
|
+
glyphCount: s,
|
|
75
|
+
bbox: [36, 38, 40, 42].map((f) => e.getInt16(U + f)),
|
|
76
|
+
fsType: d,
|
|
77
|
+
os2Version: e.getUint16(i),
|
|
78
|
+
embedding: _(d, e.getUint16(i)),
|
|
79
|
+
byteLength: n.length
|
|
80
|
+
} };
|
|
81
|
+
}
|
|
82
|
+
async function W(n, e, o, c) {
|
|
83
|
+
const a = o.length - 1, g = Array.from({ length: a }, () => []);
|
|
84
|
+
for (let i = 0; i < a; i++) {
|
|
85
|
+
const u = c();
|
|
86
|
+
u && await u;
|
|
87
|
+
const r = e + o[i], t = e + o[i + 1];
|
|
88
|
+
if (r === t) continue;
|
|
89
|
+
let s = r + 10;
|
|
90
|
+
const l = (O) => {
|
|
91
|
+
s + O > t && F();
|
|
92
|
+
}, d = () => {
|
|
93
|
+
l(2);
|
|
94
|
+
const O = n.getUint16(s);
|
|
95
|
+
return s += 2, O;
|
|
96
|
+
}, y = () => (l(1), n.getUint8(s++)), A = n.getInt16(r);
|
|
97
|
+
if ((n.getInt16(r + 2) > n.getInt16(r + 6) || n.getInt16(r + 4) > n.getInt16(r + 8)) && F(), A >= 0) {
|
|
98
|
+
let O = -1;
|
|
99
|
+
for (let E = 0; E < A; E++) {
|
|
100
|
+
const C = d();
|
|
101
|
+
C <= O && F(), O = C;
|
|
102
|
+
}
|
|
103
|
+
if (!A && s === t) continue;
|
|
104
|
+
const m = d();
|
|
105
|
+
l(m), s += m;
|
|
106
|
+
const h = O + 1;
|
|
107
|
+
let L = 0;
|
|
108
|
+
for (let E = 0; E < h; ) {
|
|
109
|
+
const C = y(), f = C & 8 ? y() + 1 : 1;
|
|
110
|
+
(C & 128 || E + f > h) && F(), L += f * ((C & 2 ? 1 : C & 16 ? 0 : 2) + (C & 4 ? 1 : C & 32 ? 0 : 2)), E += f;
|
|
111
|
+
}
|
|
112
|
+
l(L);
|
|
113
|
+
} else {
|
|
114
|
+
let O = !0, m = !1;
|
|
115
|
+
for (; O; ) {
|
|
116
|
+
const h = d(), L = d();
|
|
117
|
+
(L >= a || h & 57360 || (h & 6144) === 6144 || !g[i].length && !(h & 2)) && F(), [8, 64, 128].filter((f) => h & f).length > 1 && F();
|
|
118
|
+
const C = (h & 1 ? 4 : 2) + (h & 8 ? 2 : h & 64 ? 4 : h & 128 ? 8 : 0);
|
|
119
|
+
l(C), s += C, g[i].push(L), g[i].length > 4096 && M(), m || (m = !!(h & 256)), O = !!(h & 32);
|
|
120
|
+
}
|
|
121
|
+
if (m) {
|
|
122
|
+
const h = d();
|
|
123
|
+
l(h);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
const w = new Uint8Array(a), k = new Uint8Array(a), b = new Uint32Array(a), U = (i, u) => {
|
|
128
|
+
if (w[i] === 1 && F(), u > 32 && M(), w[i] === 2) return;
|
|
129
|
+
w[i] = 1;
|
|
130
|
+
let r = 0, t = 1;
|
|
131
|
+
for (const s of g[i])
|
|
132
|
+
U(s, u + 1), r = Math.max(r, k[s] + 1), t += b[s], (r > 32 || t > 16384) && M();
|
|
133
|
+
k[i] = r, b[i] = t, w[i] = 2;
|
|
134
|
+
};
|
|
135
|
+
for (let i = 0; i < a; i++) {
|
|
136
|
+
const u = c();
|
|
137
|
+
u && await u, U(i, 0);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
async function X(n, e, o) {
|
|
141
|
+
const c = new DataView(n.buffer, n.byteOffset, n.byteLength), { tables: a, info: g } = e, w = a.get("head"), k = a.get("loca"), b = a.get("glyf"), U = a.get("hhea"), i = a.get("hmtx"), u = c.getInt16(w.offset + 50), r = u === 0 ? 2 : 4, t = c.getUint16(U.offset + 34);
|
|
142
|
+
if (![0, 1].includes(u) || k.length < (g.glyphCount + 1) * r || !t || t > g.glyphCount || i.length < t * 4 + (g.glyphCount - t) * 2)
|
|
143
|
+
throw new Error("invalid-font");
|
|
144
|
+
let s = 0;
|
|
145
|
+
const l = new Uint32Array(g.glyphCount + 1);
|
|
146
|
+
for (let d = 0; d <= g.glyphCount; d++) {
|
|
147
|
+
const y = r === 2 ? c.getUint16(k.offset + d * 2) * 2 : c.getUint32(k.offset + d * 4);
|
|
148
|
+
if (y < s || y > b.length || d > 0 && y !== s && y - s < 10)
|
|
149
|
+
throw new Error("invalid-font");
|
|
150
|
+
s = l[d] = y;
|
|
151
|
+
}
|
|
152
|
+
await W(c, b.offset, l, o);
|
|
153
|
+
}
|
|
154
|
+
async function Y(n, e, o) {
|
|
155
|
+
const c = e.tables.get("cmap"), a = new DataView(n.buffer, n.byteOffset + c.offset, c.length), g = a.getUint16(2), w = 4 + g * 8;
|
|
156
|
+
(a.getUint16(0) || !g || w > a.byteLength) && F(), g > 256 && M();
|
|
157
|
+
let k = !1, b = 2e6;
|
|
158
|
+
const U = /* @__PURE__ */ new Set(), i = (r) => {
|
|
159
|
+
r >= e.info.glyphCount && F();
|
|
160
|
+
}, u = (r) => {
|
|
161
|
+
b -= r, b < 0 && M();
|
|
162
|
+
};
|
|
163
|
+
for (let r = 0; r < g; r++) {
|
|
164
|
+
const t = o();
|
|
165
|
+
t && await t;
|
|
166
|
+
const s = 4 + r * 8, l = a.getUint16(s), d = a.getUint16(s + 2), y = a.getUint32(s + 4);
|
|
167
|
+
(y < w || y + 6 > a.byteLength) && F();
|
|
168
|
+
const A = a.getUint16(y);
|
|
169
|
+
if ((l === 0 && d !== 5 || l === 3 && [1, 10].includes(d)) && [4, 6, 10, 12, 13].includes(A) && (k = !0), ![0, 4, 6, 10, 12, 13, 14].includes(A)) throw new v("cmap-not-supported");
|
|
170
|
+
A >= 10 && A !== 14 && y + 12 > a.byteLength && F();
|
|
171
|
+
const O = A === 14 ? a.getUint32(y + 2) : A >= 10 ? a.getUint32(y + 4) : a.getUint16(y + 2);
|
|
172
|
+
(O < 6 || y + O > a.byteLength) && F();
|
|
173
|
+
const m = new DataView(n.buffer, n.byteOffset + c.offset + y, O), h = (L, E) => {
|
|
174
|
+
(L < 0 || L + E > O) && F();
|
|
175
|
+
};
|
|
176
|
+
if (l !== 1 && A !== 14 && (A >= 10 ? m.getUint32(8) : m.getUint16(4)) && F(), !U.has(y))
|
|
177
|
+
if (U.add(y), A === 0) {
|
|
178
|
+
h(6, 256), u(256);
|
|
179
|
+
for (let L = 0; L < 256; L++) i(m.getUint8(6 + L));
|
|
180
|
+
} else if (A === 4) {
|
|
181
|
+
h(0, 16);
|
|
182
|
+
const L = m.getUint16(6), E = L / 2, C = 16 + E * 8;
|
|
183
|
+
(!L || L % 2) && F(), h(0, C), m.getUint16(14 + E * 2) && F();
|
|
184
|
+
let f = -1;
|
|
185
|
+
for (let p = 0; p < E; p++) {
|
|
186
|
+
const x = o();
|
|
187
|
+
x && await x;
|
|
188
|
+
const S = m.getUint16(14 + p * 2), B = m.getUint16(16 + E * 2 + p * 2), I = m.getInt16(16 + E * 4 + p * 2), z = 16 + E * 6 + p * 2, P = m.getUint16(z);
|
|
189
|
+
(B > S || B <= f || P % 2 || p === E - 1 && (B !== 65535 || S !== 65535)) && F(), f = S, u(S - B + 1), P && z + P < C && F(), P && h(z + P, (S - B + 1) * 2);
|
|
190
|
+
for (let D = B; D <= S; D++) {
|
|
191
|
+
const V = P ? m.getUint16(z + P + (D - B) * 2) : D;
|
|
192
|
+
i(P && !V ? 0 : V + I & 65535);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
} else if (A === 6 || A === 10) {
|
|
196
|
+
const L = A === 10, E = L ? 20 : 10;
|
|
197
|
+
h(0, E);
|
|
198
|
+
const C = L ? m.getUint32(12) : m.getUint16(6), f = L ? m.getUint32(16) : m.getUint16(8);
|
|
199
|
+
(C + f > (L ? 1114112 : 65536) || L && m.getUint16(2)) && F(), h(E, f * 2), u(f);
|
|
200
|
+
for (let p = 0; p < f; p++) {
|
|
201
|
+
if (p % 256 === 0) {
|
|
202
|
+
const x = o();
|
|
203
|
+
x && await x;
|
|
204
|
+
}
|
|
205
|
+
i(m.getUint16(E + p * 2));
|
|
206
|
+
}
|
|
207
|
+
} else if (A === 12 || A === 13) {
|
|
208
|
+
h(0, 16);
|
|
209
|
+
const L = m.getUint32(12);
|
|
210
|
+
m.getUint16(2) && F(), h(16, L * 12), u(L);
|
|
211
|
+
let E = -1;
|
|
212
|
+
for (let C = 0; C < L; C++) {
|
|
213
|
+
if (C % 256 === 0) {
|
|
214
|
+
const B = o();
|
|
215
|
+
B && await B;
|
|
216
|
+
}
|
|
217
|
+
const f = 16 + C * 12, p = m.getUint32(f), x = m.getUint32(f + 4), S = m.getUint32(f + 8);
|
|
218
|
+
(p > x || p <= E || x > 1114111) && F(), i(S), i(S + (A === 12 ? x - p : 0)), E = x;
|
|
219
|
+
}
|
|
220
|
+
} else {
|
|
221
|
+
h(0, 10);
|
|
222
|
+
const L = m.getUint32(6), E = 10 + L * 11;
|
|
223
|
+
h(0, E), u(L);
|
|
224
|
+
const C = (p) => m.getUint16(p) * 256 + m.getUint8(p + 2);
|
|
225
|
+
let f = -1;
|
|
226
|
+
for (let p = 0; p < L; p++) {
|
|
227
|
+
const x = o();
|
|
228
|
+
x && await x;
|
|
229
|
+
const S = 10 + p * 11, B = C(S);
|
|
230
|
+
(B <= f || !(B >= 65024 && B <= 65039 || B >= 917760 && B <= 917999)) && F(), f = B;
|
|
231
|
+
for (const I of [0, 1]) {
|
|
232
|
+
const z = m.getUint32(S + 3 + I * 4);
|
|
233
|
+
if (!z) continue;
|
|
234
|
+
z < E && F(), h(z, 4);
|
|
235
|
+
const P = m.getUint32(z), D = I ? 5 : 4;
|
|
236
|
+
h(z + 4, P * D), u(P);
|
|
237
|
+
let V = -1;
|
|
238
|
+
for (let T = 0; T < P; T++) {
|
|
239
|
+
if (T % 256 === 0) {
|
|
240
|
+
const $ = o();
|
|
241
|
+
$ && await $;
|
|
242
|
+
}
|
|
243
|
+
const j = z + 4 + T * D, G = C(j), H = G + (I ? 0 : m.getUint8(j + 3));
|
|
244
|
+
(G <= V || H > 1114111 || G <= 57343 && H >= 55296) && F(), I && i(m.getUint16(j + 3)), V = H;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
if (!k) throw new v("cmap-not-supported");
|
|
251
|
+
}
|
|
252
|
+
function J(n, e) {
|
|
253
|
+
const o = n.codePointAt(0), c = { start: e, end: e + n.length };
|
|
254
|
+
if (o >= 55296 && o <= 57343 || /[\u0000-\u001f\u007f-\u009f]/u.test(n)) throw new v("invalid-text", void 0, c);
|
|
255
|
+
if (/[\u061c\u200e\u200f\u202a-\u202e\u2066-\u2069]/u.test(n)) throw new v("unsupported-direction", void 0, c);
|
|
256
|
+
if (new RegExp("\\p{Script=Latin}", "u").test(n)) return "Latn";
|
|
257
|
+
if (new RegExp("\\p{Script=Han}", "u").test(n)) return "Hani";
|
|
258
|
+
if (!/[\p{Script=Common}\p{Script=Inherited}]/u.test(n)) throw new v("unsupported-script", void 0, c);
|
|
259
|
+
}
|
|
260
|
+
function at(n, e) {
|
|
261
|
+
var c;
|
|
262
|
+
if (e.direction !== "ltr") return { ok: !1, reason: "unsupported-direction" };
|
|
263
|
+
if (n.length > 1e5) return { ok: !1, reason: "resource-limit" };
|
|
264
|
+
const o = [];
|
|
265
|
+
try {
|
|
266
|
+
let a = 0;
|
|
267
|
+
for (const U of n)
|
|
268
|
+
o.push({ start: a, script: J(U, a) }), a += U.length;
|
|
269
|
+
const g = [];
|
|
270
|
+
let w = 0, k = ((c = o.find((U) => U.script)) == null ? void 0 : c.script) ?? "Latn";
|
|
271
|
+
const b = (U) => {
|
|
272
|
+
U > w && g.push({ start: w, end: U, text: n.slice(w, U), script: k, ...e });
|
|
273
|
+
};
|
|
274
|
+
for (const U of o)
|
|
275
|
+
!U.script || U.script === k || (b(U.start), w = U.start, k = U.script);
|
|
276
|
+
return b(n.length), { ok: !0, value: g };
|
|
277
|
+
} catch (a) {
|
|
278
|
+
return a instanceof v ? { ok: !1, reason: a.reason, range: a.range } : { ok: !1, reason: "invalid-text" };
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
function q(n, e, o) {
|
|
282
|
+
if (e.direction !== "ltr") throw new v("unsupported-direction");
|
|
283
|
+
if (!["Latn", "Hani"].includes(e.script)) throw new v("unsupported-script");
|
|
284
|
+
if (n.length > o) throw new v("resource-limit");
|
|
285
|
+
if (typeof e.language != "string" || !/^[a-zA-Z]{2,8}(?:-[a-zA-Z0-9]{1,8})*$/.test(e.language))
|
|
286
|
+
throw new v("invalid-text");
|
|
287
|
+
let c = 0;
|
|
288
|
+
for (const a of n) {
|
|
289
|
+
const g = J(a, c);
|
|
290
|
+
if (g && g !== e.script) throw new v("script-mismatch", void 0, { start: c, end: c + a.length });
|
|
291
|
+
c += a.length;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
function tt(n, e, o, c) {
|
|
295
|
+
const a = () => {
|
|
296
|
+
throw new v("shaper-failed");
|
|
297
|
+
};
|
|
298
|
+
if (c.length > Math.max(256, e.length * 32)) throw new v("resource-limit");
|
|
299
|
+
(e.length && !c.length || !e.length && c.length) && a();
|
|
300
|
+
const g = [];
|
|
301
|
+
let w = -1, k = 0, b = 0;
|
|
302
|
+
const U = c.map((r, t) => {
|
|
303
|
+
const { id: s, cluster: l, xOffset: d, yOffset: y } = r;
|
|
304
|
+
if ((!Number.isInteger(s) || s < 0 || s >= n.glyphCount || !Number.isInteger(l) || l < w || l >= e.length || t === 0 && l !== 0 || l > 0 && /[\udc00-\udfff]/u.test(e[l]) || ![r.xAdvance, r.yAdvance, d, y].every((A) => Number.isFinite(A) && Math.abs(A) <= 1e9)) && a(), l !== w) {
|
|
305
|
+
const A = g[g.length - 1];
|
|
306
|
+
A && (A.end = l, A.text = e.slice(A.start, l), A.glyphEnd = t), g.push({ start: l, end: e.length, text: "", glyphStart: t, glyphEnd: c.length }), w = l;
|
|
307
|
+
}
|
|
308
|
+
return k += r.xAdvance, b += r.yAdvance, { id: s, xAdvance: r.xAdvance, yAdvance: r.yAdvance, xOffset: d, yOffset: y };
|
|
309
|
+
}), i = g[g.length - 1];
|
|
310
|
+
i && (i.text = e.slice(i.start));
|
|
311
|
+
const u = g.filter((r) => U.slice(r.glyphStart, r.glyphEnd).some((t) => t.id === 0)).map(({ start: r, end: t, text: s }) => ({ start: r, end: t, text: s }));
|
|
312
|
+
return u.length ? { ok: !1, reason: "missing-glyphs", faceId: n.id, missing: u } : { ok: !0, value: {
|
|
313
|
+
faceId: n.id,
|
|
314
|
+
text: e,
|
|
315
|
+
script: o.script,
|
|
316
|
+
direction: "ltr",
|
|
317
|
+
language: o.language,
|
|
318
|
+
unitsPerEm: n.unitsPerEm,
|
|
319
|
+
xAdvance: k,
|
|
320
|
+
yAdvance: b,
|
|
321
|
+
glyphs: U,
|
|
322
|
+
clusters: g
|
|
323
|
+
} };
|
|
324
|
+
}
|
|
325
|
+
function R(n, e) {
|
|
326
|
+
return new Promise((o, c) => {
|
|
327
|
+
const a = () => {
|
|
328
|
+
e.removeEventListener("abort", a), c(new v("aborted"));
|
|
329
|
+
};
|
|
330
|
+
e.aborted ? a() : e.addEventListener("abort", a, { once: !0 }), n.then(o, c).finally(() => e.removeEventListener("abort", a));
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
function et(n, e) {
|
|
334
|
+
const o = new AbortController(), c = /* @__PURE__ */ new Map(), a = /* @__PURE__ */ new Set(), g = /* @__PURE__ */ new Map();
|
|
335
|
+
let w, k;
|
|
336
|
+
const b = () => n.loadShaper ? w ?? (w = Promise.resolve().then(() => {
|
|
337
|
+
if (o.signal.aborted) throw new v("provider-disposed");
|
|
338
|
+
return n.loadShaper(o.signal);
|
|
339
|
+
}).then((i) => {
|
|
340
|
+
if (o.signal.aborted)
|
|
341
|
+
throw i.dispose(), new v("provider-disposed");
|
|
342
|
+
return k = i, i;
|
|
343
|
+
}).catch((i) => {
|
|
344
|
+
throw w = void 0, i;
|
|
345
|
+
})) : Promise.reject(new v("shaper-unavailable")), U = async (i, u, r, t) => {
|
|
346
|
+
var d, y, A, O, m;
|
|
347
|
+
if (o.signal.aborted) return { ok: !1, reason: "provider-disposed", faceId: i };
|
|
348
|
+
const s = new AbortController(), l = () => s.abort();
|
|
349
|
+
g.has(i) || g.set(i, /* @__PURE__ */ new Set()), g.get(i).add(s), o.signal.addEventListener("abort", l, { once: !0 }), (d = u.signal) == null || d.addEventListener("abort", l, { once: !0 }), (y = u.signal) != null && y.aborted && l();
|
|
350
|
+
try {
|
|
351
|
+
if (s.signal.aborted) throw new v("aborted");
|
|
352
|
+
const h = await e(i, u);
|
|
353
|
+
if (!h.ok) return h;
|
|
354
|
+
if (a.has(i)) throw new v("face-unavailable");
|
|
355
|
+
if (s.signal.aborted) throw new v("aborted");
|
|
356
|
+
r(h.value);
|
|
357
|
+
let L = c.get(i);
|
|
358
|
+
if (!L) {
|
|
359
|
+
const C = new AbortController();
|
|
360
|
+
let f;
|
|
361
|
+
f = { pending: R(b(), C.signal).then((x) => {
|
|
362
|
+
if (a.has(i)) throw new v("face-unavailable");
|
|
363
|
+
return x.open({ ...h.value, bytes: h.value.bytes.slice() }, C.signal);
|
|
364
|
+
}).then((x) => {
|
|
365
|
+
if (o.signal.aborted || a.has(i))
|
|
366
|
+
throw x.dispose(), new v(o.signal.aborted ? "provider-disposed" : "face-unavailable");
|
|
367
|
+
return f.face = x, x;
|
|
368
|
+
}).catch((x) => {
|
|
369
|
+
throw c.delete(i), x;
|
|
370
|
+
}), controller: C }, c.set(i, f), L = f;
|
|
371
|
+
}
|
|
372
|
+
const E = await R(L.pending, s.signal);
|
|
373
|
+
return await R(Promise.resolve().then(() => {
|
|
374
|
+
if (s.signal.aborted) throw new v("aborted");
|
|
375
|
+
return t(E, h.value, s.signal);
|
|
376
|
+
}), s.signal);
|
|
377
|
+
} catch (h) {
|
|
378
|
+
return {
|
|
379
|
+
ok: !1,
|
|
380
|
+
faceId: i,
|
|
381
|
+
reason: o.signal.aborted ? "provider-disposed" : a.has(i) ? "face-unavailable" : s.signal.aborted ? "aborted" : h instanceof v ? h.reason : "shaper-failed",
|
|
382
|
+
...h instanceof v && h.missing ? { missing: h.missing } : {},
|
|
383
|
+
...h instanceof v && h.range ? { range: h.range } : {}
|
|
384
|
+
};
|
|
385
|
+
} finally {
|
|
386
|
+
o.signal.removeEventListener("abort", l), (A = u.signal) == null || A.removeEventListener("abort", l), (O = g.get(i)) == null || O.delete(s), (m = g.get(i)) != null && m.size || g.delete(i);
|
|
387
|
+
}
|
|
388
|
+
};
|
|
389
|
+
return {
|
|
390
|
+
release(i) {
|
|
391
|
+
var r;
|
|
392
|
+
a.add(i);
|
|
393
|
+
const u = c.get(i);
|
|
394
|
+
c.delete(i), (r = u == null ? void 0 : u.face) == null || r.dispose(), u == null || u.controller.abort();
|
|
395
|
+
for (const t of g.get(i) ?? []) t.abort();
|
|
396
|
+
},
|
|
397
|
+
shape(i, u, r) {
|
|
398
|
+
return U(i, r, () => {
|
|
399
|
+
var t;
|
|
400
|
+
return q(u, r, ((t = n.limits) == null ? void 0 : t.maxTextLength) ?? 1e5);
|
|
401
|
+
}, async (t, s, l) => {
|
|
402
|
+
const d = u ? await t.shape(u, { ...r, signal: l }) : [];
|
|
403
|
+
return tt(s.info, u, r, d);
|
|
404
|
+
});
|
|
405
|
+
},
|
|
406
|
+
outline(i, u, r) {
|
|
407
|
+
return U(i, r, (t) => {
|
|
408
|
+
if (!Number.isInteger(u) || u < 0 || u >= t.info.glyphCount) throw new v("invalid-glyph-id");
|
|
409
|
+
}, async (t, s, l) => {
|
|
410
|
+
const d = await t.outline(u, { ...r, signal: l });
|
|
411
|
+
if (d.length > 4e6) throw new v("resource-limit");
|
|
412
|
+
if (!/^[MmLlHhVvCcSsQqTtAaZz\d\s.,+eE-]*$/.test(d)) throw new v("shaper-failed");
|
|
413
|
+
return { ok: !0, value: d };
|
|
414
|
+
});
|
|
415
|
+
},
|
|
416
|
+
dispose() {
|
|
417
|
+
if (!o.signal.aborted) {
|
|
418
|
+
k == null || k.dispose(), k = void 0, o.abort();
|
|
419
|
+
for (const i of c.values())
|
|
420
|
+
i.controller.abort(), i.pending.then((u) => u.dispose(), () => {
|
|
421
|
+
});
|
|
422
|
+
c.clear();
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
function nt(n = {}) {
|
|
428
|
+
const e = {
|
|
429
|
+
maxFontBytes: 33554432,
|
|
430
|
+
maxTotalFontBytes: 67108864,
|
|
431
|
+
maxFaces: 64,
|
|
432
|
+
maxGlyphs: 65535,
|
|
433
|
+
maxTextLength: 1e5,
|
|
434
|
+
...n
|
|
435
|
+
};
|
|
436
|
+
if (!Object.values(e).every((o) => Number.isSafeInteger(o) && o > 0)) throw new RangeError("Invalid font resource budget");
|
|
437
|
+
return e;
|
|
438
|
+
}
|
|
439
|
+
function it(n) {
|
|
440
|
+
let e = 0;
|
|
441
|
+
return () => {
|
|
442
|
+
if (n(), !(Date.now() < e))
|
|
443
|
+
return new Promise((o) => setTimeout(o, 0)).then(() => {
|
|
444
|
+
n(), e = Date.now() + 8;
|
|
445
|
+
});
|
|
446
|
+
};
|
|
447
|
+
}
|
|
448
|
+
function Z(n) {
|
|
449
|
+
if (n.length < 36 || n[34] !== 76 || n[35] !== 80) return !1;
|
|
450
|
+
const e = new DataView(n.buffer, n.byteOffset, n.byteLength).getUint32(0);
|
|
451
|
+
return ![65536, 1953658213, 1330926671, 1953784678, 2001684038, 2001684018].includes(e);
|
|
452
|
+
}
|
|
453
|
+
function ot(n) {
|
|
454
|
+
const e = new DataView(n.buffer, n.byteOffset, n.byteLength);
|
|
455
|
+
(n.length < 96 || !Z(n) || e.getUint32(0, !0) !== n.length) && F();
|
|
456
|
+
const o = e.getUint32(8, !0), c = e.getUint32(12, !0), a = e.getUint32(4, !0);
|
|
457
|
+
if (![65536, 131073, 131074].includes(o) || c & -268435702) throw new v("eot-feature-not-supported");
|
|
458
|
+
(!a || a > n.length - 96) && F();
|
|
459
|
+
const g = n.length - a;
|
|
460
|
+
let w = 80;
|
|
461
|
+
const k = (r) => {
|
|
462
|
+
w + r > g && F();
|
|
463
|
+
}, b = () => {
|
|
464
|
+
k(2);
|
|
465
|
+
const r = e.getUint16(w, !0);
|
|
466
|
+
return w += 2, r;
|
|
467
|
+
}, U = () => {
|
|
468
|
+
k(4);
|
|
469
|
+
const r = e.getUint32(w, !0);
|
|
470
|
+
return w += 4, r;
|
|
471
|
+
}, i = () => {
|
|
472
|
+
b() && F();
|
|
473
|
+
const r = b();
|
|
474
|
+
r % 2 && F(), k(r);
|
|
475
|
+
const t = new TextDecoder("utf-16le", { fatal: !0 }).decode(n.subarray(w, w + r));
|
|
476
|
+
return w += r, t;
|
|
477
|
+
};
|
|
478
|
+
for (let r = 64; r < 80; r += 4) e.getUint32(r, !0) && F();
|
|
479
|
+
for (let r = 0; r < 4; r++) i();
|
|
480
|
+
if (o !== 65536) {
|
|
481
|
+
if (i().replace(/\0/g, "")) throw new v("eot-root-restricted");
|
|
482
|
+
if (o === 131074) {
|
|
483
|
+
const t = U();
|
|
484
|
+
U(), t !== 1346851650 && F(), b() && F();
|
|
485
|
+
const s = b();
|
|
486
|
+
k(s), w += s;
|
|
487
|
+
const l = U(), d = U();
|
|
488
|
+
if (k(d), w += d, s || l || d) throw new v("eot-feature-not-supported");
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
if (w !== g && F(), c & 32) throw new v("eot-feature-not-supported");
|
|
492
|
+
const u = _(e.getUint16(32, !0), 2);
|
|
493
|
+
if (c & 1 && !u.subsetAllowed) throw new v("subset-not-permitted");
|
|
494
|
+
return { offset: g, size: a, flags: c, rights: u };
|
|
495
|
+
}
|
|
496
|
+
function rt(n, e) {
|
|
497
|
+
const o = n.slice(e.offset);
|
|
498
|
+
if (e.flags & 268435456) for (let c = 0; c < o.length; c++) o[c] ^= 80;
|
|
499
|
+
return o;
|
|
500
|
+
}
|
|
501
|
+
function lt(n = {}) {
|
|
502
|
+
let e = !1, o = 0;
|
|
503
|
+
const c = new AbortController(), a = n.decodeEot, g = nt(n.limits), w = /* @__PURE__ */ new Set(), k = /* @__PURE__ */ new Set(), b = /* @__PURE__ */ new Map(), U = (t) => ({ ...t, bbox: [...t.bbox], embedding: { ...t.embedding } }), i = (t, s) => {
|
|
504
|
+
var l;
|
|
505
|
+
return e ? { ok: !1, reason: "provider-disposed", faceId: s } : (l = t.signal) != null && l.aborted ? { ok: !1, reason: "aborted", faceId: s } : void 0;
|
|
506
|
+
}, u = async (t, s) => {
|
|
507
|
+
const l = i(s, t);
|
|
508
|
+
if (l) return l;
|
|
509
|
+
const d = b.get(t), y = d && N(d.info.embedding, s.purpose);
|
|
510
|
+
return y ? { ok: !1, reason: y, faceId: t } : d ? { ok: !0, value: { bytes: d.bytes, info: U(d.info) } } : { ok: !1, reason: "face-unavailable", faceId: t };
|
|
511
|
+
}, r = et({ ...n, limits: g }, u);
|
|
512
|
+
return {
|
|
513
|
+
async register(t, s) {
|
|
514
|
+
var L, E, C;
|
|
515
|
+
t = { ...t, restrictions: t.restrictions && { ...t.restrictions } }, s = { ...s };
|
|
516
|
+
const l = i(s, t.id);
|
|
517
|
+
if (l) return l;
|
|
518
|
+
const d = (f) => ({ ok: !1, reason: f, faceId: t.id });
|
|
519
|
+
if (!((L = t.bytes) != null && L.length)) return d("font-bytes-unavailable");
|
|
520
|
+
if (!t.id || t.id.length > 256) return d("invalid-font");
|
|
521
|
+
if (!["explicit", "embedded", "substitute"].includes(t.origin) || !["edit", "view-print"].includes(s.purpose) || t.family !== void 0 && (!t.family.trim() || t.family.length > 256 || /[\u0000-\u001f\u007f]/.test(t.family))) return d("invalid-request");
|
|
522
|
+
if (b.has(t.id) || w.has(t.id) || k.has(t.id)) return d("duplicate-face-id");
|
|
523
|
+
const y = t.bytes.length, A = y + (Z(t.bytes) ? g.maxFontBytes : 0), O = [...b.values()].reduce((f, p) => f + p.bytes.length, 0);
|
|
524
|
+
if (y > g.maxFontBytes || O + o + A > g.maxTotalFontBytes || b.size + w.size >= g.maxFaces) return d("resource-limit");
|
|
525
|
+
w.add(t.id), o += A;
|
|
526
|
+
const m = new AbortController(), h = () => m.abort();
|
|
527
|
+
c.signal.addEventListener("abort", h, { once: !0 }), (E = s.signal) == null || E.addEventListener("abort", h, { once: !0 });
|
|
528
|
+
try {
|
|
529
|
+
let f = new Uint8Array(t.bytes);
|
|
530
|
+
const p = it(() => {
|
|
531
|
+
const P = i(s);
|
|
532
|
+
if (P) throw new v(P.reason);
|
|
533
|
+
});
|
|
534
|
+
await p();
|
|
535
|
+
const x = Z(f) ? ot(f) : void 0;
|
|
536
|
+
if (x) {
|
|
537
|
+
const P = N(x.rights, s.purpose);
|
|
538
|
+
if (P) return d(P);
|
|
539
|
+
if (x.flags & 4) {
|
|
540
|
+
if (!a) return d("decoder-unavailable");
|
|
541
|
+
let D;
|
|
542
|
+
try {
|
|
543
|
+
D = await R(Promise.resolve().then(() => {
|
|
544
|
+
if (m.signal.aborted) throw new v("aborted");
|
|
545
|
+
return a(f, { signal: m.signal, maxOutputBytes: g.maxFontBytes });
|
|
546
|
+
}), m.signal);
|
|
547
|
+
} catch (V) {
|
|
548
|
+
return d(e ? "provider-disposed" : m.signal.aborted ? "aborted" : V instanceof v ? V.reason : "decode-failed");
|
|
549
|
+
}
|
|
550
|
+
if (!(D != null && D.length)) return d("decode-failed");
|
|
551
|
+
if (D.length > g.maxFontBytes) return d("resource-limit");
|
|
552
|
+
f = new Uint8Array(D);
|
|
553
|
+
} else f = rt(f, x);
|
|
554
|
+
}
|
|
555
|
+
const S = K(f);
|
|
556
|
+
if (S.info.embedding = Q(Q(S.info.embedding, x == null ? void 0 : x.rights), t.restrictions), x && x.flags & 1 && !S.info.embedding.subsetAllowed) return d("subset-not-permitted");
|
|
557
|
+
if (S.info.glyphCount > g.maxGlyphs) return d("resource-limit");
|
|
558
|
+
const B = N(S.info.embedding, s.purpose);
|
|
559
|
+
if (B) return d(B);
|
|
560
|
+
await X(f, S, p), await Y(f, S, p);
|
|
561
|
+
const I = i(s, t.id);
|
|
562
|
+
if (I) return I;
|
|
563
|
+
const z = {
|
|
564
|
+
...S.info,
|
|
565
|
+
id: t.id,
|
|
566
|
+
family: t.family ?? S.info.sourceFamily,
|
|
567
|
+
origin: t.origin,
|
|
568
|
+
sourceLabel: t.sourceLabel,
|
|
569
|
+
embeddingEvidence: t.embeddingEvidence
|
|
570
|
+
};
|
|
571
|
+
return b.set(t.id, { info: z, bytes: f }), { ok: !0, value: U(z) };
|
|
572
|
+
} catch (f) {
|
|
573
|
+
return d(f instanceof v ? f.reason : "invalid-font");
|
|
574
|
+
} finally {
|
|
575
|
+
w.delete(t.id), o -= A, c.signal.removeEventListener("abort", h), (C = s.signal) == null || C.removeEventListener("abort", h);
|
|
576
|
+
}
|
|
577
|
+
},
|
|
578
|
+
async resolve(t) {
|
|
579
|
+
const s = i(t);
|
|
580
|
+
if (s) return s;
|
|
581
|
+
const l = [...b.values()].filter(({ info: y }) => y.family.toLowerCase() === t.family.toLowerCase() && y.weight === t.weight && y.italic === t.italic), d = l.length === 1 ? N(l[0].info.embedding, t.purpose) : void 0;
|
|
582
|
+
return d ? { ok: !1, reason: d, faceId: l[0].info.id } : l.length === 1 ? { ok: !0, value: U(l[0].info) } : { ok: !1, reason: l.length ? "ambiguous-face" : "face-unavailable" };
|
|
583
|
+
},
|
|
584
|
+
async embedding(t, s) {
|
|
585
|
+
const l = await u(t, s);
|
|
586
|
+
return l.ok ? { ok: !0, value: { info: l.value.info, bytes: l.value.bytes.slice() } } : l;
|
|
587
|
+
},
|
|
588
|
+
async faceInfo(t, s) {
|
|
589
|
+
const l = await u(t, s);
|
|
590
|
+
return l.ok ? { ok: !0, value: l.value.info } : l;
|
|
591
|
+
},
|
|
592
|
+
shape: r.shape,
|
|
593
|
+
outline: r.outline,
|
|
594
|
+
/** 释放已注册 face;同一文稿内身份不可复用,避免迟到请求混用新旧字节。 */
|
|
595
|
+
release(t) {
|
|
596
|
+
return b.delete(t) ? (k.add(t), r.release(t), !0) : !1;
|
|
597
|
+
},
|
|
598
|
+
state() {
|
|
599
|
+
return {
|
|
600
|
+
disposed: e,
|
|
601
|
+
retainedFontBytes: [...b.values()].reduce((t, s) => t + s.bytes.length, 0),
|
|
602
|
+
reservedFontBytes: o,
|
|
603
|
+
pendingRegistrations: w.size,
|
|
604
|
+
faces: b.size
|
|
605
|
+
};
|
|
606
|
+
},
|
|
607
|
+
dispose() {
|
|
608
|
+
e = !0, r.dispose(), c.abort(), b.clear(), k.clear();
|
|
609
|
+
}
|
|
610
|
+
};
|
|
611
|
+
}
|
|
612
|
+
export {
|
|
613
|
+
lt as c,
|
|
614
|
+
nt as f,
|
|
615
|
+
tt as m,
|
|
616
|
+
at as s
|
|
617
|
+
};
|
package/dist/substitute.d.ts
CHANGED
|
@@ -17,6 +17,13 @@
|
|
|
17
17
|
export interface Substitution {
|
|
18
18
|
/** 替代字体的家族名 */
|
|
19
19
|
family: string;
|
|
20
|
+
/** 中日韩字体的西文替代家族;其字形声明须在 CJK 声明之后注册。 */
|
|
21
|
+
latinFamily?: string;
|
|
22
|
+
/** 西文替代家族的实测 size-adjust(百分比):粗体整体与下划线单独缩放,用来对上原字体的前进宽度。 */
|
|
23
|
+
latinSizeAdjust?: {
|
|
24
|
+
readonly bold: number;
|
|
25
|
+
readonly underscore: number;
|
|
26
|
+
};
|
|
20
27
|
/**
|
|
21
28
|
* 前进宽度与原字体逐字相等。为真时断行与 PowerPoint 完全一致,
|
|
22
29
|
* 为假时只是形近,行高仍可能要靠 `size-adjust` 修。
|