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