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