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