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