pdfnative 1.2.0 → 1.4.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.
@@ -200,6 +200,33 @@ var BENGALI_START = 2432;
200
200
  var BENGALI_END = 2559;
201
201
  var TAMIL_START = 2944;
202
202
  var TAMIL_END = 3071;
203
+ var TELUGU_START = 3072;
204
+ var TELUGU_END = 3199;
205
+ var ETHIOPIC_START = 4608;
206
+ var ETHIOPIC_END = 4991;
207
+ var ETHIOPIC_SUPPLEMENT_START = 4992;
208
+ var ETHIOPIC_SUPPLEMENT_END = 5023;
209
+ var ETHIOPIC_EXTENDED_START = 11648;
210
+ var ETHIOPIC_EXTENDED_END = 11743;
211
+ var ETHIOPIC_EXTENDED_A_START = 43776;
212
+ var ETHIOPIC_EXTENDED_A_END = 43823;
213
+ var SINHALA_START = 3456;
214
+ var SINHALA_END = 3583;
215
+ var SINHALA_VIRAMA = 3530;
216
+ var TIBETAN_START = 3840;
217
+ var TIBETAN_END = 4095;
218
+ var KHMER_START = 6016;
219
+ var KHMER_END = 6143;
220
+ var KHMER_SYMBOLS_START = 6624;
221
+ var KHMER_SYMBOLS_END = 6655;
222
+ var KHMER_COENG = 6098;
223
+ var MYANMAR_START = 4096;
224
+ var MYANMAR_END = 4255;
225
+ var MYANMAR_EXTENDED_A_START = 43616;
226
+ var MYANMAR_EXTENDED_A_END = 43647;
227
+ var MYANMAR_EXTENDED_B_START = 43488;
228
+ var MYANMAR_EXTENDED_B_END = 43519;
229
+ var MYANMAR_VIRAMA = 4153;
203
230
  var EMOJI_RANGES = [
204
231
  [127744, 128511],
205
232
  // Miscellaneous Symbols and Pictographs
@@ -230,6 +257,9 @@ var EMOJI_RANGES = [
230
257
  ];
231
258
  var FITZPATRICK_START = 127995;
232
259
  var FITZPATRICK_END = 127999;
260
+ var ZWJ = 8205;
261
+ var VS15 = 65038;
262
+ var VS16 = 65039;
233
263
  function isArabicCodepoint(cp) {
234
264
  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;
235
265
  }
@@ -242,6 +272,24 @@ function isBengaliCodepoint(cp) {
242
272
  function isTamilCodepoint(cp) {
243
273
  return cp >= TAMIL_START && cp <= TAMIL_END;
244
274
  }
275
+ function isTeluguCodepoint(cp) {
276
+ return cp >= TELUGU_START && cp <= TELUGU_END;
277
+ }
278
+ function isEthiopicCodepoint(cp) {
279
+ 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;
280
+ }
281
+ function isSinhalaCodepoint(cp) {
282
+ return cp >= SINHALA_START && cp <= SINHALA_END;
283
+ }
284
+ function isTibetanCodepoint(cp) {
285
+ return cp >= TIBETAN_START && cp <= TIBETAN_END;
286
+ }
287
+ function isKhmerCodepoint(cp) {
288
+ return cp >= KHMER_START && cp <= KHMER_END || cp >= KHMER_SYMBOLS_START && cp <= KHMER_SYMBOLS_END;
289
+ }
290
+ function isMyanmarCodepoint(cp) {
291
+ 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;
292
+ }
245
293
  function isDevanagariCodepoint(cp) {
246
294
  return cp >= DEVANAGARI_START && cp <= DEVANAGARI_END || cp >= DEVANAGARI_EXT_START && cp <= DEVANAGARI_EXT_END;
247
295
  }
@@ -252,6 +300,9 @@ function isEmojiCodepoint(cp) {
252
300
  }
253
301
  return false;
254
302
  }
303
+ function isZeroWidthFormat(cp) {
304
+ return cp === ZWJ || cp === 8204 || cp === VS15 || cp === VS16 || cp >= FITZPATRICK_START && cp <= FITZPATRICK_END;
305
+ }
255
306
  function containsArabic(text) {
256
307
  for (let i = 0; i < text.length; ) {
257
308
  const cp = text.codePointAt(i) ?? 0;
@@ -278,6 +329,36 @@ function containsTamil(str) {
278
329
  }
279
330
  return false;
280
331
  }
332
+ function containsTelugu(str) {
333
+ for (let i = 0; i < str.length; i++) {
334
+ if (isTeluguCodepoint(str.charCodeAt(i))) return true;
335
+ }
336
+ return false;
337
+ }
338
+ function containsSinhala(str) {
339
+ for (let i = 0; i < str.length; i++) {
340
+ if (isSinhalaCodepoint(str.charCodeAt(i))) return true;
341
+ }
342
+ return false;
343
+ }
344
+ function containsTibetan(str) {
345
+ for (let i = 0; i < str.length; i++) {
346
+ if (isTibetanCodepoint(str.charCodeAt(i))) return true;
347
+ }
348
+ return false;
349
+ }
350
+ function containsKhmer(str) {
351
+ for (let i = 0; i < str.length; i++) {
352
+ if (isKhmerCodepoint(str.charCodeAt(i))) return true;
353
+ }
354
+ return false;
355
+ }
356
+ function containsMyanmar(str) {
357
+ for (let i = 0; i < str.length; i++) {
358
+ if (isMyanmarCodepoint(str.charCodeAt(i))) return true;
359
+ }
360
+ return false;
361
+ }
281
362
  function containsDevanagari(str) {
282
363
  for (let i = 0; i < str.length; i++) {
283
364
  if (isDevanagariCodepoint(str.charCodeAt(i))) return true;
@@ -289,6 +370,12 @@ function containsDevanagari(str) {
289
370
  function detectCharLang(cp) {
290
371
  if (cp >= 880 && cp <= 1023 || cp >= 7936 && cp <= 8191) return "el";
291
372
  if (cp >= 2304 && cp <= 2431 || cp >= 43232 && cp <= 43263) return "hi";
373
+ if (cp >= 3072 && cp <= 3199) return "te";
374
+ if (isSinhalaCodepoint(cp)) return "si";
375
+ if (isTibetanCodepoint(cp)) return "bo";
376
+ if (isKhmerCodepoint(cp)) return "km";
377
+ if (isMyanmarCodepoint(cp)) return "my";
378
+ if (isEthiopicCodepoint(cp)) return "am";
292
379
  if (cp >= 3584 && cp <= 3711) return "th";
293
380
  if (cp >= 12352 && cp <= 12543) return "ja";
294
381
  if (cp >= 44032 && cp <= 55215 || cp >= 4352 && cp <= 4607 || cp >= 12592 && cp <= 12687) return "ko";
@@ -322,6 +409,10 @@ function splitTextByFont(str, fontEntries) {
322
409
  i += charLen;
323
410
  continue;
324
411
  }
412
+ if (isZeroWidthFormat(normCp) && !fontEntries.some((fe) => fe.fontData.cmap[normCp])) {
413
+ i += charLen;
414
+ continue;
415
+ }
325
416
  let newEntry = null;
326
417
  const charLang = detectCharLang(normCp);
327
418
  if (charLang) {
@@ -626,18 +717,26 @@ function normalizeBidiEmbeddings(text) {
626
717
  for (let i = 0; i < text.length; ) {
627
718
  const cp = text.codePointAt(i) ?? 0;
628
719
  const cpLen = cp > 65535 ? 2 : 1;
629
- if (cp === LRE || cp === RLO || cp === LRO || cp === RLE) {
720
+ if (cp === LRE || cp === RLE) {
630
721
  if (stack.length >= MAX_DEPTH) {
631
722
  i += cpLen;
632
723
  continue;
633
724
  }
634
- stack.push(1);
635
- out.push(cp === LRE || cp === LRO ? LRI : RLI);
725
+ stack.push("E");
726
+ out.push(cp === LRE ? LRI : RLI);
636
727
  i += cpLen;
637
- } else if (cp === PDF_CP) {
638
- if (stack.pop()) {
639
- out.push(PDI);
728
+ } else if (cp === LRO || cp === RLO) {
729
+ if (stack.length >= MAX_DEPTH) {
730
+ i += cpLen;
731
+ continue;
640
732
  }
733
+ stack.push("O");
734
+ out.push(cp);
735
+ i += cpLen;
736
+ } else if (cp === PDF_CP) {
737
+ const frame = stack.pop();
738
+ if (frame === "E") out.push(PDI);
739
+ else if (frame === "O") out.push(PDF_CP);
641
740
  i += cpLen;
642
741
  } else {
643
742
  out.push(cp);
@@ -648,8 +747,95 @@ function normalizeBidiEmbeddings(text) {
648
747
  for (let i = 0; i < out.length; i++) result += String.fromCodePoint(out[i]);
649
748
  return result;
650
749
  }
750
+ function matchingPdfIndex(codePoints, openCp) {
751
+ let depth = 1;
752
+ for (let j = openCp + 1; j < codePoints.length; j++) {
753
+ const cj = codePoints[j];
754
+ if (cj === 8234 || cj === 8235 || cj === 8237 || cj === 8238) depth++;
755
+ else if (cj === 8236) {
756
+ depth--;
757
+ if (depth === 0) return j;
758
+ }
759
+ }
760
+ return -1;
761
+ }
762
+ function matchingPdiIndex(codePoints, openCp) {
763
+ let depth = 1;
764
+ for (let j = openCp + 1; j < codePoints.length; j++) {
765
+ const cj = codePoints[j];
766
+ if (cj === 8294 || cj === 8295 || cj === 8296) depth++;
767
+ else if (cj === 8297) {
768
+ depth--;
769
+ if (depth === 0) return j;
770
+ }
771
+ }
772
+ return -1;
773
+ }
774
+ function tryResolveOverrides(text, forcedLevel) {
775
+ const LRE = 8234, RLE = 8235, LRO = 8237, RLO = 8238;
776
+ if (text.indexOf("\u202D") === -1 && text.indexOf("\u202E") === -1) return null;
777
+ const codePoints = [];
778
+ const cpToStr = [];
779
+ for (let i2 = 0; i2 < text.length; ) {
780
+ cpToStr.push(i2);
781
+ const cp = text.codePointAt(i2) ?? 0;
782
+ codePoints.push(cp);
783
+ i2 += cp > 65535 ? 2 : 1;
784
+ }
785
+ cpToStr.push(text.length);
786
+ const len = codePoints.length;
787
+ const spans = [];
788
+ let i = 0;
789
+ while (i < len) {
790
+ const cp = codePoints[i];
791
+ if (cp === LRO || cp === RLO) {
792
+ const close = matchingPdfIndex(codePoints, i);
793
+ if (close === -1) {
794
+ i++;
795
+ continue;
796
+ }
797
+ spans.push({ open: i, close, rtl: cp === RLO });
798
+ i = close + 1;
799
+ } else if (cp === LRE || cp === RLE) {
800
+ const close = matchingPdfIndex(codePoints, i);
801
+ i = close === -1 ? i + 1 : close + 1;
802
+ } else if (cp === 8294 || cp === 8295 || cp === 8296) {
803
+ const close = matchingPdiIndex(codePoints, i);
804
+ i = close === -1 ? i + 1 : close + 1;
805
+ } else {
806
+ i++;
807
+ }
808
+ }
809
+ if (spans.length === 0) return null;
810
+ const out = [];
811
+ const emitGap = (cpStart, cpEnd) => {
812
+ if (cpStart >= cpEnd) return;
813
+ const segText = text.substring(cpToStr[cpStart], cpToStr[cpEnd]);
814
+ const baseStrIdx = cpToStr[cpStart];
815
+ const segRuns = forcedLevel === void 0 ? resolveBidiRuns(segText) : resolveBidiRunsForced(segText, forcedLevel);
816
+ for (const r of segRuns) {
817
+ out.push({ text: r.text, level: r.level, start: r.start + baseStrIdx });
818
+ }
819
+ };
820
+ let cursor = 0;
821
+ for (const span of spans) {
822
+ emitGap(cursor, span.open);
823
+ const innerRaw = text.substring(cpToStr[span.open + 1], cpToStr[span.close]);
824
+ const inner = stripBidiControls(innerRaw);
825
+ if (inner) {
826
+ const level = span.rtl ? 1 : 0;
827
+ const runText = span.rtl ? reverseString(inner) : inner;
828
+ out.push({ text: runText, level, start: cpToStr[span.open + 1] });
829
+ }
830
+ cursor = span.close + 1;
831
+ }
832
+ emitGap(cursor, len);
833
+ return out;
834
+ }
651
835
  function resolveBidiRuns(text) {
652
836
  if (!text) return [];
837
+ const overrideRuns = tryResolveOverrides(text);
838
+ if (overrideRuns) return overrideRuns;
653
839
  const normalized = normalizeBidiEmbeddings(text);
654
840
  if (normalized !== text) {
655
841
  return resolveBidiRuns(normalized);
@@ -712,6 +898,8 @@ function resolveBidiRuns(text) {
712
898
  }
713
899
  function resolveBidiRunsForced(text, forcedLevel) {
714
900
  if (!text) return [];
901
+ const overrideRuns = tryResolveOverrides(text, forcedLevel);
902
+ if (overrideRuns) return overrideRuns;
715
903
  const codePoints = [];
716
904
  const cpToStr = [];
717
905
  for (let i = 0; i < text.length; ) {
@@ -800,6 +988,7 @@ function resolveBidiCore(text, codePoints, cpToStr, forcedLevel) {
800
988
  function containsRTL(text) {
801
989
  for (let i = 0; i < text.length; ) {
802
990
  const cp = text.codePointAt(i) ?? 0;
991
+ if (cp === 8237 || cp === 8238) return true;
803
992
  const t = classifyBidiType(cp);
804
993
  if (t === "R" || t === "AL") return true;
805
994
  i += cp > 65535 ? 2 : 1;
@@ -881,6 +1070,57 @@ function pdfString(str) {
881
1070
  const s = toWinAnsi(stripBidiControls(str));
882
1071
  return "(" + s.replace(/\\/g, "\\\\").replace(/\(/g, "\\(").replace(/\)/g, "\\)") + ")";
883
1072
  }
1073
+ var WINANSI_HIGH_TO_UNICODE = {
1074
+ 128: 8364,
1075
+ 130: 8218,
1076
+ 131: 402,
1077
+ 132: 8222,
1078
+ 133: 8230,
1079
+ 134: 8224,
1080
+ 135: 8225,
1081
+ 136: 710,
1082
+ 137: 8240,
1083
+ 138: 352,
1084
+ 139: 8249,
1085
+ 140: 338,
1086
+ 142: 381,
1087
+ 145: 8216,
1088
+ 146: 8217,
1089
+ 147: 8220,
1090
+ 148: 8221,
1091
+ 149: 8226,
1092
+ 150: 8211,
1093
+ 151: 8212,
1094
+ 152: 732,
1095
+ 153: 8482,
1096
+ 154: 353,
1097
+ 155: 8250,
1098
+ 156: 339,
1099
+ 158: 382,
1100
+ 159: 376
1101
+ };
1102
+ function buildWinAnsiToUnicodeCMap() {
1103
+ const entries = [];
1104
+ const hex2 = (n2) => n2.toString(16).toUpperCase().padStart(2, "0");
1105
+ const hex4 = (n2) => n2.toString(16).toUpperCase().padStart(4, "0");
1106
+ for (let b = 32; b <= 255; b++) {
1107
+ if (b >= 127 && b <= 159) {
1108
+ const u = WINANSI_HIGH_TO_UNICODE[b];
1109
+ if (u === void 0) continue;
1110
+ entries.push(`<${hex2(b)}> <${hex4(u)}>`);
1111
+ } else {
1112
+ entries.push(`<${hex2(b)}> <${hex4(b)}>`);
1113
+ }
1114
+ }
1115
+ const blocks = [];
1116
+ for (let i = 0; i < entries.length; i += 100) {
1117
+ const chunk = entries.slice(i, i + 100);
1118
+ blocks.push(`${chunk.length} beginbfchar
1119
+ ${chunk.join("\n")}
1120
+ endbfchar`);
1121
+ }
1122
+ 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";
1123
+ }
884
1124
  function truncate(str, max) {
885
1125
  if (!str || str.length <= max) return str || "";
886
1126
  if (max <= 1) return "\u2026";
@@ -956,6 +1196,73 @@ function tryLigature(gids, ligatures) {
956
1196
  return null;
957
1197
  }
958
1198
 
1199
+ // src/shaping/use-lite.ts
1200
+ function devanagariCategory(cp) {
1201
+ if (cp === 2305 || cp === 2306) return "Mabv";
1202
+ if (cp === 2307) return "Mpst";
1203
+ if (cp >= 2308 && cp <= 2324) return "V";
1204
+ if (cp >= 2325 && cp <= 2361) return "B";
1205
+ if (cp === 2362) return "Mabv";
1206
+ if (cp === 2363) return "Mpst";
1207
+ if (cp === 2364) return "Mblw";
1208
+ if (cp === 2365) return "O";
1209
+ if (cp === 2366) return "Mpst";
1210
+ if (cp >= 2367 && cp <= 2368) return cp === 2367 ? "Mpre" : "Mpst";
1211
+ if (cp >= 2369 && cp <= 2376) return "Mblw";
1212
+ if (cp >= 2377 && cp <= 2380) return "Mpst";
1213
+ if (cp === 2381) return "H";
1214
+ if (cp >= 2382 && cp <= 2383) return "Mpre";
1215
+ if (cp === 2384) return "O";
1216
+ if (cp >= 2385 && cp <= 2391) return "Mabv";
1217
+ if (cp >= 2392 && cp <= 2401) return "B";
1218
+ if (cp >= 2402 && cp <= 2403) return "Mblw";
1219
+ if (cp >= 2406 && cp <= 2415) return "N";
1220
+ return "O";
1221
+ }
1222
+ function bengaliCategory(cp) {
1223
+ if (cp === 2433) return "Mabv";
1224
+ if (cp === 2434) return "Mpst";
1225
+ if (cp === 2435) return "Mpst";
1226
+ if (cp >= 2437 && cp <= 2452) return "V";
1227
+ if (cp >= 2453 && cp <= 2489) return "B";
1228
+ if (cp === 2492) return "Mblw";
1229
+ if (cp === 2493) return "O";
1230
+ if (cp === 2494) return "Mpst";
1231
+ if (cp >= 2495 && cp <= 2496) return cp === 2495 ? "Mpre" : "Mpst";
1232
+ if (cp >= 2497 && cp <= 2500) return "Mblw";
1233
+ if (cp >= 2503 && cp <= 2504) return "Mpre";
1234
+ if (cp >= 2507 && cp <= 2508) return "Mpre";
1235
+ if (cp === 2509) return "H";
1236
+ if (cp === 2510) return "B";
1237
+ if (cp === 2519) return "Mpst";
1238
+ if (cp >= 2524 && cp <= 2527) return "B";
1239
+ if (cp >= 2528 && cp <= 2531) return "O";
1240
+ if (cp >= 2534 && cp <= 2543) return "N";
1241
+ return "O";
1242
+ }
1243
+ function tamilCategory(cp) {
1244
+ if (cp === 2946) return "Mabv";
1245
+ if (cp >= 2949 && cp <= 2964) return "V";
1246
+ if (cp >= 2965 && cp <= 3001) return "B";
1247
+ if (cp === 3006) return "Mpst";
1248
+ if (cp === 3007) return "Mpst";
1249
+ if (cp >= 3008 && cp <= 3010) return "Mpst";
1250
+ if (cp >= 3014 && cp <= 3016) return "Mpre";
1251
+ if (cp >= 3018 && cp <= 3020) return "Mpre";
1252
+ if (cp === 3021) return "H";
1253
+ if (cp === 3031) return "Mpst";
1254
+ if (cp >= 3046 && cp <= 3055) return "N";
1255
+ return "O";
1256
+ }
1257
+ function classifyUseCategory(cp) {
1258
+ if (cp === 8204) return "ZWNJ";
1259
+ if (cp === 8205) return "ZWJ";
1260
+ if (cp >= 2304 && cp <= 2431) return devanagariCategory(cp);
1261
+ if (cp >= 2432 && cp <= 2559) return bengaliCategory(cp);
1262
+ if (cp >= 2944 && cp <= 3071) return tamilCategory(cp);
1263
+ return "O";
1264
+ }
1265
+
959
1266
  // src/shaping/bengali-shaper.ts
960
1267
  var HALANT = 2509;
961
1268
  var NUKTA = 2492;
@@ -997,6 +1304,10 @@ function buildBengaliClusters(str) {
997
1304
  while (i < cps.length) {
998
1305
  const cp = cps[i];
999
1306
  const type = bengaliCharType(cp);
1307
+ if (classifyUseCategory(cp) === "ZWJ" || classifyUseCategory(cp) === "ZWNJ") {
1308
+ i++;
1309
+ continue;
1310
+ }
1000
1311
  if (type < 0 || cp < BENGALI_START || cp > BENGALI_END) {
1001
1312
  clusters.push({ codepoints: [cp], baseIndex: 0, hasReph: false, preBaseMatras: [] });
1002
1313
  i++;
@@ -1024,13 +1335,24 @@ function buildBengaliClusters(str) {
1024
1335
  i++;
1025
1336
  }
1026
1337
  if (i < cps.length && cps[i] === HALANT) {
1027
- if (i + 1 < cps.length && isConsonant(cps[i + 1])) {
1338
+ let j = i + 1;
1339
+ let zwnj = false;
1340
+ if (j < cps.length) {
1341
+ const jc = classifyUseCategory(cps[j]);
1342
+ if (jc === "ZWJ") {
1343
+ j++;
1344
+ } else if (jc === "ZWNJ") {
1345
+ j++;
1346
+ zwnj = true;
1347
+ }
1348
+ }
1349
+ if (!zwnj && j < cps.length && isConsonant(cps[j])) {
1028
1350
  syllable.push(cps[i]);
1029
- i++;
1351
+ i = j;
1030
1352
  continue;
1031
1353
  } else {
1032
1354
  syllable.push(cps[i]);
1033
- i++;
1355
+ i = j;
1034
1356
  break;
1035
1357
  }
1036
1358
  }
@@ -1267,6 +1589,10 @@ function buildTamilClusters(str) {
1267
1589
  while (i < cps.length) {
1268
1590
  const cp = cps[i];
1269
1591
  const type = tamilCharType(cp);
1592
+ if (classifyUseCategory(cp) === "ZWJ" || classifyUseCategory(cp) === "ZWNJ") {
1593
+ i++;
1594
+ continue;
1595
+ }
1270
1596
  if (type < 0 || cp < TAMIL_START || cp > TAMIL_END) {
1271
1597
  clusters.push({ codepoints: [cp], baseIndex: 0, preBaseMatras: [] });
1272
1598
  i++;
@@ -1283,13 +1609,24 @@ function buildTamilClusters(str) {
1283
1609
  syllable.push(cc);
1284
1610
  i++;
1285
1611
  if (i < cps.length && cps[i] === PULLI) {
1286
- if (i + 1 < cps.length && isConsonant2(cps[i + 1])) {
1612
+ let j = i + 1;
1613
+ let zwnj = false;
1614
+ if (j < cps.length) {
1615
+ const jc = classifyUseCategory(cps[j]);
1616
+ if (jc === "ZWJ") {
1617
+ j++;
1618
+ } else if (jc === "ZWNJ") {
1619
+ j++;
1620
+ zwnj = true;
1621
+ }
1622
+ }
1623
+ if (!zwnj && j < cps.length && isConsonant2(cps[j])) {
1287
1624
  syllable.push(cps[i]);
1288
- i++;
1625
+ i = j;
1289
1626
  continue;
1290
1627
  } else {
1291
1628
  syllable.push(cps[i]);
1292
- i++;
1629
+ i = j;
1293
1630
  break;
1294
1631
  }
1295
1632
  }
@@ -1401,76 +1738,1084 @@ function shapeTamilText(str, fontData) {
1401
1738
  }
1402
1739
  }
1403
1740
  }
1404
- const clusterGids = [];
1405
- const clusterEndIdx = [];
1406
- let matraStart = codepoints.length;
1741
+ const clusterGids = [];
1742
+ const clusterEndIdx = [];
1743
+ let matraStart = codepoints.length;
1744
+ for (let ci = 0; ci < codepoints.length; ci++) {
1745
+ const ct = tamilCharType(codepoints[ci]);
1746
+ if (ct === 0 || ct === 7) {
1747
+ clusterGids.push(resolveGid(codepoints[ci]));
1748
+ clusterEndIdx.push(ci);
1749
+ } else if (ct >= 2) {
1750
+ matraStart = ci;
1751
+ break;
1752
+ } else if (ct < 0) {
1753
+ emitGlyph(resolveGid(codepoints[ci]), false);
1754
+ } else if (ct === 1) {
1755
+ emitGlyph(resolveGid(codepoints[ci]), false);
1756
+ }
1757
+ }
1758
+ let ligConsumed = 0;
1759
+ const ligResult = tryLig(clusterGids);
1760
+ if (ligResult) {
1761
+ emitGlyph(ligResult.resultGid, false);
1762
+ baseGid = ligResult.resultGid;
1763
+ ligConsumed = ligResult.consumed;
1764
+ let gi = ligConsumed;
1765
+ while (gi < clusterGids.length) {
1766
+ const subSeq = clusterGids.slice(gi);
1767
+ const subLig = tryLig(subSeq);
1768
+ if (subLig) {
1769
+ emitGlyph(subLig.resultGid, false);
1770
+ gi += subLig.consumed;
1771
+ } else {
1772
+ const origCi = clusterEndIdx[gi];
1773
+ const ct = tamilCharType(codepoints[origCi]);
1774
+ if (ct === 7) {
1775
+ emitGlyph(clusterGids[gi], true, baseGid);
1776
+ } else {
1777
+ emitGlyph(clusterGids[gi], false);
1778
+ }
1779
+ gi++;
1780
+ }
1781
+ }
1782
+ } else {
1783
+ for (let ci = 0; ci < matraStart; ci++) {
1784
+ const cp = codepoints[ci];
1785
+ const ct = tamilCharType(cp);
1786
+ if (ct === 0) {
1787
+ emitGlyph(resolveGid(cp), false);
1788
+ } else if (ct === 7) {
1789
+ emitGlyph(resolveGid(cp), true, baseGid);
1790
+ }
1791
+ }
1792
+ }
1793
+ for (let ci = matraStart; ci < codepoints.length; ci++) {
1794
+ const cp = codepoints[ci];
1795
+ const ct = tamilCharType(cp);
1796
+ if (ct === 4) continue;
1797
+ if (ct === 2 || ct === 3) {
1798
+ emitGlyph(resolveGid(cp), true, baseGid);
1799
+ } else if (ct === 5) {
1800
+ emitGlyph(resolveGid(cp), false);
1801
+ } else if (ct === 6) {
1802
+ emitGlyph(resolveGid(cp), true, baseGid);
1803
+ } else if (ct === 9) {
1804
+ emitGlyph(resolveGid(cp), false);
1805
+ } else {
1806
+ emitGlyph(resolveGid(cp), false);
1807
+ }
1808
+ }
1809
+ for (const postCp of splitPostComponents) {
1810
+ emitGlyph(resolveGid(postCp), false);
1811
+ }
1812
+ }
1813
+ return shaped;
1814
+ }
1815
+
1816
+ // src/shaping/telugu-shaper.ts
1817
+ var VIRAMA = 3149;
1818
+ function teluguCharType(cp) {
1819
+ if (cp === VIRAMA) return 7;
1820
+ if (cp >= 3072 && cp <= 3076) return 6;
1821
+ if (cp >= 3077 && cp <= 3092) return 1;
1822
+ if (cp === 3133) return 1;
1823
+ if (cp >= 3093 && cp <= 3129) return 0;
1824
+ if (cp >= 3160 && cp <= 3162) return 0;
1825
+ if (cp === 3134) return 5;
1826
+ if (cp === 3135) return 2;
1827
+ if (cp === 3136) return 2;
1828
+ if (cp === 3137) return 5;
1829
+ if (cp === 3138) return 5;
1830
+ if (cp === 3139) return 2;
1831
+ if (cp === 3140) return 2;
1832
+ if (cp === 3142) return 2;
1833
+ if (cp === 3143) return 2;
1834
+ if (cp === 3144) return 2;
1835
+ if (cp === 3146) return 5;
1836
+ if (cp === 3147) return 5;
1837
+ if (cp === 3148) return 5;
1838
+ if (cp === 3170 || cp === 3171) return 3;
1839
+ if (cp === 3157 || cp === 3158) return 6;
1840
+ if (cp === 3168 || cp === 3169) return 1;
1841
+ if (cp >= 3174 && cp <= 3183) return 9;
1842
+ if (cp >= 3192 && cp <= 3199) return 9;
1843
+ return -1;
1844
+ }
1845
+ function isConsonant3(cp) {
1846
+ return teluguCharType(cp) === 0;
1847
+ }
1848
+ function buildTeluguClusters(str) {
1849
+ const clusters = [];
1850
+ const cps = [];
1851
+ for (let i2 = 0; i2 < str.length; ) {
1852
+ const cp = str.codePointAt(i2) ?? 0;
1853
+ cps.push(cp);
1854
+ i2 += cp > 65535 ? 2 : 1;
1855
+ }
1856
+ let i = 0;
1857
+ while (i < cps.length) {
1858
+ const cp = cps[i];
1859
+ const type = teluguCharType(cp);
1860
+ if (classifyUseCategory(cp) === "ZWJ" || classifyUseCategory(cp) === "ZWNJ") {
1861
+ i++;
1862
+ continue;
1863
+ }
1864
+ if (type < 0 || cp < TELUGU_START || cp > TELUGU_END) {
1865
+ clusters.push({ codepoints: [cp], baseIndex: 0 });
1866
+ i++;
1867
+ continue;
1868
+ }
1869
+ const syllable = [];
1870
+ let lastConsonantIdx = -1;
1871
+ while (i < cps.length) {
1872
+ const cc = cps[i];
1873
+ const ct = teluguCharType(cc);
1874
+ if (ct === 0) {
1875
+ lastConsonantIdx = syllable.length;
1876
+ syllable.push(cc);
1877
+ i++;
1878
+ if (i < cps.length && cps[i] === VIRAMA) {
1879
+ let j = i + 1;
1880
+ let zwnj = false;
1881
+ if (j < cps.length) {
1882
+ const jc = classifyUseCategory(cps[j]);
1883
+ if (jc === "ZWJ") {
1884
+ j++;
1885
+ } else if (jc === "ZWNJ") {
1886
+ j++;
1887
+ zwnj = true;
1888
+ }
1889
+ }
1890
+ if (!zwnj && j < cps.length && isConsonant3(cps[j])) {
1891
+ syllable.push(cps[i]);
1892
+ i = j;
1893
+ continue;
1894
+ } else {
1895
+ syllable.push(cps[i]);
1896
+ i = j;
1897
+ break;
1898
+ }
1899
+ }
1900
+ break;
1901
+ } else {
1902
+ break;
1903
+ }
1904
+ }
1905
+ const baseIdx = lastConsonantIdx >= 0 ? lastConsonantIdx : 0;
1906
+ while (i < cps.length) {
1907
+ const ct = teluguCharType(cps[i]);
1908
+ if (ct >= 2 && ct <= 5) {
1909
+ syllable.push(cps[i]);
1910
+ i++;
1911
+ } else {
1912
+ break;
1913
+ }
1914
+ }
1915
+ while (i < cps.length && teluguCharType(cps[i]) === 6) {
1916
+ syllable.push(cps[i]);
1917
+ i++;
1918
+ }
1919
+ if (syllable.length === 0) {
1920
+ syllable.push(cps[i] ?? 32);
1921
+ i++;
1922
+ }
1923
+ clusters.push({ codepoints: syllable, baseIndex: baseIdx });
1924
+ }
1925
+ return clusters;
1926
+ }
1927
+ function shapeTeluguText(str, fontData) {
1928
+ const { cmap, ligatures, markAnchors, widths, defaultWidth } = fontData;
1929
+ const shaped = [];
1930
+ function resolveGid(cp) {
1931
+ const normCp = cp === 8239 || cp === 160 ? 32 : cp;
1932
+ return cmap[normCp] || 0;
1933
+ }
1934
+ function tryLig(gids) {
1935
+ return tryLigature(gids, ligatures);
1936
+ }
1937
+ function getAdv(gid) {
1938
+ return widths[gid] !== void 0 ? widths[gid] : defaultWidth;
1939
+ }
1940
+ function getBaseAnchor2(baseGid, markClass) {
1941
+ const base = markAnchors && markAnchors.bases && markAnchors.bases[baseGid];
1942
+ if (!base) return null;
1943
+ return base[markClass] ?? null;
1944
+ }
1945
+ function getMarkAnchor2(markGid) {
1946
+ const mark = markAnchors && markAnchors.marks && markAnchors.marks[markGid];
1947
+ if (!mark) return null;
1948
+ return { classIdx: mark[0], x: mark[1], y: mark[2] };
1949
+ }
1950
+ function emitGlyph(gid, isZero, baseGid) {
1951
+ if (isZero && baseGid !== void 0) {
1952
+ const markAnchor = getMarkAnchor2(gid);
1953
+ if (markAnchor) {
1954
+ const baseAnchorPt = getBaseAnchor2(baseGid, markAnchor.classIdx);
1955
+ if (baseAnchorPt) {
1956
+ const baseAdv = getAdv(baseGid);
1957
+ shaped.push({
1958
+ gid,
1959
+ dx: baseAnchorPt[0] - markAnchor.x - baseAdv,
1960
+ dy: baseAnchorPt[1] - markAnchor.y,
1961
+ isZeroAdvance: true
1962
+ });
1963
+ return;
1964
+ }
1965
+ }
1966
+ shaped.push({ gid, dx: 0, dy: 0, isZeroAdvance: true });
1967
+ } else {
1968
+ shaped.push({ gid, dx: 0, dy: 0, isZeroAdvance: false });
1969
+ }
1970
+ }
1971
+ const clusters = buildTeluguClusters(str);
1972
+ for (const cluster of clusters) {
1973
+ const { codepoints } = cluster;
1974
+ let baseGid = 0;
1975
+ for (let ci = 0; ci < codepoints.length; ci++) {
1976
+ const ct = teluguCharType(codepoints[ci]);
1977
+ if (ct === 0) {
1978
+ baseGid = resolveGid(codepoints[ci]);
1979
+ } else if (ct >= 2) {
1980
+ break;
1981
+ }
1982
+ }
1983
+ const clusterGids = [];
1984
+ const clusterEndIdx = [];
1985
+ let matraStart = codepoints.length;
1986
+ for (let ci = 0; ci < codepoints.length; ci++) {
1987
+ const ct = teluguCharType(codepoints[ci]);
1988
+ if (ct === 0 || ct === 7) {
1989
+ clusterGids.push(resolveGid(codepoints[ci]));
1990
+ clusterEndIdx.push(ci);
1991
+ } else if (ct >= 2) {
1992
+ matraStart = ci;
1993
+ break;
1994
+ } else if (ct < 0 || ct === 1 || ct === 9) {
1995
+ emitGlyph(resolveGid(codepoints[ci]), false);
1996
+ }
1997
+ }
1998
+ const ligResult = tryLig(clusterGids);
1999
+ if (ligResult) {
2000
+ emitGlyph(ligResult.resultGid, false);
2001
+ baseGid = ligResult.resultGid;
2002
+ let gi = ligResult.consumed;
2003
+ while (gi < clusterGids.length) {
2004
+ const subSeq = clusterGids.slice(gi);
2005
+ const subLig = tryLig(subSeq);
2006
+ if (subLig) {
2007
+ emitGlyph(subLig.resultGid, false);
2008
+ gi += subLig.consumed;
2009
+ } else {
2010
+ const origCi = clusterEndIdx[gi];
2011
+ const ct = teluguCharType(codepoints[origCi]);
2012
+ if (ct === 7) {
2013
+ emitGlyph(clusterGids[gi], true, baseGid);
2014
+ } else {
2015
+ emitGlyph(clusterGids[gi], false);
2016
+ }
2017
+ gi++;
2018
+ }
2019
+ }
2020
+ } else {
2021
+ for (let ci = 0; ci < matraStart; ci++) {
2022
+ const cp = codepoints[ci];
2023
+ const ct = teluguCharType(cp);
2024
+ if (ct === 0) {
2025
+ emitGlyph(resolveGid(cp), false);
2026
+ } else if (ct === 7) {
2027
+ emitGlyph(resolveGid(cp), true, baseGid);
2028
+ }
2029
+ }
2030
+ }
2031
+ for (let ci = matraStart; ci < codepoints.length; ci++) {
2032
+ const cp = codepoints[ci];
2033
+ const ct = teluguCharType(cp);
2034
+ if (ct === 2 || ct === 3) {
2035
+ emitGlyph(resolveGid(cp), true, baseGid);
2036
+ } else if (ct === 5) {
2037
+ emitGlyph(resolveGid(cp), false);
2038
+ } else if (ct === 6) {
2039
+ emitGlyph(resolveGid(cp), true, baseGid);
2040
+ } else if (ct === 9) {
2041
+ emitGlyph(resolveGid(cp), false);
2042
+ } else {
2043
+ emitGlyph(resolveGid(cp), false);
2044
+ }
2045
+ }
2046
+ }
2047
+ return shaped;
2048
+ }
2049
+
2050
+ // src/shaping/sinhala-shaper.ts
2051
+ var VIRAMA2 = SINHALA_VIRAMA;
2052
+ var TWO_PART_VOWELS = {
2053
+ 3546: [3545, 3530],
2054
+ // ේ (ee)
2055
+ 3548: [3545, 3535],
2056
+ // ො (o)
2057
+ 3549: [3545, 3535, 3530],
2058
+ // ෝ (oo)
2059
+ 3550: [3545, 3551]
2060
+ // ෞ (au)
2061
+ };
2062
+ function sinhalaCharType(cp) {
2063
+ if (cp === VIRAMA2) return 7;
2064
+ if (cp >= 3458 && cp <= 3460) return 6;
2065
+ if (cp >= 3461 && cp <= 3478) return 1;
2066
+ if (cp >= 3482 && cp <= 3526) return 0;
2067
+ if (cp === 3535) return 5;
2068
+ if (cp === 3536 || cp === 3537) return 5;
2069
+ if (cp === 3538 || cp === 3539) return 2;
2070
+ if (cp === 3540 || cp === 3542) return 3;
2071
+ if (cp === 3544) return 5;
2072
+ if (cp === 3545 || cp === 3547) return 4;
2073
+ if (cp === 3551) return 5;
2074
+ if (cp === 3570 || cp === 3571) return 5;
2075
+ if (cp >= 3558 && cp <= 3567) return 9;
2076
+ return -1;
2077
+ }
2078
+ function isConsonant4(cp) {
2079
+ return sinhalaCharType(cp) === 0;
2080
+ }
2081
+ function buildSinhalaClusters(str) {
2082
+ const clusters = [];
2083
+ const cps = [];
2084
+ for (let i2 = 0; i2 < str.length; ) {
2085
+ const cp = str.codePointAt(i2) ?? 0;
2086
+ const decomp = TWO_PART_VOWELS[cp];
2087
+ if (decomp) {
2088
+ for (const d of decomp) cps.push(d);
2089
+ } else {
2090
+ cps.push(cp);
2091
+ }
2092
+ i2 += cp > 65535 ? 2 : 1;
2093
+ }
2094
+ let i = 0;
2095
+ while (i < cps.length) {
2096
+ const cp = cps[i];
2097
+ const type = sinhalaCharType(cp);
2098
+ if (classifyUseCategory(cp) === "ZWNJ") {
2099
+ i++;
2100
+ continue;
2101
+ }
2102
+ if (type < 0 || cp < SINHALA_START || cp > SINHALA_END) {
2103
+ clusters.push({ codepoints: [cp], baseIndex: 0 });
2104
+ i++;
2105
+ continue;
2106
+ }
2107
+ const syllable = [];
2108
+ let lastConsonantIdx = -1;
2109
+ while (i < cps.length) {
2110
+ const cc = cps[i];
2111
+ const ct = sinhalaCharType(cc);
2112
+ if (ct === 0) {
2113
+ lastConsonantIdx = syllable.length;
2114
+ syllable.push(cc);
2115
+ i++;
2116
+ if (i < cps.length && cps[i] === VIRAMA2) {
2117
+ let j = i + 1;
2118
+ let zwnj = false;
2119
+ let zwj = false;
2120
+ if (j < cps.length) {
2121
+ const jc = classifyUseCategory(cps[j]);
2122
+ if (jc === "ZWJ") {
2123
+ j++;
2124
+ zwj = true;
2125
+ } else if (jc === "ZWNJ") {
2126
+ j++;
2127
+ zwnj = true;
2128
+ }
2129
+ }
2130
+ if (!zwnj && j < cps.length && isConsonant4(cps[j])) {
2131
+ syllable.push(cps[i]);
2132
+ if (zwj) syllable.push(8205);
2133
+ i = j;
2134
+ continue;
2135
+ } else {
2136
+ syllable.push(cps[i]);
2137
+ i = j;
2138
+ break;
2139
+ }
2140
+ }
2141
+ break;
2142
+ } else {
2143
+ break;
2144
+ }
2145
+ }
2146
+ const baseIdx = lastConsonantIdx >= 0 ? lastConsonantIdx : 0;
2147
+ while (i < cps.length) {
2148
+ const ct = sinhalaCharType(cps[i]);
2149
+ if (ct >= 2 && ct <= 5) {
2150
+ syllable.push(cps[i]);
2151
+ i++;
2152
+ } else {
2153
+ break;
2154
+ }
2155
+ }
2156
+ while (i < cps.length && sinhalaCharType(cps[i]) === 6) {
2157
+ syllable.push(cps[i]);
2158
+ i++;
2159
+ }
2160
+ if (syllable.length === 0) {
2161
+ syllable.push(cps[i] ?? 32);
2162
+ i++;
2163
+ }
2164
+ clusters.push({ codepoints: syllable, baseIndex: baseIdx });
2165
+ }
2166
+ return clusters;
2167
+ }
2168
+ function shapeSinhalaText(str, fontData) {
2169
+ const { cmap, ligatures, markAnchors, widths, defaultWidth } = fontData;
2170
+ const shaped = [];
2171
+ function resolveGid(cp) {
2172
+ const normCp = cp === 8239 || cp === 160 ? 32 : cp;
2173
+ return cmap[normCp] || 0;
2174
+ }
2175
+ function tryLig(gids) {
2176
+ return tryLigature(gids, ligatures);
2177
+ }
2178
+ function getAdv(gid) {
2179
+ return widths[gid] !== void 0 ? widths[gid] : defaultWidth;
2180
+ }
2181
+ function getBaseAnchor2(baseGid, markClass) {
2182
+ const base = markAnchors && markAnchors.bases && markAnchors.bases[baseGid];
2183
+ if (!base) return null;
2184
+ return base[markClass] ?? null;
2185
+ }
2186
+ function getMarkAnchor2(markGid) {
2187
+ const mark = markAnchors && markAnchors.marks && markAnchors.marks[markGid];
2188
+ if (!mark) return null;
2189
+ return { classIdx: mark[0], x: mark[1], y: mark[2] };
2190
+ }
2191
+ function emitGlyph(gid, isZero, baseGid) {
2192
+ if (isZero && baseGid !== void 0) {
2193
+ const markAnchor = getMarkAnchor2(gid);
2194
+ if (markAnchor) {
2195
+ const baseAnchorPt = getBaseAnchor2(baseGid, markAnchor.classIdx);
2196
+ if (baseAnchorPt) {
2197
+ const baseAdv = getAdv(baseGid);
2198
+ shaped.push({
2199
+ gid,
2200
+ dx: baseAnchorPt[0] - markAnchor.x - baseAdv,
2201
+ dy: baseAnchorPt[1] - markAnchor.y,
2202
+ isZeroAdvance: true
2203
+ });
2204
+ return;
2205
+ }
2206
+ }
2207
+ shaped.push({ gid, dx: 0, dy: 0, isZeroAdvance: true });
2208
+ } else {
2209
+ shaped.push({ gid, dx: 0, dy: 0, isZeroAdvance: false });
2210
+ }
2211
+ }
2212
+ const clusters = buildSinhalaClusters(str);
2213
+ for (const cluster of clusters) {
2214
+ const { codepoints } = cluster;
2215
+ let baseGid = 0;
2216
+ for (let ci = 0; ci < codepoints.length; ci++) {
2217
+ const ct = sinhalaCharType(codepoints[ci]);
2218
+ if (ct === 0) {
2219
+ baseGid = resolveGid(codepoints[ci]);
2220
+ } else if (ct >= 2 && ct !== 7) {
2221
+ break;
2222
+ }
2223
+ }
2224
+ for (let ci = 0; ci < codepoints.length; ci++) {
2225
+ if (sinhalaCharType(codepoints[ci]) === 4) {
2226
+ emitGlyph(resolveGid(codepoints[ci]), false);
2227
+ }
2228
+ }
2229
+ const clusterGids = [];
2230
+ const clusterEndIdx = [];
2231
+ let matraStart = codepoints.length;
2232
+ for (let ci = 0; ci < codepoints.length; ci++) {
2233
+ const cp = codepoints[ci];
2234
+ const ct = sinhalaCharType(cp);
2235
+ if (ct === 0 || ct === 7 || cp === 8205) {
2236
+ clusterGids.push(resolveGid(cp));
2237
+ clusterEndIdx.push(ci);
2238
+ } else if (ct >= 2 && ct <= 6) {
2239
+ matraStart = ci;
2240
+ break;
2241
+ } else if (ct < 0 || ct === 1 || ct === 9) {
2242
+ emitGlyph(resolveGid(cp), false);
2243
+ }
2244
+ }
2245
+ const ligResult = tryLig(clusterGids);
2246
+ if (ligResult) {
2247
+ emitGlyph(ligResult.resultGid, false);
2248
+ baseGid = ligResult.resultGid;
2249
+ let gi = ligResult.consumed;
2250
+ while (gi < clusterGids.length) {
2251
+ const subSeq = clusterGids.slice(gi);
2252
+ const subLig = tryLig(subSeq);
2253
+ if (subLig) {
2254
+ emitGlyph(subLig.resultGid, false);
2255
+ gi += subLig.consumed;
2256
+ } else {
2257
+ const origCi = clusterEndIdx[gi];
2258
+ const ct = sinhalaCharType(codepoints[origCi]);
2259
+ if (ct === 7) emitGlyph(clusterGids[gi], true, baseGid);
2260
+ else emitGlyph(clusterGids[gi], false);
2261
+ gi++;
2262
+ }
2263
+ }
2264
+ } else {
2265
+ for (let ci = 0; ci < matraStart; ci++) {
2266
+ const cp = codepoints[ci];
2267
+ const ct = sinhalaCharType(cp);
2268
+ if (ct === 0) emitGlyph(resolveGid(cp), false);
2269
+ else if (ct === 7) emitGlyph(resolveGid(cp), true, baseGid);
2270
+ else ;
2271
+ }
2272
+ }
2273
+ for (let ci = matraStart; ci < codepoints.length; ci++) {
2274
+ const cp = codepoints[ci];
2275
+ const ct = sinhalaCharType(cp);
2276
+ if (ct === 2 || ct === 3 || ct === 6) emitGlyph(resolveGid(cp), true, baseGid);
2277
+ else if (ct === 5) emitGlyph(resolveGid(cp), false);
2278
+ else if (ct === 4) ; else emitGlyph(resolveGid(cp), false);
2279
+ }
2280
+ }
2281
+ return shaped;
2282
+ }
2283
+
2284
+ // src/shaping/tibetan-shaper.ts
2285
+ function tibetanCharType(cp) {
2286
+ if (cp >= 3984 && cp <= 4028) return 8;
2287
+ if (cp === 3972) return 6;
2288
+ if (cp >= 3904 && cp <= 3948) return 0;
2289
+ if (cp === 3954 || cp === 3962 || cp === 3963 || cp === 3964 || cp === 3965 || cp === 3968 || cp === 3969 || cp === 3971 || cp === 3970 || cp === 3966) return 2;
2290
+ if (cp === 3953 || cp === 3956 || cp === 3955 || cp === 3957 || cp === 3864 || cp === 3865 || cp === 3893 || cp === 3895 || cp === 3897) return 3;
2291
+ if (cp === 3967) return 5;
2292
+ if (cp === 3840 || cp >= 3976 && cp <= 3980) return 1;
2293
+ if (cp >= 3872 && cp <= 3891) return 9;
2294
+ if (cp >= 3841 && cp <= 3863) return 9;
2295
+ if (cp >= 3898 && cp <= 3903) return 9;
2296
+ if (cp >= 4030 && cp <= 4047) return 9;
2297
+ if (cp === 3892 || cp === 3894 || cp === 3896) return 9;
2298
+ if (cp === 3973) return 9;
2299
+ return -1;
2300
+ }
2301
+ function buildTibetanStacks(str) {
2302
+ const stacks = [];
2303
+ const cps = [];
2304
+ for (let i2 = 0; i2 < str.length; ) {
2305
+ const cp = str.codePointAt(i2) ?? 0;
2306
+ cps.push(cp);
2307
+ i2 += cp > 65535 ? 2 : 1;
2308
+ }
2309
+ let i = 0;
2310
+ while (i < cps.length) {
2311
+ const cp = cps[i];
2312
+ const type = tibetanCharType(cp);
2313
+ if (type < 0 || cp < TIBETAN_START || cp > TIBETAN_END) {
2314
+ stacks.push({ codepoints: [cp] });
2315
+ i++;
2316
+ continue;
2317
+ }
2318
+ if (type !== 0 && type !== 1) {
2319
+ stacks.push({ codepoints: [cp] });
2320
+ i++;
2321
+ continue;
2322
+ }
2323
+ const stack = [cp];
2324
+ i++;
2325
+ while (i < cps.length && tibetanCharType(cps[i]) === 8) {
2326
+ stack.push(cps[i]);
2327
+ i++;
2328
+ }
2329
+ while (i < cps.length) {
2330
+ const ct = tibetanCharType(cps[i]);
2331
+ if (ct === 2 || ct === 3 || ct === 5 || ct === 6) {
2332
+ stack.push(cps[i]);
2333
+ i++;
2334
+ } else {
2335
+ break;
2336
+ }
2337
+ }
2338
+ stacks.push({ codepoints: stack });
2339
+ }
2340
+ return stacks;
2341
+ }
2342
+ function shapeTibetanText(str, fontData) {
2343
+ const { cmap, ligatures, markAnchors, widths, defaultWidth } = fontData;
2344
+ const shaped = [];
2345
+ function resolveGid(cp) {
2346
+ const normCp = cp === 8239 || cp === 160 ? 32 : cp;
2347
+ return cmap[normCp] || 0;
2348
+ }
2349
+ function tryLig(gids) {
2350
+ return tryLigature(gids, ligatures);
2351
+ }
2352
+ function getAdv(gid) {
2353
+ return widths[gid] !== void 0 ? widths[gid] : defaultWidth;
2354
+ }
2355
+ function getBaseAnchor2(baseGid, markClass) {
2356
+ const base = markAnchors && markAnchors.bases && markAnchors.bases[baseGid];
2357
+ if (!base) return null;
2358
+ return base[markClass] ?? null;
2359
+ }
2360
+ function getMarkAnchor2(markGid) {
2361
+ const mark = markAnchors && markAnchors.marks && markAnchors.marks[markGid];
2362
+ if (!mark) return null;
2363
+ return { classIdx: mark[0], x: mark[1], y: mark[2] };
2364
+ }
2365
+ function emitGlyph(gid, isZero, baseGid) {
2366
+ if (isZero && baseGid !== void 0) {
2367
+ const markAnchor = getMarkAnchor2(gid);
2368
+ if (markAnchor) {
2369
+ const baseAnchorPt = getBaseAnchor2(baseGid, markAnchor.classIdx);
2370
+ if (baseAnchorPt) {
2371
+ const baseAdv = getAdv(baseGid);
2372
+ shaped.push({
2373
+ gid,
2374
+ dx: baseAnchorPt[0] - markAnchor.x - baseAdv,
2375
+ dy: baseAnchorPt[1] - markAnchor.y,
2376
+ isZeroAdvance: true
2377
+ });
2378
+ return;
2379
+ }
2380
+ }
2381
+ shaped.push({ gid, dx: 0, dy: 0, isZeroAdvance: true });
2382
+ } else {
2383
+ shaped.push({ gid, dx: 0, dy: 0, isZeroAdvance: false });
2384
+ }
2385
+ }
2386
+ const stacks = buildTibetanStacks(str);
2387
+ for (const stack of stacks) {
2388
+ const { codepoints } = stack;
2389
+ const stackGids = [];
2390
+ const stackEndIdx = [];
2391
+ let markStart = codepoints.length;
2392
+ for (let ci = 0; ci < codepoints.length; ci++) {
2393
+ const ct = tibetanCharType(codepoints[ci]);
2394
+ if (ct === 0 || ct === 8 || ct === 1 || ct === 6) {
2395
+ stackGids.push(resolveGid(codepoints[ci]));
2396
+ stackEndIdx.push(ci);
2397
+ } else if (ct === 2 || ct === 3 || ct === 5) {
2398
+ markStart = ci;
2399
+ break;
2400
+ } else if (ct < 0 || ct === 9) {
2401
+ emitGlyph(resolveGid(codepoints[ci]), false);
2402
+ }
2403
+ }
2404
+ let baseGid = stackGids.length > 0 ? stackGids[0] : 0;
2405
+ const ligResult = tryLig(stackGids);
2406
+ if (ligResult) {
2407
+ emitGlyph(ligResult.resultGid, false);
2408
+ baseGid = ligResult.resultGid;
2409
+ let gi = ligResult.consumed;
2410
+ while (gi < stackGids.length) {
2411
+ const subSeq = stackGids.slice(gi);
2412
+ const subLig = tryLig(subSeq);
2413
+ if (subLig) {
2414
+ emitGlyph(subLig.resultGid, false);
2415
+ gi += subLig.consumed;
2416
+ } else {
2417
+ const origCi = stackEndIdx[gi];
2418
+ const ct = tibetanCharType(codepoints[origCi]);
2419
+ if (ct === 8 || ct === 6) emitGlyph(stackGids[gi], true, baseGid);
2420
+ else emitGlyph(stackGids[gi], false);
2421
+ gi++;
2422
+ }
2423
+ }
2424
+ } else {
2425
+ for (let gi = 0; gi < stackGids.length; gi++) {
2426
+ const origCi = stackEndIdx[gi];
2427
+ const ct = tibetanCharType(codepoints[origCi]);
2428
+ if (gi === 0) emitGlyph(stackGids[gi], false);
2429
+ else if (ct === 8 || ct === 6) emitGlyph(stackGids[gi], true, baseGid);
2430
+ else emitGlyph(stackGids[gi], false);
2431
+ }
2432
+ }
2433
+ for (let ci = markStart; ci < codepoints.length; ci++) {
2434
+ const cp = codepoints[ci];
2435
+ const ct = tibetanCharType(cp);
2436
+ if (ct === 2 || ct === 3) emitGlyph(resolveGid(cp), true, baseGid);
2437
+ else if (ct === 5) emitGlyph(resolveGid(cp), false);
2438
+ else emitGlyph(resolveGid(cp), false);
2439
+ }
2440
+ }
2441
+ return shaped;
2442
+ }
2443
+
2444
+ // src/shaping/khmer-shaper.ts
2445
+ var COENG = KHMER_COENG;
2446
+ var TWO_PART_VOWELS2 = {
2447
+ 6078: [6081, 6070],
2448
+ // ើ
2449
+ 6079: [6081, 6072],
2450
+ // ឿ (approx: e + y-like)
2451
+ 6080: [6081, 6073],
2452
+ // ៀ (approx)
2453
+ 6084: [6081, 6070],
2454
+ // ោ (e + aa)
2455
+ 6085: [6081, 6070]
2456
+ // ៅ (e + aa-like)
2457
+ };
2458
+ function khmerCharType(cp) {
2459
+ if (cp === COENG) return 7;
2460
+ if (cp >= 6016 && cp <= 6050) return 0;
2461
+ if (cp >= 6051 && cp <= 6069) return 1;
2462
+ if (cp === 6081 || cp === 6082 || cp === 6083) return 4;
2463
+ if (cp === 6071 || cp === 6072 || cp === 6073 || cp === 6074 || cp === 6075 || cp === 6077) return 2;
2464
+ if (cp === 6076) return 3;
2465
+ if (cp === 6070 || cp === 6084 || cp === 6085) return 5;
2466
+ if (cp >= 6086 && cp <= 6097) return 6;
2467
+ if (cp === 6091 || cp === 6093 || cp === 6094 || cp === 6095 || cp === 6096) return 6;
2468
+ if (cp === 6109) return 6;
2469
+ if (cp >= 6112 && cp <= 6121) return 9;
2470
+ if (cp >= 6100 && cp <= 6108) return 9;
2471
+ if (cp >= 6128 && cp <= 6137) return 9;
2472
+ return -1;
2473
+ }
2474
+ function isConsonant5(cp) {
2475
+ return khmerCharType(cp) === 0;
2476
+ }
2477
+ function buildKhmerClusters(str) {
2478
+ const clusters = [];
2479
+ const cps = [];
2480
+ for (let i2 = 0; i2 < str.length; ) {
2481
+ const cp = str.codePointAt(i2) ?? 0;
2482
+ const decomp = TWO_PART_VOWELS2[cp];
2483
+ if (decomp) {
2484
+ for (const d of decomp) cps.push(d);
2485
+ } else cps.push(cp);
2486
+ i2 += cp > 65535 ? 2 : 1;
2487
+ }
2488
+ let i = 0;
2489
+ while (i < cps.length) {
2490
+ const cp = cps[i];
2491
+ const type = khmerCharType(cp);
2492
+ if (type < 0 || cp < KHMER_START || cp > KHMER_END) {
2493
+ clusters.push({ codepoints: [cp] });
2494
+ i++;
2495
+ continue;
2496
+ }
2497
+ if (type !== 0 && type !== 1) {
2498
+ clusters.push({ codepoints: [cp] });
2499
+ i++;
2500
+ continue;
2501
+ }
2502
+ const cluster = [cp];
2503
+ i++;
2504
+ while (i < cps.length && cps[i] === COENG) {
2505
+ if (i + 1 < cps.length && isConsonant5(cps[i + 1])) {
2506
+ cluster.push(cps[i]);
2507
+ cluster.push(cps[i + 1]);
2508
+ i += 2;
2509
+ } else {
2510
+ cluster.push(cps[i]);
2511
+ i++;
2512
+ break;
2513
+ }
2514
+ }
2515
+ while (i < cps.length) {
2516
+ const ct = khmerCharType(cps[i]);
2517
+ if (ct >= 2 && ct <= 6) {
2518
+ cluster.push(cps[i]);
2519
+ i++;
2520
+ } else break;
2521
+ }
2522
+ clusters.push({ codepoints: cluster });
2523
+ }
2524
+ return clusters;
2525
+ }
2526
+ function shapeKhmerText(str, fontData) {
2527
+ const { cmap, ligatures, markAnchors, widths, defaultWidth } = fontData;
2528
+ const shaped = [];
2529
+ function resolveGid(cp) {
2530
+ const normCp = cp === 8239 || cp === 160 ? 32 : cp;
2531
+ return cmap[normCp] || 0;
2532
+ }
2533
+ function tryLig(gids) {
2534
+ return tryLigature(gids, ligatures);
2535
+ }
2536
+ function getAdv(gid) {
2537
+ return widths[gid] !== void 0 ? widths[gid] : defaultWidth;
2538
+ }
2539
+ function getBaseAnchor2(baseGid, markClass) {
2540
+ const base = markAnchors && markAnchors.bases && markAnchors.bases[baseGid];
2541
+ if (!base) return null;
2542
+ return base[markClass] ?? null;
2543
+ }
2544
+ function getMarkAnchor2(markGid) {
2545
+ const mark = markAnchors && markAnchors.marks && markAnchors.marks[markGid];
2546
+ if (!mark) return null;
2547
+ return { classIdx: mark[0], x: mark[1], y: mark[2] };
2548
+ }
2549
+ function emitGlyph(gid, isZero, baseGid) {
2550
+ if (isZero && baseGid !== void 0) {
2551
+ const markAnchor = getMarkAnchor2(gid);
2552
+ if (markAnchor) {
2553
+ const baseAnchorPt = getBaseAnchor2(baseGid, markAnchor.classIdx);
2554
+ if (baseAnchorPt) {
2555
+ const baseAdv = getAdv(baseGid);
2556
+ shaped.push({
2557
+ gid,
2558
+ dx: baseAnchorPt[0] - markAnchor.x - baseAdv,
2559
+ dy: baseAnchorPt[1] - markAnchor.y,
2560
+ isZeroAdvance: true
2561
+ });
2562
+ return;
2563
+ }
2564
+ }
2565
+ shaped.push({ gid, dx: 0, dy: 0, isZeroAdvance: true });
2566
+ } else {
2567
+ shaped.push({ gid, dx: 0, dy: 0, isZeroAdvance: false });
2568
+ }
2569
+ }
2570
+ const clusters = buildKhmerClusters(str);
2571
+ for (const cluster of clusters) {
2572
+ const { codepoints } = cluster;
2573
+ let baseGid = 0;
2574
+ for (let ci = 0; ci < codepoints.length; ci++) {
2575
+ const ct = khmerCharType(codepoints[ci]);
2576
+ if (ct === 0 || ct === 1) {
2577
+ baseGid = resolveGid(codepoints[ci]);
2578
+ break;
2579
+ }
2580
+ }
2581
+ for (let ci = 0; ci < codepoints.length; ci++) {
2582
+ if (khmerCharType(codepoints[ci]) === 4) emitGlyph(resolveGid(codepoints[ci]), false);
2583
+ }
2584
+ const stackGids = [];
2585
+ const stackEndIdx = [];
2586
+ let markStart = codepoints.length;
2587
+ for (let ci = 0; ci < codepoints.length; ci++) {
2588
+ const cp = codepoints[ci];
2589
+ const ct = khmerCharType(cp);
2590
+ if (ct === 0 || ct === 1 || ct === 7) {
2591
+ stackGids.push(resolveGid(cp));
2592
+ stackEndIdx.push(ci);
2593
+ } else if (ct >= 2 && ct <= 6) {
2594
+ markStart = ci;
2595
+ break;
2596
+ } else {
2597
+ emitGlyph(resolveGid(cp), false);
2598
+ }
2599
+ }
2600
+ const ligResult = tryLig(stackGids);
2601
+ if (ligResult) {
2602
+ emitGlyph(ligResult.resultGid, false);
2603
+ baseGid = ligResult.resultGid;
2604
+ let gi = ligResult.consumed;
2605
+ while (gi < stackGids.length) {
2606
+ const subLig = tryLig(stackGids.slice(gi));
2607
+ if (subLig) {
2608
+ emitGlyph(subLig.resultGid, false);
2609
+ gi += subLig.consumed;
2610
+ } else {
2611
+ const origCi = stackEndIdx[gi];
2612
+ const ct = khmerCharType(codepoints[origCi]);
2613
+ if (ct === 7) emitGlyph(stackGids[gi], true, baseGid);
2614
+ else emitGlyph(stackGids[gi], false);
2615
+ gi++;
2616
+ }
2617
+ }
2618
+ } else {
2619
+ for (let gi = 0; gi < stackGids.length; gi++) {
2620
+ const origCi = stackEndIdx[gi];
2621
+ const ct = khmerCharType(codepoints[origCi]);
2622
+ if (gi === 0) emitGlyph(stackGids[gi], false);
2623
+ else if (ct === 7) emitGlyph(stackGids[gi], true, baseGid);
2624
+ else emitGlyph(stackGids[gi], true, baseGid);
2625
+ }
2626
+ }
2627
+ for (let ci = markStart; ci < codepoints.length; ci++) {
2628
+ const cp = codepoints[ci];
2629
+ const ct = khmerCharType(cp);
2630
+ if (ct === 2 || ct === 3 || ct === 6) emitGlyph(resolveGid(cp), true, baseGid);
2631
+ else if (ct === 5) emitGlyph(resolveGid(cp), false);
2632
+ else if (ct === 4) ; else emitGlyph(resolveGid(cp), false);
2633
+ }
2634
+ }
2635
+ return shaped;
2636
+ }
2637
+
2638
+ // src/shaping/myanmar-shaper.ts
2639
+ var VIRAMA3 = MYANMAR_VIRAMA;
2640
+ var MEDIAL_RA = 4156;
2641
+ function myanmarCharType(cp) {
2642
+ if (cp === VIRAMA3) return 7;
2643
+ if (cp === MEDIAL_RA) return 4;
2644
+ if (cp === 4155 || cp === 4157 || cp === 4158) return 8;
2645
+ if (cp >= 4096 && cp <= 4138) return 0;
2646
+ if (cp === 4159) return 0;
2647
+ if (cp >= 4176 && cp <= 4181) return 0;
2648
+ if (cp === 4141 || cp === 4142 || cp === 4146 || cp === 4150 || cp === 4147 || cp === 4148 || cp === 4149) return 2;
2649
+ if (cp === 4143 || cp === 4144 || cp === 4151) return 3;
2650
+ if (cp === 4139 || cp === 4140) return 5;
2651
+ if (cp === 4145) return 4;
2652
+ if (cp === 4154) return 6;
2653
+ if (cp === 4152) return 5;
2654
+ if (cp >= 4160 && cp <= 4169) return 9;
2655
+ if (cp >= 4240 && cp <= 4249) return 9;
2656
+ if (cp === 4170 || cp === 4171) return 9;
2657
+ if (cp >= 4172 && cp <= 4175) return 9;
2658
+ return -1;
2659
+ }
2660
+ function isConsonant6(cp) {
2661
+ return myanmarCharType(cp) === 0;
2662
+ }
2663
+ function buildMyanmarClusters(str) {
2664
+ const clusters = [];
2665
+ const cps = [];
2666
+ for (let i2 = 0; i2 < str.length; ) {
2667
+ const cp = str.codePointAt(i2) ?? 0;
2668
+ cps.push(cp);
2669
+ i2 += cp > 65535 ? 2 : 1;
2670
+ }
2671
+ let i = 0;
2672
+ while (i < cps.length) {
2673
+ const cp = cps[i];
2674
+ const type = myanmarCharType(cp);
2675
+ if (type < 0 || cp < MYANMAR_START || cp > MYANMAR_END) {
2676
+ clusters.push({ codepoints: [cp] });
2677
+ i++;
2678
+ continue;
2679
+ }
2680
+ if (type !== 0) {
2681
+ clusters.push({ codepoints: [cp] });
2682
+ i++;
2683
+ continue;
2684
+ }
2685
+ const cluster = [cp];
2686
+ i++;
2687
+ while (i < cps.length && cps[i] === VIRAMA3) {
2688
+ if (i + 1 < cps.length && isConsonant6(cps[i + 1])) {
2689
+ cluster.push(cps[i]);
2690
+ cluster.push(cps[i + 1]);
2691
+ i += 2;
2692
+ } else {
2693
+ cluster.push(cps[i]);
2694
+ i++;
2695
+ break;
2696
+ }
2697
+ }
2698
+ while (i < cps.length && myanmarCharType(cps[i]) === 8) {
2699
+ cluster.push(cps[i]);
2700
+ i++;
2701
+ }
2702
+ while (i < cps.length) {
2703
+ const ct = myanmarCharType(cps[i]);
2704
+ if (ct >= 2 && ct <= 6) {
2705
+ cluster.push(cps[i]);
2706
+ i++;
2707
+ } else break;
2708
+ }
2709
+ clusters.push({ codepoints: cluster });
2710
+ }
2711
+ return clusters;
2712
+ }
2713
+ function shapeMyanmarText(str, fontData) {
2714
+ const { cmap, ligatures, markAnchors, widths, defaultWidth } = fontData;
2715
+ const shaped = [];
2716
+ function resolveGid(cp) {
2717
+ const normCp = cp === 8239 || cp === 160 ? 32 : cp;
2718
+ return cmap[normCp] || 0;
2719
+ }
2720
+ function tryLig(gids) {
2721
+ return tryLigature(gids, ligatures);
2722
+ }
2723
+ function getAdv(gid) {
2724
+ return widths[gid] !== void 0 ? widths[gid] : defaultWidth;
2725
+ }
2726
+ function getBaseAnchor2(baseGid, markClass) {
2727
+ const base = markAnchors && markAnchors.bases && markAnchors.bases[baseGid];
2728
+ if (!base) return null;
2729
+ return base[markClass] ?? null;
2730
+ }
2731
+ function getMarkAnchor2(markGid) {
2732
+ const mark = markAnchors && markAnchors.marks && markAnchors.marks[markGid];
2733
+ if (!mark) return null;
2734
+ return { classIdx: mark[0], x: mark[1], y: mark[2] };
2735
+ }
2736
+ function emitGlyph(gid, isZero, baseGid) {
2737
+ if (isZero && baseGid !== void 0) {
2738
+ const markAnchor = getMarkAnchor2(gid);
2739
+ if (markAnchor) {
2740
+ const baseAnchorPt = getBaseAnchor2(baseGid, markAnchor.classIdx);
2741
+ if (baseAnchorPt) {
2742
+ const baseAdv = getAdv(baseGid);
2743
+ shaped.push({
2744
+ gid,
2745
+ dx: baseAnchorPt[0] - markAnchor.x - baseAdv,
2746
+ dy: baseAnchorPt[1] - markAnchor.y,
2747
+ isZeroAdvance: true
2748
+ });
2749
+ return;
2750
+ }
2751
+ }
2752
+ shaped.push({ gid, dx: 0, dy: 0, isZeroAdvance: true });
2753
+ } else {
2754
+ shaped.push({ gid, dx: 0, dy: 0, isZeroAdvance: false });
2755
+ }
2756
+ }
2757
+ const clusters = buildMyanmarClusters(str);
2758
+ for (const cluster of clusters) {
2759
+ const { codepoints } = cluster;
2760
+ let baseGid = 0;
2761
+ for (let ci = 0; ci < codepoints.length; ci++) {
2762
+ if (myanmarCharType(codepoints[ci]) === 0) {
2763
+ baseGid = resolveGid(codepoints[ci]);
2764
+ break;
2765
+ }
2766
+ }
1407
2767
  for (let ci = 0; ci < codepoints.length; ci++) {
1408
- const ct = tamilCharType(codepoints[ci]);
1409
- if (ct === 0 || ct === 7) {
1410
- clusterGids.push(resolveGid(codepoints[ci]));
1411
- clusterEndIdx.push(ci);
1412
- } else if (ct >= 2) {
1413
- matraStart = ci;
2768
+ if (myanmarCharType(codepoints[ci]) === 4) emitGlyph(resolveGid(codepoints[ci]), false);
2769
+ }
2770
+ const stackGids = [];
2771
+ const stackEndIdx = [];
2772
+ let markStart = codepoints.length;
2773
+ for (let ci = 0; ci < codepoints.length; ci++) {
2774
+ const cp = codepoints[ci];
2775
+ const ct = myanmarCharType(cp);
2776
+ if (ct === 0 || ct === 7 || ct === 8) {
2777
+ stackGids.push(resolveGid(cp));
2778
+ stackEndIdx.push(ci);
2779
+ } else if (ct === 2 || ct === 3 || ct === 5 || ct === 6) {
2780
+ markStart = ci;
1414
2781
  break;
1415
- } else if (ct < 0) {
1416
- emitGlyph(resolveGid(codepoints[ci]), false);
1417
- } else if (ct === 1) {
1418
- emitGlyph(resolveGid(codepoints[ci]), false);
2782
+ } else if (ct === 4) ; else {
2783
+ emitGlyph(resolveGid(cp), false);
1419
2784
  }
1420
2785
  }
1421
- let ligConsumed = 0;
1422
- const ligResult = tryLig(clusterGids);
2786
+ const ligResult = tryLig(stackGids);
1423
2787
  if (ligResult) {
1424
2788
  emitGlyph(ligResult.resultGid, false);
1425
2789
  baseGid = ligResult.resultGid;
1426
- ligConsumed = ligResult.consumed;
1427
- let gi = ligConsumed;
1428
- while (gi < clusterGids.length) {
1429
- const subSeq = clusterGids.slice(gi);
1430
- const subLig = tryLig(subSeq);
2790
+ let gi = ligResult.consumed;
2791
+ while (gi < stackGids.length) {
2792
+ const subLig = tryLig(stackGids.slice(gi));
1431
2793
  if (subLig) {
1432
2794
  emitGlyph(subLig.resultGid, false);
1433
2795
  gi += subLig.consumed;
1434
2796
  } else {
1435
- const origCi = clusterEndIdx[gi];
1436
- const ct = tamilCharType(codepoints[origCi]);
1437
- if (ct === 7) {
1438
- emitGlyph(clusterGids[gi], true, baseGid);
1439
- } else {
1440
- emitGlyph(clusterGids[gi], false);
1441
- }
2797
+ const origCi = stackEndIdx[gi];
2798
+ const ct = myanmarCharType(codepoints[origCi]);
2799
+ if (ct === 7 || ct === 8) emitGlyph(stackGids[gi], true, baseGid);
2800
+ else emitGlyph(stackGids[gi], false);
1442
2801
  gi++;
1443
2802
  }
1444
2803
  }
1445
2804
  } else {
1446
- for (let ci = 0; ci < matraStart; ci++) {
1447
- const cp = codepoints[ci];
1448
- const ct = tamilCharType(cp);
1449
- if (ct === 0) {
1450
- emitGlyph(resolveGid(cp), false);
1451
- } else if (ct === 7) {
1452
- emitGlyph(resolveGid(cp), true, baseGid);
1453
- }
2805
+ for (let gi = 0; gi < stackGids.length; gi++) {
2806
+ const origCi = stackEndIdx[gi];
2807
+ const ct = myanmarCharType(codepoints[origCi]);
2808
+ if (gi === 0) emitGlyph(stackGids[gi], false);
2809
+ else if (ct === 7 || ct === 8) emitGlyph(stackGids[gi], true, baseGid);
2810
+ else emitGlyph(stackGids[gi], false);
1454
2811
  }
1455
2812
  }
1456
- for (let ci = matraStart; ci < codepoints.length; ci++) {
2813
+ for (let ci = markStart; ci < codepoints.length; ci++) {
1457
2814
  const cp = codepoints[ci];
1458
- const ct = tamilCharType(cp);
1459
- if (ct === 4) continue;
1460
- if (ct === 2 || ct === 3) {
1461
- emitGlyph(resolveGid(cp), true, baseGid);
1462
- } else if (ct === 5) {
1463
- emitGlyph(resolveGid(cp), false);
1464
- } else if (ct === 6) {
1465
- emitGlyph(resolveGid(cp), true, baseGid);
1466
- } else if (ct === 9) {
1467
- emitGlyph(resolveGid(cp), false);
1468
- } else {
1469
- emitGlyph(resolveGid(cp), false);
1470
- }
1471
- }
1472
- for (const postCp of splitPostComponents) {
1473
- emitGlyph(resolveGid(postCp), false);
2815
+ const ct = myanmarCharType(cp);
2816
+ if (ct === 2 || ct === 3 || ct === 6) emitGlyph(resolveGid(cp), true, baseGid);
2817
+ else if (ct === 5) emitGlyph(resolveGid(cp), false);
2818
+ else if (ct === 4) ; else emitGlyph(resolveGid(cp), false);
1474
2819
  }
1475
2820
  }
1476
2821
  return shaped;
@@ -1522,7 +2867,7 @@ function devanagariCharType(cp) {
1522
2867
  if (cp === 2384) return 1;
1523
2868
  return -1;
1524
2869
  }
1525
- function isConsonant3(cp) {
2870
+ function isConsonant7(cp) {
1526
2871
  return devanagariCharType(cp) === 0;
1527
2872
  }
1528
2873
  function buildDevanagariClusters(str) {
@@ -1537,6 +2882,10 @@ function buildDevanagariClusters(str) {
1537
2882
  while (i < cps.length) {
1538
2883
  const cp = cps[i];
1539
2884
  const type = devanagariCharType(cp);
2885
+ if (classifyUseCategory(cp) === "ZWJ" || classifyUseCategory(cp) === "ZWNJ") {
2886
+ i++;
2887
+ continue;
2888
+ }
1540
2889
  if (type < 0 || cp < DEVANAGARI_START || cp > DEVANAGARI_END) {
1541
2890
  clusters.push({ codepoints: [cp], baseIndex: 0, hasReph: false, preBaseMatras: [] });
1542
2891
  i++;
@@ -1545,7 +2894,7 @@ function buildDevanagariClusters(str) {
1545
2894
  const syllable = [];
1546
2895
  let hasReph = false;
1547
2896
  const preMatras = [];
1548
- if (isConsonant3(cp) && cp === RA2 && i + 2 < cps.length && cps[i + 1] === HALANT2 && isConsonant3(cps[i + 2])) {
2897
+ if (isConsonant7(cp) && cp === RA2 && i + 2 < cps.length && cps[i + 1] === HALANT2 && isConsonant7(cps[i + 2])) {
1549
2898
  hasReph = true;
1550
2899
  syllable.push(cp, cps[i + 1]);
1551
2900
  i += 2;
@@ -1563,13 +2912,24 @@ function buildDevanagariClusters(str) {
1563
2912
  i++;
1564
2913
  }
1565
2914
  if (i < cps.length && cps[i] === HALANT2) {
1566
- if (i + 1 < cps.length && isConsonant3(cps[i + 1])) {
2915
+ let j = i + 1;
2916
+ let zwnj = false;
2917
+ if (j < cps.length) {
2918
+ const jc = classifyUseCategory(cps[j]);
2919
+ if (jc === "ZWJ") {
2920
+ j++;
2921
+ } else if (jc === "ZWNJ") {
2922
+ j++;
2923
+ zwnj = true;
2924
+ }
2925
+ }
2926
+ if (!zwnj && j < cps.length && isConsonant7(cps[j])) {
1567
2927
  syllable.push(cps[i]);
1568
- i++;
2928
+ i = j;
1569
2929
  continue;
1570
2930
  } else {
1571
2931
  syllable.push(cps[i]);
1572
- i++;
2932
+ i = j;
1573
2933
  break;
1574
2934
  }
1575
2935
  }
@@ -1927,6 +3287,487 @@ function shapeArabicText(str, fontData) {
1927
3287
  return glyphs;
1928
3288
  }
1929
3289
 
3290
+ // src/fonts/font-loader.ts
3291
+ var _fontBinaryCache = /* @__PURE__ */ new WeakMap();
3292
+ function getDecodedFontBytes(fontData) {
3293
+ const cached = _fontBinaryCache.get(fontData);
3294
+ if (cached) return cached;
3295
+ let bytes;
3296
+ if (typeof atob === "function") {
3297
+ const binaryStr = atob(fontData.ttfBase64);
3298
+ bytes = new Uint8Array(binaryStr.length);
3299
+ for (let i = 0; i < binaryStr.length; i++) bytes[i] = binaryStr.charCodeAt(i);
3300
+ } else {
3301
+ const buf = globalThis["Buffer"];
3302
+ bytes = buf.from(fontData.ttfBase64, "base64");
3303
+ }
3304
+ _fontBinaryCache.set(fontData, bytes);
3305
+ return bytes;
3306
+ }
3307
+
3308
+ // src/fonts/glyf-outline.ts
3309
+ function readTableDirectory(view) {
3310
+ const numTables = view.getUint16(4);
3311
+ const tables = {};
3312
+ for (let i = 0; i < numTables; i++) {
3313
+ const rec = 12 + i * 16;
3314
+ const tag = String.fromCharCode(
3315
+ view.getUint8(rec),
3316
+ view.getUint8(rec + 1),
3317
+ view.getUint8(rec + 2),
3318
+ view.getUint8(rec + 3)
3319
+ );
3320
+ tables[tag] = { offset: view.getUint32(rec + 8), length: view.getUint32(rec + 12) };
3321
+ }
3322
+ return tables;
3323
+ }
3324
+ function parseGlyfFont(bytes) {
3325
+ if (bytes.length < 12) return null;
3326
+ const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
3327
+ const tables = readTableDirectory(view);
3328
+ const head = tables["head"];
3329
+ const maxp = tables["maxp"];
3330
+ const loca = tables["loca"];
3331
+ const glyf = tables["glyf"];
3332
+ if (!head || !maxp || !loca || !glyf) return null;
3333
+ const unitsPerEm = view.getUint16(head.offset + 18) || 1e3;
3334
+ const locaFormat = view.getInt16(head.offset + 50);
3335
+ const numGlyphs = view.getUint16(maxp.offset + 4);
3336
+ const locaOffsets = new Array(numGlyphs + 1);
3337
+ for (let i = 0; i <= numGlyphs; i++) {
3338
+ locaOffsets[i] = locaFormat === 0 ? view.getUint16(loca.offset + i * 2) * 2 : view.getUint32(loca.offset + i * 4);
3339
+ }
3340
+ return { view, glyfOffset: glyf.offset, locaOffsets, unitsPerEm };
3341
+ }
3342
+ function transformPoint(p, a, b, c, d, e, f) {
3343
+ return {
3344
+ x: a * p.x + c * p.y + e,
3345
+ y: b * p.x + d * p.y + f,
3346
+ onCurve: p.onCurve
3347
+ };
3348
+ }
3349
+ function readF2Dot14(view, pos) {
3350
+ return view.getInt16(pos) / 16384;
3351
+ }
3352
+ function extractGlyphContours(font, gid, depth = 0) {
3353
+ if (depth > 8) return [];
3354
+ const { view, glyfOffset, locaOffsets } = font;
3355
+ if (gid < 0 || gid + 1 >= locaOffsets.length) return [];
3356
+ const start = locaOffsets[gid];
3357
+ const end = locaOffsets[gid + 1];
3358
+ if (end <= start) return [];
3359
+ const base = glyfOffset + start;
3360
+ const numberOfContours = view.getInt16(base);
3361
+ if (numberOfContours < 0) {
3362
+ return extractCompositeContours(font, base, depth);
3363
+ }
3364
+ return extractSimpleContours(view, base, numberOfContours);
3365
+ }
3366
+ function extractSimpleContours(view, base, numberOfContours) {
3367
+ let pos = base + 10;
3368
+ const endPts = new Array(numberOfContours);
3369
+ for (let i = 0; i < numberOfContours; i++) {
3370
+ endPts[i] = view.getUint16(pos);
3371
+ pos += 2;
3372
+ }
3373
+ const numPoints = numberOfContours > 0 ? endPts[numberOfContours - 1] + 1 : 0;
3374
+ const instrLen = view.getUint16(pos);
3375
+ pos += 2 + instrLen;
3376
+ const flags = new Array(numPoints);
3377
+ for (let i = 0; i < numPoints; ) {
3378
+ const flag = view.getUint8(pos++);
3379
+ flags[i++] = flag;
3380
+ if (flag & 8) {
3381
+ let repeat = view.getUint8(pos++);
3382
+ while (repeat-- > 0 && i < numPoints) flags[i++] = flag;
3383
+ }
3384
+ }
3385
+ const xs = new Array(numPoints);
3386
+ let x = 0;
3387
+ for (let i = 0; i < numPoints; i++) {
3388
+ const flag = flags[i];
3389
+ if (flag & 2) {
3390
+ const dx = view.getUint8(pos++);
3391
+ x += flag & 16 ? dx : -dx;
3392
+ } else if (!(flag & 16)) {
3393
+ x += view.getInt16(pos);
3394
+ pos += 2;
3395
+ }
3396
+ xs[i] = x;
3397
+ }
3398
+ const ys = new Array(numPoints);
3399
+ let y = 0;
3400
+ for (let i = 0; i < numPoints; i++) {
3401
+ const flag = flags[i];
3402
+ if (flag & 4) {
3403
+ const dy = view.getUint8(pos++);
3404
+ y += flag & 32 ? dy : -dy;
3405
+ } else if (!(flag & 32)) {
3406
+ y += view.getInt16(pos);
3407
+ pos += 2;
3408
+ }
3409
+ ys[i] = y;
3410
+ }
3411
+ const contours = [];
3412
+ let startPt = 0;
3413
+ for (let c = 0; c < numberOfContours; c++) {
3414
+ const endPt = endPts[c];
3415
+ const contour = [];
3416
+ for (let i = startPt; i <= endPt; i++) {
3417
+ contour.push({ x: xs[i], y: ys[i], onCurve: (flags[i] & 1) !== 0 });
3418
+ }
3419
+ if (contour.length > 0) contours.push(contour);
3420
+ startPt = endPt + 1;
3421
+ }
3422
+ return contours;
3423
+ }
3424
+ function extractCompositeContours(font, base, depth) {
3425
+ const { view } = font;
3426
+ let pos = base + 10;
3427
+ const out = [];
3428
+ while (true) {
3429
+ const flags = view.getUint16(pos);
3430
+ pos += 2;
3431
+ const componentGid = view.getUint16(pos);
3432
+ pos += 2;
3433
+ let arg1;
3434
+ let arg2;
3435
+ if (flags & 1) {
3436
+ arg1 = view.getInt16(pos);
3437
+ pos += 2;
3438
+ arg2 = view.getInt16(pos);
3439
+ pos += 2;
3440
+ } else {
3441
+ arg1 = view.getInt8(pos);
3442
+ pos += 1;
3443
+ arg2 = view.getInt8(pos);
3444
+ pos += 1;
3445
+ }
3446
+ let a = 1, b = 0, c = 0, d = 1;
3447
+ if (flags & 8) {
3448
+ a = d = readF2Dot14(view, pos);
3449
+ pos += 2;
3450
+ } else if (flags & 64) {
3451
+ a = readF2Dot14(view, pos);
3452
+ pos += 2;
3453
+ d = readF2Dot14(view, pos);
3454
+ pos += 2;
3455
+ } else if (flags & 128) {
3456
+ a = readF2Dot14(view, pos);
3457
+ pos += 2;
3458
+ b = readF2Dot14(view, pos);
3459
+ pos += 2;
3460
+ c = readF2Dot14(view, pos);
3461
+ pos += 2;
3462
+ d = readF2Dot14(view, pos);
3463
+ pos += 2;
3464
+ }
3465
+ const e = flags & 2 ? arg1 : 0;
3466
+ const f = flags & 2 ? arg2 : 0;
3467
+ const sub = extractGlyphContours(font, componentGid, depth + 1);
3468
+ for (const contour of sub) {
3469
+ out.push(contour.map((p) => transformPoint(p, a, b, c, d, e, f)));
3470
+ }
3471
+ if (!(flags & 32)) break;
3472
+ }
3473
+ return out;
3474
+ }
3475
+
3476
+ // src/core/pdf-color-glyph.ts
3477
+ var ID = [1, 0, 0, 1, 0, 0];
3478
+ function n(v) {
3479
+ if (!Number.isFinite(v)) return "0";
3480
+ if (Number.isInteger(v)) return String(v);
3481
+ let s = v.toFixed(3);
3482
+ s = s.replace(/0+$/, "").replace(/\.$/, "");
3483
+ return s === "-0" ? "0" : s;
3484
+ }
3485
+ function tx(m, x, y) {
3486
+ return [m[0] * x + m[2] * y + m[4], m[1] * x + m[3] * y + m[5]];
3487
+ }
3488
+ function ch(v) {
3489
+ return n(Math.max(0, Math.min(1, v / 255)));
3490
+ }
3491
+ function contoursToPath(contours, m = ID) {
3492
+ const ops = [];
3493
+ for (const contour of contours) {
3494
+ if (contour.length === 0) continue;
3495
+ const pts = contour.slice();
3496
+ let startIdx = pts.findIndex((p) => p.onCurve);
3497
+ let start;
3498
+ if (startIdx < 0) {
3499
+ const a = pts[0], b = pts[pts.length - 1];
3500
+ start = [(a.x + b.x) / 2, (a.y + b.y) / 2];
3501
+ startIdx = 0;
3502
+ } else {
3503
+ start = [pts[startIdx].x, pts[startIdx].y];
3504
+ }
3505
+ const [sx, sy] = tx(m, start[0], start[1]);
3506
+ ops.push(`${n(sx)} ${n(sy)} m`);
3507
+ const len = pts.length;
3508
+ let curX = start[0], curY = start[1];
3509
+ let i = 1;
3510
+ while (i <= len) {
3511
+ const p = pts[(startIdx + i) % len];
3512
+ if (p.onCurve) {
3513
+ const [px, py] = tx(m, p.x, p.y);
3514
+ ops.push(`${n(px)} ${n(py)} l`);
3515
+ curX = p.x;
3516
+ curY = p.y;
3517
+ i++;
3518
+ } else {
3519
+ const next = pts[(startIdx + i + 1) % len];
3520
+ let endX, endY;
3521
+ let consumed;
3522
+ if (next.onCurve) {
3523
+ endX = next.x;
3524
+ endY = next.y;
3525
+ consumed = 2;
3526
+ } else {
3527
+ endX = (p.x + next.x) / 2;
3528
+ endY = (p.y + next.y) / 2;
3529
+ consumed = 1;
3530
+ }
3531
+ const c1x = curX + 2 / 3 * (p.x - curX);
3532
+ const c1y = curY + 2 / 3 * (p.y - curY);
3533
+ const c2x = endX + 2 / 3 * (p.x - endX);
3534
+ const c2y = endY + 2 / 3 * (p.y - endY);
3535
+ const [a1, b1] = tx(m, c1x, c1y);
3536
+ const [a2, b2] = tx(m, c2x, c2y);
3537
+ const [ex, ey] = tx(m, endX, endY);
3538
+ ops.push(`${n(a1)} ${n(b1)} ${n(a2)} ${n(b2)} ${n(ex)} ${n(ey)} c`);
3539
+ curX = endX;
3540
+ curY = endY;
3541
+ i += consumed;
3542
+ }
3543
+ }
3544
+ ops.push("h");
3545
+ }
3546
+ return ops.join("\n");
3547
+ }
3548
+ function buildGradientFunction(stops) {
3549
+ const sorted = stops.slice().sort((a, b) => a.offset - b.offset);
3550
+ if (sorted.length === 0) return "<< /FunctionType 2 /Domain [0 1] /C0 [0 0 0] /C1 [0 0 0] /N 1 >>";
3551
+ if (sorted.length === 1) {
3552
+ const c = sorted[0].color;
3553
+ 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 >>`;
3554
+ }
3555
+ if (sorted.length === 2) {
3556
+ const a = sorted[0].color, b = sorted[1].color;
3557
+ 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 >>`;
3558
+ }
3559
+ const subFns = [];
3560
+ const bounds = [];
3561
+ const encode = [];
3562
+ for (let i = 0; i < sorted.length - 1; i++) {
3563
+ const a = sorted[i].color, b = sorted[i + 1].color;
3564
+ 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 >>`);
3565
+ encode.push("0 1");
3566
+ if (i > 0) bounds.push(n(Math.max(0, Math.min(1, sorted[i].offset))));
3567
+ }
3568
+ return `<< /FunctionType 3 /Domain [0 1] /Functions [${subFns.join(" ")}] /Bounds [${bounds.join(" ")}] /Encode [${encode.join(" ")}] >>`;
3569
+ }
3570
+ function extendFlags(extend) {
3571
+ return extend === "pad" ? "[true true]" : "[true true]";
3572
+ }
3573
+ function linearShadingDict(p, m) {
3574
+ const [x0, y0] = tx(m, p.p0[0], p.p0[1]);
3575
+ const [x1, y1] = tx(m, p.p1[0], p.p1[1]);
3576
+ return `<< /ShadingType 2 /ColorSpace /DeviceRGB /Coords [${n(x0)} ${n(y0)} ${n(x1)} ${n(y1)}] /Function ${buildGradientFunction(p.stops)} /Extend ${extendFlags(p.extend)} >>`;
3577
+ }
3578
+ function radialShadingDict(p, m) {
3579
+ const [x0, y0] = tx(m, p.c0[0], p.c0[1]);
3580
+ const [x1, y1] = tx(m, p.c1[0], p.c1[1]);
3581
+ const sx = Math.hypot(m[0], m[1]);
3582
+ const sy = Math.hypot(m[2], m[3]);
3583
+ const s = (sx + sy) / 2 || 1;
3584
+ 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)} >>`;
3585
+ }
3586
+ function colorAtOffset(stops, t) {
3587
+ if (stops.length === 0) return [0, 0, 0, 255];
3588
+ const sorted = stops.slice().sort((a, b) => a.offset - b.offset);
3589
+ if (t <= sorted[0].offset) return sorted[0].color;
3590
+ const last = sorted[sorted.length - 1];
3591
+ if (t >= last.offset) return last.color;
3592
+ for (let i = 0; i < sorted.length - 1; i++) {
3593
+ const a = sorted[i], b = sorted[i + 1];
3594
+ if (t >= a.offset && t <= b.offset) {
3595
+ const span = b.offset - a.offset || 1;
3596
+ const f = (t - a.offset) / span;
3597
+ return [
3598
+ Math.round(a.color[0] + (b.color[0] - a.color[0]) * f),
3599
+ Math.round(a.color[1] + (b.color[1] - a.color[1]) * f),
3600
+ Math.round(a.color[2] + (b.color[2] - a.color[2]) * f),
3601
+ Math.round(a.color[3] + (b.color[3] - a.color[3]) * f)
3602
+ ];
3603
+ }
3604
+ }
3605
+ return last.color;
3606
+ }
3607
+ function emitSweep(p, cx, cy, maxR, body, gsFor, blendMode) {
3608
+ const start = p.startAngle;
3609
+ const end = p.endAngle;
3610
+ const span = end - start;
3611
+ if (Math.abs(span) < 0.01) {
3612
+ const c = colorAtOffset(p.stops, 0);
3613
+ const gs = gsFor(c[3] / 255, blendMode);
3614
+ if (gs) body.push(`/${gs} gs`);
3615
+ body.push(`${ch(c[0])} ${ch(c[1])} ${ch(c[2])} rg`);
3616
+ body.push(`${n(cx - maxR)} ${n(cy - maxR)} ${n(2 * maxR)} ${n(2 * maxR)} re`);
3617
+ body.push("f");
3618
+ return;
3619
+ }
3620
+ const steps = Math.max(12, Math.min(180, Math.ceil(Math.abs(span) / 3)));
3621
+ const r = maxR * 1.5;
3622
+ const rad = Math.PI / 180;
3623
+ for (let i = 0; i < steps; i++) {
3624
+ const a0 = start + span * i / steps;
3625
+ const a1 = start + span * (i + 1) / steps;
3626
+ const tMid = (i + 0.5) / steps;
3627
+ const c = colorAtOffset(p.stops, tMid);
3628
+ const gs = gsFor(c[3] / 255, blendMode);
3629
+ body.push("q");
3630
+ if (gs) body.push(`/${gs} gs`);
3631
+ body.push(`${ch(c[0])} ${ch(c[1])} ${ch(c[2])} rg`);
3632
+ const x0 = cx + r * Math.cos(a0 * rad), y0 = cy + r * Math.sin(a0 * rad);
3633
+ const x1 = cx + r * Math.cos(a1 * rad), y1 = cy + r * Math.sin(a1 * rad);
3634
+ body.push(`${n(cx)} ${n(cy)} m ${n(x0)} ${n(y0)} l ${n(x1)} ${n(y1)} l h`);
3635
+ body.push("f");
3636
+ body.push("Q");
3637
+ }
3638
+ }
3639
+ function renderColorGlyph(glyph, outlines, unitsPerEm) {
3640
+ const body = [];
3641
+ const shadings = [];
3642
+ const extGStates = [];
3643
+ const gsMap = /* @__PURE__ */ new Map();
3644
+ let shadingIdx = 0;
3645
+ const gsFor = (alpha, bm) => {
3646
+ const a = Math.max(0, Math.min(1, alpha));
3647
+ const needAlpha = a < 0.999;
3648
+ const needBm = bm !== void 0 && bm !== "Normal";
3649
+ if (!needAlpha && !needBm) return "";
3650
+ const key = `${needAlpha ? a.toFixed(3) : "1"}|${needBm ? bm : ""}`;
3651
+ let name = gsMap.get(key);
3652
+ if (!name) {
3653
+ name = `Gs${gsMap.size}`;
3654
+ gsMap.set(key, name);
3655
+ const parts = [];
3656
+ if (needAlpha) parts.push(`/ca ${n(a)}`, `/CA ${n(a)}`);
3657
+ if (needBm) parts.push(`/BM /${bm}`);
3658
+ extGStates.push({ name, dict: `<< ${parts.join(" ")} >>` });
3659
+ }
3660
+ return name;
3661
+ };
3662
+ let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
3663
+ for (const layer of glyph.layers) {
3664
+ const m = layer.transform ?? ID;
3665
+ const bm = layer.blendMode;
3666
+ const contours = outlines(layer.glyphId);
3667
+ if (contours.length === 0) continue;
3668
+ const path = contoursToPath(contours, m);
3669
+ for (const contour of contours) {
3670
+ for (const pt of contour) {
3671
+ const [px, py] = tx(m, pt.x, pt.y);
3672
+ if (px < minX) minX = px;
3673
+ if (py < minY) minY = py;
3674
+ if (px > maxX) maxX = px;
3675
+ if (py > maxY) maxY = py;
3676
+ }
3677
+ }
3678
+ if (layer.paint.kind === "solid") {
3679
+ const c = layer.paint.color;
3680
+ body.push("q");
3681
+ const gs = gsFor(c[3] / 255, bm);
3682
+ if (gs) body.push(`/${gs} gs`);
3683
+ body.push(`${ch(c[0])} ${ch(c[1])} ${ch(c[2])} rg`);
3684
+ body.push(path);
3685
+ body.push("f");
3686
+ body.push("Q");
3687
+ } else if (layer.paint.kind === "sweep") {
3688
+ const [cx, cy] = tx(m, layer.paint.center[0], layer.paint.center[1]);
3689
+ let r2 = 0;
3690
+ for (const contour of contours) {
3691
+ for (const pt of contour) {
3692
+ const [px, py] = tx(m, pt.x, pt.y);
3693
+ const d = (px - cx) * (px - cx) + (py - cy) * (py - cy);
3694
+ if (d > r2) r2 = d;
3695
+ }
3696
+ }
3697
+ const maxR = Math.sqrt(r2) || 1;
3698
+ body.push("q");
3699
+ body.push(path);
3700
+ body.push("W n");
3701
+ emitSweep(layer.paint, cx, cy, maxR, body, gsFor, bm);
3702
+ body.push("Q");
3703
+ } else {
3704
+ const name = `Sh${shadingIdx++}`;
3705
+ const dict = layer.paint.kind === "linear" ? linearShadingDict(layer.paint, m) : radialShadingDict(layer.paint, m);
3706
+ shadings.push({ name, dict });
3707
+ body.push("q");
3708
+ const gs = gsFor(1, bm);
3709
+ if (gs) body.push(`/${gs} gs`);
3710
+ body.push(path);
3711
+ body.push("W n");
3712
+ body.push(`/${name} sh`);
3713
+ body.push("Q");
3714
+ }
3715
+ }
3716
+ 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];
3717
+ return {
3718
+ content: body.join("\n"),
3719
+ bbox,
3720
+ shadings,
3721
+ extGStates
3722
+ };
3723
+ }
3724
+
3725
+ // src/core/color-emoji.ts
3726
+ function createColorEmojiCollector() {
3727
+ const forms = [];
3728
+ const nameByGlyph = /* @__PURE__ */ new WeakMap();
3729
+ const glyfByFont = /* @__PURE__ */ new WeakMap();
3730
+ function glyfFor(fontData) {
3731
+ let g = glyfByFont.get(fontData);
3732
+ if (g === void 0) {
3733
+ g = parseGlyfFont(getDecodedFontBytes(fontData));
3734
+ glyfByFont.set(fontData, g);
3735
+ }
3736
+ return g;
3737
+ }
3738
+ function useGlyph(fontData, gid) {
3739
+ const colorGlyph = fontData.colorGlyphs?.[gid];
3740
+ if (!colorGlyph) return null;
3741
+ let perFont = nameByGlyph.get(fontData);
3742
+ if (!perFont) {
3743
+ perFont = /* @__PURE__ */ new Map();
3744
+ nameByGlyph.set(fontData, perFont);
3745
+ }
3746
+ const cached = perFont.get(gid);
3747
+ if (cached) return cached;
3748
+ const glyf = glyfFor(fontData);
3749
+ if (!glyf) return null;
3750
+ const rendered = renderColorGlyph(
3751
+ colorGlyph,
3752
+ (baseGid) => extractGlyphContours(glyf, baseGid),
3753
+ fontData.metrics.unitsPerEm
3754
+ );
3755
+ if (rendered.content.trim() === "") return null;
3756
+ const name = `CEm${forms.length}`;
3757
+ const resParts = [];
3758
+ if (rendered.shadings.length > 0) {
3759
+ resParts.push(`/Shading << ${rendered.shadings.map((s) => `/${s.name} ${s.dict}`).join(" ")} >>`);
3760
+ }
3761
+ if (rendered.extGStates.length > 0) {
3762
+ resParts.push(`/ExtGState << ${rendered.extGStates.map((g) => `/${g.name} ${g.dict}`).join(" ")} >>`);
3763
+ }
3764
+ forms.push({ name, content: rendered.content, resources: resParts.join(" "), bbox: rendered.bbox });
3765
+ perFont.set(gid, name);
3766
+ return name;
3767
+ }
3768
+ return { useGlyph, forms };
3769
+ }
3770
+
1930
3771
  // src/core/encoding-context.ts
1931
3772
  function isWinAnsi(cp) {
1932
3773
  if (cp >= 32 && cp <= 126 || cp >= 160 && cp <= 255) return true;
@@ -2031,13 +3872,14 @@ function buildTextRunsWithFallback(text, fontRef, fd, sz, trackGid, pdfA = false
2031
3872
  if (mode === "hel") flushHel();
2032
3873
  return result;
2033
3874
  }
2034
- function createEncodingContext(fontEntries, pdfA = false) {
3875
+ function createEncodingContext(fontEntries, pdfA = false, normalize = false) {
3876
+ const _norm = normalize ? (s) => s.normalize(normalize) : (s) => s;
2035
3877
  if (!fontEntries || fontEntries.length === 0) {
2036
3878
  return {
2037
3879
  isUnicode: false,
2038
3880
  fontEntries: [],
2039
- ps: pdfString,
2040
- tw: helveticaWidth,
3881
+ ps: normalize ? (s) => pdfString(_norm(s)) : pdfString,
3882
+ tw: normalize ? (s, sz) => helveticaWidth(_norm(s), sz) : helveticaWidth,
2041
3883
  textRuns: () => [],
2042
3884
  f1: "/F1",
2043
3885
  f2: "/F2"
@@ -2050,6 +3892,7 @@ function createEncodingContext(fontEntries, pdfA = false) {
2050
3892
  const s = _usedGids.get(fontRef);
2051
3893
  if (s) s.add(gid);
2052
3894
  }
3895
+ const _colorEmoji = fontEntries.some((fe) => fe.fontData.colorGlyphs) ? createColorEmojiCollector() : void 0;
2053
3896
  return {
2054
3897
  isUnicode: true,
2055
3898
  fontEntries,
@@ -2059,8 +3902,10 @@ function createEncodingContext(fontEntries, pdfA = false) {
2059
3902
  getUsedGids() {
2060
3903
  return _usedGids;
2061
3904
  },
3905
+ colorEmoji: _colorEmoji,
2062
3906
  textRuns(str, sz) {
2063
3907
  if (!str) return [];
3908
+ str = _norm(str);
2064
3909
  str = stripBidiControls(str);
2065
3910
  if (!str) return [];
2066
3911
  if (containsRTL(str)) {
@@ -2127,6 +3972,56 @@ function createEncodingContext(fontEntries, pdfA = false) {
2127
3972
  }
2128
3973
  }
2129
3974
  result.push({ text: fRun.text, fontRef, fontData: fd, shaped, hexStr: null, widthPt: designW * sz / upm });
3975
+ } else if (containsTelugu(fRun.text)) {
3976
+ const shaped = shapeTeluguText(fRun.text, fd);
3977
+ let designW = 0;
3978
+ for (const g of shaped) {
3979
+ _trackGid(fontRef, g.gid);
3980
+ if (!g.isZeroAdvance) {
3981
+ designW += fd.widths[g.gid] !== void 0 ? fd.widths[g.gid] : fd.defaultWidth;
3982
+ }
3983
+ }
3984
+ result.push({ text: fRun.text, fontRef, fontData: fd, shaped, hexStr: null, widthPt: designW * sz / upm });
3985
+ } else if (containsSinhala(fRun.text)) {
3986
+ const shaped = shapeSinhalaText(fRun.text, fd);
3987
+ let designW = 0;
3988
+ for (const g of shaped) {
3989
+ _trackGid(fontRef, g.gid);
3990
+ if (!g.isZeroAdvance) {
3991
+ designW += fd.widths[g.gid] !== void 0 ? fd.widths[g.gid] : fd.defaultWidth;
3992
+ }
3993
+ }
3994
+ result.push({ text: fRun.text, fontRef, fontData: fd, shaped, hexStr: null, widthPt: designW * sz / upm });
3995
+ } else if (containsTibetan(fRun.text)) {
3996
+ const shaped = shapeTibetanText(fRun.text, fd);
3997
+ let designW = 0;
3998
+ for (const g of shaped) {
3999
+ _trackGid(fontRef, g.gid);
4000
+ if (!g.isZeroAdvance) {
4001
+ designW += fd.widths[g.gid] !== void 0 ? fd.widths[g.gid] : fd.defaultWidth;
4002
+ }
4003
+ }
4004
+ result.push({ text: fRun.text, fontRef, fontData: fd, shaped, hexStr: null, widthPt: designW * sz / upm });
4005
+ } else if (containsKhmer(fRun.text)) {
4006
+ const shaped = shapeKhmerText(fRun.text, fd);
4007
+ let designW = 0;
4008
+ for (const g of shaped) {
4009
+ _trackGid(fontRef, g.gid);
4010
+ if (!g.isZeroAdvance) {
4011
+ designW += fd.widths[g.gid] !== void 0 ? fd.widths[g.gid] : fd.defaultWidth;
4012
+ }
4013
+ }
4014
+ result.push({ text: fRun.text, fontRef, fontData: fd, shaped, hexStr: null, widthPt: designW * sz / upm });
4015
+ } else if (containsMyanmar(fRun.text)) {
4016
+ const shaped = shapeMyanmarText(fRun.text, fd);
4017
+ let designW = 0;
4018
+ for (const g of shaped) {
4019
+ _trackGid(fontRef, g.gid);
4020
+ if (!g.isZeroAdvance) {
4021
+ designW += fd.widths[g.gid] !== void 0 ? fd.widths[g.gid] : fd.defaultWidth;
4022
+ }
4023
+ }
4024
+ result.push({ text: fRun.text, fontRef, fontData: fd, shaped, hexStr: null, widthPt: designW * sz / upm });
2130
4025
  } else if (containsDevanagari(fRun.text)) {
2131
4026
  const shaped = shapeDevanagariText(fRun.text, fd);
2132
4027
  let designW = 0;
@@ -2184,6 +4079,61 @@ function createEncodingContext(fontEntries, pdfA = false) {
2184
4079
  }
2185
4080
  return [{ text: run.text, fontRef, fontData: fd, shaped, hexStr: null, widthPt: designW * sz / upm }];
2186
4081
  }
4082
+ if (containsTelugu(run.text)) {
4083
+ const shaped = shapeTeluguText(run.text, fd);
4084
+ let designW = 0;
4085
+ for (const g of shaped) {
4086
+ _trackGid(fontRef, g.gid);
4087
+ if (!g.isZeroAdvance) {
4088
+ designW += fd.widths[g.gid] !== void 0 ? fd.widths[g.gid] : fd.defaultWidth;
4089
+ }
4090
+ }
4091
+ return [{ text: run.text, fontRef, fontData: fd, shaped, hexStr: null, widthPt: designW * sz / upm }];
4092
+ }
4093
+ if (containsSinhala(run.text)) {
4094
+ const shaped = shapeSinhalaText(run.text, fd);
4095
+ let designW = 0;
4096
+ for (const g of shaped) {
4097
+ _trackGid(fontRef, g.gid);
4098
+ if (!g.isZeroAdvance) {
4099
+ designW += fd.widths[g.gid] !== void 0 ? fd.widths[g.gid] : fd.defaultWidth;
4100
+ }
4101
+ }
4102
+ return [{ text: run.text, fontRef, fontData: fd, shaped, hexStr: null, widthPt: designW * sz / upm }];
4103
+ }
4104
+ if (containsTibetan(run.text)) {
4105
+ const shaped = shapeTibetanText(run.text, fd);
4106
+ let designW = 0;
4107
+ for (const g of shaped) {
4108
+ _trackGid(fontRef, g.gid);
4109
+ if (!g.isZeroAdvance) {
4110
+ designW += fd.widths[g.gid] !== void 0 ? fd.widths[g.gid] : fd.defaultWidth;
4111
+ }
4112
+ }
4113
+ return [{ text: run.text, fontRef, fontData: fd, shaped, hexStr: null, widthPt: designW * sz / upm }];
4114
+ }
4115
+ if (containsKhmer(run.text)) {
4116
+ const shaped = shapeKhmerText(run.text, fd);
4117
+ let designW = 0;
4118
+ for (const g of shaped) {
4119
+ _trackGid(fontRef, g.gid);
4120
+ if (!g.isZeroAdvance) {
4121
+ designW += fd.widths[g.gid] !== void 0 ? fd.widths[g.gid] : fd.defaultWidth;
4122
+ }
4123
+ }
4124
+ return [{ text: run.text, fontRef, fontData: fd, shaped, hexStr: null, widthPt: designW * sz / upm }];
4125
+ }
4126
+ if (containsMyanmar(run.text)) {
4127
+ const shaped = shapeMyanmarText(run.text, fd);
4128
+ let designW = 0;
4129
+ for (const g of shaped) {
4130
+ _trackGid(fontRef, g.gid);
4131
+ if (!g.isZeroAdvance) {
4132
+ designW += fd.widths[g.gid] !== void 0 ? fd.widths[g.gid] : fd.defaultWidth;
4133
+ }
4134
+ }
4135
+ return [{ text: run.text, fontRef, fontData: fd, shaped, hexStr: null, widthPt: designW * sz / upm }];
4136
+ }
2187
4137
  if (containsDevanagari(run.text)) {
2188
4138
  const shaped = shapeDevanagariText(run.text, fd);
2189
4139
  let designW = 0;
@@ -2200,6 +4150,7 @@ function createEncodingContext(fontEntries, pdfA = false) {
2200
4150
  },
2201
4151
  ps(str) {
2202
4152
  if (!str) return "<>";
4153
+ str = _norm(str);
2203
4154
  str = stripBidiControls(str);
2204
4155
  if (!str) return "<>";
2205
4156
  const { cmap } = primary.fontData;
@@ -2228,7 +4179,7 @@ function createEncodingContext(fontEntries, pdfA = false) {
2228
4179
  }
2229
4180
  return `<${hex2.toUpperCase()}>`;
2230
4181
  }
2231
- if (!containsThai(str) && !containsBengali(str) && !containsTamil(str) && !containsDevanagari(str)) {
4182
+ if (!containsThai(str) && !containsBengali(str) && !containsTamil(str) && !containsTelugu(str) && !containsSinhala(str) && !containsTibetan(str) && !containsKhmer(str) && !containsMyanmar(str) && !containsDevanagari(str)) {
2232
4183
  let hex2 = "";
2233
4184
  for (let i = 0; i < str.length; i++) {
2234
4185
  const rawCp = str.codePointAt(i) ?? 0;
@@ -2240,7 +4191,7 @@ function createEncodingContext(fontEntries, pdfA = false) {
2240
4191
  }
2241
4192
  return `<${hex2.toUpperCase()}>`;
2242
4193
  }
2243
- const shapeFn = containsThai(str) ? shapeThaiText : containsBengali(str) ? shapeBengaliText : containsTamil(str) ? shapeTamilText : shapeDevanagariText;
4194
+ 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;
2244
4195
  const shaped = shapeFn(str, primary.fontData);
2245
4196
  let hex = "";
2246
4197
  for (const g of shaped) {
@@ -2322,24 +4273,6 @@ function buildSubsetWidthArray(widths, usedGids) {
2322
4273
  return parts.join(" ");
2323
4274
  }
2324
4275
 
2325
- // src/fonts/font-loader.ts
2326
- var _fontBinaryCache = /* @__PURE__ */ new WeakMap();
2327
- function getDecodedFontBytes(fontData) {
2328
- const cached = _fontBinaryCache.get(fontData);
2329
- if (cached) return cached;
2330
- let bytes;
2331
- if (typeof atob === "function") {
2332
- const binaryStr = atob(fontData.ttfBase64);
2333
- bytes = new Uint8Array(binaryStr.length);
2334
- for (let i = 0; i < binaryStr.length; i++) bytes[i] = binaryStr.charCodeAt(i);
2335
- } else {
2336
- const buf = globalThis["Buffer"];
2337
- bytes = buf.from(fontData.ttfBase64, "base64");
2338
- }
2339
- _fontBinaryCache.set(fontData, bytes);
2340
- return bytes;
2341
- }
2342
-
2343
4276
  // src/fonts/font-subsetter.ts
2344
4277
  function subsetTTF(ttfInput, usedGids) {
2345
4278
  try {
@@ -2653,7 +4586,7 @@ function buildStructureTree(root, startObjNum, pageObjToStructParents) {
2653
4586
  };
2654
4587
  }
2655
4588
  function buildPdfMetadata(now = /* @__PURE__ */ new Date()) {
2656
- const pad2 = (n) => String(n).padStart(2, "0");
4589
+ const pad2 = (n2) => String(n2).padStart(2, "0");
2657
4590
  const yyyy = now.getFullYear();
2658
4591
  const mm = pad2(now.getMonth() + 1);
2659
4592
  const dd = pad2(now.getDate());
@@ -2861,26 +4794,45 @@ function txtShaped(shaped, x, y, font, sz, fontData) {
2861
4794
  }
2862
4795
  return parts.join("\n");
2863
4796
  }
4797
+ var fmtScale = (v) => v.toFixed(5).replace(/0+$/, "").replace(/\.$/, "") || "0";
4798
+ function emitColorEmojiRun(parts, run, penX, y, sz, enc) {
4799
+ const fd = run.fontData;
4800
+ const upm = fd.metrics.unitsPerEm;
4801
+ const scale = sz / upm;
4802
+ const s = fmtScale(scale);
4803
+ const hex = (run.hexStr ?? "").replace(/[<>]/g, "");
4804
+ for (let i = 0; i + 4 <= hex.length; i += 4) {
4805
+ const tag = hex.substr(i, 4);
4806
+ const gid = parseInt(tag, 16);
4807
+ const adv = (fd.widths[gid] !== void 0 ? fd.widths[gid] : fd.defaultWidth) * scale;
4808
+ const name = enc.colorEmoji?.useGlyph(fd, gid) ?? null;
4809
+ if (name) {
4810
+ parts.push(`q ${s} 0 0 ${s} ${fmtNum(penX)} ${fmtNum(y)} cm /${name} Do Q`);
4811
+ } else {
4812
+ parts.push(`BT ${run.fontRef} ${sz} Tf ${fmtNum(penX)} ${fmtNum(y)} Td <${tag}> Tj ET`);
4813
+ }
4814
+ penX += adv;
4815
+ }
4816
+ return penX;
4817
+ }
2864
4818
  function txt(str, x, y, font, sz, enc) {
2865
4819
  if (!enc.isUnicode) {
2866
4820
  return `BT ${font} ${sz} Tf ${fmtNum(x)} ${fmtNum(y)} Td ${enc.ps(str)} Tj ET`;
2867
4821
  }
2868
4822
  const runs = enc.textRuns(str, sz);
2869
4823
  if (runs.length === 0) return "";
2870
- if (runs.length === 1) {
2871
- const run = runs[0];
2872
- if (run.shaped) return txtShaped(run.shaped, x, y, run.fontRef, sz, run.fontData);
2873
- return `BT ${run.fontRef} ${sz} Tf ${fmtNum(x)} ${fmtNum(y)} Td ${run.hexStr} Tj ET`;
2874
- }
2875
4824
  const parts = [];
2876
4825
  let penX = x;
2877
4826
  for (const run of runs) {
2878
- if (run.shaped) {
4827
+ if (enc.colorEmoji && run.fontData.colorGlyphs && run.hexStr) {
4828
+ penX = emitColorEmojiRun(parts, run, penX, y, sz, enc);
4829
+ } else if (run.shaped) {
2879
4830
  parts.push(txtShaped(run.shaped, penX, y, run.fontRef, sz, run.fontData));
4831
+ penX += run.widthPt;
2880
4832
  } else {
2881
4833
  parts.push(`BT ${run.fontRef} ${sz} Tf ${fmtNum(penX)} ${fmtNum(y)} Td ${run.hexStr} Tj ET`);
4834
+ penX += run.widthPt;
2882
4835
  }
2883
- penX += run.widthPt;
2884
4836
  }
2885
4837
  return parts.join("\n");
2886
4838
  }
@@ -2973,12 +4925,12 @@ var DEFAULT_COLUMNS = [
2973
4925
  { f: 0.18, a: "c", mx: 20, mxH: 20 }
2974
4926
  ];
2975
4927
  function computeColumnPositions(columns, marginLeft, contentWidth) {
2976
- const n = columns.length;
2977
- const cwi = new Array(n).fill(0);
2978
- const fixed = new Array(n).fill(false);
4928
+ const n2 = columns.length;
4929
+ const cwi = new Array(n2).fill(0);
4930
+ const fixed = new Array(n2).fill(false);
2979
4931
  let totalFixed = 0;
2980
4932
  let freeWeight = 0;
2981
- for (let i = 0; i < n; i++) {
4933
+ for (let i = 0; i < n2; i++) {
2982
4934
  const col = columns[i];
2983
4935
  let w = col.f * contentWidth;
2984
4936
  let clamped = false;
@@ -3000,13 +4952,13 @@ function computeColumnPositions(columns, marginLeft, contentWidth) {
3000
4952
  }
3001
4953
  const remaining = contentWidth - totalFixed;
3002
4954
  if (freeWeight > 0) {
3003
- for (let i = 0; i < n; i++) {
4955
+ for (let i = 0; i < n2; i++) {
3004
4956
  if (!fixed[i]) cwi[i] = columns[i].f / freeWeight * remaining;
3005
4957
  }
3006
4958
  }
3007
- const cx = new Array(n);
4959
+ const cx = new Array(n2);
3008
4960
  let x = marginLeft;
3009
- for (let i = 0; i < n; i++) {
4961
+ for (let i = 0; i < n2; i++) {
3010
4962
  cx[i] = x;
3011
4963
  x += cwi[i];
3012
4964
  }
@@ -3032,8 +4984,8 @@ function parseColor(input) {
3032
4984
  `Invalid color format: ${JSON.stringify(input)}. Expected "#RRGGBB", "#RGB", [r, g, b] (0\u2013255), or "R G B" (0.0\u20131.0).`
3033
4985
  );
3034
4986
  }
3035
- function fmtChannel(n) {
3036
- const clamped = Math.max(0, Math.min(1, n));
4987
+ function fmtChannel(n2) {
4988
+ const clamped = Math.max(0, Math.min(1, n2));
3037
4989
  const rounded = Math.round(clamped * 1e3) / 1e3;
3038
4990
  return String(rounded);
3039
4991
  }
@@ -3075,8 +5027,8 @@ function parseTupleColor(tuple) {
3075
5027
  function parsePdfRgbString(str) {
3076
5028
  const parts = str.split(" ");
3077
5029
  for (const p of parts) {
3078
- const n = Number(p);
3079
- if (n < 0 || n > 1) {
5030
+ const n2 = Number(p);
5031
+ if (n2 < 0 || n2 > 1) {
3080
5032
  throw new Error(
3081
5033
  `Invalid PDF RGB value: ${p} in ${JSON.stringify(str)}. Each value must be 0.0\u20131.0.`
3082
5034
  );
@@ -3216,8 +5168,8 @@ var MD5_K = new Uint32Array([
3216
5168
  718787259,
3217
5169
  3951481745
3218
5170
  ]);
3219
- function rotl32(x, n) {
3220
- return (x << n | x >>> 32 - n) >>> 0;
5171
+ function rotl32(x, n2) {
5172
+ return (x << n2 | x >>> 32 - n2) >>> 0;
3221
5173
  }
3222
5174
  function md5(input) {
3223
5175
  const len = input.length;
@@ -3455,6 +5407,9 @@ function _buildPageTemplate(template, page, pages, title, date, y, enc, mgL, mgR
3455
5407
  return { ops, structEls };
3456
5408
  }
3457
5409
  function buildPDF(params, layoutOptions) {
5410
+ return assembleTableParts(params, layoutOptions).join("");
5411
+ }
5412
+ function assembleTableParts(params, layoutOptions) {
3458
5413
  if (!params || typeof params !== "object") {
3459
5414
  throw new Error("buildPDF: params is required and must be an object");
3460
5415
  }
@@ -3480,14 +5435,14 @@ function buildPDF(params, layoutOptions) {
3480
5435
  const fontEntries = params.fontEntries || (fontData ? [{ fontData, fontRef: "/F3", lang: "unknown" }] : []);
3481
5436
  const pdfaConfig = resolvePdfAConfig();
3482
5437
  const tagged = pdfaConfig.enabled;
3483
- const enc = createEncodingContext(fontEntries, tagged);
5438
+ const enc = createEncodingContext(fontEntries, tagged, false);
3484
5439
  const footerTpl = {
3485
5440
  left: footerText || void 0,
3486
5441
  right: "{page}/{pages}"
3487
5442
  };
3488
5443
  const headerH = 0;
3489
5444
  const dateNow = /* @__PURE__ */ new Date();
3490
- const pad2d = (n) => String(n).padStart(2, "0");
5445
+ const pad2d = (n2) => String(n2).padStart(2, "0");
3491
5446
  const dateStr = `${dateNow.getFullYear()}-${pad2d(dateNow.getMonth() + 1)}-${pad2d(dateNow.getDate())}`;
3492
5447
  const infoCount = infoItems.length;
3493
5448
  const page1Header = TITLE_LN + 16 + infoCount * INFO_LN + 8 + BAL_H + 10;
@@ -3509,6 +5464,9 @@ function buildPDF(params, layoutOptions) {
3509
5464
  const pageStreams = [];
3510
5465
  let rowIdx = 0;
3511
5466
  const prePageObjStart = enc.isUnicode && fontEntries.length > 0 ? 5 + fontEntries.length * 5 + wmExtraObjs : 5 + wmExtraObjs;
5467
+ const preBaseObjCount = enc.isUnicode && fontEntries.length > 0 ? 4 + fontEntries.length * 5 + wmExtraObjs + totalPages * 2 : 4 + wmExtraObjs + totalPages * 2;
5468
+ const latinToUniObjNum = tagged ? 0 : preBaseObjCount + 2;
5469
+ const baseFontToUniRef = latinToUniObjNum ? ` /ToUnicode ${latinToUniObjNum} 0 R` : "";
3512
5470
  const pageObjToStructParents = /* @__PURE__ */ new Map();
3513
5471
  for (let p = 0; p < totalPages; p++) {
3514
5472
  const pageObjNum = prePageObjStart + p * 2;
@@ -3638,8 +5596,8 @@ function buildPDF(params, layoutOptions) {
3638
5596
  emitObj(3, refDict);
3639
5597
  emitObj(4, refDict);
3640
5598
  } else {
3641
- emitObj(3, "<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica /Encoding /WinAnsiEncoding >>");
3642
- emitObj(4, "<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding >>");
5599
+ emitObj(3, `<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica /Encoding /WinAnsiEncoding${baseFontToUniRef} >>`);
5600
+ emitObj(4, `<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding${baseFontToUniRef} >>`);
3643
5601
  }
3644
5602
  for (let fi = 0; fi < fontEntries.length; fi++) {
3645
5603
  const fe = fontEntries[fi];
@@ -3697,8 +5655,8 @@ function buildPDF(params, layoutOptions) {
3697
5655
  kids.push(`${pageObjStart + p * 2} 0 R`);
3698
5656
  }
3699
5657
  emitObj(2, `<< /Type /Pages /Kids [${kids.join(" ")}] /Count ${totalPages} >>`);
3700
- emitObj(3, "<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica /Encoding /WinAnsiEncoding >>");
3701
- emitObj(4, "<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding >>");
5658
+ emitObj(3, `<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica /Encoding /WinAnsiEncoding${baseFontToUniRef} >>`);
5659
+ emitObj(4, `<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding${baseFontToUniRef} >>`);
3702
5660
  let wmGsResLatin = "";
3703
5661
  let wmImgResLatin = "";
3704
5662
  for (let p = 0; p < totalPages; p++) {
@@ -3715,13 +5673,18 @@ function buildPDF(params, layoutOptions) {
3715
5673
  }
3716
5674
  const baseObjCount = enc.isUnicode ? 4 + fontEntries.length * 5 + wmExtraObjs + totalPages * 2 : 4 + wmExtraObjs + totalPages * 2;
3717
5675
  const infoObjNum = baseObjCount + 1;
3718
- const { pdfDate, xmpDate: isoDate } = buildPdfMetadata();
5676
+ const { pdfDate, xmpDate: isoDate } = buildPdfMetadata(layoutOptions?.creationDate);
3719
5677
  const infoTitle = params.docTitle || title || "";
3720
5678
  emitObj(
3721
5679
  infoObjNum,
3722
5680
  `<< /Title ${encodePdfTextString(infoTitle)} /Producer (pdfnative) /CreationDate (${pdfDate}) >>`
3723
5681
  );
3724
5682
  let totalObjs = infoObjNum;
5683
+ if (latinToUniObjNum) {
5684
+ const cmap = buildWinAnsiToUnicodeCMap();
5685
+ emitStreamObj(latinToUniObjNum, `<< /Length ${cmap.length}`, cmap);
5686
+ totalObjs = latinToUniObjNum;
5687
+ }
3725
5688
  let xmpObjNum = 0;
3726
5689
  let outputIntentObjNum = 0;
3727
5690
  if (tagged) {
@@ -3777,7 +5740,7 @@ endobj
3777
5740
  }
3778
5741
  const writer = { emit, emitObj, emitStreamObj, offset: getOffset, adjustOffset, objOffsets, parts };
3779
5742
  writeXrefTrailer(writer, totalObjs, infoObjNum, encState, `${infoTitle}|${pdfDate}`);
3780
- return parts.join("");
5743
+ return parts;
3781
5744
  }
3782
5745
 
3783
5746
  // src/worker/pdf-worker.ts