pdfnative 1.2.0 → 1.3.0
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/README.md +41 -14
- package/dist/index.cjs +2753 -442
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +603 -29
- package/dist/index.d.ts +603 -29
- package/dist/index.js +2727 -443
- package/dist/index.js.map +1 -1
- package/dist/worker/index.cjs +2003 -121
- package/dist/worker/index.cjs.map +1 -1
- package/dist/worker/index.js +2003 -121
- package/dist/worker/index.js.map +1 -1
- package/fonts/noto-color-emoji-data.d.ts +28 -0
- package/fonts/noto-color-emoji-data.js +26 -0
- package/fonts/noto-ethiopic-data.d.ts +13 -0
- package/fonts/noto-ethiopic-data.js +64 -0
- package/fonts/noto-khmer-data.d.ts +13 -0
- package/fonts/noto-khmer-data.js +64 -0
- package/fonts/noto-myanmar-data.d.ts +13 -0
- package/fonts/noto-myanmar-data.js +64 -0
- package/fonts/noto-sinhala-data.d.ts +13 -0
- package/fonts/noto-sinhala-data.js +64 -0
- package/fonts/noto-telugu-data.d.ts +13 -0
- package/fonts/noto-telugu-data.js +64 -0
- package/fonts/noto-tibetan-data.d.ts +13 -0
- package/fonts/noto-tibetan-data.js +64 -0
- package/package.json +22 -5
package/dist/index.cjs
CHANGED
|
@@ -121,8 +121,8 @@ function aesECB(data, key) {
|
|
|
121
121
|
aesEncryptBlock(block, expandedKey, nr);
|
|
122
122
|
return block;
|
|
123
123
|
}
|
|
124
|
-
function rotl32(x,
|
|
125
|
-
return (x <<
|
|
124
|
+
function rotl32(x, n2) {
|
|
125
|
+
return (x << n2 | x >>> 32 - n2) >>> 0;
|
|
126
126
|
}
|
|
127
127
|
function md5(input) {
|
|
128
128
|
const len = input.length;
|
|
@@ -179,8 +179,8 @@ function md5(input) {
|
|
|
179
179
|
rv.setUint32(12, d0, true);
|
|
180
180
|
return result;
|
|
181
181
|
}
|
|
182
|
-
function rotr32(x,
|
|
183
|
-
return (x >>>
|
|
182
|
+
function rotr32(x, n2) {
|
|
183
|
+
return (x >>> n2 | x << 32 - n2) >>> 0;
|
|
184
184
|
}
|
|
185
185
|
function sha256(input) {
|
|
186
186
|
const len = input.length;
|
|
@@ -212,8 +212,8 @@ function sha256(input) {
|
|
|
212
212
|
let a = h0, b = h1, c = h2, d = h3, e = h4, f = h5, g = h6, h = h7;
|
|
213
213
|
for (let j = 0; j < 64; j++) {
|
|
214
214
|
const S1 = rotr32(e, 6) ^ rotr32(e, 11) ^ rotr32(e, 25);
|
|
215
|
-
const
|
|
216
|
-
const temp1 = h + S1 +
|
|
215
|
+
const ch2 = e & f ^ ~e & g;
|
|
216
|
+
const temp1 = h + S1 + ch2 + SHA256_K[j] + W[j] >>> 0;
|
|
217
217
|
const S0 = rotr32(a, 2) ^ rotr32(a, 13) ^ rotr32(a, 22);
|
|
218
218
|
const maj = a & b ^ a & c ^ b & c;
|
|
219
219
|
const temp2 = S0 + maj >>> 0;
|
|
@@ -275,16 +275,17 @@ function computePermissions(perms) {
|
|
|
275
275
|
function fillRandom(buf) {
|
|
276
276
|
if (typeof globalThis.crypto !== "undefined" && typeof globalThis.crypto.getRandomValues === "function") {
|
|
277
277
|
globalThis.crypto.getRandomValues(buf);
|
|
278
|
-
|
|
279
|
-
for (let i = 0; i < buf.length; i++) buf[i] = Math.floor(Math.random() * 256);
|
|
278
|
+
return buf;
|
|
280
279
|
}
|
|
281
|
-
|
|
280
|
+
throw new Error(
|
|
281
|
+
"pdfnative: PDF encryption requires a cryptographically secure random source (Web Crypto `crypto.getRandomValues`), which is unavailable in this environment. Refusing to generate predictable IVs/document IDs. On older runtimes, install a Web Crypto polyfill (e.g. `react-native-get-random-values`) before encrypting."
|
|
282
|
+
);
|
|
282
283
|
}
|
|
283
284
|
function generateDocId() {
|
|
284
285
|
return fillRandom(new Uint8Array(16));
|
|
285
286
|
}
|
|
286
|
-
function randomBytes(
|
|
287
|
-
return fillRandom(new Uint8Array(
|
|
287
|
+
function randomBytes(n2) {
|
|
288
|
+
return fillRandom(new Uint8Array(n2));
|
|
288
289
|
}
|
|
289
290
|
function computeKeyR4(userPwd, oValue, pValue, docId) {
|
|
290
291
|
const buf = new Uint8Array(userPwd.length + oValue.length + 4 + docId.length);
|
|
@@ -1051,18 +1052,18 @@ function add64(ah, al, bh, bl) {
|
|
|
1051
1052
|
const hi = ah + bh + (lo < al ? 1 : 0) >>> 0;
|
|
1052
1053
|
return [hi, lo];
|
|
1053
1054
|
}
|
|
1054
|
-
function rotr64(h, l,
|
|
1055
|
-
if (
|
|
1056
|
-
return [(h >>>
|
|
1055
|
+
function rotr64(h, l, n2) {
|
|
1056
|
+
if (n2 < 32) {
|
|
1057
|
+
return [(h >>> n2 | l << 32 - n2) >>> 0, (l >>> n2 | h << 32 - n2) >>> 0];
|
|
1057
1058
|
}
|
|
1058
|
-
const m =
|
|
1059
|
+
const m = n2 - 32;
|
|
1059
1060
|
return [(l >>> m | h << 32 - m) >>> 0, (h >>> m | l << 32 - m) >>> 0];
|
|
1060
1061
|
}
|
|
1061
|
-
function shr64(h, l,
|
|
1062
|
-
if (
|
|
1063
|
-
return [h >>>
|
|
1062
|
+
function shr64(h, l, n2) {
|
|
1063
|
+
if (n2 < 32) {
|
|
1064
|
+
return [h >>> n2 >>> 0, (l >>> n2 | h << 32 - n2) >>> 0];
|
|
1064
1065
|
}
|
|
1065
|
-
return [0, h >>>
|
|
1066
|
+
return [0, h >>> n2 - 32 >>> 0];
|
|
1066
1067
|
}
|
|
1067
1068
|
function sha512Core(input, iv, outputLen) {
|
|
1068
1069
|
const len = input.length;
|
|
@@ -1108,7 +1109,7 @@ function sha512Core(input, iv, outputLen) {
|
|
|
1108
1109
|
}
|
|
1109
1110
|
let ah = h0h, al = h0l;
|
|
1110
1111
|
let bh = h1h, bl = h1l;
|
|
1111
|
-
let
|
|
1112
|
+
let ch2 = h2h, cl = h2l;
|
|
1112
1113
|
let dh = h3h, dl = h3l;
|
|
1113
1114
|
let eh = h4h, el = h4l;
|
|
1114
1115
|
let fh = h5h, fl = h5l;
|
|
@@ -1131,7 +1132,7 @@ function sha512Core(input, iv, outputLen) {
|
|
|
1131
1132
|
const [a39h, a39l] = rotr64(ah, al, 39);
|
|
1132
1133
|
const S0h = (a28h ^ a34h ^ a39h) >>> 0;
|
|
1133
1134
|
const S0l = (a28l ^ a34l ^ a39l) >>> 0;
|
|
1134
|
-
const majH = (ah & bh ^ ah &
|
|
1135
|
+
const majH = (ah & bh ^ ah & ch2 ^ bh & ch2) >>> 0;
|
|
1135
1136
|
const majL = (al & bl ^ al & cl ^ bl & cl) >>> 0;
|
|
1136
1137
|
const [t2h, t2l] = add64(S0h, S0l, majH, majL);
|
|
1137
1138
|
hh = gh;
|
|
@@ -1141,9 +1142,9 @@ function sha512Core(input, iv, outputLen) {
|
|
|
1141
1142
|
fh = eh;
|
|
1142
1143
|
fl = el;
|
|
1143
1144
|
[eh, el] = add64(dh, dl, t1h, t1l);
|
|
1144
|
-
dh =
|
|
1145
|
+
dh = ch2;
|
|
1145
1146
|
dl = cl;
|
|
1146
|
-
|
|
1147
|
+
ch2 = bh;
|
|
1147
1148
|
cl = bl;
|
|
1148
1149
|
bh = ah;
|
|
1149
1150
|
bl = al;
|
|
@@ -1151,7 +1152,7 @@ function sha512Core(input, iv, outputLen) {
|
|
|
1151
1152
|
}
|
|
1152
1153
|
[h0h, h0l] = add64(h0h, h0l, ah, al);
|
|
1153
1154
|
[h1h, h1l] = add64(h1h, h1l, bh, bl);
|
|
1154
|
-
[h2h, h2l] = add64(h2h, h2l,
|
|
1155
|
+
[h2h, h2l] = add64(h2h, h2l, ch2, cl);
|
|
1155
1156
|
[h3h, h3l] = add64(h3h, h3l, dh, dl);
|
|
1156
1157
|
[h4h, h4l] = add64(h4h, h4l, eh, el);
|
|
1157
1158
|
[h5h, h5l] = add64(h5h, h5l, fh, fl);
|
|
@@ -1766,8 +1767,8 @@ function bigIntToBytes(value, length) {
|
|
|
1766
1767
|
}
|
|
1767
1768
|
return result;
|
|
1768
1769
|
}
|
|
1769
|
-
function keyByteLen(
|
|
1770
|
-
const hex =
|
|
1770
|
+
function keyByteLen(n2) {
|
|
1771
|
+
const hex = n2.toString(16);
|
|
1771
1772
|
return Math.ceil(hex.length / 2);
|
|
1772
1773
|
}
|
|
1773
1774
|
function rsaSign(message, privateKey) {
|
|
@@ -2291,6 +2292,33 @@ var BENGALI_START = 2432;
|
|
|
2291
2292
|
var BENGALI_END = 2559;
|
|
2292
2293
|
var TAMIL_START = 2944;
|
|
2293
2294
|
var TAMIL_END = 3071;
|
|
2295
|
+
var TELUGU_START = 3072;
|
|
2296
|
+
var TELUGU_END = 3199;
|
|
2297
|
+
var ETHIOPIC_START = 4608;
|
|
2298
|
+
var ETHIOPIC_END = 4991;
|
|
2299
|
+
var ETHIOPIC_SUPPLEMENT_START = 4992;
|
|
2300
|
+
var ETHIOPIC_SUPPLEMENT_END = 5023;
|
|
2301
|
+
var ETHIOPIC_EXTENDED_START = 11648;
|
|
2302
|
+
var ETHIOPIC_EXTENDED_END = 11743;
|
|
2303
|
+
var ETHIOPIC_EXTENDED_A_START = 43776;
|
|
2304
|
+
var ETHIOPIC_EXTENDED_A_END = 43823;
|
|
2305
|
+
var SINHALA_START = 3456;
|
|
2306
|
+
var SINHALA_END = 3583;
|
|
2307
|
+
var SINHALA_VIRAMA = 3530;
|
|
2308
|
+
var TIBETAN_START = 3840;
|
|
2309
|
+
var TIBETAN_END = 4095;
|
|
2310
|
+
var KHMER_START = 6016;
|
|
2311
|
+
var KHMER_END = 6143;
|
|
2312
|
+
var KHMER_SYMBOLS_START = 6624;
|
|
2313
|
+
var KHMER_SYMBOLS_END = 6655;
|
|
2314
|
+
var KHMER_COENG = 6098;
|
|
2315
|
+
var MYANMAR_START = 4096;
|
|
2316
|
+
var MYANMAR_END = 4255;
|
|
2317
|
+
var MYANMAR_EXTENDED_A_START = 43616;
|
|
2318
|
+
var MYANMAR_EXTENDED_A_END = 43647;
|
|
2319
|
+
var MYANMAR_EXTENDED_B_START = 43488;
|
|
2320
|
+
var MYANMAR_EXTENDED_B_END = 43519;
|
|
2321
|
+
var MYANMAR_VIRAMA = 4153;
|
|
2294
2322
|
var EMOJI_RANGES = [
|
|
2295
2323
|
[127744, 128511],
|
|
2296
2324
|
// Miscellaneous Symbols and Pictographs
|
|
@@ -2321,6 +2349,9 @@ var EMOJI_RANGES = [
|
|
|
2321
2349
|
];
|
|
2322
2350
|
var FITZPATRICK_START = 127995;
|
|
2323
2351
|
var FITZPATRICK_END = 127999;
|
|
2352
|
+
var ZWJ = 8205;
|
|
2353
|
+
var VS15 = 65038;
|
|
2354
|
+
var VS16 = 65039;
|
|
2324
2355
|
function isArabicCodepoint(cp) {
|
|
2325
2356
|
return cp >= ARABIC_START && cp <= ARABIC_END || cp >= ARABIC_SUPPLEMENT_START && cp <= ARABIC_SUPPLEMENT_END || cp >= ARABIC_EXTENDED_A_START && cp <= ARABIC_EXTENDED_A_END || cp >= ARABIC_PRES_A_START && cp <= ARABIC_PRES_A_END || cp >= ARABIC_PRES_B_START && cp <= ARABIC_PRES_B_END;
|
|
2326
2357
|
}
|
|
@@ -2345,6 +2376,24 @@ function isBengaliCodepoint(cp) {
|
|
|
2345
2376
|
function isTamilCodepoint(cp) {
|
|
2346
2377
|
return cp >= TAMIL_START && cp <= TAMIL_END;
|
|
2347
2378
|
}
|
|
2379
|
+
function isTeluguCodepoint(cp) {
|
|
2380
|
+
return cp >= TELUGU_START && cp <= TELUGU_END;
|
|
2381
|
+
}
|
|
2382
|
+
function isEthiopicCodepoint(cp) {
|
|
2383
|
+
return cp >= ETHIOPIC_START && cp <= ETHIOPIC_END || cp >= ETHIOPIC_SUPPLEMENT_START && cp <= ETHIOPIC_SUPPLEMENT_END || cp >= ETHIOPIC_EXTENDED_START && cp <= ETHIOPIC_EXTENDED_END || cp >= ETHIOPIC_EXTENDED_A_START && cp <= ETHIOPIC_EXTENDED_A_END;
|
|
2384
|
+
}
|
|
2385
|
+
function isSinhalaCodepoint(cp) {
|
|
2386
|
+
return cp >= SINHALA_START && cp <= SINHALA_END;
|
|
2387
|
+
}
|
|
2388
|
+
function isTibetanCodepoint(cp) {
|
|
2389
|
+
return cp >= TIBETAN_START && cp <= TIBETAN_END;
|
|
2390
|
+
}
|
|
2391
|
+
function isKhmerCodepoint(cp) {
|
|
2392
|
+
return cp >= KHMER_START && cp <= KHMER_END || cp >= KHMER_SYMBOLS_START && cp <= KHMER_SYMBOLS_END;
|
|
2393
|
+
}
|
|
2394
|
+
function isMyanmarCodepoint(cp) {
|
|
2395
|
+
return cp >= MYANMAR_START && cp <= MYANMAR_END || cp >= MYANMAR_EXTENDED_A_START && cp <= MYANMAR_EXTENDED_A_END || cp >= MYANMAR_EXTENDED_B_START && cp <= MYANMAR_EXTENDED_B_END;
|
|
2396
|
+
}
|
|
2348
2397
|
function isDevanagariCodepoint(cp) {
|
|
2349
2398
|
return cp >= DEVANAGARI_START && cp <= DEVANAGARI_END || cp >= DEVANAGARI_EXT_START && cp <= DEVANAGARI_EXT_END;
|
|
2350
2399
|
}
|
|
@@ -2355,6 +2404,9 @@ function isEmojiCodepoint(cp) {
|
|
|
2355
2404
|
}
|
|
2356
2405
|
return false;
|
|
2357
2406
|
}
|
|
2407
|
+
function isZeroWidthFormat(cp) {
|
|
2408
|
+
return cp === ZWJ || cp === 8204 || cp === VS15 || cp === VS16 || cp >= FITZPATRICK_START && cp <= FITZPATRICK_END;
|
|
2409
|
+
}
|
|
2358
2410
|
function containsArabic(text) {
|
|
2359
2411
|
for (let i = 0; i < text.length; ) {
|
|
2360
2412
|
const cp = text.codePointAt(i) ?? 0;
|
|
@@ -2389,6 +2441,42 @@ function containsTamil(str) {
|
|
|
2389
2441
|
}
|
|
2390
2442
|
return false;
|
|
2391
2443
|
}
|
|
2444
|
+
function containsTelugu(str) {
|
|
2445
|
+
for (let i = 0; i < str.length; i++) {
|
|
2446
|
+
if (isTeluguCodepoint(str.charCodeAt(i))) return true;
|
|
2447
|
+
}
|
|
2448
|
+
return false;
|
|
2449
|
+
}
|
|
2450
|
+
function containsEthiopic(str) {
|
|
2451
|
+
for (let i = 0; i < str.length; i++) {
|
|
2452
|
+
if (isEthiopicCodepoint(str.charCodeAt(i))) return true;
|
|
2453
|
+
}
|
|
2454
|
+
return false;
|
|
2455
|
+
}
|
|
2456
|
+
function containsSinhala(str) {
|
|
2457
|
+
for (let i = 0; i < str.length; i++) {
|
|
2458
|
+
if (isSinhalaCodepoint(str.charCodeAt(i))) return true;
|
|
2459
|
+
}
|
|
2460
|
+
return false;
|
|
2461
|
+
}
|
|
2462
|
+
function containsTibetan(str) {
|
|
2463
|
+
for (let i = 0; i < str.length; i++) {
|
|
2464
|
+
if (isTibetanCodepoint(str.charCodeAt(i))) return true;
|
|
2465
|
+
}
|
|
2466
|
+
return false;
|
|
2467
|
+
}
|
|
2468
|
+
function containsKhmer(str) {
|
|
2469
|
+
for (let i = 0; i < str.length; i++) {
|
|
2470
|
+
if (isKhmerCodepoint(str.charCodeAt(i))) return true;
|
|
2471
|
+
}
|
|
2472
|
+
return false;
|
|
2473
|
+
}
|
|
2474
|
+
function containsMyanmar(str) {
|
|
2475
|
+
for (let i = 0; i < str.length; i++) {
|
|
2476
|
+
if (isMyanmarCodepoint(str.charCodeAt(i))) return true;
|
|
2477
|
+
}
|
|
2478
|
+
return false;
|
|
2479
|
+
}
|
|
2392
2480
|
function containsDevanagari(str) {
|
|
2393
2481
|
for (let i = 0; i < str.length; i++) {
|
|
2394
2482
|
if (isDevanagariCodepoint(str.charCodeAt(i))) return true;
|
|
@@ -2398,7 +2486,7 @@ function containsDevanagari(str) {
|
|
|
2398
2486
|
|
|
2399
2487
|
// src/shaping/script-detect.ts
|
|
2400
2488
|
function needsUnicodeFont(lang) {
|
|
2401
|
-
return ["th", "ja", "zh", "ko", "el", "hi", "tr", "vi", "pl", "ar", "he", "ru", "ka", "hy", "emoji"].includes(lang);
|
|
2489
|
+
return ["th", "ja", "zh", "ko", "el", "hi", "te", "tr", "vi", "pl", "ar", "he", "ru", "ka", "hy", "am", "si", "bo", "km", "my", "emoji"].includes(lang);
|
|
2402
2490
|
}
|
|
2403
2491
|
function detectFallbackLangs(texts, primaryLang) {
|
|
2404
2492
|
const needed = /* @__PURE__ */ new Set();
|
|
@@ -2415,6 +2503,30 @@ function detectFallbackLangs(texts, primaryLang) {
|
|
|
2415
2503
|
needed.add("hi");
|
|
2416
2504
|
continue;
|
|
2417
2505
|
}
|
|
2506
|
+
if (cp >= 3072 && cp <= 3199) {
|
|
2507
|
+
needed.add("te");
|
|
2508
|
+
continue;
|
|
2509
|
+
}
|
|
2510
|
+
if (isSinhalaCodepoint(cp)) {
|
|
2511
|
+
needed.add("si");
|
|
2512
|
+
continue;
|
|
2513
|
+
}
|
|
2514
|
+
if (isTibetanCodepoint(cp)) {
|
|
2515
|
+
needed.add("bo");
|
|
2516
|
+
continue;
|
|
2517
|
+
}
|
|
2518
|
+
if (isKhmerCodepoint(cp)) {
|
|
2519
|
+
needed.add("km");
|
|
2520
|
+
continue;
|
|
2521
|
+
}
|
|
2522
|
+
if (isMyanmarCodepoint(cp)) {
|
|
2523
|
+
needed.add("my");
|
|
2524
|
+
continue;
|
|
2525
|
+
}
|
|
2526
|
+
if (isEthiopicCodepoint(cp)) {
|
|
2527
|
+
needed.add("am");
|
|
2528
|
+
continue;
|
|
2529
|
+
}
|
|
2418
2530
|
if (cp >= 3584 && cp <= 3711) {
|
|
2419
2531
|
needed.add("th");
|
|
2420
2532
|
continue;
|
|
@@ -2487,6 +2599,12 @@ function detectFallbackLangs(texts, primaryLang) {
|
|
|
2487
2599
|
function detectCharLang(cp) {
|
|
2488
2600
|
if (cp >= 880 && cp <= 1023 || cp >= 7936 && cp <= 8191) return "el";
|
|
2489
2601
|
if (cp >= 2304 && cp <= 2431 || cp >= 43232 && cp <= 43263) return "hi";
|
|
2602
|
+
if (cp >= 3072 && cp <= 3199) return "te";
|
|
2603
|
+
if (isSinhalaCodepoint(cp)) return "si";
|
|
2604
|
+
if (isTibetanCodepoint(cp)) return "bo";
|
|
2605
|
+
if (isKhmerCodepoint(cp)) return "km";
|
|
2606
|
+
if (isMyanmarCodepoint(cp)) return "my";
|
|
2607
|
+
if (isEthiopicCodepoint(cp)) return "am";
|
|
2490
2608
|
if (cp >= 3584 && cp <= 3711) return "th";
|
|
2491
2609
|
if (cp >= 12352 && cp <= 12543) return "ja";
|
|
2492
2610
|
if (cp >= 44032 && cp <= 55215 || cp >= 4352 && cp <= 4607 || cp >= 12592 && cp <= 12687) return "ko";
|
|
@@ -2520,6 +2638,10 @@ function splitTextByFont(str, fontEntries) {
|
|
|
2520
2638
|
i += charLen;
|
|
2521
2639
|
continue;
|
|
2522
2640
|
}
|
|
2641
|
+
if (isZeroWidthFormat(normCp) && !fontEntries.some((fe) => fe.fontData.cmap[normCp])) {
|
|
2642
|
+
i += charLen;
|
|
2643
|
+
continue;
|
|
2644
|
+
}
|
|
2523
2645
|
let newEntry = null;
|
|
2524
2646
|
const charLang = detectCharLang(normCp);
|
|
2525
2647
|
if (charLang) {
|
|
@@ -2824,18 +2946,26 @@ function normalizeBidiEmbeddings(text) {
|
|
|
2824
2946
|
for (let i = 0; i < text.length; ) {
|
|
2825
2947
|
const cp = text.codePointAt(i) ?? 0;
|
|
2826
2948
|
const cpLen = cp > 65535 ? 2 : 1;
|
|
2827
|
-
if (cp === LRE || cp ===
|
|
2949
|
+
if (cp === LRE || cp === RLE) {
|
|
2828
2950
|
if (stack.length >= MAX_DEPTH) {
|
|
2829
2951
|
i += cpLen;
|
|
2830
2952
|
continue;
|
|
2831
2953
|
}
|
|
2832
|
-
stack.push(
|
|
2833
|
-
out.push(cp === LRE
|
|
2954
|
+
stack.push("E");
|
|
2955
|
+
out.push(cp === LRE ? LRI : RLI);
|
|
2834
2956
|
i += cpLen;
|
|
2835
|
-
} else if (cp ===
|
|
2836
|
-
if (stack.
|
|
2837
|
-
|
|
2957
|
+
} else if (cp === LRO || cp === RLO) {
|
|
2958
|
+
if (stack.length >= MAX_DEPTH) {
|
|
2959
|
+
i += cpLen;
|
|
2960
|
+
continue;
|
|
2838
2961
|
}
|
|
2962
|
+
stack.push("O");
|
|
2963
|
+
out.push(cp);
|
|
2964
|
+
i += cpLen;
|
|
2965
|
+
} else if (cp === PDF_CP) {
|
|
2966
|
+
const frame = stack.pop();
|
|
2967
|
+
if (frame === "E") out.push(PDI);
|
|
2968
|
+
else if (frame === "O") out.push(PDF_CP);
|
|
2839
2969
|
i += cpLen;
|
|
2840
2970
|
} else {
|
|
2841
2971
|
out.push(cp);
|
|
@@ -2846,8 +2976,95 @@ function normalizeBidiEmbeddings(text) {
|
|
|
2846
2976
|
for (let i = 0; i < out.length; i++) result += String.fromCodePoint(out[i]);
|
|
2847
2977
|
return result;
|
|
2848
2978
|
}
|
|
2979
|
+
function matchingPdfIndex(codePoints, openCp) {
|
|
2980
|
+
let depth = 1;
|
|
2981
|
+
for (let j = openCp + 1; j < codePoints.length; j++) {
|
|
2982
|
+
const cj = codePoints[j];
|
|
2983
|
+
if (cj === 8234 || cj === 8235 || cj === 8237 || cj === 8238) depth++;
|
|
2984
|
+
else if (cj === 8236) {
|
|
2985
|
+
depth--;
|
|
2986
|
+
if (depth === 0) return j;
|
|
2987
|
+
}
|
|
2988
|
+
}
|
|
2989
|
+
return -1;
|
|
2990
|
+
}
|
|
2991
|
+
function matchingPdiIndex(codePoints, openCp) {
|
|
2992
|
+
let depth = 1;
|
|
2993
|
+
for (let j = openCp + 1; j < codePoints.length; j++) {
|
|
2994
|
+
const cj = codePoints[j];
|
|
2995
|
+
if (cj === 8294 || cj === 8295 || cj === 8296) depth++;
|
|
2996
|
+
else if (cj === 8297) {
|
|
2997
|
+
depth--;
|
|
2998
|
+
if (depth === 0) return j;
|
|
2999
|
+
}
|
|
3000
|
+
}
|
|
3001
|
+
return -1;
|
|
3002
|
+
}
|
|
3003
|
+
function tryResolveOverrides(text, forcedLevel) {
|
|
3004
|
+
const LRE = 8234, RLE = 8235, LRO = 8237, RLO = 8238;
|
|
3005
|
+
if (text.indexOf("\u202D") === -1 && text.indexOf("\u202E") === -1) return null;
|
|
3006
|
+
const codePoints = [];
|
|
3007
|
+
const cpToStr = [];
|
|
3008
|
+
for (let i2 = 0; i2 < text.length; ) {
|
|
3009
|
+
cpToStr.push(i2);
|
|
3010
|
+
const cp = text.codePointAt(i2) ?? 0;
|
|
3011
|
+
codePoints.push(cp);
|
|
3012
|
+
i2 += cp > 65535 ? 2 : 1;
|
|
3013
|
+
}
|
|
3014
|
+
cpToStr.push(text.length);
|
|
3015
|
+
const len = codePoints.length;
|
|
3016
|
+
const spans = [];
|
|
3017
|
+
let i = 0;
|
|
3018
|
+
while (i < len) {
|
|
3019
|
+
const cp = codePoints[i];
|
|
3020
|
+
if (cp === LRO || cp === RLO) {
|
|
3021
|
+
const close = matchingPdfIndex(codePoints, i);
|
|
3022
|
+
if (close === -1) {
|
|
3023
|
+
i++;
|
|
3024
|
+
continue;
|
|
3025
|
+
}
|
|
3026
|
+
spans.push({ open: i, close, rtl: cp === RLO });
|
|
3027
|
+
i = close + 1;
|
|
3028
|
+
} else if (cp === LRE || cp === RLE) {
|
|
3029
|
+
const close = matchingPdfIndex(codePoints, i);
|
|
3030
|
+
i = close === -1 ? i + 1 : close + 1;
|
|
3031
|
+
} else if (cp === 8294 || cp === 8295 || cp === 8296) {
|
|
3032
|
+
const close = matchingPdiIndex(codePoints, i);
|
|
3033
|
+
i = close === -1 ? i + 1 : close + 1;
|
|
3034
|
+
} else {
|
|
3035
|
+
i++;
|
|
3036
|
+
}
|
|
3037
|
+
}
|
|
3038
|
+
if (spans.length === 0) return null;
|
|
3039
|
+
const out = [];
|
|
3040
|
+
const emitGap = (cpStart, cpEnd) => {
|
|
3041
|
+
if (cpStart >= cpEnd) return;
|
|
3042
|
+
const segText = text.substring(cpToStr[cpStart], cpToStr[cpEnd]);
|
|
3043
|
+
const baseStrIdx = cpToStr[cpStart];
|
|
3044
|
+
const segRuns = forcedLevel === void 0 ? resolveBidiRuns(segText) : resolveBidiRunsForced(segText, forcedLevel);
|
|
3045
|
+
for (const r of segRuns) {
|
|
3046
|
+
out.push({ text: r.text, level: r.level, start: r.start + baseStrIdx });
|
|
3047
|
+
}
|
|
3048
|
+
};
|
|
3049
|
+
let cursor = 0;
|
|
3050
|
+
for (const span of spans) {
|
|
3051
|
+
emitGap(cursor, span.open);
|
|
3052
|
+
const innerRaw = text.substring(cpToStr[span.open + 1], cpToStr[span.close]);
|
|
3053
|
+
const inner = stripBidiControls(innerRaw);
|
|
3054
|
+
if (inner) {
|
|
3055
|
+
const level = span.rtl ? 1 : 0;
|
|
3056
|
+
const runText = span.rtl ? reverseString(inner) : inner;
|
|
3057
|
+
out.push({ text: runText, level, start: cpToStr[span.open + 1] });
|
|
3058
|
+
}
|
|
3059
|
+
cursor = span.close + 1;
|
|
3060
|
+
}
|
|
3061
|
+
emitGap(cursor, len);
|
|
3062
|
+
return out;
|
|
3063
|
+
}
|
|
2849
3064
|
function resolveBidiRuns(text) {
|
|
2850
3065
|
if (!text) return [];
|
|
3066
|
+
const overrideRuns = tryResolveOverrides(text);
|
|
3067
|
+
if (overrideRuns) return overrideRuns;
|
|
2851
3068
|
const normalized = normalizeBidiEmbeddings(text);
|
|
2852
3069
|
if (normalized !== text) {
|
|
2853
3070
|
return resolveBidiRuns(normalized);
|
|
@@ -2910,6 +3127,8 @@ function resolveBidiRuns(text) {
|
|
|
2910
3127
|
}
|
|
2911
3128
|
function resolveBidiRunsForced(text, forcedLevel) {
|
|
2912
3129
|
if (!text) return [];
|
|
3130
|
+
const overrideRuns = tryResolveOverrides(text, forcedLevel);
|
|
3131
|
+
if (overrideRuns) return overrideRuns;
|
|
2913
3132
|
const codePoints = [];
|
|
2914
3133
|
const cpToStr = [];
|
|
2915
3134
|
for (let i = 0; i < text.length; ) {
|
|
@@ -2998,6 +3217,7 @@ function resolveBidiCore(text, codePoints, cpToStr, forcedLevel) {
|
|
|
2998
3217
|
function containsRTL(text) {
|
|
2999
3218
|
for (let i = 0; i < text.length; ) {
|
|
3000
3219
|
const cp = text.codePointAt(i) ?? 0;
|
|
3220
|
+
if (cp === 8237 || cp === 8238) return true;
|
|
3001
3221
|
const t = classifyBidiType(cp);
|
|
3002
3222
|
if (t === "R" || t === "AL") return true;
|
|
3003
3223
|
i += cp > 65535 ? 2 : 1;
|
|
@@ -3079,6 +3299,57 @@ function pdfString(str) {
|
|
|
3079
3299
|
const s = toWinAnsi(stripBidiControls(str));
|
|
3080
3300
|
return "(" + s.replace(/\\/g, "\\\\").replace(/\(/g, "\\(").replace(/\)/g, "\\)") + ")";
|
|
3081
3301
|
}
|
|
3302
|
+
var WINANSI_HIGH_TO_UNICODE = {
|
|
3303
|
+
128: 8364,
|
|
3304
|
+
130: 8218,
|
|
3305
|
+
131: 402,
|
|
3306
|
+
132: 8222,
|
|
3307
|
+
133: 8230,
|
|
3308
|
+
134: 8224,
|
|
3309
|
+
135: 8225,
|
|
3310
|
+
136: 710,
|
|
3311
|
+
137: 8240,
|
|
3312
|
+
138: 352,
|
|
3313
|
+
139: 8249,
|
|
3314
|
+
140: 338,
|
|
3315
|
+
142: 381,
|
|
3316
|
+
145: 8216,
|
|
3317
|
+
146: 8217,
|
|
3318
|
+
147: 8220,
|
|
3319
|
+
148: 8221,
|
|
3320
|
+
149: 8226,
|
|
3321
|
+
150: 8211,
|
|
3322
|
+
151: 8212,
|
|
3323
|
+
152: 732,
|
|
3324
|
+
153: 8482,
|
|
3325
|
+
154: 353,
|
|
3326
|
+
155: 8250,
|
|
3327
|
+
156: 339,
|
|
3328
|
+
158: 382,
|
|
3329
|
+
159: 376
|
|
3330
|
+
};
|
|
3331
|
+
function buildWinAnsiToUnicodeCMap() {
|
|
3332
|
+
const entries = [];
|
|
3333
|
+
const hex2 = (n2) => n2.toString(16).toUpperCase().padStart(2, "0");
|
|
3334
|
+
const hex4 = (n2) => n2.toString(16).toUpperCase().padStart(4, "0");
|
|
3335
|
+
for (let b = 32; b <= 255; b++) {
|
|
3336
|
+
if (b >= 127 && b <= 159) {
|
|
3337
|
+
const u = WINANSI_HIGH_TO_UNICODE[b];
|
|
3338
|
+
if (u === void 0) continue;
|
|
3339
|
+
entries.push(`<${hex2(b)}> <${hex4(u)}>`);
|
|
3340
|
+
} else {
|
|
3341
|
+
entries.push(`<${hex2(b)}> <${hex4(b)}>`);
|
|
3342
|
+
}
|
|
3343
|
+
}
|
|
3344
|
+
const blocks = [];
|
|
3345
|
+
for (let i = 0; i < entries.length; i += 100) {
|
|
3346
|
+
const chunk = entries.slice(i, i + 100);
|
|
3347
|
+
blocks.push(`${chunk.length} beginbfchar
|
|
3348
|
+
${chunk.join("\n")}
|
|
3349
|
+
endbfchar`);
|
|
3350
|
+
}
|
|
3351
|
+
return "/CIDInit /ProcSet findresource begin\n12 dict begin\nbegincmap\n/CIDSystemInfo << /Registry (Adobe) /Ordering (UCS) /Supplement 0 >> def\n/CMapName /Adobe-Identity-UCS def\n/CMapType 2 def\n1 begincodespacerange\n<20> <FF>\nendcodespacerange\n" + blocks.join("\n") + "\nendcmap\nCMapName currentdict /CMap defineresource pop\nend\nend";
|
|
3352
|
+
}
|
|
3082
3353
|
function truncate(str, max) {
|
|
3083
3354
|
if (!str || str.length <= max) return str || "";
|
|
3084
3355
|
if (max <= 1) return "\u2026";
|
|
@@ -3170,56 +3441,238 @@ function tryLigature(gids, ligatures) {
|
|
|
3170
3441
|
return null;
|
|
3171
3442
|
}
|
|
3172
3443
|
|
|
3173
|
-
// src/shaping/
|
|
3174
|
-
|
|
3175
|
-
|
|
3176
|
-
|
|
3177
|
-
|
|
3178
|
-
if (cp
|
|
3179
|
-
if (cp ===
|
|
3180
|
-
if (cp
|
|
3181
|
-
if (cp
|
|
3182
|
-
if (cp
|
|
3183
|
-
if (cp ===
|
|
3184
|
-
if (cp
|
|
3185
|
-
if (cp
|
|
3186
|
-
if (cp
|
|
3187
|
-
if (cp ===
|
|
3188
|
-
if (cp
|
|
3189
|
-
if (cp ===
|
|
3190
|
-
if (cp
|
|
3191
|
-
if (cp >=
|
|
3192
|
-
if (cp
|
|
3193
|
-
if (cp >=
|
|
3194
|
-
|
|
3195
|
-
if (cp === 2493) return 1;
|
|
3196
|
-
if (cp === 2510) return 0;
|
|
3197
|
-
return -1;
|
|
3444
|
+
// src/shaping/use-lite.ts
|
|
3445
|
+
function devanagariCategory(cp) {
|
|
3446
|
+
if (cp === 2305 || cp === 2306) return "Mabv";
|
|
3447
|
+
if (cp === 2307) return "Mpst";
|
|
3448
|
+
if (cp >= 2308 && cp <= 2324) return "V";
|
|
3449
|
+
if (cp >= 2325 && cp <= 2361) return "B";
|
|
3450
|
+
if (cp === 2362) return "Mabv";
|
|
3451
|
+
if (cp === 2363) return "Mpst";
|
|
3452
|
+
if (cp === 2364) return "Mblw";
|
|
3453
|
+
if (cp === 2365) return "O";
|
|
3454
|
+
if (cp === 2366) return "Mpst";
|
|
3455
|
+
if (cp >= 2367 && cp <= 2368) return cp === 2367 ? "Mpre" : "Mpst";
|
|
3456
|
+
if (cp >= 2369 && cp <= 2376) return "Mblw";
|
|
3457
|
+
if (cp >= 2377 && cp <= 2380) return "Mpst";
|
|
3458
|
+
if (cp === 2381) return "H";
|
|
3459
|
+
if (cp >= 2382 && cp <= 2383) return "Mpre";
|
|
3460
|
+
if (cp === 2384) return "O";
|
|
3461
|
+
if (cp >= 2385 && cp <= 2391) return "Mabv";
|
|
3462
|
+
if (cp >= 2392 && cp <= 2401) return "B";
|
|
3463
|
+
if (cp >= 2402 && cp <= 2403) return "Mblw";
|
|
3464
|
+
if (cp >= 2406 && cp <= 2415) return "N";
|
|
3465
|
+
return "O";
|
|
3198
3466
|
}
|
|
3199
|
-
function
|
|
3200
|
-
|
|
3467
|
+
function bengaliCategory(cp) {
|
|
3468
|
+
if (cp === 2433) return "Mabv";
|
|
3469
|
+
if (cp === 2434) return "Mpst";
|
|
3470
|
+
if (cp === 2435) return "Mpst";
|
|
3471
|
+
if (cp >= 2437 && cp <= 2452) return "V";
|
|
3472
|
+
if (cp >= 2453 && cp <= 2489) return "B";
|
|
3473
|
+
if (cp === 2492) return "Mblw";
|
|
3474
|
+
if (cp === 2493) return "O";
|
|
3475
|
+
if (cp === 2494) return "Mpst";
|
|
3476
|
+
if (cp >= 2495 && cp <= 2496) return cp === 2495 ? "Mpre" : "Mpst";
|
|
3477
|
+
if (cp >= 2497 && cp <= 2500) return "Mblw";
|
|
3478
|
+
if (cp >= 2503 && cp <= 2504) return "Mpre";
|
|
3479
|
+
if (cp >= 2507 && cp <= 2508) return "Mpre";
|
|
3480
|
+
if (cp === 2509) return "H";
|
|
3481
|
+
if (cp === 2510) return "B";
|
|
3482
|
+
if (cp === 2519) return "Mpst";
|
|
3483
|
+
if (cp >= 2524 && cp <= 2527) return "B";
|
|
3484
|
+
if (cp >= 2528 && cp <= 2531) return "O";
|
|
3485
|
+
if (cp >= 2534 && cp <= 2543) return "N";
|
|
3486
|
+
return "O";
|
|
3201
3487
|
}
|
|
3202
|
-
function
|
|
3203
|
-
|
|
3204
|
-
|
|
3205
|
-
|
|
3206
|
-
|
|
3207
|
-
|
|
3208
|
-
|
|
3209
|
-
|
|
3488
|
+
function tamilCategory(cp) {
|
|
3489
|
+
if (cp === 2946) return "Mabv";
|
|
3490
|
+
if (cp >= 2949 && cp <= 2964) return "V";
|
|
3491
|
+
if (cp >= 2965 && cp <= 3001) return "B";
|
|
3492
|
+
if (cp === 3006) return "Mpst";
|
|
3493
|
+
if (cp === 3007) return "Mpst";
|
|
3494
|
+
if (cp >= 3008 && cp <= 3010) return "Mpst";
|
|
3495
|
+
if (cp >= 3014 && cp <= 3016) return "Mpre";
|
|
3496
|
+
if (cp >= 3018 && cp <= 3020) return "Mpre";
|
|
3497
|
+
if (cp === 3021) return "H";
|
|
3498
|
+
if (cp === 3031) return "Mpst";
|
|
3499
|
+
if (cp >= 3046 && cp <= 3055) return "N";
|
|
3500
|
+
return "O";
|
|
3501
|
+
}
|
|
3502
|
+
function classifyUseCategory(cp) {
|
|
3503
|
+
if (cp === 8204) return "ZWNJ";
|
|
3504
|
+
if (cp === 8205) return "ZWJ";
|
|
3505
|
+
if (cp >= 2304 && cp <= 2431) return devanagariCategory(cp);
|
|
3506
|
+
if (cp >= 2432 && cp <= 2559) return bengaliCategory(cp);
|
|
3507
|
+
if (cp >= 2944 && cp <= 3071) return tamilCategory(cp);
|
|
3508
|
+
return "O";
|
|
3509
|
+
}
|
|
3510
|
+
function classifyClusters(codePoints) {
|
|
3511
|
+
const out = [];
|
|
3210
3512
|
let i = 0;
|
|
3513
|
+
while (i < codePoints.length) {
|
|
3514
|
+
const cluster = nextCluster(codePoints, i);
|
|
3515
|
+
out.push(cluster.cluster);
|
|
3516
|
+
i = cluster.next;
|
|
3517
|
+
}
|
|
3518
|
+
return out;
|
|
3519
|
+
}
|
|
3520
|
+
var DEVA_RA = 2352;
|
|
3521
|
+
var BENG_RA = 2480;
|
|
3522
|
+
function nextCluster(cps, start) {
|
|
3523
|
+
const prebase = [];
|
|
3524
|
+
const above = [];
|
|
3525
|
+
const below = [];
|
|
3526
|
+
const post = [];
|
|
3527
|
+
const tail = [];
|
|
3528
|
+
let i = start;
|
|
3529
|
+
let eyelash = false;
|
|
3530
|
+
const cp0 = cps[i];
|
|
3531
|
+
const cat0 = classifyUseCategory(cp0);
|
|
3532
|
+
const isRa = cp0 === DEVA_RA || cp0 === BENG_RA;
|
|
3533
|
+
if (isRa && i + 1 < cps.length && classifyUseCategory(cps[i + 1]) === "H" && i + 2 < cps.length) {
|
|
3534
|
+
const cat2 = classifyUseCategory(cps[i + 2]);
|
|
3535
|
+
if (cat2 === "B") {
|
|
3536
|
+
prebase.push({ cp: cp0, category: "R" });
|
|
3537
|
+
i += 2;
|
|
3538
|
+
} else if (cat2 === "ZWJ") {
|
|
3539
|
+
prebase.push({ cp: cp0, category: "R" });
|
|
3540
|
+
eyelash = true;
|
|
3541
|
+
i += 3;
|
|
3542
|
+
}
|
|
3543
|
+
}
|
|
3544
|
+
while (i < cps.length && classifyUseCategory(cps[i]) === "Mpre") {
|
|
3545
|
+
prebase.push({ cp: cps[i], category: "Mpre" });
|
|
3546
|
+
i++;
|
|
3547
|
+
}
|
|
3548
|
+
let base = null;
|
|
3549
|
+
if (i < cps.length) {
|
|
3550
|
+
const cat = classifyUseCategory(cps[i]);
|
|
3551
|
+
if (cat === "B" || cat === "V") {
|
|
3552
|
+
base = { cp: cps[i], category: cat };
|
|
3553
|
+
i++;
|
|
3554
|
+
}
|
|
3555
|
+
}
|
|
3211
3556
|
while (i < cps.length) {
|
|
3212
3557
|
const cp = cps[i];
|
|
3213
|
-
const
|
|
3214
|
-
if (
|
|
3215
|
-
|
|
3558
|
+
const cat = classifyUseCategory(cp);
|
|
3559
|
+
if (cat === "B" || cat === "V") break;
|
|
3560
|
+
if (cat === "H") {
|
|
3561
|
+
tail.push({ cp, category: "H" });
|
|
3216
3562
|
i++;
|
|
3217
|
-
|
|
3218
|
-
|
|
3219
|
-
|
|
3220
|
-
|
|
3221
|
-
|
|
3222
|
-
|
|
3563
|
+
if (i < cps.length) {
|
|
3564
|
+
const nextCat = classifyUseCategory(cps[i]);
|
|
3565
|
+
if (nextCat === "B") {
|
|
3566
|
+
tail.push({ cp: cps[i], category: "B" });
|
|
3567
|
+
i++;
|
|
3568
|
+
}
|
|
3569
|
+
}
|
|
3570
|
+
continue;
|
|
3571
|
+
}
|
|
3572
|
+
if (cat === "Mabv") {
|
|
3573
|
+
above.push({ cp, category: "Mabv" });
|
|
3574
|
+
i++;
|
|
3575
|
+
continue;
|
|
3576
|
+
}
|
|
3577
|
+
if (cat === "Mblw") {
|
|
3578
|
+
below.push({ cp, category: "Mblw" });
|
|
3579
|
+
i++;
|
|
3580
|
+
continue;
|
|
3581
|
+
}
|
|
3582
|
+
if (cat === "Mpst") {
|
|
3583
|
+
post.push({ cp, category: "Mpst" });
|
|
3584
|
+
i++;
|
|
3585
|
+
continue;
|
|
3586
|
+
}
|
|
3587
|
+
if (cat === "Mpre") {
|
|
3588
|
+
prebase.push({ cp, category: "Mpre" });
|
|
3589
|
+
i++;
|
|
3590
|
+
continue;
|
|
3591
|
+
}
|
|
3592
|
+
if (cat === "M") {
|
|
3593
|
+
post.push({ cp, category: "M" });
|
|
3594
|
+
i++;
|
|
3595
|
+
continue;
|
|
3596
|
+
}
|
|
3597
|
+
if (cat === "ZWJ" || cat === "ZWNJ") {
|
|
3598
|
+
tail.push({ cp, category: cat });
|
|
3599
|
+
i++;
|
|
3600
|
+
continue;
|
|
3601
|
+
}
|
|
3602
|
+
if (cat === "N" || cat === "O") {
|
|
3603
|
+
if (!base) {
|
|
3604
|
+
base = { cp, category: cat === "N" ? "N" : "O" };
|
|
3605
|
+
i++;
|
|
3606
|
+
continue;
|
|
3607
|
+
}
|
|
3608
|
+
break;
|
|
3609
|
+
}
|
|
3610
|
+
break;
|
|
3611
|
+
}
|
|
3612
|
+
if (i === start) {
|
|
3613
|
+
base = { cp: cps[i], category: cat0 };
|
|
3614
|
+
i++;
|
|
3615
|
+
}
|
|
3616
|
+
return {
|
|
3617
|
+
cluster: eyelash ? { prebase, base, above, below, post, tail, eyelash: true } : { prebase, base, above, below, post, tail },
|
|
3618
|
+
next: i
|
|
3619
|
+
};
|
|
3620
|
+
}
|
|
3621
|
+
|
|
3622
|
+
// src/shaping/bengali-shaper.ts
|
|
3623
|
+
var HALANT = 2509;
|
|
3624
|
+
var NUKTA = 2492;
|
|
3625
|
+
var RA = 2480;
|
|
3626
|
+
function bengaliCharType(cp) {
|
|
3627
|
+
if (cp === HALANT) return 7;
|
|
3628
|
+
if (cp === NUKTA) return 8;
|
|
3629
|
+
if (cp >= 2433 && cp <= 2435) return 6;
|
|
3630
|
+
if (cp >= 2437 && cp <= 2452) return 1;
|
|
3631
|
+
if (cp >= 2453 && cp <= 2489) return 0;
|
|
3632
|
+
if (cp === 2495) return 4;
|
|
3633
|
+
if (cp === 2503) return 4;
|
|
3634
|
+
if (cp === 2504) return 4;
|
|
3635
|
+
if (cp === 2497 || cp === 2498 || cp === 2499 || cp === 2500) return 3;
|
|
3636
|
+
if (cp === 2494) return 5;
|
|
3637
|
+
if (cp === 2496) return 5;
|
|
3638
|
+
if (cp === 2507) return 4;
|
|
3639
|
+
if (cp === 2508) return 4;
|
|
3640
|
+
if (cp >= 2494 && cp <= 2508) return 2;
|
|
3641
|
+
if (cp === 2519) return 5;
|
|
3642
|
+
if (cp >= 2534 && cp <= 2543) return 9;
|
|
3643
|
+
if (cp === 2527) return 0;
|
|
3644
|
+
if (cp === 2493) return 1;
|
|
3645
|
+
if (cp === 2510) return 0;
|
|
3646
|
+
return -1;
|
|
3647
|
+
}
|
|
3648
|
+
function isConsonant(cp) {
|
|
3649
|
+
return bengaliCharType(cp) === 0;
|
|
3650
|
+
}
|
|
3651
|
+
function buildBengaliClusters(str) {
|
|
3652
|
+
const clusters = [];
|
|
3653
|
+
const cps = [];
|
|
3654
|
+
for (let i2 = 0; i2 < str.length; ) {
|
|
3655
|
+
const cp = str.codePointAt(i2) ?? 0;
|
|
3656
|
+
cps.push(cp);
|
|
3657
|
+
i2 += cp > 65535 ? 2 : 1;
|
|
3658
|
+
}
|
|
3659
|
+
let i = 0;
|
|
3660
|
+
while (i < cps.length) {
|
|
3661
|
+
const cp = cps[i];
|
|
3662
|
+
const type = bengaliCharType(cp);
|
|
3663
|
+
if (classifyUseCategory(cp) === "ZWJ" || classifyUseCategory(cp) === "ZWNJ") {
|
|
3664
|
+
i++;
|
|
3665
|
+
continue;
|
|
3666
|
+
}
|
|
3667
|
+
if (type < 0 || cp < BENGALI_START || cp > BENGALI_END) {
|
|
3668
|
+
clusters.push({ codepoints: [cp], baseIndex: 0, hasReph: false, preBaseMatras: [] });
|
|
3669
|
+
i++;
|
|
3670
|
+
continue;
|
|
3671
|
+
}
|
|
3672
|
+
const syllable = [];
|
|
3673
|
+
let baseIdx = 0;
|
|
3674
|
+
let hasReph = false;
|
|
3675
|
+
const preMatras = [];
|
|
3223
3676
|
if (isConsonant(cp) && cp === RA && i + 2 < cps.length && cps[i + 1] === HALANT && isConsonant(cps[i + 2])) {
|
|
3224
3677
|
hasReph = true;
|
|
3225
3678
|
syllable.push(cp, cps[i + 1]);
|
|
@@ -3238,13 +3691,24 @@ function buildBengaliClusters(str) {
|
|
|
3238
3691
|
i++;
|
|
3239
3692
|
}
|
|
3240
3693
|
if (i < cps.length && cps[i] === HALANT) {
|
|
3241
|
-
|
|
3694
|
+
let j = i + 1;
|
|
3695
|
+
let zwnj = false;
|
|
3696
|
+
if (j < cps.length) {
|
|
3697
|
+
const jc = classifyUseCategory(cps[j]);
|
|
3698
|
+
if (jc === "ZWJ") {
|
|
3699
|
+
j++;
|
|
3700
|
+
} else if (jc === "ZWNJ") {
|
|
3701
|
+
j++;
|
|
3702
|
+
zwnj = true;
|
|
3703
|
+
}
|
|
3704
|
+
}
|
|
3705
|
+
if (!zwnj && j < cps.length && isConsonant(cps[j])) {
|
|
3242
3706
|
syllable.push(cps[i]);
|
|
3243
|
-
i
|
|
3707
|
+
i = j;
|
|
3244
3708
|
continue;
|
|
3245
3709
|
} else {
|
|
3246
3710
|
syllable.push(cps[i]);
|
|
3247
|
-
i
|
|
3711
|
+
i = j;
|
|
3248
3712
|
break;
|
|
3249
3713
|
}
|
|
3250
3714
|
}
|
|
@@ -3481,6 +3945,10 @@ function buildTamilClusters(str) {
|
|
|
3481
3945
|
while (i < cps.length) {
|
|
3482
3946
|
const cp = cps[i];
|
|
3483
3947
|
const type = tamilCharType(cp);
|
|
3948
|
+
if (classifyUseCategory(cp) === "ZWJ" || classifyUseCategory(cp) === "ZWNJ") {
|
|
3949
|
+
i++;
|
|
3950
|
+
continue;
|
|
3951
|
+
}
|
|
3484
3952
|
if (type < 0 || cp < TAMIL_START || cp > TAMIL_END) {
|
|
3485
3953
|
clusters.push({ codepoints: [cp], baseIndex: 0, preBaseMatras: [] });
|
|
3486
3954
|
i++;
|
|
@@ -3497,13 +3965,24 @@ function buildTamilClusters(str) {
|
|
|
3497
3965
|
syllable.push(cc);
|
|
3498
3966
|
i++;
|
|
3499
3967
|
if (i < cps.length && cps[i] === PULLI) {
|
|
3500
|
-
|
|
3968
|
+
let j = i + 1;
|
|
3969
|
+
let zwnj = false;
|
|
3970
|
+
if (j < cps.length) {
|
|
3971
|
+
const jc = classifyUseCategory(cps[j]);
|
|
3972
|
+
if (jc === "ZWJ") {
|
|
3973
|
+
j++;
|
|
3974
|
+
} else if (jc === "ZWNJ") {
|
|
3975
|
+
j++;
|
|
3976
|
+
zwnj = true;
|
|
3977
|
+
}
|
|
3978
|
+
}
|
|
3979
|
+
if (!zwnj && j < cps.length && isConsonant2(cps[j])) {
|
|
3501
3980
|
syllable.push(cps[i]);
|
|
3502
|
-
i
|
|
3981
|
+
i = j;
|
|
3503
3982
|
continue;
|
|
3504
3983
|
} else {
|
|
3505
3984
|
syllable.push(cps[i]);
|
|
3506
|
-
i
|
|
3985
|
+
i = j;
|
|
3507
3986
|
break;
|
|
3508
3987
|
}
|
|
3509
3988
|
}
|
|
@@ -3683,8 +4162,1016 @@ function shapeTamilText(str, fontData) {
|
|
|
3683
4162
|
emitGlyph(resolveGid(cp), false);
|
|
3684
4163
|
}
|
|
3685
4164
|
}
|
|
3686
|
-
for (const postCp of splitPostComponents) {
|
|
3687
|
-
emitGlyph(resolveGid(postCp), false);
|
|
4165
|
+
for (const postCp of splitPostComponents) {
|
|
4166
|
+
emitGlyph(resolveGid(postCp), false);
|
|
4167
|
+
}
|
|
4168
|
+
}
|
|
4169
|
+
return shaped;
|
|
4170
|
+
}
|
|
4171
|
+
|
|
4172
|
+
// src/shaping/telugu-shaper.ts
|
|
4173
|
+
var VIRAMA = 3149;
|
|
4174
|
+
function teluguCharType(cp) {
|
|
4175
|
+
if (cp === VIRAMA) return 7;
|
|
4176
|
+
if (cp >= 3072 && cp <= 3076) return 6;
|
|
4177
|
+
if (cp >= 3077 && cp <= 3092) return 1;
|
|
4178
|
+
if (cp === 3133) return 1;
|
|
4179
|
+
if (cp >= 3093 && cp <= 3129) return 0;
|
|
4180
|
+
if (cp >= 3160 && cp <= 3162) return 0;
|
|
4181
|
+
if (cp === 3134) return 5;
|
|
4182
|
+
if (cp === 3135) return 2;
|
|
4183
|
+
if (cp === 3136) return 2;
|
|
4184
|
+
if (cp === 3137) return 5;
|
|
4185
|
+
if (cp === 3138) return 5;
|
|
4186
|
+
if (cp === 3139) return 2;
|
|
4187
|
+
if (cp === 3140) return 2;
|
|
4188
|
+
if (cp === 3142) return 2;
|
|
4189
|
+
if (cp === 3143) return 2;
|
|
4190
|
+
if (cp === 3144) return 2;
|
|
4191
|
+
if (cp === 3146) return 5;
|
|
4192
|
+
if (cp === 3147) return 5;
|
|
4193
|
+
if (cp === 3148) return 5;
|
|
4194
|
+
if (cp === 3170 || cp === 3171) return 3;
|
|
4195
|
+
if (cp === 3157 || cp === 3158) return 6;
|
|
4196
|
+
if (cp === 3168 || cp === 3169) return 1;
|
|
4197
|
+
if (cp >= 3174 && cp <= 3183) return 9;
|
|
4198
|
+
if (cp >= 3192 && cp <= 3199) return 9;
|
|
4199
|
+
return -1;
|
|
4200
|
+
}
|
|
4201
|
+
function isConsonant3(cp) {
|
|
4202
|
+
return teluguCharType(cp) === 0;
|
|
4203
|
+
}
|
|
4204
|
+
function buildTeluguClusters(str) {
|
|
4205
|
+
const clusters = [];
|
|
4206
|
+
const cps = [];
|
|
4207
|
+
for (let i2 = 0; i2 < str.length; ) {
|
|
4208
|
+
const cp = str.codePointAt(i2) ?? 0;
|
|
4209
|
+
cps.push(cp);
|
|
4210
|
+
i2 += cp > 65535 ? 2 : 1;
|
|
4211
|
+
}
|
|
4212
|
+
let i = 0;
|
|
4213
|
+
while (i < cps.length) {
|
|
4214
|
+
const cp = cps[i];
|
|
4215
|
+
const type = teluguCharType(cp);
|
|
4216
|
+
if (classifyUseCategory(cp) === "ZWJ" || classifyUseCategory(cp) === "ZWNJ") {
|
|
4217
|
+
i++;
|
|
4218
|
+
continue;
|
|
4219
|
+
}
|
|
4220
|
+
if (type < 0 || cp < TELUGU_START || cp > TELUGU_END) {
|
|
4221
|
+
clusters.push({ codepoints: [cp], baseIndex: 0 });
|
|
4222
|
+
i++;
|
|
4223
|
+
continue;
|
|
4224
|
+
}
|
|
4225
|
+
const syllable = [];
|
|
4226
|
+
let lastConsonantIdx = -1;
|
|
4227
|
+
while (i < cps.length) {
|
|
4228
|
+
const cc = cps[i];
|
|
4229
|
+
const ct = teluguCharType(cc);
|
|
4230
|
+
if (ct === 0) {
|
|
4231
|
+
lastConsonantIdx = syllable.length;
|
|
4232
|
+
syllable.push(cc);
|
|
4233
|
+
i++;
|
|
4234
|
+
if (i < cps.length && cps[i] === VIRAMA) {
|
|
4235
|
+
let j = i + 1;
|
|
4236
|
+
let zwnj = false;
|
|
4237
|
+
if (j < cps.length) {
|
|
4238
|
+
const jc = classifyUseCategory(cps[j]);
|
|
4239
|
+
if (jc === "ZWJ") {
|
|
4240
|
+
j++;
|
|
4241
|
+
} else if (jc === "ZWNJ") {
|
|
4242
|
+
j++;
|
|
4243
|
+
zwnj = true;
|
|
4244
|
+
}
|
|
4245
|
+
}
|
|
4246
|
+
if (!zwnj && j < cps.length && isConsonant3(cps[j])) {
|
|
4247
|
+
syllable.push(cps[i]);
|
|
4248
|
+
i = j;
|
|
4249
|
+
continue;
|
|
4250
|
+
} else {
|
|
4251
|
+
syllable.push(cps[i]);
|
|
4252
|
+
i = j;
|
|
4253
|
+
break;
|
|
4254
|
+
}
|
|
4255
|
+
}
|
|
4256
|
+
break;
|
|
4257
|
+
} else {
|
|
4258
|
+
break;
|
|
4259
|
+
}
|
|
4260
|
+
}
|
|
4261
|
+
const baseIdx = lastConsonantIdx >= 0 ? lastConsonantIdx : 0;
|
|
4262
|
+
while (i < cps.length) {
|
|
4263
|
+
const ct = teluguCharType(cps[i]);
|
|
4264
|
+
if (ct >= 2 && ct <= 5) {
|
|
4265
|
+
syllable.push(cps[i]);
|
|
4266
|
+
i++;
|
|
4267
|
+
} else {
|
|
4268
|
+
break;
|
|
4269
|
+
}
|
|
4270
|
+
}
|
|
4271
|
+
while (i < cps.length && teluguCharType(cps[i]) === 6) {
|
|
4272
|
+
syllable.push(cps[i]);
|
|
4273
|
+
i++;
|
|
4274
|
+
}
|
|
4275
|
+
if (syllable.length === 0) {
|
|
4276
|
+
syllable.push(cps[i] ?? 32);
|
|
4277
|
+
i++;
|
|
4278
|
+
}
|
|
4279
|
+
clusters.push({ codepoints: syllable, baseIndex: baseIdx });
|
|
4280
|
+
}
|
|
4281
|
+
return clusters;
|
|
4282
|
+
}
|
|
4283
|
+
function shapeTeluguText(str, fontData) {
|
|
4284
|
+
const { cmap, ligatures, markAnchors, widths, defaultWidth } = fontData;
|
|
4285
|
+
const shaped = [];
|
|
4286
|
+
function resolveGid(cp) {
|
|
4287
|
+
const normCp = cp === 8239 || cp === 160 ? 32 : cp;
|
|
4288
|
+
return cmap[normCp] || 0;
|
|
4289
|
+
}
|
|
4290
|
+
function tryLig(gids) {
|
|
4291
|
+
return tryLigature(gids, ligatures);
|
|
4292
|
+
}
|
|
4293
|
+
function getAdv(gid) {
|
|
4294
|
+
return widths[gid] !== void 0 ? widths[gid] : defaultWidth;
|
|
4295
|
+
}
|
|
4296
|
+
function getBaseAnchor2(baseGid, markClass) {
|
|
4297
|
+
const base = markAnchors && markAnchors.bases && markAnchors.bases[baseGid];
|
|
4298
|
+
if (!base) return null;
|
|
4299
|
+
return base[markClass] ?? null;
|
|
4300
|
+
}
|
|
4301
|
+
function getMarkAnchor2(markGid) {
|
|
4302
|
+
const mark = markAnchors && markAnchors.marks && markAnchors.marks[markGid];
|
|
4303
|
+
if (!mark) return null;
|
|
4304
|
+
return { classIdx: mark[0], x: mark[1], y: mark[2] };
|
|
4305
|
+
}
|
|
4306
|
+
function emitGlyph(gid, isZero, baseGid) {
|
|
4307
|
+
if (isZero && baseGid !== void 0) {
|
|
4308
|
+
const markAnchor = getMarkAnchor2(gid);
|
|
4309
|
+
if (markAnchor) {
|
|
4310
|
+
const baseAnchorPt = getBaseAnchor2(baseGid, markAnchor.classIdx);
|
|
4311
|
+
if (baseAnchorPt) {
|
|
4312
|
+
const baseAdv = getAdv(baseGid);
|
|
4313
|
+
shaped.push({
|
|
4314
|
+
gid,
|
|
4315
|
+
dx: baseAnchorPt[0] - markAnchor.x - baseAdv,
|
|
4316
|
+
dy: baseAnchorPt[1] - markAnchor.y,
|
|
4317
|
+
isZeroAdvance: true
|
|
4318
|
+
});
|
|
4319
|
+
return;
|
|
4320
|
+
}
|
|
4321
|
+
}
|
|
4322
|
+
shaped.push({ gid, dx: 0, dy: 0, isZeroAdvance: true });
|
|
4323
|
+
} else {
|
|
4324
|
+
shaped.push({ gid, dx: 0, dy: 0, isZeroAdvance: false });
|
|
4325
|
+
}
|
|
4326
|
+
}
|
|
4327
|
+
const clusters = buildTeluguClusters(str);
|
|
4328
|
+
for (const cluster of clusters) {
|
|
4329
|
+
const { codepoints } = cluster;
|
|
4330
|
+
let baseGid = 0;
|
|
4331
|
+
for (let ci = 0; ci < codepoints.length; ci++) {
|
|
4332
|
+
const ct = teluguCharType(codepoints[ci]);
|
|
4333
|
+
if (ct === 0) {
|
|
4334
|
+
baseGid = resolveGid(codepoints[ci]);
|
|
4335
|
+
} else if (ct >= 2) {
|
|
4336
|
+
break;
|
|
4337
|
+
}
|
|
4338
|
+
}
|
|
4339
|
+
const clusterGids = [];
|
|
4340
|
+
const clusterEndIdx = [];
|
|
4341
|
+
let matraStart = codepoints.length;
|
|
4342
|
+
for (let ci = 0; ci < codepoints.length; ci++) {
|
|
4343
|
+
const ct = teluguCharType(codepoints[ci]);
|
|
4344
|
+
if (ct === 0 || ct === 7) {
|
|
4345
|
+
clusterGids.push(resolveGid(codepoints[ci]));
|
|
4346
|
+
clusterEndIdx.push(ci);
|
|
4347
|
+
} else if (ct >= 2) {
|
|
4348
|
+
matraStart = ci;
|
|
4349
|
+
break;
|
|
4350
|
+
} else if (ct < 0 || ct === 1 || ct === 9) {
|
|
4351
|
+
emitGlyph(resolveGid(codepoints[ci]), false);
|
|
4352
|
+
}
|
|
4353
|
+
}
|
|
4354
|
+
const ligResult = tryLig(clusterGids);
|
|
4355
|
+
if (ligResult) {
|
|
4356
|
+
emitGlyph(ligResult.resultGid, false);
|
|
4357
|
+
baseGid = ligResult.resultGid;
|
|
4358
|
+
let gi = ligResult.consumed;
|
|
4359
|
+
while (gi < clusterGids.length) {
|
|
4360
|
+
const subSeq = clusterGids.slice(gi);
|
|
4361
|
+
const subLig = tryLig(subSeq);
|
|
4362
|
+
if (subLig) {
|
|
4363
|
+
emitGlyph(subLig.resultGid, false);
|
|
4364
|
+
gi += subLig.consumed;
|
|
4365
|
+
} else {
|
|
4366
|
+
const origCi = clusterEndIdx[gi];
|
|
4367
|
+
const ct = teluguCharType(codepoints[origCi]);
|
|
4368
|
+
if (ct === 7) {
|
|
4369
|
+
emitGlyph(clusterGids[gi], true, baseGid);
|
|
4370
|
+
} else {
|
|
4371
|
+
emitGlyph(clusterGids[gi], false);
|
|
4372
|
+
}
|
|
4373
|
+
gi++;
|
|
4374
|
+
}
|
|
4375
|
+
}
|
|
4376
|
+
} else {
|
|
4377
|
+
for (let ci = 0; ci < matraStart; ci++) {
|
|
4378
|
+
const cp = codepoints[ci];
|
|
4379
|
+
const ct = teluguCharType(cp);
|
|
4380
|
+
if (ct === 0) {
|
|
4381
|
+
emitGlyph(resolveGid(cp), false);
|
|
4382
|
+
} else if (ct === 7) {
|
|
4383
|
+
emitGlyph(resolveGid(cp), true, baseGid);
|
|
4384
|
+
}
|
|
4385
|
+
}
|
|
4386
|
+
}
|
|
4387
|
+
for (let ci = matraStart; ci < codepoints.length; ci++) {
|
|
4388
|
+
const cp = codepoints[ci];
|
|
4389
|
+
const ct = teluguCharType(cp);
|
|
4390
|
+
if (ct === 2 || ct === 3) {
|
|
4391
|
+
emitGlyph(resolveGid(cp), true, baseGid);
|
|
4392
|
+
} else if (ct === 5) {
|
|
4393
|
+
emitGlyph(resolveGid(cp), false);
|
|
4394
|
+
} else if (ct === 6) {
|
|
4395
|
+
emitGlyph(resolveGid(cp), true, baseGid);
|
|
4396
|
+
} else if (ct === 9) {
|
|
4397
|
+
emitGlyph(resolveGid(cp), false);
|
|
4398
|
+
} else {
|
|
4399
|
+
emitGlyph(resolveGid(cp), false);
|
|
4400
|
+
}
|
|
4401
|
+
}
|
|
4402
|
+
}
|
|
4403
|
+
return shaped;
|
|
4404
|
+
}
|
|
4405
|
+
|
|
4406
|
+
// src/shaping/sinhala-shaper.ts
|
|
4407
|
+
var VIRAMA2 = SINHALA_VIRAMA;
|
|
4408
|
+
var TWO_PART_VOWELS = {
|
|
4409
|
+
3546: [3545, 3530],
|
|
4410
|
+
// ේ (ee)
|
|
4411
|
+
3548: [3545, 3535],
|
|
4412
|
+
// ො (o)
|
|
4413
|
+
3549: [3545, 3535, 3530],
|
|
4414
|
+
// ෝ (oo)
|
|
4415
|
+
3550: [3545, 3551]
|
|
4416
|
+
// ෞ (au)
|
|
4417
|
+
};
|
|
4418
|
+
function sinhalaCharType(cp) {
|
|
4419
|
+
if (cp === VIRAMA2) return 7;
|
|
4420
|
+
if (cp >= 3458 && cp <= 3460) return 6;
|
|
4421
|
+
if (cp >= 3461 && cp <= 3478) return 1;
|
|
4422
|
+
if (cp >= 3482 && cp <= 3526) return 0;
|
|
4423
|
+
if (cp === 3535) return 5;
|
|
4424
|
+
if (cp === 3536 || cp === 3537) return 5;
|
|
4425
|
+
if (cp === 3538 || cp === 3539) return 2;
|
|
4426
|
+
if (cp === 3540 || cp === 3542) return 3;
|
|
4427
|
+
if (cp === 3544) return 5;
|
|
4428
|
+
if (cp === 3545 || cp === 3547) return 4;
|
|
4429
|
+
if (cp === 3551) return 5;
|
|
4430
|
+
if (cp === 3570 || cp === 3571) return 5;
|
|
4431
|
+
if (cp >= 3558 && cp <= 3567) return 9;
|
|
4432
|
+
return -1;
|
|
4433
|
+
}
|
|
4434
|
+
function isConsonant4(cp) {
|
|
4435
|
+
return sinhalaCharType(cp) === 0;
|
|
4436
|
+
}
|
|
4437
|
+
function buildSinhalaClusters(str) {
|
|
4438
|
+
const clusters = [];
|
|
4439
|
+
const cps = [];
|
|
4440
|
+
for (let i2 = 0; i2 < str.length; ) {
|
|
4441
|
+
const cp = str.codePointAt(i2) ?? 0;
|
|
4442
|
+
const decomp = TWO_PART_VOWELS[cp];
|
|
4443
|
+
if (decomp) {
|
|
4444
|
+
for (const d of decomp) cps.push(d);
|
|
4445
|
+
} else {
|
|
4446
|
+
cps.push(cp);
|
|
4447
|
+
}
|
|
4448
|
+
i2 += cp > 65535 ? 2 : 1;
|
|
4449
|
+
}
|
|
4450
|
+
let i = 0;
|
|
4451
|
+
while (i < cps.length) {
|
|
4452
|
+
const cp = cps[i];
|
|
4453
|
+
const type = sinhalaCharType(cp);
|
|
4454
|
+
if (classifyUseCategory(cp) === "ZWNJ") {
|
|
4455
|
+
i++;
|
|
4456
|
+
continue;
|
|
4457
|
+
}
|
|
4458
|
+
if (type < 0 || cp < SINHALA_START || cp > SINHALA_END) {
|
|
4459
|
+
clusters.push({ codepoints: [cp], baseIndex: 0 });
|
|
4460
|
+
i++;
|
|
4461
|
+
continue;
|
|
4462
|
+
}
|
|
4463
|
+
const syllable = [];
|
|
4464
|
+
let lastConsonantIdx = -1;
|
|
4465
|
+
while (i < cps.length) {
|
|
4466
|
+
const cc = cps[i];
|
|
4467
|
+
const ct = sinhalaCharType(cc);
|
|
4468
|
+
if (ct === 0) {
|
|
4469
|
+
lastConsonantIdx = syllable.length;
|
|
4470
|
+
syllable.push(cc);
|
|
4471
|
+
i++;
|
|
4472
|
+
if (i < cps.length && cps[i] === VIRAMA2) {
|
|
4473
|
+
let j = i + 1;
|
|
4474
|
+
let zwnj = false;
|
|
4475
|
+
let zwj = false;
|
|
4476
|
+
if (j < cps.length) {
|
|
4477
|
+
const jc = classifyUseCategory(cps[j]);
|
|
4478
|
+
if (jc === "ZWJ") {
|
|
4479
|
+
j++;
|
|
4480
|
+
zwj = true;
|
|
4481
|
+
} else if (jc === "ZWNJ") {
|
|
4482
|
+
j++;
|
|
4483
|
+
zwnj = true;
|
|
4484
|
+
}
|
|
4485
|
+
}
|
|
4486
|
+
if (!zwnj && j < cps.length && isConsonant4(cps[j])) {
|
|
4487
|
+
syllable.push(cps[i]);
|
|
4488
|
+
if (zwj) syllable.push(8205);
|
|
4489
|
+
i = j;
|
|
4490
|
+
continue;
|
|
4491
|
+
} else {
|
|
4492
|
+
syllable.push(cps[i]);
|
|
4493
|
+
i = j;
|
|
4494
|
+
break;
|
|
4495
|
+
}
|
|
4496
|
+
}
|
|
4497
|
+
break;
|
|
4498
|
+
} else {
|
|
4499
|
+
break;
|
|
4500
|
+
}
|
|
4501
|
+
}
|
|
4502
|
+
const baseIdx = lastConsonantIdx >= 0 ? lastConsonantIdx : 0;
|
|
4503
|
+
while (i < cps.length) {
|
|
4504
|
+
const ct = sinhalaCharType(cps[i]);
|
|
4505
|
+
if (ct >= 2 && ct <= 5) {
|
|
4506
|
+
syllable.push(cps[i]);
|
|
4507
|
+
i++;
|
|
4508
|
+
} else {
|
|
4509
|
+
break;
|
|
4510
|
+
}
|
|
4511
|
+
}
|
|
4512
|
+
while (i < cps.length && sinhalaCharType(cps[i]) === 6) {
|
|
4513
|
+
syllable.push(cps[i]);
|
|
4514
|
+
i++;
|
|
4515
|
+
}
|
|
4516
|
+
if (syllable.length === 0) {
|
|
4517
|
+
syllable.push(cps[i] ?? 32);
|
|
4518
|
+
i++;
|
|
4519
|
+
}
|
|
4520
|
+
clusters.push({ codepoints: syllable, baseIndex: baseIdx });
|
|
4521
|
+
}
|
|
4522
|
+
return clusters;
|
|
4523
|
+
}
|
|
4524
|
+
function shapeSinhalaText(str, fontData) {
|
|
4525
|
+
const { cmap, ligatures, markAnchors, widths, defaultWidth } = fontData;
|
|
4526
|
+
const shaped = [];
|
|
4527
|
+
function resolveGid(cp) {
|
|
4528
|
+
const normCp = cp === 8239 || cp === 160 ? 32 : cp;
|
|
4529
|
+
return cmap[normCp] || 0;
|
|
4530
|
+
}
|
|
4531
|
+
function tryLig(gids) {
|
|
4532
|
+
return tryLigature(gids, ligatures);
|
|
4533
|
+
}
|
|
4534
|
+
function getAdv(gid) {
|
|
4535
|
+
return widths[gid] !== void 0 ? widths[gid] : defaultWidth;
|
|
4536
|
+
}
|
|
4537
|
+
function getBaseAnchor2(baseGid, markClass) {
|
|
4538
|
+
const base = markAnchors && markAnchors.bases && markAnchors.bases[baseGid];
|
|
4539
|
+
if (!base) return null;
|
|
4540
|
+
return base[markClass] ?? null;
|
|
4541
|
+
}
|
|
4542
|
+
function getMarkAnchor2(markGid) {
|
|
4543
|
+
const mark = markAnchors && markAnchors.marks && markAnchors.marks[markGid];
|
|
4544
|
+
if (!mark) return null;
|
|
4545
|
+
return { classIdx: mark[0], x: mark[1], y: mark[2] };
|
|
4546
|
+
}
|
|
4547
|
+
function emitGlyph(gid, isZero, baseGid) {
|
|
4548
|
+
if (isZero && baseGid !== void 0) {
|
|
4549
|
+
const markAnchor = getMarkAnchor2(gid);
|
|
4550
|
+
if (markAnchor) {
|
|
4551
|
+
const baseAnchorPt = getBaseAnchor2(baseGid, markAnchor.classIdx);
|
|
4552
|
+
if (baseAnchorPt) {
|
|
4553
|
+
const baseAdv = getAdv(baseGid);
|
|
4554
|
+
shaped.push({
|
|
4555
|
+
gid,
|
|
4556
|
+
dx: baseAnchorPt[0] - markAnchor.x - baseAdv,
|
|
4557
|
+
dy: baseAnchorPt[1] - markAnchor.y,
|
|
4558
|
+
isZeroAdvance: true
|
|
4559
|
+
});
|
|
4560
|
+
return;
|
|
4561
|
+
}
|
|
4562
|
+
}
|
|
4563
|
+
shaped.push({ gid, dx: 0, dy: 0, isZeroAdvance: true });
|
|
4564
|
+
} else {
|
|
4565
|
+
shaped.push({ gid, dx: 0, dy: 0, isZeroAdvance: false });
|
|
4566
|
+
}
|
|
4567
|
+
}
|
|
4568
|
+
const clusters = buildSinhalaClusters(str);
|
|
4569
|
+
for (const cluster of clusters) {
|
|
4570
|
+
const { codepoints } = cluster;
|
|
4571
|
+
let baseGid = 0;
|
|
4572
|
+
for (let ci = 0; ci < codepoints.length; ci++) {
|
|
4573
|
+
const ct = sinhalaCharType(codepoints[ci]);
|
|
4574
|
+
if (ct === 0) {
|
|
4575
|
+
baseGid = resolveGid(codepoints[ci]);
|
|
4576
|
+
} else if (ct >= 2 && ct !== 7) {
|
|
4577
|
+
break;
|
|
4578
|
+
}
|
|
4579
|
+
}
|
|
4580
|
+
for (let ci = 0; ci < codepoints.length; ci++) {
|
|
4581
|
+
if (sinhalaCharType(codepoints[ci]) === 4) {
|
|
4582
|
+
emitGlyph(resolveGid(codepoints[ci]), false);
|
|
4583
|
+
}
|
|
4584
|
+
}
|
|
4585
|
+
const clusterGids = [];
|
|
4586
|
+
const clusterEndIdx = [];
|
|
4587
|
+
let matraStart = codepoints.length;
|
|
4588
|
+
for (let ci = 0; ci < codepoints.length; ci++) {
|
|
4589
|
+
const cp = codepoints[ci];
|
|
4590
|
+
const ct = sinhalaCharType(cp);
|
|
4591
|
+
if (ct === 0 || ct === 7 || cp === 8205) {
|
|
4592
|
+
clusterGids.push(resolveGid(cp));
|
|
4593
|
+
clusterEndIdx.push(ci);
|
|
4594
|
+
} else if (ct >= 2 && ct <= 6) {
|
|
4595
|
+
matraStart = ci;
|
|
4596
|
+
break;
|
|
4597
|
+
} else if (ct < 0 || ct === 1 || ct === 9) {
|
|
4598
|
+
emitGlyph(resolveGid(cp), false);
|
|
4599
|
+
}
|
|
4600
|
+
}
|
|
4601
|
+
const ligResult = tryLig(clusterGids);
|
|
4602
|
+
if (ligResult) {
|
|
4603
|
+
emitGlyph(ligResult.resultGid, false);
|
|
4604
|
+
baseGid = ligResult.resultGid;
|
|
4605
|
+
let gi = ligResult.consumed;
|
|
4606
|
+
while (gi < clusterGids.length) {
|
|
4607
|
+
const subSeq = clusterGids.slice(gi);
|
|
4608
|
+
const subLig = tryLig(subSeq);
|
|
4609
|
+
if (subLig) {
|
|
4610
|
+
emitGlyph(subLig.resultGid, false);
|
|
4611
|
+
gi += subLig.consumed;
|
|
4612
|
+
} else {
|
|
4613
|
+
const origCi = clusterEndIdx[gi];
|
|
4614
|
+
const ct = sinhalaCharType(codepoints[origCi]);
|
|
4615
|
+
if (ct === 7) emitGlyph(clusterGids[gi], true, baseGid);
|
|
4616
|
+
else emitGlyph(clusterGids[gi], false);
|
|
4617
|
+
gi++;
|
|
4618
|
+
}
|
|
4619
|
+
}
|
|
4620
|
+
} else {
|
|
4621
|
+
for (let ci = 0; ci < matraStart; ci++) {
|
|
4622
|
+
const cp = codepoints[ci];
|
|
4623
|
+
const ct = sinhalaCharType(cp);
|
|
4624
|
+
if (ct === 0) emitGlyph(resolveGid(cp), false);
|
|
4625
|
+
else if (ct === 7) emitGlyph(resolveGid(cp), true, baseGid);
|
|
4626
|
+
else ;
|
|
4627
|
+
}
|
|
4628
|
+
}
|
|
4629
|
+
for (let ci = matraStart; ci < codepoints.length; ci++) {
|
|
4630
|
+
const cp = codepoints[ci];
|
|
4631
|
+
const ct = sinhalaCharType(cp);
|
|
4632
|
+
if (ct === 2 || ct === 3 || ct === 6) emitGlyph(resolveGid(cp), true, baseGid);
|
|
4633
|
+
else if (ct === 5) emitGlyph(resolveGid(cp), false);
|
|
4634
|
+
else if (ct === 4) ; else emitGlyph(resolveGid(cp), false);
|
|
4635
|
+
}
|
|
4636
|
+
}
|
|
4637
|
+
return shaped;
|
|
4638
|
+
}
|
|
4639
|
+
|
|
4640
|
+
// src/shaping/tibetan-shaper.ts
|
|
4641
|
+
function tibetanCharType(cp) {
|
|
4642
|
+
if (cp >= 3984 && cp <= 4028) return 8;
|
|
4643
|
+
if (cp === 3972) return 6;
|
|
4644
|
+
if (cp >= 3904 && cp <= 3948) return 0;
|
|
4645
|
+
if (cp === 3954 || cp === 3962 || cp === 3963 || cp === 3964 || cp === 3965 || cp === 3968 || cp === 3969 || cp === 3971 || cp === 3970 || cp === 3966) return 2;
|
|
4646
|
+
if (cp === 3953 || cp === 3956 || cp === 3955 || cp === 3957 || cp === 3864 || cp === 3865 || cp === 3893 || cp === 3895 || cp === 3897) return 3;
|
|
4647
|
+
if (cp === 3967) return 5;
|
|
4648
|
+
if (cp === 3840 || cp >= 3976 && cp <= 3980) return 1;
|
|
4649
|
+
if (cp >= 3872 && cp <= 3891) return 9;
|
|
4650
|
+
if (cp >= 3841 && cp <= 3863) return 9;
|
|
4651
|
+
if (cp >= 3898 && cp <= 3903) return 9;
|
|
4652
|
+
if (cp >= 4030 && cp <= 4047) return 9;
|
|
4653
|
+
if (cp === 3892 || cp === 3894 || cp === 3896) return 9;
|
|
4654
|
+
if (cp === 3973) return 9;
|
|
4655
|
+
return -1;
|
|
4656
|
+
}
|
|
4657
|
+
function buildTibetanStacks(str) {
|
|
4658
|
+
const stacks = [];
|
|
4659
|
+
const cps = [];
|
|
4660
|
+
for (let i2 = 0; i2 < str.length; ) {
|
|
4661
|
+
const cp = str.codePointAt(i2) ?? 0;
|
|
4662
|
+
cps.push(cp);
|
|
4663
|
+
i2 += cp > 65535 ? 2 : 1;
|
|
4664
|
+
}
|
|
4665
|
+
let i = 0;
|
|
4666
|
+
while (i < cps.length) {
|
|
4667
|
+
const cp = cps[i];
|
|
4668
|
+
const type = tibetanCharType(cp);
|
|
4669
|
+
if (type < 0 || cp < TIBETAN_START || cp > TIBETAN_END) {
|
|
4670
|
+
stacks.push({ codepoints: [cp] });
|
|
4671
|
+
i++;
|
|
4672
|
+
continue;
|
|
4673
|
+
}
|
|
4674
|
+
if (type !== 0 && type !== 1) {
|
|
4675
|
+
stacks.push({ codepoints: [cp] });
|
|
4676
|
+
i++;
|
|
4677
|
+
continue;
|
|
4678
|
+
}
|
|
4679
|
+
const stack = [cp];
|
|
4680
|
+
i++;
|
|
4681
|
+
while (i < cps.length && tibetanCharType(cps[i]) === 8) {
|
|
4682
|
+
stack.push(cps[i]);
|
|
4683
|
+
i++;
|
|
4684
|
+
}
|
|
4685
|
+
while (i < cps.length) {
|
|
4686
|
+
const ct = tibetanCharType(cps[i]);
|
|
4687
|
+
if (ct === 2 || ct === 3 || ct === 5 || ct === 6) {
|
|
4688
|
+
stack.push(cps[i]);
|
|
4689
|
+
i++;
|
|
4690
|
+
} else {
|
|
4691
|
+
break;
|
|
4692
|
+
}
|
|
4693
|
+
}
|
|
4694
|
+
stacks.push({ codepoints: stack });
|
|
4695
|
+
}
|
|
4696
|
+
return stacks;
|
|
4697
|
+
}
|
|
4698
|
+
function shapeTibetanText(str, fontData) {
|
|
4699
|
+
const { cmap, ligatures, markAnchors, widths, defaultWidth } = fontData;
|
|
4700
|
+
const shaped = [];
|
|
4701
|
+
function resolveGid(cp) {
|
|
4702
|
+
const normCp = cp === 8239 || cp === 160 ? 32 : cp;
|
|
4703
|
+
return cmap[normCp] || 0;
|
|
4704
|
+
}
|
|
4705
|
+
function tryLig(gids) {
|
|
4706
|
+
return tryLigature(gids, ligatures);
|
|
4707
|
+
}
|
|
4708
|
+
function getAdv(gid) {
|
|
4709
|
+
return widths[gid] !== void 0 ? widths[gid] : defaultWidth;
|
|
4710
|
+
}
|
|
4711
|
+
function getBaseAnchor2(baseGid, markClass) {
|
|
4712
|
+
const base = markAnchors && markAnchors.bases && markAnchors.bases[baseGid];
|
|
4713
|
+
if (!base) return null;
|
|
4714
|
+
return base[markClass] ?? null;
|
|
4715
|
+
}
|
|
4716
|
+
function getMarkAnchor2(markGid) {
|
|
4717
|
+
const mark = markAnchors && markAnchors.marks && markAnchors.marks[markGid];
|
|
4718
|
+
if (!mark) return null;
|
|
4719
|
+
return { classIdx: mark[0], x: mark[1], y: mark[2] };
|
|
4720
|
+
}
|
|
4721
|
+
function emitGlyph(gid, isZero, baseGid) {
|
|
4722
|
+
if (isZero && baseGid !== void 0) {
|
|
4723
|
+
const markAnchor = getMarkAnchor2(gid);
|
|
4724
|
+
if (markAnchor) {
|
|
4725
|
+
const baseAnchorPt = getBaseAnchor2(baseGid, markAnchor.classIdx);
|
|
4726
|
+
if (baseAnchorPt) {
|
|
4727
|
+
const baseAdv = getAdv(baseGid);
|
|
4728
|
+
shaped.push({
|
|
4729
|
+
gid,
|
|
4730
|
+
dx: baseAnchorPt[0] - markAnchor.x - baseAdv,
|
|
4731
|
+
dy: baseAnchorPt[1] - markAnchor.y,
|
|
4732
|
+
isZeroAdvance: true
|
|
4733
|
+
});
|
|
4734
|
+
return;
|
|
4735
|
+
}
|
|
4736
|
+
}
|
|
4737
|
+
shaped.push({ gid, dx: 0, dy: 0, isZeroAdvance: true });
|
|
4738
|
+
} else {
|
|
4739
|
+
shaped.push({ gid, dx: 0, dy: 0, isZeroAdvance: false });
|
|
4740
|
+
}
|
|
4741
|
+
}
|
|
4742
|
+
const stacks = buildTibetanStacks(str);
|
|
4743
|
+
for (const stack of stacks) {
|
|
4744
|
+
const { codepoints } = stack;
|
|
4745
|
+
const stackGids = [];
|
|
4746
|
+
const stackEndIdx = [];
|
|
4747
|
+
let markStart = codepoints.length;
|
|
4748
|
+
for (let ci = 0; ci < codepoints.length; ci++) {
|
|
4749
|
+
const ct = tibetanCharType(codepoints[ci]);
|
|
4750
|
+
if (ct === 0 || ct === 8 || ct === 1 || ct === 6) {
|
|
4751
|
+
stackGids.push(resolveGid(codepoints[ci]));
|
|
4752
|
+
stackEndIdx.push(ci);
|
|
4753
|
+
} else if (ct === 2 || ct === 3 || ct === 5) {
|
|
4754
|
+
markStart = ci;
|
|
4755
|
+
break;
|
|
4756
|
+
} else if (ct < 0 || ct === 9) {
|
|
4757
|
+
emitGlyph(resolveGid(codepoints[ci]), false);
|
|
4758
|
+
}
|
|
4759
|
+
}
|
|
4760
|
+
let baseGid = stackGids.length > 0 ? stackGids[0] : 0;
|
|
4761
|
+
const ligResult = tryLig(stackGids);
|
|
4762
|
+
if (ligResult) {
|
|
4763
|
+
emitGlyph(ligResult.resultGid, false);
|
|
4764
|
+
baseGid = ligResult.resultGid;
|
|
4765
|
+
let gi = ligResult.consumed;
|
|
4766
|
+
while (gi < stackGids.length) {
|
|
4767
|
+
const subSeq = stackGids.slice(gi);
|
|
4768
|
+
const subLig = tryLig(subSeq);
|
|
4769
|
+
if (subLig) {
|
|
4770
|
+
emitGlyph(subLig.resultGid, false);
|
|
4771
|
+
gi += subLig.consumed;
|
|
4772
|
+
} else {
|
|
4773
|
+
const origCi = stackEndIdx[gi];
|
|
4774
|
+
const ct = tibetanCharType(codepoints[origCi]);
|
|
4775
|
+
if (ct === 8 || ct === 6) emitGlyph(stackGids[gi], true, baseGid);
|
|
4776
|
+
else emitGlyph(stackGids[gi], false);
|
|
4777
|
+
gi++;
|
|
4778
|
+
}
|
|
4779
|
+
}
|
|
4780
|
+
} else {
|
|
4781
|
+
for (let gi = 0; gi < stackGids.length; gi++) {
|
|
4782
|
+
const origCi = stackEndIdx[gi];
|
|
4783
|
+
const ct = tibetanCharType(codepoints[origCi]);
|
|
4784
|
+
if (gi === 0) emitGlyph(stackGids[gi], false);
|
|
4785
|
+
else if (ct === 8 || ct === 6) emitGlyph(stackGids[gi], true, baseGid);
|
|
4786
|
+
else emitGlyph(stackGids[gi], false);
|
|
4787
|
+
}
|
|
4788
|
+
}
|
|
4789
|
+
for (let ci = markStart; ci < codepoints.length; ci++) {
|
|
4790
|
+
const cp = codepoints[ci];
|
|
4791
|
+
const ct = tibetanCharType(cp);
|
|
4792
|
+
if (ct === 2 || ct === 3) emitGlyph(resolveGid(cp), true, baseGid);
|
|
4793
|
+
else if (ct === 5) emitGlyph(resolveGid(cp), false);
|
|
4794
|
+
else emitGlyph(resolveGid(cp), false);
|
|
4795
|
+
}
|
|
4796
|
+
}
|
|
4797
|
+
return shaped;
|
|
4798
|
+
}
|
|
4799
|
+
|
|
4800
|
+
// src/shaping/khmer-shaper.ts
|
|
4801
|
+
var COENG = KHMER_COENG;
|
|
4802
|
+
var TWO_PART_VOWELS2 = {
|
|
4803
|
+
6078: [6081, 6070],
|
|
4804
|
+
// ើ
|
|
4805
|
+
6079: [6081, 6072],
|
|
4806
|
+
// ឿ (approx: e + y-like)
|
|
4807
|
+
6080: [6081, 6073],
|
|
4808
|
+
// ៀ (approx)
|
|
4809
|
+
6084: [6081, 6070],
|
|
4810
|
+
// ោ (e + aa)
|
|
4811
|
+
6085: [6081, 6070]
|
|
4812
|
+
// ៅ (e + aa-like)
|
|
4813
|
+
};
|
|
4814
|
+
function khmerCharType(cp) {
|
|
4815
|
+
if (cp === COENG) return 7;
|
|
4816
|
+
if (cp >= 6016 && cp <= 6050) return 0;
|
|
4817
|
+
if (cp >= 6051 && cp <= 6069) return 1;
|
|
4818
|
+
if (cp === 6081 || cp === 6082 || cp === 6083) return 4;
|
|
4819
|
+
if (cp === 6071 || cp === 6072 || cp === 6073 || cp === 6074 || cp === 6075 || cp === 6077) return 2;
|
|
4820
|
+
if (cp === 6076) return 3;
|
|
4821
|
+
if (cp === 6070 || cp === 6084 || cp === 6085) return 5;
|
|
4822
|
+
if (cp >= 6086 && cp <= 6097) return 6;
|
|
4823
|
+
if (cp === 6091 || cp === 6093 || cp === 6094 || cp === 6095 || cp === 6096) return 6;
|
|
4824
|
+
if (cp === 6109) return 6;
|
|
4825
|
+
if (cp >= 6112 && cp <= 6121) return 9;
|
|
4826
|
+
if (cp >= 6100 && cp <= 6108) return 9;
|
|
4827
|
+
if (cp >= 6128 && cp <= 6137) return 9;
|
|
4828
|
+
return -1;
|
|
4829
|
+
}
|
|
4830
|
+
function isConsonant5(cp) {
|
|
4831
|
+
return khmerCharType(cp) === 0;
|
|
4832
|
+
}
|
|
4833
|
+
function buildKhmerClusters(str) {
|
|
4834
|
+
const clusters = [];
|
|
4835
|
+
const cps = [];
|
|
4836
|
+
for (let i2 = 0; i2 < str.length; ) {
|
|
4837
|
+
const cp = str.codePointAt(i2) ?? 0;
|
|
4838
|
+
const decomp = TWO_PART_VOWELS2[cp];
|
|
4839
|
+
if (decomp) {
|
|
4840
|
+
for (const d of decomp) cps.push(d);
|
|
4841
|
+
} else cps.push(cp);
|
|
4842
|
+
i2 += cp > 65535 ? 2 : 1;
|
|
4843
|
+
}
|
|
4844
|
+
let i = 0;
|
|
4845
|
+
while (i < cps.length) {
|
|
4846
|
+
const cp = cps[i];
|
|
4847
|
+
const type = khmerCharType(cp);
|
|
4848
|
+
if (type < 0 || cp < KHMER_START || cp > KHMER_END) {
|
|
4849
|
+
clusters.push({ codepoints: [cp] });
|
|
4850
|
+
i++;
|
|
4851
|
+
continue;
|
|
4852
|
+
}
|
|
4853
|
+
if (type !== 0 && type !== 1) {
|
|
4854
|
+
clusters.push({ codepoints: [cp] });
|
|
4855
|
+
i++;
|
|
4856
|
+
continue;
|
|
4857
|
+
}
|
|
4858
|
+
const cluster = [cp];
|
|
4859
|
+
i++;
|
|
4860
|
+
while (i < cps.length && cps[i] === COENG) {
|
|
4861
|
+
if (i + 1 < cps.length && isConsonant5(cps[i + 1])) {
|
|
4862
|
+
cluster.push(cps[i]);
|
|
4863
|
+
cluster.push(cps[i + 1]);
|
|
4864
|
+
i += 2;
|
|
4865
|
+
} else {
|
|
4866
|
+
cluster.push(cps[i]);
|
|
4867
|
+
i++;
|
|
4868
|
+
break;
|
|
4869
|
+
}
|
|
4870
|
+
}
|
|
4871
|
+
while (i < cps.length) {
|
|
4872
|
+
const ct = khmerCharType(cps[i]);
|
|
4873
|
+
if (ct >= 2 && ct <= 6) {
|
|
4874
|
+
cluster.push(cps[i]);
|
|
4875
|
+
i++;
|
|
4876
|
+
} else break;
|
|
4877
|
+
}
|
|
4878
|
+
clusters.push({ codepoints: cluster });
|
|
4879
|
+
}
|
|
4880
|
+
return clusters;
|
|
4881
|
+
}
|
|
4882
|
+
function shapeKhmerText(str, fontData) {
|
|
4883
|
+
const { cmap, ligatures, markAnchors, widths, defaultWidth } = fontData;
|
|
4884
|
+
const shaped = [];
|
|
4885
|
+
function resolveGid(cp) {
|
|
4886
|
+
const normCp = cp === 8239 || cp === 160 ? 32 : cp;
|
|
4887
|
+
return cmap[normCp] || 0;
|
|
4888
|
+
}
|
|
4889
|
+
function tryLig(gids) {
|
|
4890
|
+
return tryLigature(gids, ligatures);
|
|
4891
|
+
}
|
|
4892
|
+
function getAdv(gid) {
|
|
4893
|
+
return widths[gid] !== void 0 ? widths[gid] : defaultWidth;
|
|
4894
|
+
}
|
|
4895
|
+
function getBaseAnchor2(baseGid, markClass) {
|
|
4896
|
+
const base = markAnchors && markAnchors.bases && markAnchors.bases[baseGid];
|
|
4897
|
+
if (!base) return null;
|
|
4898
|
+
return base[markClass] ?? null;
|
|
4899
|
+
}
|
|
4900
|
+
function getMarkAnchor2(markGid) {
|
|
4901
|
+
const mark = markAnchors && markAnchors.marks && markAnchors.marks[markGid];
|
|
4902
|
+
if (!mark) return null;
|
|
4903
|
+
return { classIdx: mark[0], x: mark[1], y: mark[2] };
|
|
4904
|
+
}
|
|
4905
|
+
function emitGlyph(gid, isZero, baseGid) {
|
|
4906
|
+
if (isZero && baseGid !== void 0) {
|
|
4907
|
+
const markAnchor = getMarkAnchor2(gid);
|
|
4908
|
+
if (markAnchor) {
|
|
4909
|
+
const baseAnchorPt = getBaseAnchor2(baseGid, markAnchor.classIdx);
|
|
4910
|
+
if (baseAnchorPt) {
|
|
4911
|
+
const baseAdv = getAdv(baseGid);
|
|
4912
|
+
shaped.push({
|
|
4913
|
+
gid,
|
|
4914
|
+
dx: baseAnchorPt[0] - markAnchor.x - baseAdv,
|
|
4915
|
+
dy: baseAnchorPt[1] - markAnchor.y,
|
|
4916
|
+
isZeroAdvance: true
|
|
4917
|
+
});
|
|
4918
|
+
return;
|
|
4919
|
+
}
|
|
4920
|
+
}
|
|
4921
|
+
shaped.push({ gid, dx: 0, dy: 0, isZeroAdvance: true });
|
|
4922
|
+
} else {
|
|
4923
|
+
shaped.push({ gid, dx: 0, dy: 0, isZeroAdvance: false });
|
|
4924
|
+
}
|
|
4925
|
+
}
|
|
4926
|
+
const clusters = buildKhmerClusters(str);
|
|
4927
|
+
for (const cluster of clusters) {
|
|
4928
|
+
const { codepoints } = cluster;
|
|
4929
|
+
let baseGid = 0;
|
|
4930
|
+
for (let ci = 0; ci < codepoints.length; ci++) {
|
|
4931
|
+
const ct = khmerCharType(codepoints[ci]);
|
|
4932
|
+
if (ct === 0 || ct === 1) {
|
|
4933
|
+
baseGid = resolveGid(codepoints[ci]);
|
|
4934
|
+
break;
|
|
4935
|
+
}
|
|
4936
|
+
}
|
|
4937
|
+
for (let ci = 0; ci < codepoints.length; ci++) {
|
|
4938
|
+
if (khmerCharType(codepoints[ci]) === 4) emitGlyph(resolveGid(codepoints[ci]), false);
|
|
4939
|
+
}
|
|
4940
|
+
const stackGids = [];
|
|
4941
|
+
const stackEndIdx = [];
|
|
4942
|
+
let markStart = codepoints.length;
|
|
4943
|
+
for (let ci = 0; ci < codepoints.length; ci++) {
|
|
4944
|
+
const cp = codepoints[ci];
|
|
4945
|
+
const ct = khmerCharType(cp);
|
|
4946
|
+
if (ct === 0 || ct === 1 || ct === 7) {
|
|
4947
|
+
stackGids.push(resolveGid(cp));
|
|
4948
|
+
stackEndIdx.push(ci);
|
|
4949
|
+
} else if (ct >= 2 && ct <= 6) {
|
|
4950
|
+
markStart = ci;
|
|
4951
|
+
break;
|
|
4952
|
+
} else {
|
|
4953
|
+
emitGlyph(resolveGid(cp), false);
|
|
4954
|
+
}
|
|
4955
|
+
}
|
|
4956
|
+
const ligResult = tryLig(stackGids);
|
|
4957
|
+
if (ligResult) {
|
|
4958
|
+
emitGlyph(ligResult.resultGid, false);
|
|
4959
|
+
baseGid = ligResult.resultGid;
|
|
4960
|
+
let gi = ligResult.consumed;
|
|
4961
|
+
while (gi < stackGids.length) {
|
|
4962
|
+
const subLig = tryLig(stackGids.slice(gi));
|
|
4963
|
+
if (subLig) {
|
|
4964
|
+
emitGlyph(subLig.resultGid, false);
|
|
4965
|
+
gi += subLig.consumed;
|
|
4966
|
+
} else {
|
|
4967
|
+
const origCi = stackEndIdx[gi];
|
|
4968
|
+
const ct = khmerCharType(codepoints[origCi]);
|
|
4969
|
+
if (ct === 7) emitGlyph(stackGids[gi], true, baseGid);
|
|
4970
|
+
else emitGlyph(stackGids[gi], false);
|
|
4971
|
+
gi++;
|
|
4972
|
+
}
|
|
4973
|
+
}
|
|
4974
|
+
} else {
|
|
4975
|
+
for (let gi = 0; gi < stackGids.length; gi++) {
|
|
4976
|
+
const origCi = stackEndIdx[gi];
|
|
4977
|
+
const ct = khmerCharType(codepoints[origCi]);
|
|
4978
|
+
if (gi === 0) emitGlyph(stackGids[gi], false);
|
|
4979
|
+
else if (ct === 7) emitGlyph(stackGids[gi], true, baseGid);
|
|
4980
|
+
else emitGlyph(stackGids[gi], true, baseGid);
|
|
4981
|
+
}
|
|
4982
|
+
}
|
|
4983
|
+
for (let ci = markStart; ci < codepoints.length; ci++) {
|
|
4984
|
+
const cp = codepoints[ci];
|
|
4985
|
+
const ct = khmerCharType(cp);
|
|
4986
|
+
if (ct === 2 || ct === 3 || ct === 6) emitGlyph(resolveGid(cp), true, baseGid);
|
|
4987
|
+
else if (ct === 5) emitGlyph(resolveGid(cp), false);
|
|
4988
|
+
else if (ct === 4) ; else emitGlyph(resolveGid(cp), false);
|
|
4989
|
+
}
|
|
4990
|
+
}
|
|
4991
|
+
return shaped;
|
|
4992
|
+
}
|
|
4993
|
+
|
|
4994
|
+
// src/shaping/myanmar-shaper.ts
|
|
4995
|
+
var VIRAMA3 = MYANMAR_VIRAMA;
|
|
4996
|
+
var MEDIAL_RA = 4156;
|
|
4997
|
+
function myanmarCharType(cp) {
|
|
4998
|
+
if (cp === VIRAMA3) return 7;
|
|
4999
|
+
if (cp === MEDIAL_RA) return 4;
|
|
5000
|
+
if (cp === 4155 || cp === 4157 || cp === 4158) return 8;
|
|
5001
|
+
if (cp >= 4096 && cp <= 4138) return 0;
|
|
5002
|
+
if (cp === 4159) return 0;
|
|
5003
|
+
if (cp >= 4176 && cp <= 4181) return 0;
|
|
5004
|
+
if (cp === 4141 || cp === 4142 || cp === 4146 || cp === 4150 || cp === 4147 || cp === 4148 || cp === 4149) return 2;
|
|
5005
|
+
if (cp === 4143 || cp === 4144 || cp === 4151) return 3;
|
|
5006
|
+
if (cp === 4139 || cp === 4140) return 5;
|
|
5007
|
+
if (cp === 4145) return 4;
|
|
5008
|
+
if (cp === 4154) return 6;
|
|
5009
|
+
if (cp === 4152) return 5;
|
|
5010
|
+
if (cp >= 4160 && cp <= 4169) return 9;
|
|
5011
|
+
if (cp >= 4240 && cp <= 4249) return 9;
|
|
5012
|
+
if (cp === 4170 || cp === 4171) return 9;
|
|
5013
|
+
if (cp >= 4172 && cp <= 4175) return 9;
|
|
5014
|
+
return -1;
|
|
5015
|
+
}
|
|
5016
|
+
function isConsonant6(cp) {
|
|
5017
|
+
return myanmarCharType(cp) === 0;
|
|
5018
|
+
}
|
|
5019
|
+
function buildMyanmarClusters(str) {
|
|
5020
|
+
const clusters = [];
|
|
5021
|
+
const cps = [];
|
|
5022
|
+
for (let i2 = 0; i2 < str.length; ) {
|
|
5023
|
+
const cp = str.codePointAt(i2) ?? 0;
|
|
5024
|
+
cps.push(cp);
|
|
5025
|
+
i2 += cp > 65535 ? 2 : 1;
|
|
5026
|
+
}
|
|
5027
|
+
let i = 0;
|
|
5028
|
+
while (i < cps.length) {
|
|
5029
|
+
const cp = cps[i];
|
|
5030
|
+
const type = myanmarCharType(cp);
|
|
5031
|
+
if (type < 0 || cp < MYANMAR_START || cp > MYANMAR_END) {
|
|
5032
|
+
clusters.push({ codepoints: [cp] });
|
|
5033
|
+
i++;
|
|
5034
|
+
continue;
|
|
5035
|
+
}
|
|
5036
|
+
if (type !== 0) {
|
|
5037
|
+
clusters.push({ codepoints: [cp] });
|
|
5038
|
+
i++;
|
|
5039
|
+
continue;
|
|
5040
|
+
}
|
|
5041
|
+
const cluster = [cp];
|
|
5042
|
+
i++;
|
|
5043
|
+
while (i < cps.length && cps[i] === VIRAMA3) {
|
|
5044
|
+
if (i + 1 < cps.length && isConsonant6(cps[i + 1])) {
|
|
5045
|
+
cluster.push(cps[i]);
|
|
5046
|
+
cluster.push(cps[i + 1]);
|
|
5047
|
+
i += 2;
|
|
5048
|
+
} else {
|
|
5049
|
+
cluster.push(cps[i]);
|
|
5050
|
+
i++;
|
|
5051
|
+
break;
|
|
5052
|
+
}
|
|
5053
|
+
}
|
|
5054
|
+
while (i < cps.length && myanmarCharType(cps[i]) === 8) {
|
|
5055
|
+
cluster.push(cps[i]);
|
|
5056
|
+
i++;
|
|
5057
|
+
}
|
|
5058
|
+
while (i < cps.length) {
|
|
5059
|
+
const ct = myanmarCharType(cps[i]);
|
|
5060
|
+
if (ct >= 2 && ct <= 6) {
|
|
5061
|
+
cluster.push(cps[i]);
|
|
5062
|
+
i++;
|
|
5063
|
+
} else break;
|
|
5064
|
+
}
|
|
5065
|
+
clusters.push({ codepoints: cluster });
|
|
5066
|
+
}
|
|
5067
|
+
return clusters;
|
|
5068
|
+
}
|
|
5069
|
+
function shapeMyanmarText(str, fontData) {
|
|
5070
|
+
const { cmap, ligatures, markAnchors, widths, defaultWidth } = fontData;
|
|
5071
|
+
const shaped = [];
|
|
5072
|
+
function resolveGid(cp) {
|
|
5073
|
+
const normCp = cp === 8239 || cp === 160 ? 32 : cp;
|
|
5074
|
+
return cmap[normCp] || 0;
|
|
5075
|
+
}
|
|
5076
|
+
function tryLig(gids) {
|
|
5077
|
+
return tryLigature(gids, ligatures);
|
|
5078
|
+
}
|
|
5079
|
+
function getAdv(gid) {
|
|
5080
|
+
return widths[gid] !== void 0 ? widths[gid] : defaultWidth;
|
|
5081
|
+
}
|
|
5082
|
+
function getBaseAnchor2(baseGid, markClass) {
|
|
5083
|
+
const base = markAnchors && markAnchors.bases && markAnchors.bases[baseGid];
|
|
5084
|
+
if (!base) return null;
|
|
5085
|
+
return base[markClass] ?? null;
|
|
5086
|
+
}
|
|
5087
|
+
function getMarkAnchor2(markGid) {
|
|
5088
|
+
const mark = markAnchors && markAnchors.marks && markAnchors.marks[markGid];
|
|
5089
|
+
if (!mark) return null;
|
|
5090
|
+
return { classIdx: mark[0], x: mark[1], y: mark[2] };
|
|
5091
|
+
}
|
|
5092
|
+
function emitGlyph(gid, isZero, baseGid) {
|
|
5093
|
+
if (isZero && baseGid !== void 0) {
|
|
5094
|
+
const markAnchor = getMarkAnchor2(gid);
|
|
5095
|
+
if (markAnchor) {
|
|
5096
|
+
const baseAnchorPt = getBaseAnchor2(baseGid, markAnchor.classIdx);
|
|
5097
|
+
if (baseAnchorPt) {
|
|
5098
|
+
const baseAdv = getAdv(baseGid);
|
|
5099
|
+
shaped.push({
|
|
5100
|
+
gid,
|
|
5101
|
+
dx: baseAnchorPt[0] - markAnchor.x - baseAdv,
|
|
5102
|
+
dy: baseAnchorPt[1] - markAnchor.y,
|
|
5103
|
+
isZeroAdvance: true
|
|
5104
|
+
});
|
|
5105
|
+
return;
|
|
5106
|
+
}
|
|
5107
|
+
}
|
|
5108
|
+
shaped.push({ gid, dx: 0, dy: 0, isZeroAdvance: true });
|
|
5109
|
+
} else {
|
|
5110
|
+
shaped.push({ gid, dx: 0, dy: 0, isZeroAdvance: false });
|
|
5111
|
+
}
|
|
5112
|
+
}
|
|
5113
|
+
const clusters = buildMyanmarClusters(str);
|
|
5114
|
+
for (const cluster of clusters) {
|
|
5115
|
+
const { codepoints } = cluster;
|
|
5116
|
+
let baseGid = 0;
|
|
5117
|
+
for (let ci = 0; ci < codepoints.length; ci++) {
|
|
5118
|
+
if (myanmarCharType(codepoints[ci]) === 0) {
|
|
5119
|
+
baseGid = resolveGid(codepoints[ci]);
|
|
5120
|
+
break;
|
|
5121
|
+
}
|
|
5122
|
+
}
|
|
5123
|
+
for (let ci = 0; ci < codepoints.length; ci++) {
|
|
5124
|
+
if (myanmarCharType(codepoints[ci]) === 4) emitGlyph(resolveGid(codepoints[ci]), false);
|
|
5125
|
+
}
|
|
5126
|
+
const stackGids = [];
|
|
5127
|
+
const stackEndIdx = [];
|
|
5128
|
+
let markStart = codepoints.length;
|
|
5129
|
+
for (let ci = 0; ci < codepoints.length; ci++) {
|
|
5130
|
+
const cp = codepoints[ci];
|
|
5131
|
+
const ct = myanmarCharType(cp);
|
|
5132
|
+
if (ct === 0 || ct === 7 || ct === 8) {
|
|
5133
|
+
stackGids.push(resolveGid(cp));
|
|
5134
|
+
stackEndIdx.push(ci);
|
|
5135
|
+
} else if (ct === 2 || ct === 3 || ct === 5 || ct === 6) {
|
|
5136
|
+
markStart = ci;
|
|
5137
|
+
break;
|
|
5138
|
+
} else if (ct === 4) ; else {
|
|
5139
|
+
emitGlyph(resolveGid(cp), false);
|
|
5140
|
+
}
|
|
5141
|
+
}
|
|
5142
|
+
const ligResult = tryLig(stackGids);
|
|
5143
|
+
if (ligResult) {
|
|
5144
|
+
emitGlyph(ligResult.resultGid, false);
|
|
5145
|
+
baseGid = ligResult.resultGid;
|
|
5146
|
+
let gi = ligResult.consumed;
|
|
5147
|
+
while (gi < stackGids.length) {
|
|
5148
|
+
const subLig = tryLig(stackGids.slice(gi));
|
|
5149
|
+
if (subLig) {
|
|
5150
|
+
emitGlyph(subLig.resultGid, false);
|
|
5151
|
+
gi += subLig.consumed;
|
|
5152
|
+
} else {
|
|
5153
|
+
const origCi = stackEndIdx[gi];
|
|
5154
|
+
const ct = myanmarCharType(codepoints[origCi]);
|
|
5155
|
+
if (ct === 7 || ct === 8) emitGlyph(stackGids[gi], true, baseGid);
|
|
5156
|
+
else emitGlyph(stackGids[gi], false);
|
|
5157
|
+
gi++;
|
|
5158
|
+
}
|
|
5159
|
+
}
|
|
5160
|
+
} else {
|
|
5161
|
+
for (let gi = 0; gi < stackGids.length; gi++) {
|
|
5162
|
+
const origCi = stackEndIdx[gi];
|
|
5163
|
+
const ct = myanmarCharType(codepoints[origCi]);
|
|
5164
|
+
if (gi === 0) emitGlyph(stackGids[gi], false);
|
|
5165
|
+
else if (ct === 7 || ct === 8) emitGlyph(stackGids[gi], true, baseGid);
|
|
5166
|
+
else emitGlyph(stackGids[gi], false);
|
|
5167
|
+
}
|
|
5168
|
+
}
|
|
5169
|
+
for (let ci = markStart; ci < codepoints.length; ci++) {
|
|
5170
|
+
const cp = codepoints[ci];
|
|
5171
|
+
const ct = myanmarCharType(cp);
|
|
5172
|
+
if (ct === 2 || ct === 3 || ct === 6) emitGlyph(resolveGid(cp), true, baseGid);
|
|
5173
|
+
else if (ct === 5) emitGlyph(resolveGid(cp), false);
|
|
5174
|
+
else if (ct === 4) ; else emitGlyph(resolveGid(cp), false);
|
|
3688
5175
|
}
|
|
3689
5176
|
}
|
|
3690
5177
|
return shaped;
|
|
@@ -3736,7 +5223,7 @@ function devanagariCharType(cp) {
|
|
|
3736
5223
|
if (cp === 2384) return 1;
|
|
3737
5224
|
return -1;
|
|
3738
5225
|
}
|
|
3739
|
-
function
|
|
5226
|
+
function isConsonant7(cp) {
|
|
3740
5227
|
return devanagariCharType(cp) === 0;
|
|
3741
5228
|
}
|
|
3742
5229
|
function buildDevanagariClusters(str) {
|
|
@@ -3751,6 +5238,10 @@ function buildDevanagariClusters(str) {
|
|
|
3751
5238
|
while (i < cps.length) {
|
|
3752
5239
|
const cp = cps[i];
|
|
3753
5240
|
const type = devanagariCharType(cp);
|
|
5241
|
+
if (classifyUseCategory(cp) === "ZWJ" || classifyUseCategory(cp) === "ZWNJ") {
|
|
5242
|
+
i++;
|
|
5243
|
+
continue;
|
|
5244
|
+
}
|
|
3754
5245
|
if (type < 0 || cp < DEVANAGARI_START || cp > DEVANAGARI_END) {
|
|
3755
5246
|
clusters.push({ codepoints: [cp], baseIndex: 0, hasReph: false, preBaseMatras: [] });
|
|
3756
5247
|
i++;
|
|
@@ -3759,7 +5250,7 @@ function buildDevanagariClusters(str) {
|
|
|
3759
5250
|
const syllable = [];
|
|
3760
5251
|
let hasReph = false;
|
|
3761
5252
|
const preMatras = [];
|
|
3762
|
-
if (
|
|
5253
|
+
if (isConsonant7(cp) && cp === RA2 && i + 2 < cps.length && cps[i + 1] === HALANT2 && isConsonant7(cps[i + 2])) {
|
|
3763
5254
|
hasReph = true;
|
|
3764
5255
|
syllable.push(cp, cps[i + 1]);
|
|
3765
5256
|
i += 2;
|
|
@@ -3777,13 +5268,24 @@ function buildDevanagariClusters(str) {
|
|
|
3777
5268
|
i++;
|
|
3778
5269
|
}
|
|
3779
5270
|
if (i < cps.length && cps[i] === HALANT2) {
|
|
3780
|
-
|
|
5271
|
+
let j = i + 1;
|
|
5272
|
+
let zwnj = false;
|
|
5273
|
+
if (j < cps.length) {
|
|
5274
|
+
const jc = classifyUseCategory(cps[j]);
|
|
5275
|
+
if (jc === "ZWJ") {
|
|
5276
|
+
j++;
|
|
5277
|
+
} else if (jc === "ZWNJ") {
|
|
5278
|
+
j++;
|
|
5279
|
+
zwnj = true;
|
|
5280
|
+
}
|
|
5281
|
+
}
|
|
5282
|
+
if (!zwnj && j < cps.length && isConsonant7(cps[j])) {
|
|
3781
5283
|
syllable.push(cps[i]);
|
|
3782
|
-
i
|
|
5284
|
+
i = j;
|
|
3783
5285
|
continue;
|
|
3784
5286
|
} else {
|
|
3785
5287
|
syllable.push(cps[i]);
|
|
3786
|
-
i
|
|
5288
|
+
i = j;
|
|
3787
5289
|
break;
|
|
3788
5290
|
}
|
|
3789
5291
|
}
|
|
@@ -4141,6 +5643,448 @@ function shapeArabicText(str, fontData) {
|
|
|
4141
5643
|
return glyphs;
|
|
4142
5644
|
}
|
|
4143
5645
|
|
|
5646
|
+
// src/fonts/font-loader.ts
|
|
5647
|
+
var _fontRegistry = /* @__PURE__ */ new Map();
|
|
5648
|
+
var _fontDataCache = /* @__PURE__ */ new Map();
|
|
5649
|
+
var _fontBinaryCache = /* @__PURE__ */ new WeakMap();
|
|
5650
|
+
function getDecodedFontBytes(fontData) {
|
|
5651
|
+
const cached = _fontBinaryCache.get(fontData);
|
|
5652
|
+
if (cached) return cached;
|
|
5653
|
+
let bytes;
|
|
5654
|
+
if (typeof atob === "function") {
|
|
5655
|
+
const binaryStr = atob(fontData.ttfBase64);
|
|
5656
|
+
bytes = new Uint8Array(binaryStr.length);
|
|
5657
|
+
for (let i = 0; i < binaryStr.length; i++) bytes[i] = binaryStr.charCodeAt(i);
|
|
5658
|
+
} else {
|
|
5659
|
+
const buf = globalThis["Buffer"];
|
|
5660
|
+
bytes = buf.from(fontData.ttfBase64, "base64");
|
|
5661
|
+
}
|
|
5662
|
+
_fontBinaryCache.set(fontData, bytes);
|
|
5663
|
+
return bytes;
|
|
5664
|
+
}
|
|
5665
|
+
function registerFont(lang, loader) {
|
|
5666
|
+
_fontRegistry.set(lang, loader);
|
|
5667
|
+
}
|
|
5668
|
+
function registerFonts(fonts) {
|
|
5669
|
+
for (const [lang, loader] of Object.entries(fonts)) {
|
|
5670
|
+
_fontRegistry.set(lang, loader);
|
|
5671
|
+
}
|
|
5672
|
+
}
|
|
5673
|
+
async function loadFontData(lang) {
|
|
5674
|
+
const cached = _fontDataCache.get(lang);
|
|
5675
|
+
if (cached) return cached;
|
|
5676
|
+
const loader = _fontRegistry.get(lang);
|
|
5677
|
+
if (!loader) return null;
|
|
5678
|
+
for (let attempt = 0; attempt < 2; attempt++) {
|
|
5679
|
+
try {
|
|
5680
|
+
const mod2 = await loader();
|
|
5681
|
+
const fontData = "default" in mod2 ? mod2.default : mod2;
|
|
5682
|
+
_fontDataCache.set(lang, fontData);
|
|
5683
|
+
return fontData;
|
|
5684
|
+
} catch {
|
|
5685
|
+
if (attempt === 0) {
|
|
5686
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
5687
|
+
}
|
|
5688
|
+
}
|
|
5689
|
+
}
|
|
5690
|
+
return null;
|
|
5691
|
+
}
|
|
5692
|
+
function hasFontLoader(lang) {
|
|
5693
|
+
return _fontRegistry.has(lang);
|
|
5694
|
+
}
|
|
5695
|
+
function getRegisteredLangs() {
|
|
5696
|
+
return [..._fontRegistry.keys()];
|
|
5697
|
+
}
|
|
5698
|
+
function clearFontCache() {
|
|
5699
|
+
_fontDataCache.clear();
|
|
5700
|
+
}
|
|
5701
|
+
function resetFontRegistry() {
|
|
5702
|
+
_fontRegistry.clear();
|
|
5703
|
+
_fontDataCache.clear();
|
|
5704
|
+
}
|
|
5705
|
+
|
|
5706
|
+
// src/fonts/glyf-outline.ts
|
|
5707
|
+
function readTableDirectory(view) {
|
|
5708
|
+
const numTables = view.getUint16(4);
|
|
5709
|
+
const tables = {};
|
|
5710
|
+
for (let i = 0; i < numTables; i++) {
|
|
5711
|
+
const rec = 12 + i * 16;
|
|
5712
|
+
const tag = String.fromCharCode(
|
|
5713
|
+
view.getUint8(rec),
|
|
5714
|
+
view.getUint8(rec + 1),
|
|
5715
|
+
view.getUint8(rec + 2),
|
|
5716
|
+
view.getUint8(rec + 3)
|
|
5717
|
+
);
|
|
5718
|
+
tables[tag] = { offset: view.getUint32(rec + 8), length: view.getUint32(rec + 12) };
|
|
5719
|
+
}
|
|
5720
|
+
return tables;
|
|
5721
|
+
}
|
|
5722
|
+
function parseGlyfFont(bytes) {
|
|
5723
|
+
if (bytes.length < 12) return null;
|
|
5724
|
+
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
5725
|
+
const tables = readTableDirectory(view);
|
|
5726
|
+
const head = tables["head"];
|
|
5727
|
+
const maxp = tables["maxp"];
|
|
5728
|
+
const loca = tables["loca"];
|
|
5729
|
+
const glyf = tables["glyf"];
|
|
5730
|
+
if (!head || !maxp || !loca || !glyf) return null;
|
|
5731
|
+
const unitsPerEm = view.getUint16(head.offset + 18) || 1e3;
|
|
5732
|
+
const locaFormat = view.getInt16(head.offset + 50);
|
|
5733
|
+
const numGlyphs = view.getUint16(maxp.offset + 4);
|
|
5734
|
+
const locaOffsets = new Array(numGlyphs + 1);
|
|
5735
|
+
for (let i = 0; i <= numGlyphs; i++) {
|
|
5736
|
+
locaOffsets[i] = locaFormat === 0 ? view.getUint16(loca.offset + i * 2) * 2 : view.getUint32(loca.offset + i * 4);
|
|
5737
|
+
}
|
|
5738
|
+
return { view, glyfOffset: glyf.offset, locaOffsets, unitsPerEm };
|
|
5739
|
+
}
|
|
5740
|
+
function transformPoint(p, a, b, c, d, e, f) {
|
|
5741
|
+
return {
|
|
5742
|
+
x: a * p.x + c * p.y + e,
|
|
5743
|
+
y: b * p.x + d * p.y + f,
|
|
5744
|
+
onCurve: p.onCurve
|
|
5745
|
+
};
|
|
5746
|
+
}
|
|
5747
|
+
function readF2Dot14(view, pos) {
|
|
5748
|
+
return view.getInt16(pos) / 16384;
|
|
5749
|
+
}
|
|
5750
|
+
function extractGlyphContours(font, gid, depth = 0) {
|
|
5751
|
+
if (depth > 8) return [];
|
|
5752
|
+
const { view, glyfOffset, locaOffsets } = font;
|
|
5753
|
+
if (gid < 0 || gid + 1 >= locaOffsets.length) return [];
|
|
5754
|
+
const start = locaOffsets[gid];
|
|
5755
|
+
const end = locaOffsets[gid + 1];
|
|
5756
|
+
if (end <= start) return [];
|
|
5757
|
+
const base = glyfOffset + start;
|
|
5758
|
+
const numberOfContours = view.getInt16(base);
|
|
5759
|
+
if (numberOfContours < 0) {
|
|
5760
|
+
return extractCompositeContours(font, base, depth);
|
|
5761
|
+
}
|
|
5762
|
+
return extractSimpleContours(view, base, numberOfContours);
|
|
5763
|
+
}
|
|
5764
|
+
function extractSimpleContours(view, base, numberOfContours) {
|
|
5765
|
+
let pos = base + 10;
|
|
5766
|
+
const endPts = new Array(numberOfContours);
|
|
5767
|
+
for (let i = 0; i < numberOfContours; i++) {
|
|
5768
|
+
endPts[i] = view.getUint16(pos);
|
|
5769
|
+
pos += 2;
|
|
5770
|
+
}
|
|
5771
|
+
const numPoints = numberOfContours > 0 ? endPts[numberOfContours - 1] + 1 : 0;
|
|
5772
|
+
const instrLen = view.getUint16(pos);
|
|
5773
|
+
pos += 2 + instrLen;
|
|
5774
|
+
const flags = new Array(numPoints);
|
|
5775
|
+
for (let i = 0; i < numPoints; ) {
|
|
5776
|
+
const flag = view.getUint8(pos++);
|
|
5777
|
+
flags[i++] = flag;
|
|
5778
|
+
if (flag & 8) {
|
|
5779
|
+
let repeat = view.getUint8(pos++);
|
|
5780
|
+
while (repeat-- > 0 && i < numPoints) flags[i++] = flag;
|
|
5781
|
+
}
|
|
5782
|
+
}
|
|
5783
|
+
const xs = new Array(numPoints);
|
|
5784
|
+
let x = 0;
|
|
5785
|
+
for (let i = 0; i < numPoints; i++) {
|
|
5786
|
+
const flag = flags[i];
|
|
5787
|
+
if (flag & 2) {
|
|
5788
|
+
const dx = view.getUint8(pos++);
|
|
5789
|
+
x += flag & 16 ? dx : -dx;
|
|
5790
|
+
} else if (!(flag & 16)) {
|
|
5791
|
+
x += view.getInt16(pos);
|
|
5792
|
+
pos += 2;
|
|
5793
|
+
}
|
|
5794
|
+
xs[i] = x;
|
|
5795
|
+
}
|
|
5796
|
+
const ys = new Array(numPoints);
|
|
5797
|
+
let y = 0;
|
|
5798
|
+
for (let i = 0; i < numPoints; i++) {
|
|
5799
|
+
const flag = flags[i];
|
|
5800
|
+
if (flag & 4) {
|
|
5801
|
+
const dy = view.getUint8(pos++);
|
|
5802
|
+
y += flag & 32 ? dy : -dy;
|
|
5803
|
+
} else if (!(flag & 32)) {
|
|
5804
|
+
y += view.getInt16(pos);
|
|
5805
|
+
pos += 2;
|
|
5806
|
+
}
|
|
5807
|
+
ys[i] = y;
|
|
5808
|
+
}
|
|
5809
|
+
const contours = [];
|
|
5810
|
+
let startPt = 0;
|
|
5811
|
+
for (let c = 0; c < numberOfContours; c++) {
|
|
5812
|
+
const endPt = endPts[c];
|
|
5813
|
+
const contour = [];
|
|
5814
|
+
for (let i = startPt; i <= endPt; i++) {
|
|
5815
|
+
contour.push({ x: xs[i], y: ys[i], onCurve: (flags[i] & 1) !== 0 });
|
|
5816
|
+
}
|
|
5817
|
+
if (contour.length > 0) contours.push(contour);
|
|
5818
|
+
startPt = endPt + 1;
|
|
5819
|
+
}
|
|
5820
|
+
return contours;
|
|
5821
|
+
}
|
|
5822
|
+
function extractCompositeContours(font, base, depth) {
|
|
5823
|
+
const { view } = font;
|
|
5824
|
+
let pos = base + 10;
|
|
5825
|
+
const out = [];
|
|
5826
|
+
while (true) {
|
|
5827
|
+
const flags = view.getUint16(pos);
|
|
5828
|
+
pos += 2;
|
|
5829
|
+
const componentGid = view.getUint16(pos);
|
|
5830
|
+
pos += 2;
|
|
5831
|
+
let arg1;
|
|
5832
|
+
let arg2;
|
|
5833
|
+
if (flags & 1) {
|
|
5834
|
+
arg1 = view.getInt16(pos);
|
|
5835
|
+
pos += 2;
|
|
5836
|
+
arg2 = view.getInt16(pos);
|
|
5837
|
+
pos += 2;
|
|
5838
|
+
} else {
|
|
5839
|
+
arg1 = view.getInt8(pos);
|
|
5840
|
+
pos += 1;
|
|
5841
|
+
arg2 = view.getInt8(pos);
|
|
5842
|
+
pos += 1;
|
|
5843
|
+
}
|
|
5844
|
+
let a = 1, b = 0, c = 0, d = 1;
|
|
5845
|
+
if (flags & 8) {
|
|
5846
|
+
a = d = readF2Dot14(view, pos);
|
|
5847
|
+
pos += 2;
|
|
5848
|
+
} else if (flags & 64) {
|
|
5849
|
+
a = readF2Dot14(view, pos);
|
|
5850
|
+
pos += 2;
|
|
5851
|
+
d = readF2Dot14(view, pos);
|
|
5852
|
+
pos += 2;
|
|
5853
|
+
} else if (flags & 128) {
|
|
5854
|
+
a = readF2Dot14(view, pos);
|
|
5855
|
+
pos += 2;
|
|
5856
|
+
b = readF2Dot14(view, pos);
|
|
5857
|
+
pos += 2;
|
|
5858
|
+
c = readF2Dot14(view, pos);
|
|
5859
|
+
pos += 2;
|
|
5860
|
+
d = readF2Dot14(view, pos);
|
|
5861
|
+
pos += 2;
|
|
5862
|
+
}
|
|
5863
|
+
const e = flags & 2 ? arg1 : 0;
|
|
5864
|
+
const f = flags & 2 ? arg2 : 0;
|
|
5865
|
+
const sub = extractGlyphContours(font, componentGid, depth + 1);
|
|
5866
|
+
for (const contour of sub) {
|
|
5867
|
+
out.push(contour.map((p) => transformPoint(p, a, b, c, d, e, f)));
|
|
5868
|
+
}
|
|
5869
|
+
if (!(flags & 32)) break;
|
|
5870
|
+
}
|
|
5871
|
+
return out;
|
|
5872
|
+
}
|
|
5873
|
+
|
|
5874
|
+
// src/core/pdf-color-glyph.ts
|
|
5875
|
+
var ID = [1, 0, 0, 1, 0, 0];
|
|
5876
|
+
function n(v) {
|
|
5877
|
+
if (!Number.isFinite(v)) return "0";
|
|
5878
|
+
if (Number.isInteger(v)) return String(v);
|
|
5879
|
+
let s = v.toFixed(3);
|
|
5880
|
+
s = s.replace(/0+$/, "").replace(/\.$/, "");
|
|
5881
|
+
return s === "-0" ? "0" : s;
|
|
5882
|
+
}
|
|
5883
|
+
function tx(m, x, y) {
|
|
5884
|
+
return [m[0] * x + m[2] * y + m[4], m[1] * x + m[3] * y + m[5]];
|
|
5885
|
+
}
|
|
5886
|
+
function ch(v) {
|
|
5887
|
+
return n(Math.max(0, Math.min(1, v / 255)));
|
|
5888
|
+
}
|
|
5889
|
+
function contoursToPath(contours, m = ID) {
|
|
5890
|
+
const ops = [];
|
|
5891
|
+
for (const contour of contours) {
|
|
5892
|
+
if (contour.length === 0) continue;
|
|
5893
|
+
const pts = contour.slice();
|
|
5894
|
+
let startIdx = pts.findIndex((p) => p.onCurve);
|
|
5895
|
+
let start;
|
|
5896
|
+
if (startIdx < 0) {
|
|
5897
|
+
const a = pts[0], b = pts[pts.length - 1];
|
|
5898
|
+
start = [(a.x + b.x) / 2, (a.y + b.y) / 2];
|
|
5899
|
+
startIdx = 0;
|
|
5900
|
+
} else {
|
|
5901
|
+
start = [pts[startIdx].x, pts[startIdx].y];
|
|
5902
|
+
}
|
|
5903
|
+
const [sx, sy] = tx(m, start[0], start[1]);
|
|
5904
|
+
ops.push(`${n(sx)} ${n(sy)} m`);
|
|
5905
|
+
const len = pts.length;
|
|
5906
|
+
let curX = start[0], curY = start[1];
|
|
5907
|
+
let i = 1;
|
|
5908
|
+
while (i <= len) {
|
|
5909
|
+
const p = pts[(startIdx + i) % len];
|
|
5910
|
+
if (p.onCurve) {
|
|
5911
|
+
const [px, py] = tx(m, p.x, p.y);
|
|
5912
|
+
ops.push(`${n(px)} ${n(py)} l`);
|
|
5913
|
+
curX = p.x;
|
|
5914
|
+
curY = p.y;
|
|
5915
|
+
i++;
|
|
5916
|
+
} else {
|
|
5917
|
+
const next = pts[(startIdx + i + 1) % len];
|
|
5918
|
+
let endX, endY;
|
|
5919
|
+
let consumed;
|
|
5920
|
+
if (next.onCurve) {
|
|
5921
|
+
endX = next.x;
|
|
5922
|
+
endY = next.y;
|
|
5923
|
+
consumed = 2;
|
|
5924
|
+
} else {
|
|
5925
|
+
endX = (p.x + next.x) / 2;
|
|
5926
|
+
endY = (p.y + next.y) / 2;
|
|
5927
|
+
consumed = 1;
|
|
5928
|
+
}
|
|
5929
|
+
const c1x = curX + 2 / 3 * (p.x - curX);
|
|
5930
|
+
const c1y = curY + 2 / 3 * (p.y - curY);
|
|
5931
|
+
const c2x = endX + 2 / 3 * (p.x - endX);
|
|
5932
|
+
const c2y = endY + 2 / 3 * (p.y - endY);
|
|
5933
|
+
const [a1, b1] = tx(m, c1x, c1y);
|
|
5934
|
+
const [a2, b2] = tx(m, c2x, c2y);
|
|
5935
|
+
const [ex, ey] = tx(m, endX, endY);
|
|
5936
|
+
ops.push(`${n(a1)} ${n(b1)} ${n(a2)} ${n(b2)} ${n(ex)} ${n(ey)} c`);
|
|
5937
|
+
curX = endX;
|
|
5938
|
+
curY = endY;
|
|
5939
|
+
i += consumed;
|
|
5940
|
+
}
|
|
5941
|
+
}
|
|
5942
|
+
ops.push("h");
|
|
5943
|
+
}
|
|
5944
|
+
return ops.join("\n");
|
|
5945
|
+
}
|
|
5946
|
+
function buildGradientFunction(stops) {
|
|
5947
|
+
const sorted = stops.slice().sort((a, b) => a.offset - b.offset);
|
|
5948
|
+
if (sorted.length === 0) return "<< /FunctionType 2 /Domain [0 1] /C0 [0 0 0] /C1 [0 0 0] /N 1 >>";
|
|
5949
|
+
if (sorted.length === 1) {
|
|
5950
|
+
const c = sorted[0].color;
|
|
5951
|
+
return `<< /FunctionType 2 /Domain [0 1] /C0 [${ch(c[0])} ${ch(c[1])} ${ch(c[2])}] /C1 [${ch(c[0])} ${ch(c[1])} ${ch(c[2])}] /N 1 >>`;
|
|
5952
|
+
}
|
|
5953
|
+
if (sorted.length === 2) {
|
|
5954
|
+
const a = sorted[0].color, b = sorted[1].color;
|
|
5955
|
+
return `<< /FunctionType 2 /Domain [0 1] /C0 [${ch(a[0])} ${ch(a[1])} ${ch(a[2])}] /C1 [${ch(b[0])} ${ch(b[1])} ${ch(b[2])}] /N 1 >>`;
|
|
5956
|
+
}
|
|
5957
|
+
const subFns = [];
|
|
5958
|
+
const bounds = [];
|
|
5959
|
+
const encode = [];
|
|
5960
|
+
for (let i = 0; i < sorted.length - 1; i++) {
|
|
5961
|
+
const a = sorted[i].color, b = sorted[i + 1].color;
|
|
5962
|
+
subFns.push(`<< /FunctionType 2 /Domain [0 1] /C0 [${ch(a[0])} ${ch(a[1])} ${ch(a[2])}] /C1 [${ch(b[0])} ${ch(b[1])} ${ch(b[2])}] /N 1 >>`);
|
|
5963
|
+
encode.push("0 1");
|
|
5964
|
+
if (i > 0) bounds.push(n(Math.max(0, Math.min(1, sorted[i].offset))));
|
|
5965
|
+
}
|
|
5966
|
+
return `<< /FunctionType 3 /Domain [0 1] /Functions [${subFns.join(" ")}] /Bounds [${bounds.join(" ")}] /Encode [${encode.join(" ")}] >>`;
|
|
5967
|
+
}
|
|
5968
|
+
function extendFlags(extend) {
|
|
5969
|
+
return extend === "pad" ? "[true true]" : "[true true]";
|
|
5970
|
+
}
|
|
5971
|
+
function linearShadingDict(p, m) {
|
|
5972
|
+
const [x0, y0] = tx(m, p.p0[0], p.p0[1]);
|
|
5973
|
+
const [x1, y1] = tx(m, p.p1[0], p.p1[1]);
|
|
5974
|
+
return `<< /ShadingType 2 /ColorSpace /DeviceRGB /Coords [${n(x0)} ${n(y0)} ${n(x1)} ${n(y1)}] /Function ${buildGradientFunction(p.stops)} /Extend ${extendFlags(p.extend)} >>`;
|
|
5975
|
+
}
|
|
5976
|
+
function radialShadingDict(p, m) {
|
|
5977
|
+
const [x0, y0] = tx(m, p.c0[0], p.c0[1]);
|
|
5978
|
+
const [x1, y1] = tx(m, p.c1[0], p.c1[1]);
|
|
5979
|
+
const sx = Math.hypot(m[0], m[1]);
|
|
5980
|
+
const sy = Math.hypot(m[2], m[3]);
|
|
5981
|
+
const s = (sx + sy) / 2 || 1;
|
|
5982
|
+
return `<< /ShadingType 3 /ColorSpace /DeviceRGB /Coords [${n(x0)} ${n(y0)} ${n(p.r0 * s)} ${n(x1)} ${n(y1)} ${n(p.r1 * s)}] /Function ${buildGradientFunction(p.stops)} /Extend ${extendFlags(p.extend)} >>`;
|
|
5983
|
+
}
|
|
5984
|
+
function renderColorGlyph(glyph, outlines, unitsPerEm) {
|
|
5985
|
+
const body = [];
|
|
5986
|
+
const shadings = [];
|
|
5987
|
+
const extGStates = [];
|
|
5988
|
+
const alphaMap = /* @__PURE__ */ new Map();
|
|
5989
|
+
let shadingIdx = 0;
|
|
5990
|
+
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
|
|
5991
|
+
for (const layer of glyph.layers) {
|
|
5992
|
+
const m = layer.transform ?? ID;
|
|
5993
|
+
const contours = outlines(layer.glyphId);
|
|
5994
|
+
if (contours.length === 0) continue;
|
|
5995
|
+
const path = contoursToPath(contours, m);
|
|
5996
|
+
for (const contour of contours) {
|
|
5997
|
+
for (const pt of contour) {
|
|
5998
|
+
const [px, py] = tx(m, pt.x, pt.y);
|
|
5999
|
+
if (px < minX) minX = px;
|
|
6000
|
+
if (py < minY) minY = py;
|
|
6001
|
+
if (px > maxX) maxX = px;
|
|
6002
|
+
if (py > maxY) maxY = py;
|
|
6003
|
+
}
|
|
6004
|
+
}
|
|
6005
|
+
if (layer.paint.kind === "solid") {
|
|
6006
|
+
const c = layer.paint.color;
|
|
6007
|
+
const alpha = c[3] / 255;
|
|
6008
|
+
body.push("q");
|
|
6009
|
+
if (alpha < 0.999) {
|
|
6010
|
+
let gs = alphaMap.get(c[3]);
|
|
6011
|
+
if (!gs) {
|
|
6012
|
+
gs = `GsA${alphaMap.size}`;
|
|
6013
|
+
alphaMap.set(c[3], gs);
|
|
6014
|
+
extGStates.push({ name: gs, dict: `<< /ca ${n(alpha)} /CA ${n(alpha)} >>` });
|
|
6015
|
+
}
|
|
6016
|
+
body.push(`/${gs} gs`);
|
|
6017
|
+
}
|
|
6018
|
+
body.push(`${ch(c[0])} ${ch(c[1])} ${ch(c[2])} rg`);
|
|
6019
|
+
body.push(path);
|
|
6020
|
+
body.push("f");
|
|
6021
|
+
body.push("Q");
|
|
6022
|
+
} else {
|
|
6023
|
+
const name = `Sh${shadingIdx++}`;
|
|
6024
|
+
const dict = layer.paint.kind === "linear" ? linearShadingDict(layer.paint, m) : radialShadingDict(layer.paint, m);
|
|
6025
|
+
shadings.push({ name, dict });
|
|
6026
|
+
body.push("q");
|
|
6027
|
+
body.push(path);
|
|
6028
|
+
body.push("W n");
|
|
6029
|
+
body.push(`/${name} sh`);
|
|
6030
|
+
body.push("Q");
|
|
6031
|
+
}
|
|
6032
|
+
}
|
|
6033
|
+
const bbox = Number.isFinite(minX) ? [Math.floor(minX) - 1, Math.floor(minY) - 1, Math.ceil(maxX) + 1, Math.ceil(maxY) + 1] : [0, 0, unitsPerEm, unitsPerEm];
|
|
6034
|
+
return {
|
|
6035
|
+
content: body.join("\n"),
|
|
6036
|
+
bbox,
|
|
6037
|
+
shadings,
|
|
6038
|
+
extGStates
|
|
6039
|
+
};
|
|
6040
|
+
}
|
|
6041
|
+
|
|
6042
|
+
// src/core/color-emoji.ts
|
|
6043
|
+
function createColorEmojiCollector() {
|
|
6044
|
+
const forms = [];
|
|
6045
|
+
const nameByGlyph = /* @__PURE__ */ new WeakMap();
|
|
6046
|
+
const glyfByFont = /* @__PURE__ */ new WeakMap();
|
|
6047
|
+
function glyfFor(fontData) {
|
|
6048
|
+
let g = glyfByFont.get(fontData);
|
|
6049
|
+
if (g === void 0) {
|
|
6050
|
+
g = parseGlyfFont(getDecodedFontBytes(fontData));
|
|
6051
|
+
glyfByFont.set(fontData, g);
|
|
6052
|
+
}
|
|
6053
|
+
return g;
|
|
6054
|
+
}
|
|
6055
|
+
function useGlyph(fontData, gid) {
|
|
6056
|
+
const colorGlyph = fontData.colorGlyphs?.[gid];
|
|
6057
|
+
if (!colorGlyph) return null;
|
|
6058
|
+
let perFont = nameByGlyph.get(fontData);
|
|
6059
|
+
if (!perFont) {
|
|
6060
|
+
perFont = /* @__PURE__ */ new Map();
|
|
6061
|
+
nameByGlyph.set(fontData, perFont);
|
|
6062
|
+
}
|
|
6063
|
+
const cached = perFont.get(gid);
|
|
6064
|
+
if (cached) return cached;
|
|
6065
|
+
const glyf = glyfFor(fontData);
|
|
6066
|
+
if (!glyf) return null;
|
|
6067
|
+
const rendered = renderColorGlyph(
|
|
6068
|
+
colorGlyph,
|
|
6069
|
+
(baseGid) => extractGlyphContours(glyf, baseGid),
|
|
6070
|
+
fontData.metrics.unitsPerEm
|
|
6071
|
+
);
|
|
6072
|
+
if (rendered.content.trim() === "") return null;
|
|
6073
|
+
const name = `CEm${forms.length}`;
|
|
6074
|
+
const resParts = [];
|
|
6075
|
+
if (rendered.shadings.length > 0) {
|
|
6076
|
+
resParts.push(`/Shading << ${rendered.shadings.map((s) => `/${s.name} ${s.dict}`).join(" ")} >>`);
|
|
6077
|
+
}
|
|
6078
|
+
if (rendered.extGStates.length > 0) {
|
|
6079
|
+
resParts.push(`/ExtGState << ${rendered.extGStates.map((g) => `/${g.name} ${g.dict}`).join(" ")} >>`);
|
|
6080
|
+
}
|
|
6081
|
+
forms.push({ name, content: rendered.content, resources: resParts.join(" "), bbox: rendered.bbox });
|
|
6082
|
+
perFont.set(gid, name);
|
|
6083
|
+
return name;
|
|
6084
|
+
}
|
|
6085
|
+
return { useGlyph, forms };
|
|
6086
|
+
}
|
|
6087
|
+
|
|
4144
6088
|
// src/core/encoding-context.ts
|
|
4145
6089
|
function isWinAnsi(cp) {
|
|
4146
6090
|
if (cp >= 32 && cp <= 126 || cp >= 160 && cp <= 255) return true;
|
|
@@ -4245,13 +6189,14 @@ function buildTextRunsWithFallback(text, fontRef, fd, sz, trackGid, pdfA = false
|
|
|
4245
6189
|
if (mode === "hel") flushHel();
|
|
4246
6190
|
return result;
|
|
4247
6191
|
}
|
|
4248
|
-
function createEncodingContext(fontEntries, pdfA = false) {
|
|
6192
|
+
function createEncodingContext(fontEntries, pdfA = false, normalize = false) {
|
|
6193
|
+
const _norm = normalize ? (s) => s.normalize(normalize) : (s) => s;
|
|
4249
6194
|
if (!fontEntries || fontEntries.length === 0) {
|
|
4250
6195
|
return {
|
|
4251
6196
|
isUnicode: false,
|
|
4252
6197
|
fontEntries: [],
|
|
4253
|
-
ps: pdfString,
|
|
4254
|
-
tw: helveticaWidth,
|
|
6198
|
+
ps: normalize ? (s) => pdfString(_norm(s)) : pdfString,
|
|
6199
|
+
tw: normalize ? (s, sz) => helveticaWidth(_norm(s), sz) : helveticaWidth,
|
|
4255
6200
|
textRuns: () => [],
|
|
4256
6201
|
f1: "/F1",
|
|
4257
6202
|
f2: "/F2"
|
|
@@ -4264,6 +6209,7 @@ function createEncodingContext(fontEntries, pdfA = false) {
|
|
|
4264
6209
|
const s = _usedGids.get(fontRef);
|
|
4265
6210
|
if (s) s.add(gid);
|
|
4266
6211
|
}
|
|
6212
|
+
const _colorEmoji = fontEntries.some((fe) => fe.fontData.colorGlyphs) ? createColorEmojiCollector() : void 0;
|
|
4267
6213
|
return {
|
|
4268
6214
|
isUnicode: true,
|
|
4269
6215
|
fontEntries,
|
|
@@ -4273,8 +6219,10 @@ function createEncodingContext(fontEntries, pdfA = false) {
|
|
|
4273
6219
|
getUsedGids() {
|
|
4274
6220
|
return _usedGids;
|
|
4275
6221
|
},
|
|
6222
|
+
colorEmoji: _colorEmoji,
|
|
4276
6223
|
textRuns(str, sz) {
|
|
4277
6224
|
if (!str) return [];
|
|
6225
|
+
str = _norm(str);
|
|
4278
6226
|
str = stripBidiControls(str);
|
|
4279
6227
|
if (!str) return [];
|
|
4280
6228
|
if (containsRTL(str)) {
|
|
@@ -4341,6 +6289,56 @@ function createEncodingContext(fontEntries, pdfA = false) {
|
|
|
4341
6289
|
}
|
|
4342
6290
|
}
|
|
4343
6291
|
result.push({ text: fRun.text, fontRef, fontData: fd, shaped, hexStr: null, widthPt: designW * sz / upm });
|
|
6292
|
+
} else if (containsTelugu(fRun.text)) {
|
|
6293
|
+
const shaped = shapeTeluguText(fRun.text, fd);
|
|
6294
|
+
let designW = 0;
|
|
6295
|
+
for (const g of shaped) {
|
|
6296
|
+
_trackGid(fontRef, g.gid);
|
|
6297
|
+
if (!g.isZeroAdvance) {
|
|
6298
|
+
designW += fd.widths[g.gid] !== void 0 ? fd.widths[g.gid] : fd.defaultWidth;
|
|
6299
|
+
}
|
|
6300
|
+
}
|
|
6301
|
+
result.push({ text: fRun.text, fontRef, fontData: fd, shaped, hexStr: null, widthPt: designW * sz / upm });
|
|
6302
|
+
} else if (containsSinhala(fRun.text)) {
|
|
6303
|
+
const shaped = shapeSinhalaText(fRun.text, fd);
|
|
6304
|
+
let designW = 0;
|
|
6305
|
+
for (const g of shaped) {
|
|
6306
|
+
_trackGid(fontRef, g.gid);
|
|
6307
|
+
if (!g.isZeroAdvance) {
|
|
6308
|
+
designW += fd.widths[g.gid] !== void 0 ? fd.widths[g.gid] : fd.defaultWidth;
|
|
6309
|
+
}
|
|
6310
|
+
}
|
|
6311
|
+
result.push({ text: fRun.text, fontRef, fontData: fd, shaped, hexStr: null, widthPt: designW * sz / upm });
|
|
6312
|
+
} else if (containsTibetan(fRun.text)) {
|
|
6313
|
+
const shaped = shapeTibetanText(fRun.text, fd);
|
|
6314
|
+
let designW = 0;
|
|
6315
|
+
for (const g of shaped) {
|
|
6316
|
+
_trackGid(fontRef, g.gid);
|
|
6317
|
+
if (!g.isZeroAdvance) {
|
|
6318
|
+
designW += fd.widths[g.gid] !== void 0 ? fd.widths[g.gid] : fd.defaultWidth;
|
|
6319
|
+
}
|
|
6320
|
+
}
|
|
6321
|
+
result.push({ text: fRun.text, fontRef, fontData: fd, shaped, hexStr: null, widthPt: designW * sz / upm });
|
|
6322
|
+
} else if (containsKhmer(fRun.text)) {
|
|
6323
|
+
const shaped = shapeKhmerText(fRun.text, fd);
|
|
6324
|
+
let designW = 0;
|
|
6325
|
+
for (const g of shaped) {
|
|
6326
|
+
_trackGid(fontRef, g.gid);
|
|
6327
|
+
if (!g.isZeroAdvance) {
|
|
6328
|
+
designW += fd.widths[g.gid] !== void 0 ? fd.widths[g.gid] : fd.defaultWidth;
|
|
6329
|
+
}
|
|
6330
|
+
}
|
|
6331
|
+
result.push({ text: fRun.text, fontRef, fontData: fd, shaped, hexStr: null, widthPt: designW * sz / upm });
|
|
6332
|
+
} else if (containsMyanmar(fRun.text)) {
|
|
6333
|
+
const shaped = shapeMyanmarText(fRun.text, fd);
|
|
6334
|
+
let designW = 0;
|
|
6335
|
+
for (const g of shaped) {
|
|
6336
|
+
_trackGid(fontRef, g.gid);
|
|
6337
|
+
if (!g.isZeroAdvance) {
|
|
6338
|
+
designW += fd.widths[g.gid] !== void 0 ? fd.widths[g.gid] : fd.defaultWidth;
|
|
6339
|
+
}
|
|
6340
|
+
}
|
|
6341
|
+
result.push({ text: fRun.text, fontRef, fontData: fd, shaped, hexStr: null, widthPt: designW * sz / upm });
|
|
4344
6342
|
} else if (containsDevanagari(fRun.text)) {
|
|
4345
6343
|
const shaped = shapeDevanagariText(fRun.text, fd);
|
|
4346
6344
|
let designW = 0;
|
|
@@ -4398,6 +6396,61 @@ function createEncodingContext(fontEntries, pdfA = false) {
|
|
|
4398
6396
|
}
|
|
4399
6397
|
return [{ text: run.text, fontRef, fontData: fd, shaped, hexStr: null, widthPt: designW * sz / upm }];
|
|
4400
6398
|
}
|
|
6399
|
+
if (containsTelugu(run.text)) {
|
|
6400
|
+
const shaped = shapeTeluguText(run.text, fd);
|
|
6401
|
+
let designW = 0;
|
|
6402
|
+
for (const g of shaped) {
|
|
6403
|
+
_trackGid(fontRef, g.gid);
|
|
6404
|
+
if (!g.isZeroAdvance) {
|
|
6405
|
+
designW += fd.widths[g.gid] !== void 0 ? fd.widths[g.gid] : fd.defaultWidth;
|
|
6406
|
+
}
|
|
6407
|
+
}
|
|
6408
|
+
return [{ text: run.text, fontRef, fontData: fd, shaped, hexStr: null, widthPt: designW * sz / upm }];
|
|
6409
|
+
}
|
|
6410
|
+
if (containsSinhala(run.text)) {
|
|
6411
|
+
const shaped = shapeSinhalaText(run.text, fd);
|
|
6412
|
+
let designW = 0;
|
|
6413
|
+
for (const g of shaped) {
|
|
6414
|
+
_trackGid(fontRef, g.gid);
|
|
6415
|
+
if (!g.isZeroAdvance) {
|
|
6416
|
+
designW += fd.widths[g.gid] !== void 0 ? fd.widths[g.gid] : fd.defaultWidth;
|
|
6417
|
+
}
|
|
6418
|
+
}
|
|
6419
|
+
return [{ text: run.text, fontRef, fontData: fd, shaped, hexStr: null, widthPt: designW * sz / upm }];
|
|
6420
|
+
}
|
|
6421
|
+
if (containsTibetan(run.text)) {
|
|
6422
|
+
const shaped = shapeTibetanText(run.text, fd);
|
|
6423
|
+
let designW = 0;
|
|
6424
|
+
for (const g of shaped) {
|
|
6425
|
+
_trackGid(fontRef, g.gid);
|
|
6426
|
+
if (!g.isZeroAdvance) {
|
|
6427
|
+
designW += fd.widths[g.gid] !== void 0 ? fd.widths[g.gid] : fd.defaultWidth;
|
|
6428
|
+
}
|
|
6429
|
+
}
|
|
6430
|
+
return [{ text: run.text, fontRef, fontData: fd, shaped, hexStr: null, widthPt: designW * sz / upm }];
|
|
6431
|
+
}
|
|
6432
|
+
if (containsKhmer(run.text)) {
|
|
6433
|
+
const shaped = shapeKhmerText(run.text, fd);
|
|
6434
|
+
let designW = 0;
|
|
6435
|
+
for (const g of shaped) {
|
|
6436
|
+
_trackGid(fontRef, g.gid);
|
|
6437
|
+
if (!g.isZeroAdvance) {
|
|
6438
|
+
designW += fd.widths[g.gid] !== void 0 ? fd.widths[g.gid] : fd.defaultWidth;
|
|
6439
|
+
}
|
|
6440
|
+
}
|
|
6441
|
+
return [{ text: run.text, fontRef, fontData: fd, shaped, hexStr: null, widthPt: designW * sz / upm }];
|
|
6442
|
+
}
|
|
6443
|
+
if (containsMyanmar(run.text)) {
|
|
6444
|
+
const shaped = shapeMyanmarText(run.text, fd);
|
|
6445
|
+
let designW = 0;
|
|
6446
|
+
for (const g of shaped) {
|
|
6447
|
+
_trackGid(fontRef, g.gid);
|
|
6448
|
+
if (!g.isZeroAdvance) {
|
|
6449
|
+
designW += fd.widths[g.gid] !== void 0 ? fd.widths[g.gid] : fd.defaultWidth;
|
|
6450
|
+
}
|
|
6451
|
+
}
|
|
6452
|
+
return [{ text: run.text, fontRef, fontData: fd, shaped, hexStr: null, widthPt: designW * sz / upm }];
|
|
6453
|
+
}
|
|
4401
6454
|
if (containsDevanagari(run.text)) {
|
|
4402
6455
|
const shaped = shapeDevanagariText(run.text, fd);
|
|
4403
6456
|
let designW = 0;
|
|
@@ -4414,6 +6467,7 @@ function createEncodingContext(fontEntries, pdfA = false) {
|
|
|
4414
6467
|
},
|
|
4415
6468
|
ps(str) {
|
|
4416
6469
|
if (!str) return "<>";
|
|
6470
|
+
str = _norm(str);
|
|
4417
6471
|
str = stripBidiControls(str);
|
|
4418
6472
|
if (!str) return "<>";
|
|
4419
6473
|
const { cmap } = primary.fontData;
|
|
@@ -4442,7 +6496,7 @@ function createEncodingContext(fontEntries, pdfA = false) {
|
|
|
4442
6496
|
}
|
|
4443
6497
|
return `<${hex2.toUpperCase()}>`;
|
|
4444
6498
|
}
|
|
4445
|
-
if (!containsThai(str) && !containsBengali(str) && !containsTamil(str) && !containsDevanagari(str)) {
|
|
6499
|
+
if (!containsThai(str) && !containsBengali(str) && !containsTamil(str) && !containsTelugu(str) && !containsSinhala(str) && !containsTibetan(str) && !containsKhmer(str) && !containsMyanmar(str) && !containsDevanagari(str)) {
|
|
4446
6500
|
let hex2 = "";
|
|
4447
6501
|
for (let i = 0; i < str.length; i++) {
|
|
4448
6502
|
const rawCp = str.codePointAt(i) ?? 0;
|
|
@@ -4454,7 +6508,7 @@ function createEncodingContext(fontEntries, pdfA = false) {
|
|
|
4454
6508
|
}
|
|
4455
6509
|
return `<${hex2.toUpperCase()}>`;
|
|
4456
6510
|
}
|
|
4457
|
-
const shapeFn = containsThai(str) ? shapeThaiText : containsBengali(str) ? shapeBengaliText : containsTamil(str) ? shapeTamilText : shapeDevanagariText;
|
|
6511
|
+
const shapeFn = containsThai(str) ? shapeThaiText : containsBengali(str) ? shapeBengaliText : containsTamil(str) ? shapeTamilText : containsTelugu(str) ? shapeTeluguText : containsSinhala(str) ? shapeSinhalaText : containsTibetan(str) ? shapeTibetanText : containsKhmer(str) ? shapeKhmerText : containsMyanmar(str) ? shapeMyanmarText : shapeDevanagariText;
|
|
4458
6512
|
const shaped = shapeFn(str, primary.fontData);
|
|
4459
6513
|
let hex = "";
|
|
4460
6514
|
for (const g of shaped) {
|
|
@@ -4523,77 +6577,17 @@ function buildSubsetWidthArray(widths, usedGids) {
|
|
|
4523
6577
|
if (sorted.length === 0) return null;
|
|
4524
6578
|
const parts = [];
|
|
4525
6579
|
let i = 0;
|
|
4526
|
-
while (i < sorted.length) {
|
|
4527
|
-
const start = sorted[i];
|
|
4528
|
-
const run = [widths[start]];
|
|
4529
|
-
while (i + 1 < sorted.length && sorted[i + 1] === sorted[i] + 1) {
|
|
4530
|
-
i++;
|
|
4531
|
-
run.push(widths[sorted[i]]);
|
|
4532
|
-
}
|
|
4533
|
-
parts.push(`${start} [${run.join(" ")}]`);
|
|
4534
|
-
i++;
|
|
4535
|
-
}
|
|
4536
|
-
return parts.join(" ");
|
|
4537
|
-
}
|
|
4538
|
-
|
|
4539
|
-
// src/fonts/font-loader.ts
|
|
4540
|
-
var _fontRegistry = /* @__PURE__ */ new Map();
|
|
4541
|
-
var _fontDataCache = /* @__PURE__ */ new Map();
|
|
4542
|
-
var _fontBinaryCache = /* @__PURE__ */ new WeakMap();
|
|
4543
|
-
function getDecodedFontBytes(fontData) {
|
|
4544
|
-
const cached = _fontBinaryCache.get(fontData);
|
|
4545
|
-
if (cached) return cached;
|
|
4546
|
-
let bytes;
|
|
4547
|
-
if (typeof atob === "function") {
|
|
4548
|
-
const binaryStr = atob(fontData.ttfBase64);
|
|
4549
|
-
bytes = new Uint8Array(binaryStr.length);
|
|
4550
|
-
for (let i = 0; i < binaryStr.length; i++) bytes[i] = binaryStr.charCodeAt(i);
|
|
4551
|
-
} else {
|
|
4552
|
-
const buf = globalThis["Buffer"];
|
|
4553
|
-
bytes = buf.from(fontData.ttfBase64, "base64");
|
|
4554
|
-
}
|
|
4555
|
-
_fontBinaryCache.set(fontData, bytes);
|
|
4556
|
-
return bytes;
|
|
4557
|
-
}
|
|
4558
|
-
function registerFont(lang, loader) {
|
|
4559
|
-
_fontRegistry.set(lang, loader);
|
|
4560
|
-
}
|
|
4561
|
-
function registerFonts(fonts) {
|
|
4562
|
-
for (const [lang, loader] of Object.entries(fonts)) {
|
|
4563
|
-
_fontRegistry.set(lang, loader);
|
|
4564
|
-
}
|
|
4565
|
-
}
|
|
4566
|
-
async function loadFontData(lang) {
|
|
4567
|
-
const cached = _fontDataCache.get(lang);
|
|
4568
|
-
if (cached) return cached;
|
|
4569
|
-
const loader = _fontRegistry.get(lang);
|
|
4570
|
-
if (!loader) return null;
|
|
4571
|
-
for (let attempt = 0; attempt < 2; attempt++) {
|
|
4572
|
-
try {
|
|
4573
|
-
const mod2 = await loader();
|
|
4574
|
-
const fontData = "default" in mod2 ? mod2.default : mod2;
|
|
4575
|
-
_fontDataCache.set(lang, fontData);
|
|
4576
|
-
return fontData;
|
|
4577
|
-
} catch {
|
|
4578
|
-
if (attempt === 0) {
|
|
4579
|
-
await new Promise((r) => setTimeout(r, 500));
|
|
4580
|
-
}
|
|
6580
|
+
while (i < sorted.length) {
|
|
6581
|
+
const start = sorted[i];
|
|
6582
|
+
const run = [widths[start]];
|
|
6583
|
+
while (i + 1 < sorted.length && sorted[i + 1] === sorted[i] + 1) {
|
|
6584
|
+
i++;
|
|
6585
|
+
run.push(widths[sorted[i]]);
|
|
4581
6586
|
}
|
|
6587
|
+
parts.push(`${start} [${run.join(" ")}]`);
|
|
6588
|
+
i++;
|
|
4582
6589
|
}
|
|
4583
|
-
return
|
|
4584
|
-
}
|
|
4585
|
-
function hasFontLoader(lang) {
|
|
4586
|
-
return _fontRegistry.has(lang);
|
|
4587
|
-
}
|
|
4588
|
-
function getRegisteredLangs() {
|
|
4589
|
-
return [..._fontRegistry.keys()];
|
|
4590
|
-
}
|
|
4591
|
-
function clearFontCache() {
|
|
4592
|
-
_fontDataCache.clear();
|
|
4593
|
-
}
|
|
4594
|
-
function resetFontRegistry() {
|
|
4595
|
-
_fontRegistry.clear();
|
|
4596
|
-
_fontDataCache.clear();
|
|
6590
|
+
return parts.join(" ");
|
|
4597
6591
|
}
|
|
4598
6592
|
|
|
4599
6593
|
// src/fonts/font-subsetter.ts
|
|
@@ -4909,7 +6903,7 @@ function buildStructureTree(root, startObjNum, pageObjToStructParents) {
|
|
|
4909
6903
|
};
|
|
4910
6904
|
}
|
|
4911
6905
|
function buildPdfMetadata(now = /* @__PURE__ */ new Date()) {
|
|
4912
|
-
const pad2 = (
|
|
6906
|
+
const pad2 = (n2) => String(n2).padStart(2, "0");
|
|
4913
6907
|
const yyyy = now.getFullYear();
|
|
4914
6908
|
const mm = pad2(now.getMonth() + 1);
|
|
4915
6909
|
const dd = pad2(now.getDate());
|
|
@@ -5191,26 +7185,45 @@ function txtShaped(shaped, x, y, font, sz, fontData) {
|
|
|
5191
7185
|
}
|
|
5192
7186
|
return parts.join("\n");
|
|
5193
7187
|
}
|
|
7188
|
+
var fmtScale = (v) => v.toFixed(5).replace(/0+$/, "").replace(/\.$/, "") || "0";
|
|
7189
|
+
function emitColorEmojiRun(parts, run, penX, y, sz, enc) {
|
|
7190
|
+
const fd = run.fontData;
|
|
7191
|
+
const upm = fd.metrics.unitsPerEm;
|
|
7192
|
+
const scale = sz / upm;
|
|
7193
|
+
const s = fmtScale(scale);
|
|
7194
|
+
const hex = (run.hexStr ?? "").replace(/[<>]/g, "");
|
|
7195
|
+
for (let i = 0; i + 4 <= hex.length; i += 4) {
|
|
7196
|
+
const tag = hex.substr(i, 4);
|
|
7197
|
+
const gid = parseInt(tag, 16);
|
|
7198
|
+
const adv = (fd.widths[gid] !== void 0 ? fd.widths[gid] : fd.defaultWidth) * scale;
|
|
7199
|
+
const name = enc.colorEmoji?.useGlyph(fd, gid) ?? null;
|
|
7200
|
+
if (name) {
|
|
7201
|
+
parts.push(`q ${s} 0 0 ${s} ${fmtNum(penX)} ${fmtNum(y)} cm /${name} Do Q`);
|
|
7202
|
+
} else {
|
|
7203
|
+
parts.push(`BT ${run.fontRef} ${sz} Tf ${fmtNum(penX)} ${fmtNum(y)} Td <${tag}> Tj ET`);
|
|
7204
|
+
}
|
|
7205
|
+
penX += adv;
|
|
7206
|
+
}
|
|
7207
|
+
return penX;
|
|
7208
|
+
}
|
|
5194
7209
|
function txt(str, x, y, font, sz, enc) {
|
|
5195
7210
|
if (!enc.isUnicode) {
|
|
5196
7211
|
return `BT ${font} ${sz} Tf ${fmtNum(x)} ${fmtNum(y)} Td ${enc.ps(str)} Tj ET`;
|
|
5197
7212
|
}
|
|
5198
7213
|
const runs = enc.textRuns(str, sz);
|
|
5199
7214
|
if (runs.length === 0) return "";
|
|
5200
|
-
if (runs.length === 1) {
|
|
5201
|
-
const run = runs[0];
|
|
5202
|
-
if (run.shaped) return txtShaped(run.shaped, x, y, run.fontRef, sz, run.fontData);
|
|
5203
|
-
return `BT ${run.fontRef} ${sz} Tf ${fmtNum(x)} ${fmtNum(y)} Td ${run.hexStr} Tj ET`;
|
|
5204
|
-
}
|
|
5205
7215
|
const parts = [];
|
|
5206
7216
|
let penX = x;
|
|
5207
7217
|
for (const run of runs) {
|
|
5208
|
-
if (run.
|
|
7218
|
+
if (enc.colorEmoji && run.fontData.colorGlyphs && run.hexStr) {
|
|
7219
|
+
penX = emitColorEmojiRun(parts, run, penX, y, sz, enc);
|
|
7220
|
+
} else if (run.shaped) {
|
|
5209
7221
|
parts.push(txtShaped(run.shaped, penX, y, run.fontRef, sz, run.fontData));
|
|
7222
|
+
penX += run.widthPt;
|
|
5210
7223
|
} else {
|
|
5211
7224
|
parts.push(`BT ${run.fontRef} ${sz} Tf ${fmtNum(penX)} ${fmtNum(y)} Td ${run.hexStr} Tj ET`);
|
|
7225
|
+
penX += run.widthPt;
|
|
5212
7226
|
}
|
|
5213
|
-
penX += run.widthPt;
|
|
5214
7227
|
}
|
|
5215
7228
|
return parts.join("\n");
|
|
5216
7229
|
}
|
|
@@ -5290,6 +7303,7 @@ var PG_W = 595.28;
|
|
|
5290
7303
|
var PG_H = 841.89;
|
|
5291
7304
|
var DEFAULT_MARGINS = { t: 45, r: 36, b: 35, l: 36 };
|
|
5292
7305
|
var DEFAULT_CW = PG_W - DEFAULT_MARGINS.l - DEFAULT_MARGINS.r;
|
|
7306
|
+
var DEFAULT_MAX_BLOCKS = 1e5;
|
|
5293
7307
|
var ROW_H = 12;
|
|
5294
7308
|
var TH_H = 15;
|
|
5295
7309
|
var INFO_LN = 13;
|
|
@@ -5327,12 +7341,12 @@ var DEFAULT_COLUMNS = [
|
|
|
5327
7341
|
{ f: 0.18, a: "c", mx: 20, mxH: 20 }
|
|
5328
7342
|
];
|
|
5329
7343
|
function computeColumnPositions(columns, marginLeft, contentWidth) {
|
|
5330
|
-
const
|
|
5331
|
-
const cwi = new Array(
|
|
5332
|
-
const
|
|
7344
|
+
const n2 = columns.length;
|
|
7345
|
+
const cwi = new Array(n2).fill(0);
|
|
7346
|
+
const fixed2 = new Array(n2).fill(false);
|
|
5333
7347
|
let totalFixed = 0;
|
|
5334
7348
|
let freeWeight = 0;
|
|
5335
|
-
for (let i = 0; i <
|
|
7349
|
+
for (let i = 0; i < n2; i++) {
|
|
5336
7350
|
const col = columns[i];
|
|
5337
7351
|
let w = col.f * contentWidth;
|
|
5338
7352
|
let clamped = false;
|
|
@@ -5346,7 +7360,7 @@ function computeColumnPositions(columns, marginLeft, contentWidth) {
|
|
|
5346
7360
|
}
|
|
5347
7361
|
if (clamped) {
|
|
5348
7362
|
cwi[i] = w;
|
|
5349
|
-
|
|
7363
|
+
fixed2[i] = true;
|
|
5350
7364
|
totalFixed += w;
|
|
5351
7365
|
} else {
|
|
5352
7366
|
freeWeight += col.f;
|
|
@@ -5354,13 +7368,13 @@ function computeColumnPositions(columns, marginLeft, contentWidth) {
|
|
|
5354
7368
|
}
|
|
5355
7369
|
const remaining = contentWidth - totalFixed;
|
|
5356
7370
|
if (freeWeight > 0) {
|
|
5357
|
-
for (let i = 0; i <
|
|
5358
|
-
if (!
|
|
7371
|
+
for (let i = 0; i < n2; i++) {
|
|
7372
|
+
if (!fixed2[i]) cwi[i] = columns[i].f / freeWeight * remaining;
|
|
5359
7373
|
}
|
|
5360
7374
|
}
|
|
5361
|
-
const cx = new Array(
|
|
7375
|
+
const cx = new Array(n2);
|
|
5362
7376
|
let x = marginLeft;
|
|
5363
|
-
for (let i = 0; i <
|
|
7377
|
+
for (let i = 0; i < n2; i++) {
|
|
5364
7378
|
cx[i] = x;
|
|
5365
7379
|
x += cwi[i];
|
|
5366
7380
|
}
|
|
@@ -5401,8 +7415,8 @@ function isValidPdfRgb(str) {
|
|
|
5401
7415
|
if (!PDF_RGB_RE.test(str)) return false;
|
|
5402
7416
|
const parts = str.split(" ");
|
|
5403
7417
|
return parts.length === 3 && parts.every((p) => {
|
|
5404
|
-
const
|
|
5405
|
-
return
|
|
7418
|
+
const n2 = Number(p);
|
|
7419
|
+
return n2 >= 0 && n2 <= 1;
|
|
5406
7420
|
});
|
|
5407
7421
|
}
|
|
5408
7422
|
function normalizeColors(colors) {
|
|
@@ -5421,8 +7435,8 @@ function normalizeColors(colors) {
|
|
|
5421
7435
|
footer: parseColor(colors.footer)
|
|
5422
7436
|
};
|
|
5423
7437
|
}
|
|
5424
|
-
function fmtChannel(
|
|
5425
|
-
const clamped = Math.max(0, Math.min(1,
|
|
7438
|
+
function fmtChannel(n2) {
|
|
7439
|
+
const clamped = Math.max(0, Math.min(1, n2));
|
|
5426
7440
|
const rounded = Math.round(clamped * 1e3) / 1e3;
|
|
5427
7441
|
return String(rounded);
|
|
5428
7442
|
}
|
|
@@ -5464,8 +7478,8 @@ function parseTupleColor(tuple) {
|
|
|
5464
7478
|
function parsePdfRgbString(str) {
|
|
5465
7479
|
const parts = str.split(" ");
|
|
5466
7480
|
for (const p of parts) {
|
|
5467
|
-
const
|
|
5468
|
-
if (
|
|
7481
|
+
const n2 = Number(p);
|
|
7482
|
+
if (n2 < 0 || n2 > 1) {
|
|
5469
7483
|
throw new Error(
|
|
5470
7484
|
`Invalid PDF RGB value: ${p} in ${JSON.stringify(str)}. Each value must be 0.0\u20131.0.`
|
|
5471
7485
|
);
|
|
@@ -5926,7 +7940,7 @@ function _buildTextWatermarkOps(wm, pgW, pgH, enc, gsName) {
|
|
|
5926
7940
|
const textWidth = enc.tw(wm.text, sz);
|
|
5927
7941
|
const offsetX = -textWidth / 2;
|
|
5928
7942
|
const offsetY = -sz * capRatio / 2;
|
|
5929
|
-
const
|
|
7943
|
+
const tx2 = cx + offsetX * cos - offsetY * sin;
|
|
5930
7944
|
const ty = cy + offsetX * sin + offsetY * cos;
|
|
5931
7945
|
const escapedText = enc.ps(wm.text);
|
|
5932
7946
|
const ops = [
|
|
@@ -5935,7 +7949,7 @@ function _buildTextWatermarkOps(wm, pgW, pgH, enc, gsName) {
|
|
|
5935
7949
|
"BT",
|
|
5936
7950
|
`${color} rg`,
|
|
5937
7951
|
`${enc.f2} ${fmtNum(sz)} Tf`,
|
|
5938
|
-
`${fmtNum(cos)} ${fmtNum(sin)} ${fmtNum(-sin)} ${fmtNum(cos)} ${fmtNum(
|
|
7952
|
+
`${fmtNum(cos)} ${fmtNum(sin)} ${fmtNum(-sin)} ${fmtNum(cos)} ${fmtNum(tx2)} ${fmtNum(ty)} Tm`,
|
|
5939
7953
|
`${escapedText} Tj`,
|
|
5940
7954
|
"ET",
|
|
5941
7955
|
"Q"
|
|
@@ -6090,6 +8104,9 @@ function _buildPageTemplate(template, page, pages, title, date, y, enc, mgL, mgR
|
|
|
6090
8104
|
return { ops, structEls };
|
|
6091
8105
|
}
|
|
6092
8106
|
function buildPDF(params, layoutOptions) {
|
|
8107
|
+
return assembleTableParts(params, layoutOptions).join("");
|
|
8108
|
+
}
|
|
8109
|
+
function assembleTableParts(params, layoutOptions) {
|
|
6093
8110
|
if (!params || typeof params !== "object") {
|
|
6094
8111
|
throw new Error("buildPDF: params is required and must be an object");
|
|
6095
8112
|
}
|
|
@@ -6115,7 +8132,7 @@ function buildPDF(params, layoutOptions) {
|
|
|
6115
8132
|
const fontEntries = params.fontEntries || (fontData ? [{ fontData, fontRef: "/F3", lang: "unknown" }] : []);
|
|
6116
8133
|
const pdfaConfig = resolvePdfAConfig(layoutOptions?.tagged);
|
|
6117
8134
|
const tagged = pdfaConfig.enabled;
|
|
6118
|
-
const enc = createEncodingContext(fontEntries, tagged);
|
|
8135
|
+
const enc = createEncodingContext(fontEntries, tagged, layoutOptions?.normalize ?? false);
|
|
6119
8136
|
const footerTpl = layoutOptions?.footerTemplate ?? {
|
|
6120
8137
|
left: footerText || void 0,
|
|
6121
8138
|
right: "{page}/{pages}"
|
|
@@ -6123,7 +8140,7 @@ function buildPDF(params, layoutOptions) {
|
|
|
6123
8140
|
const headerTpl = layoutOptions?.headerTemplate;
|
|
6124
8141
|
const headerH = headerTpl ? HEADER_H : 0;
|
|
6125
8142
|
const dateNow = /* @__PURE__ */ new Date();
|
|
6126
|
-
const pad2d = (
|
|
8143
|
+
const pad2d = (n2) => String(n2).padStart(2, "0");
|
|
6127
8144
|
const dateStr = `${dateNow.getFullYear()}-${pad2d(dateNow.getMonth() + 1)}-${pad2d(dateNow.getDate())}`;
|
|
6128
8145
|
const infoCount = infoItems.length;
|
|
6129
8146
|
const page1Header = TITLE_LN + 16 + infoCount * INFO_LN + 8 + BAL_H + 10;
|
|
@@ -6157,6 +8174,9 @@ function buildPDF(params, layoutOptions) {
|
|
|
6157
8174
|
const pageStreams = [];
|
|
6158
8175
|
let rowIdx = 0;
|
|
6159
8176
|
const prePageObjStart = enc.isUnicode && fontEntries.length > 0 ? 5 + fontEntries.length * 5 + wmExtraObjs : 5 + wmExtraObjs;
|
|
8177
|
+
const preBaseObjCount = enc.isUnicode && fontEntries.length > 0 ? 4 + fontEntries.length * 5 + wmExtraObjs + totalPages * 2 : 4 + wmExtraObjs + totalPages * 2;
|
|
8178
|
+
const latinToUniObjNum = tagged ? 0 : preBaseObjCount + 2;
|
|
8179
|
+
const baseFontToUniRef = latinToUniObjNum ? ` /ToUnicode ${latinToUniObjNum} 0 R` : "";
|
|
6160
8180
|
const pageObjToStructParents = /* @__PURE__ */ new Map();
|
|
6161
8181
|
for (let p = 0; p < totalPages; p++) {
|
|
6162
8182
|
const pageObjNum = prePageObjStart + p * 2;
|
|
@@ -6313,8 +8333,8 @@ function buildPDF(params, layoutOptions) {
|
|
|
6313
8333
|
emitObj(3, refDict);
|
|
6314
8334
|
emitObj(4, refDict);
|
|
6315
8335
|
} else {
|
|
6316
|
-
emitObj(3,
|
|
6317
|
-
emitObj(4,
|
|
8336
|
+
emitObj(3, `<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica /Encoding /WinAnsiEncoding${baseFontToUniRef} >>`);
|
|
8337
|
+
emitObj(4, `<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding${baseFontToUniRef} >>`);
|
|
6318
8338
|
}
|
|
6319
8339
|
for (let fi = 0; fi < fontEntries.length; fi++) {
|
|
6320
8340
|
const fe = fontEntries[fi];
|
|
@@ -6386,8 +8406,8 @@ function buildPDF(params, layoutOptions) {
|
|
|
6386
8406
|
kids.push(`${pageObjStart + p * 2} 0 R`);
|
|
6387
8407
|
}
|
|
6388
8408
|
emitObj(2, `<< /Type /Pages /Kids [${kids.join(" ")}] /Count ${totalPages} >>`);
|
|
6389
|
-
emitObj(3,
|
|
6390
|
-
emitObj(4,
|
|
8409
|
+
emitObj(3, `<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica /Encoding /WinAnsiEncoding${baseFontToUniRef} >>`);
|
|
8410
|
+
emitObj(4, `<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding${baseFontToUniRef} >>`);
|
|
6391
8411
|
const wmObjStartLatin = 5;
|
|
6392
8412
|
let wmGsResLatin = "";
|
|
6393
8413
|
let wmImgResLatin = "";
|
|
@@ -6419,13 +8439,18 @@ function buildPDF(params, layoutOptions) {
|
|
|
6419
8439
|
}
|
|
6420
8440
|
const baseObjCount = enc.isUnicode ? 4 + fontEntries.length * 5 + wmExtraObjs + totalPages * 2 : 4 + wmExtraObjs + totalPages * 2;
|
|
6421
8441
|
const infoObjNum = baseObjCount + 1;
|
|
6422
|
-
const { pdfDate, xmpDate: isoDate } = buildPdfMetadata();
|
|
8442
|
+
const { pdfDate, xmpDate: isoDate } = buildPdfMetadata(layoutOptions?.creationDate);
|
|
6423
8443
|
const infoTitle = params.docTitle || title || "";
|
|
6424
8444
|
emitObj(
|
|
6425
8445
|
infoObjNum,
|
|
6426
8446
|
`<< /Title ${encodePdfTextString(infoTitle)} /Producer (pdfnative) /CreationDate (${pdfDate}) >>`
|
|
6427
8447
|
);
|
|
6428
8448
|
let totalObjs = infoObjNum;
|
|
8449
|
+
if (latinToUniObjNum) {
|
|
8450
|
+
const cmap = buildWinAnsiToUnicodeCMap();
|
|
8451
|
+
emitStreamObj(latinToUniObjNum, `<< /Length ${cmap.length}`, cmap);
|
|
8452
|
+
totalObjs = latinToUniObjNum;
|
|
8453
|
+
}
|
|
6429
8454
|
let xmpObjNum = 0;
|
|
6430
8455
|
let outputIntentObjNum = 0;
|
|
6431
8456
|
let afArrayStr = "";
|
|
@@ -6469,7 +8494,7 @@ function buildPDF(params, layoutOptions) {
|
|
|
6469
8494
|
emitObj(objNum, content);
|
|
6470
8495
|
}
|
|
6471
8496
|
}
|
|
6472
|
-
afArrayStr = efResult.filespecObjNums.map((
|
|
8497
|
+
afArrayStr = efResult.filespecObjNums.map((n2) => `${n2} 0 R`).join(" ");
|
|
6473
8498
|
embeddedFilesNamesDict = efResult.namesDict;
|
|
6474
8499
|
totalObjs += efResult.totalObjects;
|
|
6475
8500
|
}
|
|
@@ -6500,7 +8525,7 @@ endobj
|
|
|
6500
8525
|
}
|
|
6501
8526
|
const writer = { emit, emitObj, emitStreamObj, offset: getOffset, adjustOffset, objOffsets, parts };
|
|
6502
8527
|
writeXrefTrailer(writer, totalObjs, infoObjNum, encState, `${infoTitle}|${pdfDate}`);
|
|
6503
|
-
return parts
|
|
8528
|
+
return parts;
|
|
6504
8529
|
}
|
|
6505
8530
|
function buildPDFBytes(params, layoutOptions) {
|
|
6506
8531
|
return toBytes(buildPDF(params, layoutOptions));
|
|
@@ -6522,8 +8547,8 @@ var CHECK_COLOR = "0 0 0";
|
|
|
6522
8547
|
function pdfStr(s) {
|
|
6523
8548
|
return s.replace(/\\/g, "\\\\").replace(/\(/g, "\\(").replace(/\)/g, "\\)");
|
|
6524
8549
|
}
|
|
6525
|
-
function fmtNum2(
|
|
6526
|
-
return Math.round(
|
|
8550
|
+
function fmtNum2(n2) {
|
|
8551
|
+
return Math.round(n2 * 100) / 100 === Math.round(n2) ? String(Math.round(n2)) : (Math.round(n2 * 100) / 100).toString();
|
|
6527
8552
|
}
|
|
6528
8553
|
function defaultFieldHeight(fieldType) {
|
|
6529
8554
|
switch (fieldType) {
|
|
@@ -6819,7 +8844,7 @@ function buildFormWidget(field, apObjNum, radioCtx) {
|
|
|
6819
8844
|
return { widgetDict, appearanceStream: apStream, apYesStream, apOffStream };
|
|
6820
8845
|
}
|
|
6821
8846
|
function buildAcroFormDict(fieldObjNums, fontObjNum) {
|
|
6822
|
-
const refs = fieldObjNums.map((
|
|
8847
|
+
const refs = fieldObjNums.map((n2) => `${n2} 0 R`).join(" ");
|
|
6823
8848
|
const fontEntry = fontObjNum !== void 0 ? `/Helv ${fontObjNum} 0 R` : "/Helv << /Type /Font /Subtype /Type1 /BaseFont /Helvetica /Encoding /WinAnsiEncoding >>";
|
|
6824
8849
|
return `/AcroForm << /Fields [${refs}] /DR << /Font << ${fontEntry} >> >> /NeedAppearances false >>`;
|
|
6825
8850
|
}
|
|
@@ -6831,7 +8856,7 @@ function buildRadioGroupParent(name, selectedValue, childObjNums, readOnly, requ
|
|
|
6831
8856
|
let ff = FF_RADIO | FF_NO_TOGGLE_TO_OFF;
|
|
6832
8857
|
if (readOnly) ff |= FF_READONLY;
|
|
6833
8858
|
if (required) ff |= FF_REQUIRED;
|
|
6834
|
-
const kids = childObjNums.map((
|
|
8859
|
+
const kids = childObjNums.map((n2) => `${n2} 0 R`).join(" ");
|
|
6835
8860
|
const v = selectedValue ? `/${selectedValue}` : "/Off";
|
|
6836
8861
|
return `<< /FT /Btn /Ff ${ff} /T (${pdfStr(name)}) /V ${v} /Kids [${kids}] >>`;
|
|
6837
8862
|
}
|
|
@@ -7376,14 +9401,14 @@ function generateQR(data, ecLevel = "M") {
|
|
|
7376
9401
|
}
|
|
7377
9402
|
function renderQR(data, x, y, size, ecLevel = "M") {
|
|
7378
9403
|
const modules = generateQR(data, ecLevel);
|
|
7379
|
-
const
|
|
7380
|
-
const moduleSize = size /
|
|
9404
|
+
const n2 = modules.length;
|
|
9405
|
+
const moduleSize = size / n2;
|
|
7381
9406
|
const ops = ["q", "0 0 0 rg"];
|
|
7382
|
-
for (let row = 0; row <
|
|
7383
|
-
for (let col = 0; col <
|
|
9407
|
+
for (let row = 0; row < n2; row++) {
|
|
9408
|
+
for (let col = 0; col < n2; col++) {
|
|
7384
9409
|
if (modules[row][col]) {
|
|
7385
9410
|
const mx = x + col * moduleSize;
|
|
7386
|
-
const my = y + (
|
|
9411
|
+
const my = y + (n2 - 1 - row) * moduleSize;
|
|
7387
9412
|
ops.push(`${fmtNum(mx)} ${fmtNum(my)} ${fmtNum(moduleSize)} ${fmtNum(moduleSize)} re f`);
|
|
7388
9413
|
}
|
|
7389
9414
|
}
|
|
@@ -8003,14 +10028,14 @@ function generateDataMatrix(data) {
|
|
|
8003
10028
|
}
|
|
8004
10029
|
function renderDataMatrix(data, x, y, size) {
|
|
8005
10030
|
const modules = generateDataMatrix(data);
|
|
8006
|
-
const
|
|
8007
|
-
const moduleSize = size /
|
|
10031
|
+
const n2 = modules.length;
|
|
10032
|
+
const moduleSize = size / n2;
|
|
8008
10033
|
const ops = ["q", "0 0 0 rg"];
|
|
8009
|
-
for (let row = 0; row <
|
|
10034
|
+
for (let row = 0; row < n2; row++) {
|
|
8010
10035
|
for (let col = 0; col < modules[row].length; col++) {
|
|
8011
10036
|
if (modules[row][col]) {
|
|
8012
10037
|
const mx = x + col * moduleSize;
|
|
8013
|
-
const my = y + (
|
|
10038
|
+
const my = y + (n2 - 1 - row) * moduleSize;
|
|
8014
10039
|
ops.push(`${fmtNum(mx)} ${fmtNum(my)} ${fmtNum(moduleSize)} ${fmtNum(moduleSize)} re f`);
|
|
8015
10040
|
}
|
|
8016
10041
|
}
|
|
@@ -8328,18 +10353,18 @@ function tokenize(d) {
|
|
|
8328
10353
|
const len = d.length;
|
|
8329
10354
|
let i = 0;
|
|
8330
10355
|
while (i < len) {
|
|
8331
|
-
const
|
|
8332
|
-
if (
|
|
10356
|
+
const ch2 = d.charCodeAt(i);
|
|
10357
|
+
if (ch2 === 32 || ch2 === 44 || ch2 === 9 || ch2 === 10 || ch2 === 13) {
|
|
8333
10358
|
i++;
|
|
8334
10359
|
continue;
|
|
8335
10360
|
}
|
|
8336
|
-
if (
|
|
10361
|
+
if (ch2 >= 65 && ch2 <= 90 || ch2 >= 97 && ch2 <= 122) {
|
|
8337
10362
|
tokens.push(d[i]);
|
|
8338
10363
|
i++;
|
|
8339
10364
|
continue;
|
|
8340
10365
|
}
|
|
8341
10366
|
const start = i;
|
|
8342
|
-
if (
|
|
10367
|
+
if (ch2 === 43 || ch2 === 45) i++;
|
|
8343
10368
|
let hasDot = false;
|
|
8344
10369
|
while (i < len) {
|
|
8345
10370
|
const c = d.charCodeAt(i);
|
|
@@ -8671,7 +10696,7 @@ function parseSvgMarkup(svg) {
|
|
|
8671
10696
|
let viewBox;
|
|
8672
10697
|
if (vbMatch) {
|
|
8673
10698
|
const p = vbMatch[1].trim().split(/[\s,]+/).map(Number);
|
|
8674
|
-
if (p.length === 4 && p.every((
|
|
10699
|
+
if (p.length === 4 && p.every((n2) => !isNaN(n2))) {
|
|
8675
10700
|
viewBox = [p[0], p[1], p[2], p[3]];
|
|
8676
10701
|
}
|
|
8677
10702
|
}
|
|
@@ -8726,7 +10751,7 @@ function parseSvgMarkup(svg) {
|
|
|
8726
10751
|
}
|
|
8727
10752
|
return { elements, viewBox };
|
|
8728
10753
|
}
|
|
8729
|
-
var fn = (
|
|
10754
|
+
var fn = (n2) => n2.toFixed(2);
|
|
8730
10755
|
function resolveColor(color, fallback) {
|
|
8731
10756
|
if (color === "none") return null;
|
|
8732
10757
|
if (color !== void 0) return parseColor(color);
|
|
@@ -8798,10 +10823,10 @@ var CELL_PAD_LEFT = 3;
|
|
|
8798
10823
|
var CELL_PAD_RIGHT = 3;
|
|
8799
10824
|
var CELL_PAD_TOTAL = CELL_PAD_LEFT + CELL_PAD_RIGHT;
|
|
8800
10825
|
function computeAutoFitColumns(columns, headers, rows, enc, thSize, tdSize) {
|
|
8801
|
-
const
|
|
8802
|
-
if (
|
|
8803
|
-
const desired = new Array(
|
|
8804
|
-
for (let i = 0; i <
|
|
10826
|
+
const n2 = columns.length;
|
|
10827
|
+
if (n2 === 0) return [];
|
|
10828
|
+
const desired = new Array(n2).fill(0);
|
|
10829
|
+
for (let i = 0; i < n2; i++) {
|
|
8805
10830
|
let max = 0;
|
|
8806
10831
|
const hdr = headers[i];
|
|
8807
10832
|
if (hdr) {
|
|
@@ -8817,10 +10842,10 @@ function computeAutoFitColumns(columns, headers, rows, enc, thSize, tdSize) {
|
|
|
8817
10842
|
desired[i] = max + CELL_PAD_TOTAL;
|
|
8818
10843
|
}
|
|
8819
10844
|
let total = 0;
|
|
8820
|
-
for (let i = 0; i <
|
|
10845
|
+
for (let i = 0; i < n2; i++) total += desired[i];
|
|
8821
10846
|
if (total <= 0) return columns.slice();
|
|
8822
|
-
const out = new Array(
|
|
8823
|
-
for (let i = 0; i <
|
|
10847
|
+
const out = new Array(n2);
|
|
10848
|
+
for (let i = 0; i < n2; i++) {
|
|
8824
10849
|
out[i] = { ...columns[i], f: desired[i] / total };
|
|
8825
10850
|
}
|
|
8826
10851
|
return out;
|
|
@@ -8861,20 +10886,20 @@ function isCJKBreakable(cp) {
|
|
|
8861
10886
|
function tokenizeForWrap(text) {
|
|
8862
10887
|
const segments = [];
|
|
8863
10888
|
let buf = "";
|
|
8864
|
-
for (const
|
|
8865
|
-
const cp =
|
|
10889
|
+
for (const ch2 of text) {
|
|
10890
|
+
const cp = ch2.codePointAt(0) ?? 0;
|
|
8866
10891
|
if (isCJKBreakable(cp)) {
|
|
8867
10892
|
if (buf) {
|
|
8868
10893
|
segments.push(buf);
|
|
8869
10894
|
buf = "";
|
|
8870
10895
|
}
|
|
8871
|
-
segments.push(
|
|
10896
|
+
segments.push(ch2);
|
|
8872
10897
|
} else if (cp === 32 || cp === 9) {
|
|
8873
|
-
buf +=
|
|
10898
|
+
buf += ch2;
|
|
8874
10899
|
segments.push(buf);
|
|
8875
10900
|
buf = "";
|
|
8876
10901
|
} else {
|
|
8877
|
-
buf +=
|
|
10902
|
+
buf += ch2;
|
|
8878
10903
|
}
|
|
8879
10904
|
}
|
|
8880
10905
|
if (buf) segments.push(buf);
|
|
@@ -8883,14 +10908,14 @@ function tokenizeForWrap(text) {
|
|
|
8883
10908
|
function hardBreakSegment(seg, maxWidth, fontSize, enc) {
|
|
8884
10909
|
const pieces = [];
|
|
8885
10910
|
let buf = "";
|
|
8886
|
-
for (const
|
|
8887
|
-
const candidate = buf +
|
|
10911
|
+
for (const ch2 of seg) {
|
|
10912
|
+
const candidate = buf + ch2;
|
|
8888
10913
|
const w = measureText(candidate, fontSize, enc);
|
|
8889
10914
|
if (w <= maxWidth || buf === "") {
|
|
8890
10915
|
buf = candidate;
|
|
8891
10916
|
} else {
|
|
8892
10917
|
pieces.push(buf);
|
|
8893
|
-
buf =
|
|
10918
|
+
buf = ch2;
|
|
8894
10919
|
}
|
|
8895
10920
|
}
|
|
8896
10921
|
if (buf) pieces.push(buf);
|
|
@@ -9132,7 +11157,7 @@ function renderTable(block, y, enc, mgL, mgR, pgW, cw, tagCtx, documentChildren,
|
|
|
9132
11157
|
const clipCell = (op, i, top, h) => clip ? `q ${fmtNum(cx[i])} ${fmtNum(top - h)} ${fmtNum(cwi[i])} ${fmtNum(h)} re W n
|
|
9133
11158
|
${op}
|
|
9134
11159
|
Q` : op;
|
|
9135
|
-
function emitCell(lines, colIdx, rowTop, rowH, font, sz,
|
|
11160
|
+
function emitCell(lines, colIdx, rowTop, rowH, font, sz, mcRefsOut, isHeader) {
|
|
9136
11161
|
const col = columns[colIdx];
|
|
9137
11162
|
const out = [];
|
|
9138
11163
|
const lineH = sz * TABLE_LINE_HEIGHT;
|
|
@@ -9141,13 +11166,15 @@ Q` : op;
|
|
|
9141
11166
|
const t = lines.length === 1 && wrapMode === "never" ? truncate(lines[li], isHeader && col.mxH !== void 0 ? col.mxH : col.mx) : lines[li];
|
|
9142
11167
|
const baselineY = lines.length === 1 ? rowTop - rowH + padBottom : rowTop - pad - sz + sz * 0.2 - li * lineH;
|
|
9143
11168
|
let op;
|
|
9144
|
-
if (
|
|
11169
|
+
if (mcRefsOut !== null && tagCtx?.tagged) {
|
|
11170
|
+
const mcid = tagCtx.mcidAlloc.next(tagCtx.pageObjNum);
|
|
11171
|
+
mcRefsOut.push({ mcid, pageObjNum: tagCtx.pageObjNum });
|
|
9145
11172
|
if (col.a === "r") {
|
|
9146
|
-
op = txtRTagged(t, cx[colIdx] + cwi[colIdx] - pad, baselineY, font, sz, enc,
|
|
11173
|
+
op = txtRTagged(t, cx[colIdx] + cwi[colIdx] - pad, baselineY, font, sz, enc, mcid, isHeader);
|
|
9147
11174
|
} else if (col.a === "c") {
|
|
9148
|
-
op = txtCTagged(t, cx[colIdx], baselineY, font, sz, cwi[colIdx], enc,
|
|
11175
|
+
op = txtCTagged(t, cx[colIdx], baselineY, font, sz, cwi[colIdx], enc, mcid, isHeader);
|
|
9149
11176
|
} else {
|
|
9150
|
-
op = txtTagged(t, cx[colIdx] + pad, baselineY, font, sz, enc,
|
|
11177
|
+
op = txtTagged(t, cx[colIdx] + pad, baselineY, font, sz, enc, mcid);
|
|
9151
11178
|
}
|
|
9152
11179
|
} else {
|
|
9153
11180
|
if (col.a === "r") {
|
|
@@ -9166,22 +11193,20 @@ Q` : op;
|
|
|
9166
11193
|
ops.push(`${colors.text} rg`);
|
|
9167
11194
|
const lineH = CAPTION_FONT_SIZE * TABLE_LINE_HEIGHT;
|
|
9168
11195
|
let cy = y - CAPTION_FONT_SIZE;
|
|
9169
|
-
|
|
9170
|
-
if (tagCtx?.tagged) {
|
|
9171
|
-
captionMcid = tagCtx.mcidAlloc.next(tagCtx.pageObjNum);
|
|
9172
|
-
tableStructAccum.push({
|
|
9173
|
-
type: "Caption",
|
|
9174
|
-
children: [{ mcid: captionMcid, pageObjNum: tagCtx.pageObjNum }]
|
|
9175
|
-
});
|
|
9176
|
-
}
|
|
11196
|
+
const captionRefs = tagCtx?.tagged ? [] : null;
|
|
9177
11197
|
for (const line of plan.captionLines) {
|
|
9178
|
-
if (
|
|
9179
|
-
|
|
11198
|
+
if (captionRefs !== null && tagCtx?.tagged) {
|
|
11199
|
+
const mcid = tagCtx.mcidAlloc.next(tagCtx.pageObjNum);
|
|
11200
|
+
captionRefs.push({ mcid, pageObjNum: tagCtx.pageObjNum });
|
|
11201
|
+
ops.push(txtCTagged(line, mgL, cy, enc.f2, CAPTION_FONT_SIZE, cw, enc, mcid, true));
|
|
9180
11202
|
} else {
|
|
9181
11203
|
ops.push(txtC(line, mgL, cy, enc.f2, CAPTION_FONT_SIZE, cw, enc, true));
|
|
9182
11204
|
}
|
|
9183
11205
|
cy -= lineH;
|
|
9184
11206
|
}
|
|
11207
|
+
if (captionRefs && captionRefs.length > 0) {
|
|
11208
|
+
tableStructAccum.push({ type: "Caption", children: captionRefs });
|
|
11209
|
+
}
|
|
9185
11210
|
y -= plan.captionHeight;
|
|
9186
11211
|
}
|
|
9187
11212
|
if (drawHeader) {
|
|
@@ -9192,12 +11217,11 @@ Q` : op;
|
|
|
9192
11217
|
ops.push(`${colors.text} rg`);
|
|
9193
11218
|
const thChildren = [];
|
|
9194
11219
|
for (let i = 0; i < block.headers.length && i < columns.length; i++) {
|
|
9195
|
-
|
|
9196
|
-
|
|
9197
|
-
|
|
9198
|
-
thChildren.push({ type: "TH", children:
|
|
11220
|
+
const cellRefs = tagCtx?.tagged ? [] : null;
|
|
11221
|
+
ops.push(...emitCell(headerLines[i] ?? [""], i, y, headerHeight, enc.f2, fs.th, cellRefs, true));
|
|
11222
|
+
if (cellRefs && cellRefs.length > 0) {
|
|
11223
|
+
thChildren.push({ type: "TH", children: cellRefs });
|
|
9199
11224
|
}
|
|
9200
|
-
ops.push(...emitCell(headerLines[i] ?? [""], i, y, headerHeight, enc.f2, fs.th, mcid, true));
|
|
9201
11225
|
}
|
|
9202
11226
|
if (tagCtx?.tagged && thChildren.length > 0) {
|
|
9203
11227
|
tableStructAccum.push({ type: "TR", children: thChildren });
|
|
@@ -9220,12 +11244,11 @@ Q` : op;
|
|
|
9220
11244
|
const color = isAmount ? row.type === "credit" ? colors.credit : colors.debit : colors.text;
|
|
9221
11245
|
const font = isAmount ? enc.f2 : enc.f1;
|
|
9222
11246
|
ops.push(`${color} rg`);
|
|
9223
|
-
|
|
9224
|
-
|
|
9225
|
-
|
|
9226
|
-
tdChildren.push({ type: "TD", children:
|
|
11247
|
+
const cellRefs = tagCtx?.tagged ? [] : null;
|
|
11248
|
+
ops.push(...emitCell(cells[i] ?? [""], i, y, rowH, font, fs.td, cellRefs, false));
|
|
11249
|
+
if (cellRefs && cellRefs.length > 0) {
|
|
11250
|
+
tdChildren.push({ type: "TD", children: cellRefs });
|
|
9227
11251
|
}
|
|
9228
|
-
ops.push(...emitCell(cells[i] ?? [""], i, y, rowH, font, fs.td, mcid, false));
|
|
9229
11252
|
}
|
|
9230
11253
|
if (tagCtx?.tagged && tdChildren.length > 0) {
|
|
9231
11254
|
tableStructAccum.push({ type: "TR", children: tdChildren });
|
|
@@ -9638,16 +11661,20 @@ function estimateBlockHeight(block, enc, cw, headings) {
|
|
|
9638
11661
|
|
|
9639
11662
|
// src/core/pdf-document.ts
|
|
9640
11663
|
function buildDocumentPDF(params, layoutOptions) {
|
|
11664
|
+
return assembleDocumentParts(params, layoutOptions).join("");
|
|
11665
|
+
}
|
|
11666
|
+
function assembleDocumentParts(params, layoutOptions) {
|
|
9641
11667
|
if (!params || typeof params !== "object") {
|
|
9642
11668
|
throw new Error("buildDocumentPDF: params is required and must be an object");
|
|
9643
11669
|
}
|
|
9644
11670
|
if (!Array.isArray(params.blocks)) {
|
|
9645
11671
|
throw new Error("buildDocumentPDF: params.blocks must be an array");
|
|
9646
11672
|
}
|
|
9647
|
-
if (params.blocks.length > 1e4) {
|
|
9648
|
-
throw new Error(`buildDocumentPDF: block count (${params.blocks.length}) exceeds safe limit (10,000)`);
|
|
9649
|
-
}
|
|
9650
11673
|
const layout = layoutOptions ?? params.layout;
|
|
11674
|
+
const maxBlocks = layout?.maxBlocks ?? DEFAULT_MAX_BLOCKS;
|
|
11675
|
+
if (params.blocks.length > maxBlocks) {
|
|
11676
|
+
throw new Error(`buildDocumentPDF: block count (${params.blocks.length}) exceeds safe limit (${maxBlocks}). Raise it via layout.maxBlocks if this is intentional.`);
|
|
11677
|
+
}
|
|
9651
11678
|
const pgW = layout?.pageWidth ?? PG_W;
|
|
9652
11679
|
const pgH = layout?.pageHeight ?? PG_H;
|
|
9653
11680
|
const mg = layout?.margins ?? { ...DEFAULT_MARGINS };
|
|
@@ -9655,7 +11682,7 @@ function buildDocumentPDF(params, layoutOptions) {
|
|
|
9655
11682
|
const fontEntries = params.fontEntries ? [...params.fontEntries] : [];
|
|
9656
11683
|
const pdfaConfig = resolvePdfAConfig(layout?.tagged);
|
|
9657
11684
|
const tagged = pdfaConfig.enabled;
|
|
9658
|
-
const enc = createEncodingContext(fontEntries, tagged);
|
|
11685
|
+
const enc = createEncodingContext(fontEntries, tagged, layout?.normalize ?? false);
|
|
9659
11686
|
const encryptionOpts = layout?.encryption;
|
|
9660
11687
|
if (tagged && encryptionOpts) {
|
|
9661
11688
|
throw new Error("PDF/A and encryption are mutually exclusive (ISO 19005-1 \xA76.3.2)");
|
|
@@ -9678,7 +11705,7 @@ function buildDocumentPDF(params, layoutOptions) {
|
|
|
9678
11705
|
const headerTpl = layout?.headerTemplate;
|
|
9679
11706
|
const headerH = headerTpl ? HEADER_H : 0;
|
|
9680
11707
|
const dateNow = /* @__PURE__ */ new Date();
|
|
9681
|
-
const pad2d = (
|
|
11708
|
+
const pad2d = (n2) => String(n2).padStart(2, "0");
|
|
9682
11709
|
const dateStr = `${dateNow.getFullYear()}-${pad2d(dateNow.getMonth() + 1)}-${pad2d(dateNow.getDate())}`;
|
|
9683
11710
|
const docTitle = params.title ?? "";
|
|
9684
11711
|
const availableH = pgH - mg.t - mg.b - FT_H - headerH;
|
|
@@ -10040,6 +12067,15 @@ function buildDocumentPDF(params, layoutOptions) {
|
|
|
10040
12067
|
const numRadioGroups = radioGroups.size;
|
|
10041
12068
|
const totalFormObjs = totalFieldObjs + numRadioGroups;
|
|
10042
12069
|
const formFontObjs = totalFormFields > 0 ? 1 : 0;
|
|
12070
|
+
const preBaseObjCount = enc.isUnicode && fontEntries.length > 0 ? 4 + fontEntries.length * 5 + imageCount + wmExtraObjs + totalPages * 2 + totalAnnots + totalFormObjs + formFontObjs : 4 + imageCount + wmExtraObjs + totalPages * 2 + totalAnnots + totalFormObjs + formFontObjs;
|
|
12071
|
+
const latinToUniObjNum = tagged ? 0 : preBaseObjCount + 2;
|
|
12072
|
+
const baseFontToUniRef = latinToUniObjNum ? ` /ToUnicode ${latinToUniObjNum} 0 R` : "";
|
|
12073
|
+
const colorEmojiForms = enc.colorEmoji?.forms ?? [];
|
|
12074
|
+
const colorEmojiStart = colorEmojiForms.length > 0 ? (latinToUniObjNum || preBaseObjCount + 1) + 1 : 0;
|
|
12075
|
+
let colorEmojiXObjRefs = "";
|
|
12076
|
+
for (let k = 0; k < colorEmojiForms.length; k++) {
|
|
12077
|
+
colorEmojiXObjRefs += ` /${colorEmojiForms[k].name} ${colorEmojiStart + k} 0 R`;
|
|
12078
|
+
}
|
|
10043
12079
|
const { emit, emitObj, emitStreamObj, offset: getOffset, adjustOffset, objOffsets, parts } = createPdfWriter(compress, encState);
|
|
10044
12080
|
emit(`%PDF-${pdfaConfig.pdfVersion}
|
|
10045
12081
|
`);
|
|
@@ -10070,9 +12106,10 @@ function buildDocumentPDF(params, layoutOptions) {
|
|
|
10070
12106
|
wmImgRef = `/ImW1 ${wmObjStart + wmObjIdx} 0 R`;
|
|
10071
12107
|
}
|
|
10072
12108
|
}
|
|
10073
|
-
if (imgXObjRes || wmImgRef) {
|
|
12109
|
+
if (imgXObjRes || wmImgRef || colorEmojiXObjRefs) {
|
|
10074
12110
|
if (!imgXObjRes) imgXObjRes = " /XObject <<";
|
|
10075
12111
|
if (wmImgRef) imgXObjRes += ` ${wmImgRef}`;
|
|
12112
|
+
imgXObjRes += colorEmojiXObjRefs;
|
|
10076
12113
|
imgXObjRes += " >>";
|
|
10077
12114
|
}
|
|
10078
12115
|
if (enc.isUnicode && fontEntries.length > 0) {
|
|
@@ -10090,8 +12127,8 @@ function buildDocumentPDF(params, layoutOptions) {
|
|
|
10090
12127
|
emitObj(3, refDict);
|
|
10091
12128
|
emitObj(4, refDict);
|
|
10092
12129
|
} else {
|
|
10093
|
-
emitObj(3,
|
|
10094
|
-
emitObj(4,
|
|
12130
|
+
emitObj(3, `<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica /Encoding /WinAnsiEncoding${baseFontToUniRef} >>`);
|
|
12131
|
+
emitObj(4, `<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding${baseFontToUniRef} >>`);
|
|
10095
12132
|
}
|
|
10096
12133
|
for (let fi = 0; fi < fontEntries.length; fi++) {
|
|
10097
12134
|
const fe = fontEntries[fi];
|
|
@@ -10245,8 +12282,8 @@ function buildDocumentPDF(params, layoutOptions) {
|
|
|
10245
12282
|
kids.push(`${5 + imageCount + wmExtraObjs + p * 2} 0 R`);
|
|
10246
12283
|
}
|
|
10247
12284
|
emitObj(2, `<< /Type /Pages /Kids [${kids.join(" ")}] /Count ${totalPages} >>`);
|
|
10248
|
-
emitObj(3,
|
|
10249
|
-
emitObj(4,
|
|
12285
|
+
emitObj(3, `<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica /Encoding /WinAnsiEncoding${baseFontToUniRef} >>`);
|
|
12286
|
+
emitObj(4, `<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding${baseFontToUniRef} >>`);
|
|
10250
12287
|
for (let i = 0; i < imageCount; i++) {
|
|
10251
12288
|
const img = resolvedImages[i];
|
|
10252
12289
|
emitObj(imageObjStart + i, buildImageXObject(img.parsed));
|
|
@@ -10364,7 +12401,7 @@ function buildDocumentPDF(params, layoutOptions) {
|
|
|
10364
12401
|
}
|
|
10365
12402
|
const baseObjCount = enc.isUnicode ? 4 + fontEntries.length * 5 + imageCount + wmExtraObjs + totalPages * 2 + totalAnnots + totalFormObjs + formFontObjs : 4 + imageCount + wmExtraObjs + totalPages * 2 + totalAnnots + totalFormObjs + formFontObjs;
|
|
10366
12403
|
const infoObjNum = baseObjCount + 1;
|
|
10367
|
-
const { pdfDate, xmpDate: isoDate } = buildPdfMetadata();
|
|
12404
|
+
const { pdfDate, xmpDate: isoDate } = buildPdfMetadata(layout?.creationDate);
|
|
10368
12405
|
const infoTitle = params.title ?? "";
|
|
10369
12406
|
const metaParts = [`/Title ${encodePdfTextString(infoTitle)}`, "/Producer (pdfnative)", `/CreationDate (${pdfDate})`];
|
|
10370
12407
|
if (params.metadata?.author) {
|
|
@@ -10378,6 +12415,24 @@ function buildDocumentPDF(params, layoutOptions) {
|
|
|
10378
12415
|
}
|
|
10379
12416
|
emitObj(infoObjNum, `<< ${metaParts.join(" ")} >>`);
|
|
10380
12417
|
let totalObjs = infoObjNum;
|
|
12418
|
+
if (latinToUniObjNum) {
|
|
12419
|
+
const cmap = buildWinAnsiToUnicodeCMap();
|
|
12420
|
+
emitStreamObj(latinToUniObjNum, `<< /Length ${cmap.length}`, cmap);
|
|
12421
|
+
totalObjs = latinToUniObjNum;
|
|
12422
|
+
}
|
|
12423
|
+
if (colorEmojiForms.length > 0) {
|
|
12424
|
+
for (let k = 0; k < colorEmojiForms.length; k++) {
|
|
12425
|
+
const f = colorEmojiForms[k];
|
|
12426
|
+
const objNum = colorEmojiStart + k;
|
|
12427
|
+
const res = f.resources ? ` /Resources << ${f.resources} >>` : "";
|
|
12428
|
+
emitStreamObj(
|
|
12429
|
+
objNum,
|
|
12430
|
+
`<< /Type /XObject /Subtype /Form /BBox [${f.bbox.join(" ")}]${res} /Length ${f.content.length}`,
|
|
12431
|
+
f.content
|
|
12432
|
+
);
|
|
12433
|
+
}
|
|
12434
|
+
totalObjs = colorEmojiStart + colorEmojiForms.length - 1;
|
|
12435
|
+
}
|
|
10381
12436
|
let xmpObjNum = 0;
|
|
10382
12437
|
let outputIntentObjNum = 0;
|
|
10383
12438
|
let afArrayStr = "";
|
|
@@ -10421,7 +12476,7 @@ function buildDocumentPDF(params, layoutOptions) {
|
|
|
10421
12476
|
emitObj(objNum, content);
|
|
10422
12477
|
}
|
|
10423
12478
|
}
|
|
10424
|
-
afArrayStr = efResult.filespecObjNums.map((
|
|
12479
|
+
afArrayStr = efResult.filespecObjNums.map((n2) => `${n2} 0 R`).join(" ");
|
|
10425
12480
|
embeddedFilesNamesDict = efResult.namesDict;
|
|
10426
12481
|
totalObjs += efResult.totalObjects;
|
|
10427
12482
|
}
|
|
@@ -10499,7 +12554,7 @@ endobj
|
|
|
10499
12554
|
}
|
|
10500
12555
|
const writer = { emit, emitObj, emitStreamObj, offset: getOffset, adjustOffset, objOffsets, parts };
|
|
10501
12556
|
writeXrefTrailer(writer, totalObjs, infoObjNum, encState, `${infoTitle}|${pdfDate}`);
|
|
10502
|
-
return parts
|
|
12557
|
+
return parts;
|
|
10503
12558
|
}
|
|
10504
12559
|
function buildDocumentPDFBytes(params, layoutOptions) {
|
|
10505
12560
|
return toBytes(buildDocumentPDF(params, layoutOptions));
|
|
@@ -10690,8 +12745,8 @@ function formatPdfDate(d) {
|
|
|
10690
12745
|
const sec = String(d.getUTCSeconds()).padStart(2, "0");
|
|
10691
12746
|
return `${y}${m}${day}${h}${min}${sec}Z`;
|
|
10692
12747
|
}
|
|
10693
|
-
function padNum(
|
|
10694
|
-
return String(
|
|
12748
|
+
function padNum(n2) {
|
|
12749
|
+
return String(n2).padStart(10, "0");
|
|
10695
12750
|
}
|
|
10696
12751
|
function uint8ToLatin1(bytes) {
|
|
10697
12752
|
let s = "";
|
|
@@ -11292,15 +13347,15 @@ function buildHuffmanTable(lengths, maxSymbol) {
|
|
|
11292
13347
|
}
|
|
11293
13348
|
return { counts, symbols };
|
|
11294
13349
|
}
|
|
11295
|
-
function readBits(br,
|
|
11296
|
-
while (br.bitCnt <
|
|
13350
|
+
function readBits(br, n2) {
|
|
13351
|
+
while (br.bitCnt < n2) {
|
|
11297
13352
|
if (br.pos >= br.buf.length) throw new Error("inflate: unexpected end of data");
|
|
11298
13353
|
br.bitBuf |= br.buf[br.pos++] << br.bitCnt;
|
|
11299
13354
|
br.bitCnt += 8;
|
|
11300
13355
|
}
|
|
11301
|
-
const val = br.bitBuf & (1 <<
|
|
11302
|
-
br.bitBuf >>>=
|
|
11303
|
-
br.bitCnt -=
|
|
13356
|
+
const val = br.bitBuf & (1 << n2) - 1;
|
|
13357
|
+
br.bitBuf >>>= n2;
|
|
13358
|
+
br.bitCnt -= n2;
|
|
11304
13359
|
return val;
|
|
11305
13360
|
}
|
|
11306
13361
|
function decodeSymbol(br, table) {
|
|
@@ -11609,8 +13664,8 @@ function getTrailerRef(trailer, key) {
|
|
|
11609
13664
|
|
|
11610
13665
|
// src/parser/pdf-decode-filters.ts
|
|
11611
13666
|
var MAX_DECODE_OUTPUT = 256 * 1024 * 1024;
|
|
11612
|
-
function checkOutputSize(
|
|
11613
|
-
if (
|
|
13667
|
+
function checkOutputSize(n2, filter) {
|
|
13668
|
+
if (n2 > MAX_DECODE_OUTPUT) {
|
|
11614
13669
|
throw new Error(
|
|
11615
13670
|
`${filter} output exceeds ${MAX_DECODE_OUTPUT} bytes (possible zip-bomb)`
|
|
11616
13671
|
);
|
|
@@ -11740,17 +13795,17 @@ function decodeRunLength(data) {
|
|
|
11740
13795
|
const out = [];
|
|
11741
13796
|
let p = 0;
|
|
11742
13797
|
while (p < data.length) {
|
|
11743
|
-
const
|
|
11744
|
-
if (
|
|
11745
|
-
if (
|
|
11746
|
-
const len =
|
|
13798
|
+
const n2 = data[p++];
|
|
13799
|
+
if (n2 === 128) break;
|
|
13800
|
+
if (n2 < 128) {
|
|
13801
|
+
const len = n2 + 1;
|
|
11747
13802
|
if (p + len > data.length) throw new Error("RunLengthDecode: truncated literal");
|
|
11748
13803
|
for (let i = 0; i < len; i++) out.push(data[p + i]);
|
|
11749
13804
|
p += len;
|
|
11750
13805
|
} else {
|
|
11751
13806
|
if (p >= data.length) throw new Error("RunLengthDecode: truncated repeat");
|
|
11752
13807
|
const v = data[p++];
|
|
11753
|
-
const len = 257 -
|
|
13808
|
+
const len = 257 - n2;
|
|
11754
13809
|
for (let i = 0; i < len; i++) out.push(v);
|
|
11755
13810
|
}
|
|
11756
13811
|
checkOutputSize(out.length, "RunLengthDecode");
|
|
@@ -11915,11 +13970,11 @@ function resolveCompressedObject(buf, xref, _cache, streamObjNum, indexInStream,
|
|
|
11915
13970
|
if (filter === "FlateDecode") {
|
|
11916
13971
|
data = inflateSync(data);
|
|
11917
13972
|
}
|
|
11918
|
-
const
|
|
13973
|
+
const n2 = dictGetNum(streamObj.dict, "N") ?? 0;
|
|
11919
13974
|
const first = dictGetNum(streamObj.dict, "First") ?? 0;
|
|
11920
13975
|
const headerTok = createTokenizer(data, 0);
|
|
11921
13976
|
const objectOffsets = [];
|
|
11922
|
-
for (let i = 0; i <
|
|
13977
|
+
for (let i = 0; i < n2; i++) {
|
|
11923
13978
|
const numTok = headerTok.next();
|
|
11924
13979
|
const offTok = headerTok.next();
|
|
11925
13980
|
if (!numTok || !offTok) break;
|
|
@@ -12429,6 +14484,38 @@ async function* buildPDFStream(params, layoutOptions, streamOptions) {
|
|
|
12429
14484
|
const binary = buildPDF(params, layoutOptions);
|
|
12430
14485
|
yield* chunkBinaryString(binary, chunkSize);
|
|
12431
14486
|
}
|
|
14487
|
+
function* streamPartsChunked(parts, chunkSize) {
|
|
14488
|
+
let buf = new Uint8Array(chunkSize);
|
|
14489
|
+
let filled = 0;
|
|
14490
|
+
for (let p = 0; p < parts.length; p++) {
|
|
14491
|
+
const part = parts[p];
|
|
14492
|
+
parts[p] = "";
|
|
14493
|
+
const len = part.length;
|
|
14494
|
+
for (let i = 0; i < len; i++) {
|
|
14495
|
+
buf[filled++] = part.charCodeAt(i) & 255;
|
|
14496
|
+
if (filled === chunkSize) {
|
|
14497
|
+
yield buf;
|
|
14498
|
+
buf = new Uint8Array(chunkSize);
|
|
14499
|
+
filled = 0;
|
|
14500
|
+
}
|
|
14501
|
+
}
|
|
14502
|
+
}
|
|
14503
|
+
if (filled > 0) {
|
|
14504
|
+
yield buf.subarray(0, filled);
|
|
14505
|
+
}
|
|
14506
|
+
}
|
|
14507
|
+
async function* buildDocumentPDFStreamTrue(params, layoutOptions, streamOptions) {
|
|
14508
|
+
validateDocumentStreamable(params, layoutOptions);
|
|
14509
|
+
const chunkSize = resolveChunkSize(streamOptions?.chunkSize);
|
|
14510
|
+
const parts = assembleDocumentParts(params, layoutOptions);
|
|
14511
|
+
yield* streamPartsChunked(parts, chunkSize);
|
|
14512
|
+
}
|
|
14513
|
+
async function* buildPDFStreamTrue(params, layoutOptions, streamOptions) {
|
|
14514
|
+
validateTableStreamable(params, layoutOptions);
|
|
14515
|
+
const chunkSize = resolveChunkSize(streamOptions?.chunkSize);
|
|
14516
|
+
const parts = assembleTableParts(params, layoutOptions);
|
|
14517
|
+
yield* streamPartsChunked(parts, chunkSize);
|
|
14518
|
+
}
|
|
12432
14519
|
function concatChunks(chunks) {
|
|
12433
14520
|
let totalLen = 0;
|
|
12434
14521
|
for (let i = 0; i < chunks.length; i++) totalLen += chunks[i].length;
|
|
@@ -12662,177 +14749,374 @@ async function initCrypto() {
|
|
|
12662
14749
|
initEcdsaAsn12(asn1);
|
|
12663
14750
|
}
|
|
12664
14751
|
|
|
12665
|
-
// src/
|
|
12666
|
-
|
|
12667
|
-
|
|
12668
|
-
|
|
12669
|
-
|
|
12670
|
-
|
|
12671
|
-
|
|
12672
|
-
|
|
12673
|
-
|
|
12674
|
-
|
|
12675
|
-
|
|
12676
|
-
|
|
12677
|
-
|
|
12678
|
-
if (cp >= 2377 && cp <= 2380) return "Mpst";
|
|
12679
|
-
if (cp === 2381) return "H";
|
|
12680
|
-
if (cp >= 2382 && cp <= 2383) return "Mpre";
|
|
12681
|
-
if (cp === 2384) return "O";
|
|
12682
|
-
if (cp >= 2385 && cp <= 2391) return "Mabv";
|
|
12683
|
-
if (cp >= 2392 && cp <= 2401) return "B";
|
|
12684
|
-
if (cp >= 2402 && cp <= 2403) return "Mblw";
|
|
12685
|
-
if (cp >= 2406 && cp <= 2415) return "N";
|
|
12686
|
-
return "O";
|
|
12687
|
-
}
|
|
12688
|
-
function bengaliCategory(cp) {
|
|
12689
|
-
if (cp === 2433) return "Mabv";
|
|
12690
|
-
if (cp === 2434) return "Mpst";
|
|
12691
|
-
if (cp === 2435) return "Mpst";
|
|
12692
|
-
if (cp >= 2437 && cp <= 2452) return "V";
|
|
12693
|
-
if (cp >= 2453 && cp <= 2489) return "B";
|
|
12694
|
-
if (cp === 2492) return "Mblw";
|
|
12695
|
-
if (cp === 2493) return "O";
|
|
12696
|
-
if (cp === 2494) return "Mpst";
|
|
12697
|
-
if (cp >= 2495 && cp <= 2496) return cp === 2495 ? "Mpre" : "Mpst";
|
|
12698
|
-
if (cp >= 2497 && cp <= 2500) return "Mblw";
|
|
12699
|
-
if (cp >= 2503 && cp <= 2504) return "Mpre";
|
|
12700
|
-
if (cp >= 2507 && cp <= 2508) return "Mpre";
|
|
12701
|
-
if (cp === 2509) return "H";
|
|
12702
|
-
if (cp === 2510) return "B";
|
|
12703
|
-
if (cp === 2519) return "Mpst";
|
|
12704
|
-
if (cp >= 2524 && cp <= 2527) return "B";
|
|
12705
|
-
if (cp >= 2528 && cp <= 2531) return "O";
|
|
12706
|
-
if (cp >= 2534 && cp <= 2543) return "N";
|
|
12707
|
-
return "O";
|
|
12708
|
-
}
|
|
12709
|
-
function tamilCategory(cp) {
|
|
12710
|
-
if (cp === 2946) return "Mabv";
|
|
12711
|
-
if (cp >= 2949 && cp <= 2964) return "V";
|
|
12712
|
-
if (cp >= 2965 && cp <= 3001) return "B";
|
|
12713
|
-
if (cp === 3006) return "Mpst";
|
|
12714
|
-
if (cp === 3007) return "Mpst";
|
|
12715
|
-
if (cp >= 3008 && cp <= 3010) return "Mpst";
|
|
12716
|
-
if (cp >= 3014 && cp <= 3016) return "Mpre";
|
|
12717
|
-
if (cp >= 3018 && cp <= 3020) return "Mpre";
|
|
12718
|
-
if (cp === 3021) return "H";
|
|
12719
|
-
if (cp === 3031) return "Mpst";
|
|
12720
|
-
if (cp >= 3046 && cp <= 3055) return "N";
|
|
12721
|
-
return "O";
|
|
12722
|
-
}
|
|
12723
|
-
function classifyUseCategory(cp) {
|
|
12724
|
-
if (cp === 8204) return "ZWNJ";
|
|
12725
|
-
if (cp === 8205) return "ZWJ";
|
|
12726
|
-
if (cp >= 2304 && cp <= 2431) return devanagariCategory(cp);
|
|
12727
|
-
if (cp >= 2432 && cp <= 2559) return bengaliCategory(cp);
|
|
12728
|
-
if (cp >= 2944 && cp <= 3071) return tamilCategory(cp);
|
|
12729
|
-
return "O";
|
|
12730
|
-
}
|
|
12731
|
-
function classifyClusters(codePoints) {
|
|
12732
|
-
const out = [];
|
|
12733
|
-
let i = 0;
|
|
12734
|
-
while (i < codePoints.length) {
|
|
12735
|
-
const cluster = nextCluster(codePoints, i);
|
|
12736
|
-
out.push(cluster.cluster);
|
|
12737
|
-
i = cluster.next;
|
|
12738
|
-
}
|
|
12739
|
-
return out;
|
|
14752
|
+
// src/fonts/colr-parser.ts
|
|
14753
|
+
var IDENTITY = [1, 0, 0, 1, 0, 0];
|
|
14754
|
+
function compose(outer, inner) {
|
|
14755
|
+
const [a1, b1, c1, d1, e1, f1] = outer;
|
|
14756
|
+
const [a2, b2, c2, d2, e2, f2] = inner;
|
|
14757
|
+
return [
|
|
14758
|
+
a1 * a2 + c1 * b2,
|
|
14759
|
+
b1 * a2 + d1 * b2,
|
|
14760
|
+
a1 * c2 + c1 * d2,
|
|
14761
|
+
b1 * c2 + d1 * d2,
|
|
14762
|
+
a1 * e2 + c1 * f2 + e1,
|
|
14763
|
+
b1 * e2 + d1 * f2 + f1
|
|
14764
|
+
];
|
|
12740
14765
|
}
|
|
12741
|
-
var
|
|
12742
|
-
|
|
12743
|
-
function
|
|
12744
|
-
const
|
|
12745
|
-
const
|
|
12746
|
-
|
|
12747
|
-
|
|
12748
|
-
|
|
12749
|
-
|
|
12750
|
-
|
|
12751
|
-
|
|
12752
|
-
|
|
12753
|
-
|
|
12754
|
-
|
|
12755
|
-
|
|
12756
|
-
|
|
12757
|
-
|
|
14766
|
+
var UnsupportedPaint = class extends Error {
|
|
14767
|
+
};
|
|
14768
|
+
function tableDirectory(view) {
|
|
14769
|
+
const numTables = view.getUint16(4);
|
|
14770
|
+
const tables = {};
|
|
14771
|
+
for (let i = 0; i < numTables; i++) {
|
|
14772
|
+
const rec = 12 + i * 16;
|
|
14773
|
+
const tag = String.fromCharCode(
|
|
14774
|
+
view.getUint8(rec),
|
|
14775
|
+
view.getUint8(rec + 1),
|
|
14776
|
+
view.getUint8(rec + 2),
|
|
14777
|
+
view.getUint8(rec + 3)
|
|
14778
|
+
);
|
|
14779
|
+
tables[tag] = { offset: view.getUint32(rec + 8), length: view.getUint32(rec + 12) };
|
|
14780
|
+
}
|
|
14781
|
+
return tables;
|
|
14782
|
+
}
|
|
14783
|
+
function getUint24(view, pos) {
|
|
14784
|
+
return view.getUint8(pos) << 16 | view.getUint8(pos + 1) << 8 | view.getUint8(pos + 2);
|
|
14785
|
+
}
|
|
14786
|
+
function f2dot14(view, pos) {
|
|
14787
|
+
return view.getInt16(pos) / 16384;
|
|
14788
|
+
}
|
|
14789
|
+
function fixed(view, pos) {
|
|
14790
|
+
return view.getInt32(pos) / 65536;
|
|
14791
|
+
}
|
|
14792
|
+
function parseCpal(bytes) {
|
|
14793
|
+
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
14794
|
+
const tables = tableDirectory(view);
|
|
14795
|
+
const cpal = tables["CPAL"];
|
|
14796
|
+
if (!cpal) return null;
|
|
14797
|
+
const base = cpal.offset;
|
|
14798
|
+
const numPaletteEntries = view.getUint16(base + 2);
|
|
14799
|
+
const colorRecordsArrayOffset = view.getUint32(base + 8);
|
|
14800
|
+
const firstIndex = view.getUint16(base + 12);
|
|
14801
|
+
const recBase = base + colorRecordsArrayOffset + firstIndex * 4;
|
|
14802
|
+
const colors = [];
|
|
14803
|
+
for (let i = 0; i < numPaletteEntries; i++) {
|
|
14804
|
+
const p = recBase + i * 4;
|
|
14805
|
+
const b = view.getUint8(p);
|
|
14806
|
+
const g = view.getUint8(p + 1);
|
|
14807
|
+
const r = view.getUint8(p + 2);
|
|
14808
|
+
const a = view.getUint8(p + 3);
|
|
14809
|
+
colors.push([r, g, b, a]);
|
|
14810
|
+
}
|
|
14811
|
+
return colors;
|
|
14812
|
+
}
|
|
14813
|
+
function resolveColor2(ctx, paletteIndex, alpha) {
|
|
14814
|
+
const base = paletteIndex === 65535 ? [0, 0, 0, 255] : ctx.palette[paletteIndex] ?? [0, 0, 0, 255];
|
|
14815
|
+
const a = Math.round(base[3] * Math.max(0, Math.min(1, alpha)));
|
|
14816
|
+
return [base[0], base[1], base[2], a];
|
|
14817
|
+
}
|
|
14818
|
+
var EXTEND = ["pad", "repeat", "reflect"];
|
|
14819
|
+
function readColorLine(ctx, offset) {
|
|
14820
|
+
const { view } = ctx;
|
|
14821
|
+
const extend = EXTEND[view.getUint8(offset)] ?? "pad";
|
|
14822
|
+
const numStops = view.getUint16(offset + 1);
|
|
14823
|
+
const stops = [];
|
|
14824
|
+
let p = offset + 3;
|
|
14825
|
+
for (let i = 0; i < numStops; i++) {
|
|
14826
|
+
const stopOffset = f2dot14(view, p);
|
|
14827
|
+
const paletteIndex = view.getUint16(p + 2);
|
|
14828
|
+
const alpha = f2dot14(view, p + 4);
|
|
14829
|
+
stops.push({ offset: stopOffset, color: resolveColor2(ctx, paletteIndex, alpha) });
|
|
14830
|
+
p += 6;
|
|
14831
|
+
}
|
|
14832
|
+
return { stops, extend };
|
|
14833
|
+
}
|
|
14834
|
+
function apply(m, x, y) {
|
|
14835
|
+
return [m[0] * x + m[2] * y + m[4], m[1] * x + m[3] * y + m[5]];
|
|
14836
|
+
}
|
|
14837
|
+
function avgScale(m) {
|
|
14838
|
+
const sx = Math.hypot(m[0], m[1]);
|
|
14839
|
+
const sy = Math.hypot(m[2], m[3]);
|
|
14840
|
+
return (sx + sy) / 2 || 1;
|
|
14841
|
+
}
|
|
14842
|
+
function resolveFill(ctx, offset, m) {
|
|
14843
|
+
const { view } = ctx;
|
|
14844
|
+
const format = view.getUint8(offset);
|
|
14845
|
+
switch (format) {
|
|
14846
|
+
case 2: {
|
|
14847
|
+
const paletteIndex = view.getUint16(offset + 1);
|
|
14848
|
+
const alpha = f2dot14(view, offset + 3);
|
|
14849
|
+
return { kind: "solid", color: resolveColor2(ctx, paletteIndex, alpha) };
|
|
14850
|
+
}
|
|
14851
|
+
case 4: {
|
|
14852
|
+
const colorLineOffset = getUint24(view, offset + 1);
|
|
14853
|
+
const x0 = view.getInt16(offset + 4), y0 = view.getInt16(offset + 6);
|
|
14854
|
+
const x1 = view.getInt16(offset + 8), y1 = view.getInt16(offset + 10);
|
|
14855
|
+
const { stops, extend } = readColorLine(ctx, offset + colorLineOffset);
|
|
14856
|
+
return { kind: "linear", p0: apply(m, x0, y0), p1: apply(m, x1, y1), stops, extend };
|
|
14857
|
+
}
|
|
14858
|
+
case 6: {
|
|
14859
|
+
const colorLineOffset = getUint24(view, offset + 1);
|
|
14860
|
+
const x0 = view.getInt16(offset + 4), y0 = view.getInt16(offset + 6);
|
|
14861
|
+
const r0 = view.getUint16(offset + 8);
|
|
14862
|
+
const x1 = view.getInt16(offset + 10), y1 = view.getInt16(offset + 12);
|
|
14863
|
+
const r1 = view.getUint16(offset + 14);
|
|
14864
|
+
const { stops, extend } = readColorLine(ctx, offset + colorLineOffset);
|
|
14865
|
+
const s = avgScale(m);
|
|
14866
|
+
return { kind: "radial", c0: apply(m, x0, y0), r0: r0 * s, c1: apply(m, x1, y1), r1: r1 * s, stops, extend };
|
|
14867
|
+
}
|
|
14868
|
+
case 12: {
|
|
14869
|
+
const subOffset = getUint24(view, offset + 1);
|
|
14870
|
+
const transformOffset = getUint24(view, offset + 4);
|
|
14871
|
+
const t = readAffine(view, offset + transformOffset);
|
|
14872
|
+
return resolveFill(ctx, offset + subOffset, compose(m, t));
|
|
14873
|
+
}
|
|
14874
|
+
case 14: {
|
|
14875
|
+
const subOffset = getUint24(view, offset + 1);
|
|
14876
|
+
const dx = view.getInt16(offset + 4), dy = view.getInt16(offset + 6);
|
|
14877
|
+
return resolveFill(ctx, offset + subOffset, compose(m, [1, 0, 0, 1, dx, dy]));
|
|
14878
|
+
}
|
|
14879
|
+
case 16: {
|
|
14880
|
+
const subOffset = getUint24(view, offset + 1);
|
|
14881
|
+
const sx = f2dot14(view, offset + 4), sy = f2dot14(view, offset + 6);
|
|
14882
|
+
return resolveFill(ctx, offset + subOffset, compose(m, [sx, 0, 0, sy, 0, 0]));
|
|
12758
14883
|
}
|
|
14884
|
+
default:
|
|
14885
|
+
throw new UnsupportedPaint(`fill paint format ${format}`);
|
|
14886
|
+
}
|
|
14887
|
+
}
|
|
14888
|
+
function readAffine(view, pos) {
|
|
14889
|
+
const xx = fixed(view, pos);
|
|
14890
|
+
const yx = fixed(view, pos + 4);
|
|
14891
|
+
const xy = fixed(view, pos + 8);
|
|
14892
|
+
const yy = fixed(view, pos + 12);
|
|
14893
|
+
const dx = fixed(view, pos + 16);
|
|
14894
|
+
const dy = fixed(view, pos + 20);
|
|
14895
|
+
return [xx, yx, xy, yy, dx, dy];
|
|
14896
|
+
}
|
|
14897
|
+
function collectLayers(ctx, offset, m, out, depth) {
|
|
14898
|
+
if (depth > 16) throw new UnsupportedPaint("paint recursion too deep");
|
|
14899
|
+
const { view } = ctx;
|
|
14900
|
+
const format = view.getUint8(offset);
|
|
14901
|
+
switch (format) {
|
|
14902
|
+
case 1: {
|
|
14903
|
+
const numLayers = view.getUint8(offset + 1);
|
|
14904
|
+
const firstLayerIndex = view.getUint32(offset + 2);
|
|
14905
|
+
if (!ctx.layerListBase) throw new UnsupportedPaint("PaintColrLayers without LayerList");
|
|
14906
|
+
for (let i = 0; i < numLayers; i++) {
|
|
14907
|
+
const idx = firstLayerIndex + i;
|
|
14908
|
+
const paintOffset = view.getUint32(ctx.layerListBase + 4 + idx * 4);
|
|
14909
|
+
collectLayers(ctx, ctx.layerListBase + paintOffset, m, out, depth + 1);
|
|
14910
|
+
}
|
|
14911
|
+
return;
|
|
14912
|
+
}
|
|
14913
|
+
case 10: {
|
|
14914
|
+
const subOffset = getUint24(view, offset + 1);
|
|
14915
|
+
const glyphId = view.getUint16(offset + 4);
|
|
14916
|
+
const paint = resolveFill(ctx, offset + subOffset, IDENTITY);
|
|
14917
|
+
out.push(m === IDENTITY ? { glyphId, paint } : { glyphId, paint, transform: m });
|
|
14918
|
+
return;
|
|
14919
|
+
}
|
|
14920
|
+
case 11: {
|
|
14921
|
+
const glyphId = view.getUint16(offset + 1);
|
|
14922
|
+
const paintOffset = baseGlyphPaintOffset(ctx, glyphId);
|
|
14923
|
+
if (paintOffset === null) throw new UnsupportedPaint("PaintColrGlyph missing base");
|
|
14924
|
+
collectLayers(ctx, paintOffset, m, out, depth + 1);
|
|
14925
|
+
return;
|
|
14926
|
+
}
|
|
14927
|
+
case 12: {
|
|
14928
|
+
const subOffset = getUint24(view, offset + 1);
|
|
14929
|
+
const transformOffset = getUint24(view, offset + 4);
|
|
14930
|
+
const t = readAffine(view, offset + transformOffset);
|
|
14931
|
+
collectLayers(ctx, offset + subOffset, compose(m, t), out, depth + 1);
|
|
14932
|
+
return;
|
|
14933
|
+
}
|
|
14934
|
+
case 14: {
|
|
14935
|
+
const subOffset = getUint24(view, offset + 1);
|
|
14936
|
+
const dx = view.getInt16(offset + 4), dy = view.getInt16(offset + 6);
|
|
14937
|
+
collectLayers(ctx, offset + subOffset, compose(m, [1, 0, 0, 1, dx, dy]), out, depth + 1);
|
|
14938
|
+
return;
|
|
14939
|
+
}
|
|
14940
|
+
case 16: {
|
|
14941
|
+
const subOffset = getUint24(view, offset + 1);
|
|
14942
|
+
const sx = f2dot14(view, offset + 4), sy = f2dot14(view, offset + 6);
|
|
14943
|
+
collectLayers(ctx, offset + subOffset, compose(m, [sx, 0, 0, sy, 0, 0]), out, depth + 1);
|
|
14944
|
+
return;
|
|
14945
|
+
}
|
|
14946
|
+
default:
|
|
14947
|
+
throw new UnsupportedPaint(`structural paint format ${format}`);
|
|
12759
14948
|
}
|
|
12760
|
-
|
|
12761
|
-
|
|
12762
|
-
|
|
12763
|
-
|
|
12764
|
-
|
|
12765
|
-
|
|
12766
|
-
|
|
12767
|
-
|
|
12768
|
-
|
|
12769
|
-
|
|
14949
|
+
}
|
|
14950
|
+
function baseGlyphPaintOffset(ctx, glyphId) {
|
|
14951
|
+
const { view, colrBase } = ctx;
|
|
14952
|
+
const baseGlyphListOffset = view.getUint32(colrBase + 14);
|
|
14953
|
+
if (!baseGlyphListOffset) return null;
|
|
14954
|
+
const listBase = colrBase + baseGlyphListOffset;
|
|
14955
|
+
const numRecords = view.getUint32(listBase);
|
|
14956
|
+
for (let i = 0; i < numRecords; i++) {
|
|
14957
|
+
const rec = listBase + 4 + i * 6;
|
|
14958
|
+
const gid = view.getUint16(rec);
|
|
14959
|
+
if (gid === glyphId) {
|
|
14960
|
+
return listBase + view.getUint32(rec + 2);
|
|
12770
14961
|
}
|
|
12771
14962
|
}
|
|
12772
|
-
|
|
12773
|
-
|
|
12774
|
-
|
|
12775
|
-
|
|
12776
|
-
|
|
12777
|
-
|
|
12778
|
-
|
|
12779
|
-
|
|
12780
|
-
|
|
12781
|
-
|
|
12782
|
-
|
|
12783
|
-
|
|
14963
|
+
return null;
|
|
14964
|
+
}
|
|
14965
|
+
function parseColrCpal(bytes) {
|
|
14966
|
+
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
14967
|
+
const tables = tableDirectory(view);
|
|
14968
|
+
const colr = tables["COLR"];
|
|
14969
|
+
if (!colr) return null;
|
|
14970
|
+
const palette = parseCpal(bytes) ?? [];
|
|
14971
|
+
const colrBase = colr.offset;
|
|
14972
|
+
const version = view.getUint16(colrBase);
|
|
14973
|
+
const result = {};
|
|
14974
|
+
const numBaseGlyphRecords = view.getUint16(colrBase + 2);
|
|
14975
|
+
const baseGlyphRecordsOffset = view.getUint32(colrBase + 4);
|
|
14976
|
+
const layerRecordsOffset = view.getUint32(colrBase + 8);
|
|
14977
|
+
if (numBaseGlyphRecords && baseGlyphRecordsOffset && layerRecordsOffset) {
|
|
14978
|
+
const recBase = colrBase + baseGlyphRecordsOffset;
|
|
14979
|
+
const layerBase = colrBase + layerRecordsOffset;
|
|
14980
|
+
for (let i = 0; i < numBaseGlyphRecords; i++) {
|
|
14981
|
+
const rec = recBase + i * 6;
|
|
14982
|
+
const gid = view.getUint16(rec);
|
|
14983
|
+
const firstLayer = view.getUint16(rec + 2);
|
|
14984
|
+
const numLayers = view.getUint16(rec + 4);
|
|
14985
|
+
const layers = [];
|
|
14986
|
+
for (let j = 0; j < numLayers; j++) {
|
|
14987
|
+
const lr = layerBase + (firstLayer + j) * 4;
|
|
14988
|
+
const layerGid = view.getUint16(lr);
|
|
14989
|
+
const paletteIndex = view.getUint16(lr + 2);
|
|
14990
|
+
const color = paletteIndex === 65535 ? [0, 0, 0, 255] : palette[paletteIndex] ?? [0, 0, 0, 255];
|
|
14991
|
+
layers.push({ glyphId: layerGid, paint: { kind: "solid", color } });
|
|
14992
|
+
}
|
|
14993
|
+
if (layers.length) result[gid] = { layers };
|
|
14994
|
+
}
|
|
14995
|
+
}
|
|
14996
|
+
if (version >= 1) {
|
|
14997
|
+
const layerListOffset = view.getUint32(colrBase + 18);
|
|
14998
|
+
const ctx = {
|
|
14999
|
+
view,
|
|
15000
|
+
palette,
|
|
15001
|
+
colrBase,
|
|
15002
|
+
layerListBase: layerListOffset ? colrBase + layerListOffset : 0
|
|
15003
|
+
};
|
|
15004
|
+
const baseGlyphListOffset = view.getUint32(colrBase + 14);
|
|
15005
|
+
if (baseGlyphListOffset) {
|
|
15006
|
+
const listBase = colrBase + baseGlyphListOffset;
|
|
15007
|
+
const numRecords = view.getUint32(listBase);
|
|
15008
|
+
for (let i = 0; i < numRecords; i++) {
|
|
15009
|
+
const rec = listBase + 4 + i * 6;
|
|
15010
|
+
const gid = view.getUint16(rec);
|
|
15011
|
+
const paintOffset = listBase + view.getUint32(rec + 2);
|
|
15012
|
+
try {
|
|
15013
|
+
const layers = [];
|
|
15014
|
+
collectLayers(ctx, paintOffset, IDENTITY, layers, 0);
|
|
15015
|
+
if (layers.length) result[gid] = { layers };
|
|
15016
|
+
} catch (e) {
|
|
15017
|
+
if (!(e instanceof UnsupportedPaint)) throw e;
|
|
12784
15018
|
}
|
|
12785
15019
|
}
|
|
12786
|
-
continue;
|
|
12787
|
-
}
|
|
12788
|
-
if (cat === "Mabv") {
|
|
12789
|
-
above.push({ cp, category: "Mabv" });
|
|
12790
|
-
i++;
|
|
12791
|
-
continue;
|
|
12792
15020
|
}
|
|
12793
|
-
|
|
12794
|
-
|
|
12795
|
-
|
|
12796
|
-
|
|
15021
|
+
}
|
|
15022
|
+
return Object.keys(result).length ? result : null;
|
|
15023
|
+
}
|
|
15024
|
+
|
|
15025
|
+
// src/parser/pdf-ua-validator.ts
|
|
15026
|
+
function pageContentText(reader, page) {
|
|
15027
|
+
const contents = page.get("Contents");
|
|
15028
|
+
if (contents === void 0) return "";
|
|
15029
|
+
const resolved = reader.resolveValue(contents);
|
|
15030
|
+
const streams = [];
|
|
15031
|
+
if (isStream(resolved)) {
|
|
15032
|
+
streams.push(resolved);
|
|
15033
|
+
} else if (isArray(resolved)) {
|
|
15034
|
+
for (const item of resolved) {
|
|
15035
|
+
const s = reader.resolveValue(item);
|
|
15036
|
+
if (isStream(s)) streams.push(s);
|
|
15037
|
+
}
|
|
15038
|
+
}
|
|
15039
|
+
let text = "";
|
|
15040
|
+
for (const s of streams) {
|
|
15041
|
+
try {
|
|
15042
|
+
const data = reader.decodeStream(s);
|
|
15043
|
+
text += new TextDecoder("latin1").decode(data);
|
|
15044
|
+
} catch {
|
|
12797
15045
|
}
|
|
12798
|
-
|
|
12799
|
-
|
|
12800
|
-
|
|
12801
|
-
|
|
15046
|
+
}
|
|
15047
|
+
return text;
|
|
15048
|
+
}
|
|
15049
|
+
function validatePdfUA(bytes) {
|
|
15050
|
+
const errors = [];
|
|
15051
|
+
const warnings = [];
|
|
15052
|
+
let reader;
|
|
15053
|
+
try {
|
|
15054
|
+
reader = openPdf(bytes);
|
|
15055
|
+
} catch (err) {
|
|
15056
|
+
return { valid: false, errors: [`Unparseable PDF: ${err.message}`], warnings: [] };
|
|
15057
|
+
}
|
|
15058
|
+
let catalog;
|
|
15059
|
+
try {
|
|
15060
|
+
catalog = reader.getCatalog();
|
|
15061
|
+
} catch (err) {
|
|
15062
|
+
return { valid: false, errors: [`No document catalog: ${err.message}`], warnings: [] };
|
|
15063
|
+
}
|
|
15064
|
+
const markInfo = reader.resolveValue(catalog.get("MarkInfo") ?? null);
|
|
15065
|
+
if (!isDict(markInfo)) {
|
|
15066
|
+
errors.push("Catalog is missing /MarkInfo (ISO 14289-1 \xA77.1).");
|
|
15067
|
+
} else if (markInfo.get("Marked") !== true) {
|
|
15068
|
+
errors.push("Catalog /MarkInfo /Marked must be true (ISO 14289-1 \xA77.1).");
|
|
15069
|
+
}
|
|
15070
|
+
const structRootRef = dictGetRef(catalog, "StructTreeRoot");
|
|
15071
|
+
const structRoot = structRootRef ? reader.resolveValue(structRootRef) : reader.resolveValue(catalog.get("StructTreeRoot") ?? null);
|
|
15072
|
+
if (!isDict(structRoot)) {
|
|
15073
|
+
errors.push("Catalog is missing /StructTreeRoot (ISO 14289-1 \xA77.1).");
|
|
15074
|
+
} else {
|
|
15075
|
+
const parentTree = structRoot.get("ParentTree");
|
|
15076
|
+
if (parentTree === void 0) {
|
|
15077
|
+
errors.push("/StructTreeRoot is missing /ParentTree (ISO 32000-1 \xA714.7.4.4).");
|
|
12802
15078
|
}
|
|
12803
|
-
if (
|
|
12804
|
-
|
|
12805
|
-
i++;
|
|
12806
|
-
continue;
|
|
15079
|
+
if (dictGetName(structRoot, "Type") !== "StructTreeRoot") {
|
|
15080
|
+
warnings.push("/StructTreeRoot should declare /Type /StructTreeRoot.");
|
|
12807
15081
|
}
|
|
12808
|
-
|
|
12809
|
-
|
|
12810
|
-
|
|
12811
|
-
|
|
15082
|
+
}
|
|
15083
|
+
if (catalog.get("Metadata") === void 0) {
|
|
15084
|
+
errors.push("Catalog is missing /Metadata (XMP) (ISO 14289-1 \xA77.2).");
|
|
15085
|
+
}
|
|
15086
|
+
const lang = catalog.get("Lang");
|
|
15087
|
+
if (lang === void 0) {
|
|
15088
|
+
warnings.push("Catalog has no /Lang \u2014 a default natural language is recommended (ISO 14289-1 \xA77.2).");
|
|
15089
|
+
}
|
|
15090
|
+
let pages = [];
|
|
15091
|
+
try {
|
|
15092
|
+
pages = reader.getPages();
|
|
15093
|
+
} catch (err) {
|
|
15094
|
+
errors.push(`Unable to enumerate pages: ${err.message}`);
|
|
15095
|
+
}
|
|
15096
|
+
for (let p = 0; p < pages.length; p++) {
|
|
15097
|
+
const page = pages[p];
|
|
15098
|
+
const content = pageContentText(reader, page);
|
|
15099
|
+
if (!content) continue;
|
|
15100
|
+
const seen = /* @__PURE__ */ new Set();
|
|
15101
|
+
const re = /\/MCID\s+(\d+)/g;
|
|
15102
|
+
let m;
|
|
15103
|
+
let count = 0;
|
|
15104
|
+
while ((m = re.exec(content)) !== null) {
|
|
15105
|
+
count++;
|
|
15106
|
+
const mcid = Number(m[1]);
|
|
15107
|
+
if (seen.has(mcid)) {
|
|
15108
|
+
errors.push(`Page ${p + 1}: duplicate /MCID ${mcid} in content stream (ISO 32000-1 \xA714.7.4.3).`);
|
|
15109
|
+
}
|
|
15110
|
+
seen.add(mcid);
|
|
12812
15111
|
}
|
|
12813
|
-
if (
|
|
12814
|
-
|
|
12815
|
-
i++;
|
|
12816
|
-
continue;
|
|
15112
|
+
if (count === 0 && /\b(Tj|TJ)\b/.test(content)) {
|
|
15113
|
+
warnings.push(`Page ${p + 1}: text content is not wrapped in marked-content (no /MCID found).`);
|
|
12817
15114
|
}
|
|
12818
|
-
if (
|
|
12819
|
-
|
|
12820
|
-
base = { cp, category: cat === "N" ? "N" : "O" };
|
|
12821
|
-
i++;
|
|
12822
|
-
continue;
|
|
12823
|
-
}
|
|
12824
|
-
break;
|
|
15115
|
+
if (count > 0 && page.get("StructParents") === void 0) {
|
|
15116
|
+
warnings.push(`Page ${p + 1}: marked content present but page has no /StructParents key.`);
|
|
12825
15117
|
}
|
|
12826
|
-
break;
|
|
12827
|
-
}
|
|
12828
|
-
if (i === start) {
|
|
12829
|
-
base = { cp: cps[i], category: cat0 };
|
|
12830
|
-
i++;
|
|
12831
15118
|
}
|
|
12832
|
-
return {
|
|
12833
|
-
cluster: { prebase, base, above, below, post, tail },
|
|
12834
|
-
next: i
|
|
12835
|
-
};
|
|
15119
|
+
return { valid: errors.length === 0, errors, warnings };
|
|
12836
15120
|
}
|
|
12837
15121
|
|
|
12838
15122
|
// src/worker/worker-api.ts
|
|
@@ -12901,6 +15185,7 @@ exports.DEFAULT_COLUMNS = DEFAULT_COLUMNS;
|
|
|
12901
15185
|
exports.DEFAULT_CW = DEFAULT_CW;
|
|
12902
15186
|
exports.DEFAULT_FONT_SIZES = DEFAULT_FONT_SIZES;
|
|
12903
15187
|
exports.DEFAULT_MARGINS = DEFAULT_MARGINS;
|
|
15188
|
+
exports.DEFAULT_MAX_BLOCKS = DEFAULT_MAX_BLOCKS;
|
|
12904
15189
|
exports.DEFAULT_MAX_INFLATE_OUTPUT = DEFAULT_MAX_INFLATE_OUTPUT;
|
|
12905
15190
|
exports.FT_H = FT_H;
|
|
12906
15191
|
exports.HEADER_H = HEADER_H;
|
|
@@ -12926,6 +15211,7 @@ exports.buildDocumentPDF = buildDocumentPDF;
|
|
|
12926
15211
|
exports.buildDocumentPDFBytes = buildDocumentPDFBytes;
|
|
12927
15212
|
exports.buildDocumentPDFStream = buildDocumentPDFStream;
|
|
12928
15213
|
exports.buildDocumentPDFStreamPageByPage = buildDocumentPDFStreamPageByPage;
|
|
15214
|
+
exports.buildDocumentPDFStreamTrue = buildDocumentPDFStreamTrue;
|
|
12929
15215
|
exports.buildEmbeddedFiles = buildEmbeddedFiles;
|
|
12930
15216
|
exports.buildFormWidget = buildFormWidget;
|
|
12931
15217
|
exports.buildImageOperators = buildImageOperators;
|
|
@@ -12936,6 +15222,7 @@ exports.buildPDF = buildPDF;
|
|
|
12936
15222
|
exports.buildPDFBytes = buildPDFBytes;
|
|
12937
15223
|
exports.buildPDFStream = buildPDFStream;
|
|
12938
15224
|
exports.buildPDFStreamPageByPage = buildPDFStreamPageByPage;
|
|
15225
|
+
exports.buildPDFStreamTrue = buildPDFStreamTrue;
|
|
12939
15226
|
exports.buildRadioGroupParent = buildRadioGroupParent;
|
|
12940
15227
|
exports.buildSMaskXObject = buildSMaskXObject;
|
|
12941
15228
|
exports.buildSigDict = buildSigDict;
|
|
@@ -12949,10 +15236,17 @@ exports.concatChunks = concatChunks;
|
|
|
12949
15236
|
exports.containsArabic = containsArabic;
|
|
12950
15237
|
exports.containsBengali = containsBengali;
|
|
12951
15238
|
exports.containsDevanagari = containsDevanagari;
|
|
15239
|
+
exports.containsEthiopic = containsEthiopic;
|
|
12952
15240
|
exports.containsHebrew = containsHebrew;
|
|
15241
|
+
exports.containsKhmer = containsKhmer;
|
|
15242
|
+
exports.containsMyanmar = containsMyanmar;
|
|
12953
15243
|
exports.containsRTL = containsRTL;
|
|
15244
|
+
exports.containsSinhala = containsSinhala;
|
|
12954
15245
|
exports.containsTamil = containsTamil;
|
|
15246
|
+
exports.containsTelugu = containsTelugu;
|
|
12955
15247
|
exports.containsThai = containsThai;
|
|
15248
|
+
exports.containsTibetan = containsTibetan;
|
|
15249
|
+
exports.contoursToPath = contoursToPath;
|
|
12956
15250
|
exports.createEncodingContext = createEncodingContext;
|
|
12957
15251
|
exports.createModifier = createModifier;
|
|
12958
15252
|
exports.createPDF = createPDF;
|
|
@@ -12989,6 +15283,7 @@ exports.encodePDF417 = encodePDF417;
|
|
|
12989
15283
|
exports.encodePdfTextString = encodePdfTextString;
|
|
12990
15284
|
exports.estimateCmsSize = estimateCmsSize;
|
|
12991
15285
|
exports.estimateContentsSize = estimateContentsSize;
|
|
15286
|
+
exports.extractGlyphContours = extractGlyphContours;
|
|
12992
15287
|
exports.findStartxref = findStartxref;
|
|
12993
15288
|
exports.generateDataMatrix = generateDataMatrix;
|
|
12994
15289
|
exports.generatePDFInWorker = generatePDFInWorker;
|
|
@@ -13012,13 +15307,19 @@ exports.isBengaliCodepoint = isBengaliCodepoint;
|
|
|
13012
15307
|
exports.isCyrillicCodepoint = isCyrillicCodepoint;
|
|
13013
15308
|
exports.isDevanagariCodepoint = isDevanagariCodepoint;
|
|
13014
15309
|
exports.isDict = isDict;
|
|
15310
|
+
exports.isEthiopicCodepoint = isEthiopicCodepoint;
|
|
13015
15311
|
exports.isGeorgianCodepoint = isGeorgianCodepoint;
|
|
15312
|
+
exports.isKhmerCodepoint = isKhmerCodepoint;
|
|
13016
15313
|
exports.isLinkAnnotation = isLinkAnnotation;
|
|
15314
|
+
exports.isMyanmarCodepoint = isMyanmarCodepoint;
|
|
13017
15315
|
exports.isName = isName;
|
|
13018
15316
|
exports.isRef = isRef;
|
|
13019
15317
|
exports.isSelfSigned = isSelfSigned;
|
|
15318
|
+
exports.isSinhalaCodepoint = isSinhalaCodepoint;
|
|
13020
15319
|
exports.isStream = isStream;
|
|
13021
15320
|
exports.isTamilCodepoint = isTamilCodepoint;
|
|
15321
|
+
exports.isTeluguCodepoint = isTeluguCodepoint;
|
|
15322
|
+
exports.isTibetanCodepoint = isTibetanCodepoint;
|
|
13022
15323
|
exports.isValidPdfRgb = isValidPdfRgb;
|
|
13023
15324
|
exports.loadFontData = loadFontData;
|
|
13024
15325
|
exports.nameValue = nameValue;
|
|
@@ -13028,6 +15329,9 @@ exports.normalizeColors = normalizeColors;
|
|
|
13028
15329
|
exports.openPdf = openPdf;
|
|
13029
15330
|
exports.parseCertificate = parseCertificate;
|
|
13030
15331
|
exports.parseColor = parseColor;
|
|
15332
|
+
exports.parseColrCpal = parseColrCpal;
|
|
15333
|
+
exports.parseCpal = parseCpal;
|
|
15334
|
+
exports.parseGlyfFont = parseGlyfFont;
|
|
13031
15335
|
exports.parseImage = parseImage;
|
|
13032
15336
|
exports.parseIndirectObject = parseIndirectObject;
|
|
13033
15337
|
exports.parseJPEG = parseJPEG;
|
|
@@ -13042,6 +15346,7 @@ exports.registerFont = registerFont;
|
|
|
13042
15346
|
exports.registerFonts = registerFonts;
|
|
13043
15347
|
exports.renderBarcode = renderBarcode;
|
|
13044
15348
|
exports.renderCode128 = renderCode128;
|
|
15349
|
+
exports.renderColorGlyph = renderColorGlyph;
|
|
13045
15350
|
exports.renderDataMatrix = renderDataMatrix;
|
|
13046
15351
|
exports.renderEAN13 = renderEAN13;
|
|
13047
15352
|
exports.renderPDF417 = renderPDF417;
|
|
@@ -13064,8 +15369,13 @@ exports.sha512 = sha512;
|
|
|
13064
15369
|
exports.shapeArabicText = shapeArabicText;
|
|
13065
15370
|
exports.shapeBengaliText = shapeBengaliText;
|
|
13066
15371
|
exports.shapeDevanagariText = shapeDevanagariText;
|
|
15372
|
+
exports.shapeKhmerText = shapeKhmerText;
|
|
15373
|
+
exports.shapeMyanmarText = shapeMyanmarText;
|
|
15374
|
+
exports.shapeSinhalaText = shapeSinhalaText;
|
|
13067
15375
|
exports.shapeTamilText = shapeTamilText;
|
|
15376
|
+
exports.shapeTeluguText = shapeTeluguText;
|
|
13068
15377
|
exports.shapeThaiText = shapeThaiText;
|
|
15378
|
+
exports.shapeTibetanText = shapeTibetanText;
|
|
13069
15379
|
exports.signPdfBytes = signPdfBytes;
|
|
13070
15380
|
exports.slugify = slugify;
|
|
13071
15381
|
exports.splitTextByFont = splitTextByFont;
|
|
@@ -13077,6 +15387,7 @@ exports.truncate = truncate;
|
|
|
13077
15387
|
exports.truncateToWidth = truncateToWidth;
|
|
13078
15388
|
exports.validateAttachments = validateAttachments;
|
|
13079
15389
|
exports.validateDocumentStreamable = validateDocumentStreamable;
|
|
15390
|
+
exports.validatePdfUA = validatePdfUA;
|
|
13080
15391
|
exports.validateTableStreamable = validateTableStreamable;
|
|
13081
15392
|
exports.validateURL = validateURL;
|
|
13082
15393
|
exports.validateWatermark = validateWatermark;
|