pdfnative 1.1.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -200,6 +200,33 @@ var BENGALI_START = 2432;
200
200
  var BENGALI_END = 2559;
201
201
  var TAMIL_START = 2944;
202
202
  var TAMIL_END = 3071;
203
+ var TELUGU_START = 3072;
204
+ var TELUGU_END = 3199;
205
+ var ETHIOPIC_START = 4608;
206
+ var ETHIOPIC_END = 4991;
207
+ var ETHIOPIC_SUPPLEMENT_START = 4992;
208
+ var ETHIOPIC_SUPPLEMENT_END = 5023;
209
+ var ETHIOPIC_EXTENDED_START = 11648;
210
+ var ETHIOPIC_EXTENDED_END = 11743;
211
+ var ETHIOPIC_EXTENDED_A_START = 43776;
212
+ var ETHIOPIC_EXTENDED_A_END = 43823;
213
+ var SINHALA_START = 3456;
214
+ var SINHALA_END = 3583;
215
+ var SINHALA_VIRAMA = 3530;
216
+ var TIBETAN_START = 3840;
217
+ var TIBETAN_END = 4095;
218
+ var KHMER_START = 6016;
219
+ var KHMER_END = 6143;
220
+ var KHMER_SYMBOLS_START = 6624;
221
+ var KHMER_SYMBOLS_END = 6655;
222
+ var KHMER_COENG = 6098;
223
+ var MYANMAR_START = 4096;
224
+ var MYANMAR_END = 4255;
225
+ var MYANMAR_EXTENDED_A_START = 43616;
226
+ var MYANMAR_EXTENDED_A_END = 43647;
227
+ var MYANMAR_EXTENDED_B_START = 43488;
228
+ var MYANMAR_EXTENDED_B_END = 43519;
229
+ var MYANMAR_VIRAMA = 4153;
203
230
  var EMOJI_RANGES = [
204
231
  [127744, 128511],
205
232
  // Miscellaneous Symbols and Pictographs
@@ -230,6 +257,9 @@ var EMOJI_RANGES = [
230
257
  ];
231
258
  var FITZPATRICK_START = 127995;
232
259
  var FITZPATRICK_END = 127999;
260
+ var ZWJ = 8205;
261
+ var VS15 = 65038;
262
+ var VS16 = 65039;
233
263
  function isArabicCodepoint(cp) {
234
264
  return cp >= ARABIC_START && cp <= ARABIC_END || cp >= ARABIC_SUPPLEMENT_START && cp <= ARABIC_SUPPLEMENT_END || cp >= ARABIC_EXTENDED_A_START && cp <= ARABIC_EXTENDED_A_END || cp >= ARABIC_PRES_A_START && cp <= ARABIC_PRES_A_END || cp >= ARABIC_PRES_B_START && cp <= ARABIC_PRES_B_END;
235
265
  }
@@ -242,6 +272,24 @@ function isBengaliCodepoint(cp) {
242
272
  function isTamilCodepoint(cp) {
243
273
  return cp >= TAMIL_START && cp <= TAMIL_END;
244
274
  }
275
+ function isTeluguCodepoint(cp) {
276
+ return cp >= TELUGU_START && cp <= TELUGU_END;
277
+ }
278
+ function isEthiopicCodepoint(cp) {
279
+ return cp >= ETHIOPIC_START && cp <= ETHIOPIC_END || cp >= ETHIOPIC_SUPPLEMENT_START && cp <= ETHIOPIC_SUPPLEMENT_END || cp >= ETHIOPIC_EXTENDED_START && cp <= ETHIOPIC_EXTENDED_END || cp >= ETHIOPIC_EXTENDED_A_START && cp <= ETHIOPIC_EXTENDED_A_END;
280
+ }
281
+ function isSinhalaCodepoint(cp) {
282
+ return cp >= SINHALA_START && cp <= SINHALA_END;
283
+ }
284
+ function isTibetanCodepoint(cp) {
285
+ return cp >= TIBETAN_START && cp <= TIBETAN_END;
286
+ }
287
+ function isKhmerCodepoint(cp) {
288
+ return cp >= KHMER_START && cp <= KHMER_END || cp >= KHMER_SYMBOLS_START && cp <= KHMER_SYMBOLS_END;
289
+ }
290
+ function isMyanmarCodepoint(cp) {
291
+ return cp >= MYANMAR_START && cp <= MYANMAR_END || cp >= MYANMAR_EXTENDED_A_START && cp <= MYANMAR_EXTENDED_A_END || cp >= MYANMAR_EXTENDED_B_START && cp <= MYANMAR_EXTENDED_B_END;
292
+ }
245
293
  function isDevanagariCodepoint(cp) {
246
294
  return cp >= DEVANAGARI_START && cp <= DEVANAGARI_END || cp >= DEVANAGARI_EXT_START && cp <= DEVANAGARI_EXT_END;
247
295
  }
@@ -252,6 +300,9 @@ function isEmojiCodepoint(cp) {
252
300
  }
253
301
  return false;
254
302
  }
303
+ function isZeroWidthFormat(cp) {
304
+ return cp === ZWJ || cp === 8204 || cp === VS15 || cp === VS16 || cp >= FITZPATRICK_START && cp <= FITZPATRICK_END;
305
+ }
255
306
  function containsArabic(text) {
256
307
  for (let i = 0; i < text.length; ) {
257
308
  const cp = text.codePointAt(i) ?? 0;
@@ -278,6 +329,36 @@ function containsTamil(str) {
278
329
  }
279
330
  return false;
280
331
  }
332
+ function containsTelugu(str) {
333
+ for (let i = 0; i < str.length; i++) {
334
+ if (isTeluguCodepoint(str.charCodeAt(i))) return true;
335
+ }
336
+ return false;
337
+ }
338
+ function containsSinhala(str) {
339
+ for (let i = 0; i < str.length; i++) {
340
+ if (isSinhalaCodepoint(str.charCodeAt(i))) return true;
341
+ }
342
+ return false;
343
+ }
344
+ function containsTibetan(str) {
345
+ for (let i = 0; i < str.length; i++) {
346
+ if (isTibetanCodepoint(str.charCodeAt(i))) return true;
347
+ }
348
+ return false;
349
+ }
350
+ function containsKhmer(str) {
351
+ for (let i = 0; i < str.length; i++) {
352
+ if (isKhmerCodepoint(str.charCodeAt(i))) return true;
353
+ }
354
+ return false;
355
+ }
356
+ function containsMyanmar(str) {
357
+ for (let i = 0; i < str.length; i++) {
358
+ if (isMyanmarCodepoint(str.charCodeAt(i))) return true;
359
+ }
360
+ return false;
361
+ }
281
362
  function containsDevanagari(str) {
282
363
  for (let i = 0; i < str.length; i++) {
283
364
  if (isDevanagariCodepoint(str.charCodeAt(i))) return true;
@@ -289,6 +370,12 @@ function containsDevanagari(str) {
289
370
  function detectCharLang(cp) {
290
371
  if (cp >= 880 && cp <= 1023 || cp >= 7936 && cp <= 8191) return "el";
291
372
  if (cp >= 2304 && cp <= 2431 || cp >= 43232 && cp <= 43263) return "hi";
373
+ if (cp >= 3072 && cp <= 3199) return "te";
374
+ if (isSinhalaCodepoint(cp)) return "si";
375
+ if (isTibetanCodepoint(cp)) return "bo";
376
+ if (isKhmerCodepoint(cp)) return "km";
377
+ if (isMyanmarCodepoint(cp)) return "my";
378
+ if (isEthiopicCodepoint(cp)) return "am";
292
379
  if (cp >= 3584 && cp <= 3711) return "th";
293
380
  if (cp >= 12352 && cp <= 12543) return "ja";
294
381
  if (cp >= 44032 && cp <= 55215 || cp >= 4352 && cp <= 4607 || cp >= 12592 && cp <= 12687) return "ko";
@@ -322,6 +409,10 @@ function splitTextByFont(str, fontEntries) {
322
409
  i += charLen;
323
410
  continue;
324
411
  }
412
+ if (isZeroWidthFormat(normCp) && !fontEntries.some((fe) => fe.fontData.cmap[normCp])) {
413
+ i += charLen;
414
+ continue;
415
+ }
325
416
  let newEntry = null;
326
417
  const charLang = detectCharLang(normCp);
327
418
  if (charLang) {
@@ -354,103 +445,826 @@ function splitTextByFont(str, fontEntries) {
354
445
  return runs;
355
446
  }
356
447
 
357
- // src/fonts/encoding.ts
358
- function toWinAnsi(str) {
359
- if (!str) return "";
360
- let r = "";
361
- for (let i = 0; i < str.length; i++) {
362
- const c = str.charCodeAt(i);
363
- if (c >= 32 && c <= 126) r += str[i];
364
- else if (c >= 160 && c <= 255) r += str[i];
365
- else if (c === 8364) r += "\x80";
366
- else if (c === 8218) r += "\x82";
367
- else if (c === 402) r += "\x83";
368
- else if (c === 8222) r += "\x84";
369
- else if (c === 8230) r += "\x85";
370
- else if (c === 8224) r += "\x86";
371
- else if (c === 8225) r += "\x87";
372
- else if (c === 710) r += "\x88";
373
- else if (c === 8240) r += "\x89";
374
- else if (c === 352) r += "\x8A";
375
- else if (c === 8249) r += "\x8B";
376
- else if (c === 338) r += "\x8C";
377
- else if (c === 381) r += "\x8E";
378
- else if (c === 8216) r += "\x91";
379
- else if (c === 8217) r += "\x92";
380
- else if (c === 8220) r += "\x93";
381
- else if (c === 8221) r += "\x94";
382
- else if (c === 8226) r += "\x95";
383
- else if (c === 8211) r += "\x96";
384
- else if (c === 8212) r += "\x97";
385
- else if (c === 732) r += "\x98";
386
- else if (c === 8482) r += "\x99";
387
- else if (c === 353) r += "\x9A";
388
- else if (c === 8250) r += "\x9B";
389
- else if (c === 339) r += "\x9C";
390
- else if (c === 382) r += "\x9E";
391
- else if (c === 376) r += "\x9F";
392
- else if (c === 160 || c === 8239) r += " ";
393
- else if (c === 9 || c === 10 || c === 13) r += " ";
394
- else if (c < 32) ; else r += "?";
395
- }
396
- return r;
397
- }
398
- function pdfString(str) {
399
- const s = toWinAnsi(str);
400
- return "(" + s.replace(/\\/g, "\\\\").replace(/\(/g, "\\(").replace(/\)/g, "\\)") + ")";
448
+ // src/shaping/bidi.ts
449
+ function classifyBidiType(cp) {
450
+ if (cp >= 768 && cp <= 879 || // Combining Diacritical Marks
451
+ cp >= 1425 && cp <= 1469 || // Hebrew marks
452
+ cp >= 1471 && cp <= 1471 || cp >= 1473 && cp <= 1474 || cp >= 1476 && cp <= 1477 || cp >= 1479 && cp <= 1479 || cp >= 1552 && cp <= 1562 || // Arabic marks
453
+ cp >= 1611 && cp <= 1631 || // Arabic harakat
454
+ cp >= 1648 && cp <= 1648 || // Arabic superscript alef
455
+ cp >= 1750 && cp <= 1756 || cp >= 1759 && cp <= 1764 || cp >= 1767 && cp <= 1768 || cp >= 1770 && cp <= 1773 || cp >= 65056 && cp <= 65071) return "NSM";
456
+ if (cp === 8203 || cp === 8204 || cp === 8205 || cp === 8206 || cp === 8207 || cp === 65279 || cp === 8294 || cp === 8295 || cp === 8296 || cp === 8297) return "BN";
457
+ if (cp >= 1632 && cp <= 1641) return "AN";
458
+ if (cp >= 1776 && cp <= 1785) return "AN";
459
+ if (cp >= 48 && cp <= 57) return "EN";
460
+ if (cp === 43 || cp === 45) return "ES";
461
+ if (cp === 35 || cp === 36 || cp === 37 || cp === 162 || cp === 163 || cp === 164 || cp === 165 || cp === 8364 || cp === 8377 || cp === 8378) return "ET";
462
+ if (cp === 44 || cp === 46 || cp === 47 || cp === 58 || cp === 160) return "CS";
463
+ if (cp === 32 || cp === 9 || cp === 10 || cp === 13 || cp === 12 || cp === 8192 || cp === 8202 || cp === 8232 || cp === 8233 || cp === 8239 || cp === 8287 || cp === 12288) return "WS";
464
+ if (cp >= 1536 && cp <= 1791) return "AL";
465
+ if (cp >= 1872 && cp <= 1919) return "AL";
466
+ if (cp >= 2208 && cp <= 2303) return "AL";
467
+ if (cp >= 64336 && cp <= 65023) return "AL";
468
+ if (cp >= 65136 && cp <= 65278) return "AL";
469
+ if (cp >= 1488 && cp <= 1514) return "R";
470
+ if (cp >= 1520 && cp <= 1524) return "R";
471
+ if (cp >= 1792 && cp <= 1871) return "R";
472
+ if (cp >= 1920 && cp <= 1983) return "R";
473
+ if (cp >= 64285 && cp <= 64335) return "R";
474
+ if (cp >= 65 && cp <= 90) return "L";
475
+ if (cp >= 97 && cp <= 122) return "L";
476
+ if (cp >= 192 && cp <= 591) return "L";
477
+ if (cp >= 880 && cp <= 1023) return "L";
478
+ if (cp >= 1024 && cp <= 1279) return "L";
479
+ if (cp >= 3584 && cp <= 3711) return "L";
480
+ if (cp >= 2304 && cp <= 2431) return "L";
481
+ if (cp >= 12352 && cp <= 12543) return "L";
482
+ if (cp >= 19968 && cp <= 40959) return "L";
483
+ if (cp >= 44032 && cp <= 55215) return "L";
484
+ if (cp >= 33 && cp <= 47) return "ON";
485
+ if (cp >= 58 && cp <= 64) return "ON";
486
+ if (cp >= 91 && cp <= 96) return "ON";
487
+ if (cp >= 123 && cp <= 126) return "ON";
488
+ if (cp >= 161 && cp <= 191) return "ON";
489
+ if (cp >= 8208 && cp <= 8231) return "ON";
490
+ if (cp >= 8240 && cp <= 8286) return "ON";
491
+ return "L";
401
492
  }
402
- function truncate(str, max) {
403
- if (!str || str.length <= max) return str || "";
404
- if (max <= 1) return "\u2026";
405
- return str.slice(0, max - 1) + "\u2026";
493
+ function detectParagraphLevel(types) {
494
+ for (const t of types) {
495
+ if (t === "L") return 0;
496
+ if (t === "R" || t === "AL") return 1;
497
+ }
498
+ return 0;
406
499
  }
407
- function helveticaWidth(str, sz) {
408
- let w = 0;
409
- for (let i = 0; i < str.length; i++) {
410
- const cp = str.codePointAt(i) ?? 0;
411
- if (cp > 65535) i++;
412
- if (cp >= 48 && cp <= 57) w += 556;
413
- else if (cp >= 65 && cp <= 90) w += 680;
414
- else if (cp >= 97 && cp <= 122) w += 500;
415
- else if (cp === 32) w += 278;
416
- else if (cp === 46 || cp === 44) w += 278;
417
- else if (cp === 43) w += 584;
418
- else if (cp === 45) w += 333;
419
- else if (cp === 47 || cp === 58) w += 278;
420
- else if (cp === 8212) w += 1e3;
421
- else if (cp === 8211) w += 556;
422
- else if (cp === 8230) w += 1e3;
423
- else if (cp === 8216 || cp === 8217) w += 222;
424
- else if (cp === 8220 || cp === 8221) w += 333;
425
- else if (cp === 8364) w += 556;
426
- else w += 556;
500
+ function resolveWeakTypes(types, paraLevel) {
501
+ const len = types.length;
502
+ let prevType = paraLevel === 0 ? "L" : "R";
503
+ for (let i = 0; i < len; i++) {
504
+ if (types[i] === "BN") continue;
505
+ if (types[i] === "NSM") {
506
+ types[i] = prevType;
507
+ }
508
+ prevType = types[i];
509
+ }
510
+ let lastStrong = paraLevel === 0 ? "L" : "R";
511
+ for (let i = 0; i < len; i++) {
512
+ if (types[i] === "BN") continue;
513
+ if (types[i] === "R" || types[i] === "L" || types[i] === "AL") {
514
+ lastStrong = types[i];
515
+ } else if (types[i] === "EN" && lastStrong === "AL") {
516
+ types[i] = "AN";
517
+ }
518
+ }
519
+ for (let i = 0; i < len; i++) {
520
+ if (types[i] === "AL") types[i] = "R";
521
+ }
522
+ for (let i = 1; i < len - 1; i++) {
523
+ if (types[i] === "BN") continue;
524
+ if (types[i] === "ES" && types[i - 1] === "EN" && types[i + 1] === "EN") {
525
+ types[i] = "EN";
526
+ } else if (types[i] === "CS") {
527
+ if (types[i - 1] === "EN" && types[i + 1] === "EN") types[i] = "EN";
528
+ else if (types[i - 1] === "AN" && types[i + 1] === "AN") types[i] = "AN";
529
+ }
530
+ }
531
+ for (let i = 0; i < len; i++) {
532
+ if (types[i] === "ET") {
533
+ let found = false;
534
+ for (let j = i - 1; j >= 0; j--) {
535
+ if (types[j] === "EN") {
536
+ found = true;
537
+ break;
538
+ }
539
+ if (types[j] !== "ET" && types[j] !== "BN") break;
540
+ }
541
+ if (!found) {
542
+ for (let j = i + 1; j < len; j++) {
543
+ if (types[j] === "EN") {
544
+ found = true;
545
+ break;
546
+ }
547
+ if (types[j] !== "ET" && types[j] !== "BN") break;
548
+ }
549
+ }
550
+ if (found) types[i] = "EN";
551
+ }
552
+ }
553
+ for (let i = 0; i < len; i++) {
554
+ if (types[i] === "ES" || types[i] === "ET" || types[i] === "CS") {
555
+ types[i] = "ON";
556
+ }
557
+ }
558
+ lastStrong = paraLevel === 0 ? "L" : "R";
559
+ for (let i = 0; i < len; i++) {
560
+ if (types[i] === "BN") continue;
561
+ if (types[i] === "L" || types[i] === "R") {
562
+ lastStrong = types[i];
563
+ } else if (types[i] === "EN" && lastStrong === "L") {
564
+ types[i] = "L";
565
+ }
427
566
  }
428
- return w * sz / 1e3;
429
567
  }
430
-
431
- // src/shaping/gsub-driver.ts
432
- function tryLigature(gids, ligatures) {
433
- if (!ligatures || gids.length < 2) return null;
434
- const firstGid = gids[0];
435
- const entries = ligatures[firstGid];
436
- if (!entries) return null;
437
- for (const entry of entries) {
438
- const compCount = entry.length - 1;
439
- if (compCount > gids.length - 1) continue;
440
- let match = true;
441
- for (let ci = 0; ci < compCount; ci++) {
442
- if (gids[1 + ci] !== entry[1 + ci]) {
443
- match = false;
568
+ function resolveNeutralTypes(types, paraLevel) {
569
+ const len = types.length;
570
+ const paraDir = paraLevel === 0 ? "L" : "R";
571
+ for (let i = 0; i < len; i++) {
572
+ if (types[i] !== "ON" && types[i] !== "WS" && types[i] !== "BN") continue;
573
+ const start = i;
574
+ while (i < len && (types[i] === "ON" || types[i] === "WS" || types[i] === "BN")) i++;
575
+ const end = i;
576
+ let prevStrong = paraDir;
577
+ for (let j = start - 1; j >= 0; j--) {
578
+ if (types[j] === "L" || types[j] === "R" || types[j] === "EN" || types[j] === "AN") {
579
+ prevStrong = types[j] === "EN" || types[j] === "AN" ? "R" : types[j];
444
580
  break;
445
581
  }
446
582
  }
447
- if (match) return { resultGid: entry[0], consumed: compCount + 1 };
583
+ let nextStrong = paraDir;
584
+ for (let j = end; j < len; j++) {
585
+ if (types[j] === "L" || types[j] === "R" || types[j] === "EN" || types[j] === "AN") {
586
+ nextStrong = types[j] === "EN" || types[j] === "AN" ? "R" : types[j];
587
+ break;
588
+ }
589
+ }
590
+ const resolved = prevStrong === nextStrong ? prevStrong : paraDir;
591
+ for (let j = start; j < end; j++) {
592
+ types[j] = resolved;
593
+ }
448
594
  }
449
- return null;
450
595
  }
451
-
452
- // src/shaping/bengali-shaper.ts
453
- var HALANT = 2509;
596
+ function assignLevels(types, paraLevel) {
597
+ const levels = [];
598
+ for (const t of types) {
599
+ if (paraLevel === 0) {
600
+ levels.push(t === "R" || t === "AN" ? 1 : 0);
601
+ } else {
602
+ levels.push(t === "L" ? 2 : 1);
603
+ }
604
+ }
605
+ return levels;
606
+ }
607
+ var SENTENCE_PUNCT = /* @__PURE__ */ new Set([
608
+ 46,
609
+ // .
610
+ 44,
611
+ // ,
612
+ 59,
613
+ // ;
614
+ 58,
615
+ // :
616
+ 33,
617
+ // !
618
+ 63
619
+ // ?
620
+ ]);
621
+ function fixPunctuationAffinity(types, codePoints, len) {
622
+ for (let i = 1; i < len; i++) {
623
+ if (types[i] === "R" && SENTENCE_PUNCT.has(codePoints[i])) {
624
+ let prevIdx = i - 1;
625
+ while (prevIdx >= 0 && (types[prevIdx] === "WS" || types[prevIdx] === "BN")) prevIdx--;
626
+ if (prevIdx >= 0 && types[prevIdx] === "L") {
627
+ types[i] = "L";
628
+ }
629
+ }
630
+ }
631
+ }
632
+ function fixBracketPairing(types, codePoints, len) {
633
+ const OPEN_BRACKETS = {
634
+ 40: 41,
635
+ // ( → )
636
+ 91: 93,
637
+ // [ → ]
638
+ 123: 125
639
+ // { → }
640
+ };
641
+ for (let i = 0; i < len; i++) {
642
+ const closer = OPEN_BRACKETS[codePoints[i]];
643
+ if (closer === void 0) continue;
644
+ let depth = 1;
645
+ let closeIdx = -1;
646
+ for (let j = i + 1; j < len; j++) {
647
+ if (codePoints[j] === codePoints[i]) depth++;
648
+ else if (codePoints[j] === closer) {
649
+ depth--;
650
+ if (depth === 0) {
651
+ closeIdx = j;
652
+ break;
653
+ }
654
+ }
655
+ }
656
+ if (closeIdx === -1) continue;
657
+ let hasL = false;
658
+ for (let j = i + 1; j < closeIdx; j++) {
659
+ if (types[j] === "L") {
660
+ hasL = true;
661
+ break;
662
+ }
663
+ }
664
+ if (hasL) {
665
+ types[i] = "L";
666
+ types[closeIdx] = "L";
667
+ }
668
+ }
669
+ }
670
+ function findOutermostIsolatePairs(codePoints) {
671
+ const pairs = [];
672
+ let i = 0;
673
+ while (i < codePoints.length) {
674
+ const cp = codePoints[i];
675
+ if (cp === 8294 || cp === 8295 || cp === 8296) {
676
+ let depth = 1;
677
+ let close = -1;
678
+ for (let j = i + 1; j < codePoints.length; j++) {
679
+ const cj = codePoints[j];
680
+ if (cj === 8294 || cj === 8295 || cj === 8296) depth++;
681
+ else if (cj === 8297) {
682
+ depth--;
683
+ if (depth === 0) {
684
+ close = j;
685
+ break;
686
+ }
687
+ }
688
+ }
689
+ if (close === -1) {
690
+ i++;
691
+ continue;
692
+ }
693
+ const kind = cp === 8294 ? "LRI" : cp === 8295 ? "RLI" : "FSI";
694
+ pairs.push({ open: i, close, kind });
695
+ i = close + 1;
696
+ } else {
697
+ i++;
698
+ }
699
+ }
700
+ return pairs;
701
+ }
702
+ function normalizeBidiEmbeddings(text) {
703
+ const LRE = 8234, RLE = 8235, PDF_CP = 8236, LRO = 8237, RLO = 8238;
704
+ const LRI = 8294, RLI = 8295, PDI = 8297;
705
+ let hasEmbed = false;
706
+ for (let i = 0; i < text.length; i++) {
707
+ const c = text.charCodeAt(i);
708
+ if (c === LRE || c === RLE || c === PDF_CP || c === LRO || c === RLO) {
709
+ hasEmbed = true;
710
+ break;
711
+ }
712
+ }
713
+ if (!hasEmbed) return text;
714
+ const stack = [];
715
+ const out = [];
716
+ const MAX_DEPTH = 125;
717
+ for (let i = 0; i < text.length; ) {
718
+ const cp = text.codePointAt(i) ?? 0;
719
+ const cpLen = cp > 65535 ? 2 : 1;
720
+ if (cp === LRE || cp === RLE) {
721
+ if (stack.length >= MAX_DEPTH) {
722
+ i += cpLen;
723
+ continue;
724
+ }
725
+ stack.push("E");
726
+ out.push(cp === LRE ? LRI : RLI);
727
+ i += cpLen;
728
+ } else if (cp === LRO || cp === RLO) {
729
+ if (stack.length >= MAX_DEPTH) {
730
+ i += cpLen;
731
+ continue;
732
+ }
733
+ stack.push("O");
734
+ out.push(cp);
735
+ i += cpLen;
736
+ } else if (cp === PDF_CP) {
737
+ const frame = stack.pop();
738
+ if (frame === "E") out.push(PDI);
739
+ else if (frame === "O") out.push(PDF_CP);
740
+ i += cpLen;
741
+ } else {
742
+ out.push(cp);
743
+ i += cpLen;
744
+ }
745
+ }
746
+ let result = "";
747
+ for (let i = 0; i < out.length; i++) result += String.fromCodePoint(out[i]);
748
+ return result;
749
+ }
750
+ function matchingPdfIndex(codePoints, openCp) {
751
+ let depth = 1;
752
+ for (let j = openCp + 1; j < codePoints.length; j++) {
753
+ const cj = codePoints[j];
754
+ if (cj === 8234 || cj === 8235 || cj === 8237 || cj === 8238) depth++;
755
+ else if (cj === 8236) {
756
+ depth--;
757
+ if (depth === 0) return j;
758
+ }
759
+ }
760
+ return -1;
761
+ }
762
+ function matchingPdiIndex(codePoints, openCp) {
763
+ let depth = 1;
764
+ for (let j = openCp + 1; j < codePoints.length; j++) {
765
+ const cj = codePoints[j];
766
+ if (cj === 8294 || cj === 8295 || cj === 8296) depth++;
767
+ else if (cj === 8297) {
768
+ depth--;
769
+ if (depth === 0) return j;
770
+ }
771
+ }
772
+ return -1;
773
+ }
774
+ function tryResolveOverrides(text, forcedLevel) {
775
+ const LRE = 8234, RLE = 8235, LRO = 8237, RLO = 8238;
776
+ if (text.indexOf("\u202D") === -1 && text.indexOf("\u202E") === -1) return null;
777
+ const codePoints = [];
778
+ const cpToStr = [];
779
+ for (let i2 = 0; i2 < text.length; ) {
780
+ cpToStr.push(i2);
781
+ const cp = text.codePointAt(i2) ?? 0;
782
+ codePoints.push(cp);
783
+ i2 += cp > 65535 ? 2 : 1;
784
+ }
785
+ cpToStr.push(text.length);
786
+ const len = codePoints.length;
787
+ const spans = [];
788
+ let i = 0;
789
+ while (i < len) {
790
+ const cp = codePoints[i];
791
+ if (cp === LRO || cp === RLO) {
792
+ const close = matchingPdfIndex(codePoints, i);
793
+ if (close === -1) {
794
+ i++;
795
+ continue;
796
+ }
797
+ spans.push({ open: i, close, rtl: cp === RLO });
798
+ i = close + 1;
799
+ } else if (cp === LRE || cp === RLE) {
800
+ const close = matchingPdfIndex(codePoints, i);
801
+ i = close === -1 ? i + 1 : close + 1;
802
+ } else if (cp === 8294 || cp === 8295 || cp === 8296) {
803
+ const close = matchingPdiIndex(codePoints, i);
804
+ i = close === -1 ? i + 1 : close + 1;
805
+ } else {
806
+ i++;
807
+ }
808
+ }
809
+ if (spans.length === 0) return null;
810
+ const out = [];
811
+ const emitGap = (cpStart, cpEnd) => {
812
+ if (cpStart >= cpEnd) return;
813
+ const segText = text.substring(cpToStr[cpStart], cpToStr[cpEnd]);
814
+ const baseStrIdx = cpToStr[cpStart];
815
+ const segRuns = forcedLevel === void 0 ? resolveBidiRuns(segText) : resolveBidiRunsForced(segText, forcedLevel);
816
+ for (const r of segRuns) {
817
+ out.push({ text: r.text, level: r.level, start: r.start + baseStrIdx });
818
+ }
819
+ };
820
+ let cursor = 0;
821
+ for (const span of spans) {
822
+ emitGap(cursor, span.open);
823
+ const innerRaw = text.substring(cpToStr[span.open + 1], cpToStr[span.close]);
824
+ const inner = stripBidiControls(innerRaw);
825
+ if (inner) {
826
+ const level = span.rtl ? 1 : 0;
827
+ const runText = span.rtl ? reverseString(inner) : inner;
828
+ out.push({ text: runText, level, start: cpToStr[span.open + 1] });
829
+ }
830
+ cursor = span.close + 1;
831
+ }
832
+ emitGap(cursor, len);
833
+ return out;
834
+ }
835
+ function resolveBidiRuns(text) {
836
+ if (!text) return [];
837
+ const overrideRuns = tryResolveOverrides(text);
838
+ if (overrideRuns) return overrideRuns;
839
+ const normalized = normalizeBidiEmbeddings(text);
840
+ if (normalized !== text) {
841
+ return resolveBidiRuns(normalized);
842
+ }
843
+ const codePoints = [];
844
+ const cpToStr = [];
845
+ for (let i = 0; i < text.length; ) {
846
+ cpToStr.push(i);
847
+ const cp = text.codePointAt(i) ?? 0;
848
+ codePoints.push(cp);
849
+ i += cp > 65535 ? 2 : 1;
850
+ }
851
+ cpToStr.push(text.length);
852
+ const isolates = findOutermostIsolatePairs(codePoints);
853
+ if (isolates.length === 0) {
854
+ return resolveBidiCore(text, codePoints, cpToStr);
855
+ }
856
+ const insideIsolate = new Array(codePoints.length).fill(false);
857
+ for (const p of isolates) {
858
+ for (let k = p.open; k <= p.close; k++) insideIsolate[k] = true;
859
+ }
860
+ const outerTypes = codePoints.map((cp, idx) => insideIsolate[idx] ? "BN" : classifyBidiType(cp));
861
+ const parentLevel = detectParagraphLevel(outerTypes);
862
+ const out = [];
863
+ const emitSegment = (cpStart, cpEnd, forced) => {
864
+ if (cpStart >= cpEnd) return;
865
+ const segText = text.substring(cpToStr[cpStart], cpToStr[cpEnd]);
866
+ const segCps = codePoints.slice(cpStart, cpEnd);
867
+ const baseStrIdx = cpToStr[cpStart];
868
+ const segCpToStr = cpToStr.slice(cpStart, cpEnd + 1).map((x) => x - baseStrIdx);
869
+ const segRuns = forced === void 0 ? resolveBidiRuns(segText) : resolveBidiCore(segText, segCps, segCpToStr, forced);
870
+ for (const r of segRuns) {
871
+ out.push({ text: r.text, level: r.level, start: r.start + baseStrIdx });
872
+ }
873
+ };
874
+ let cursor = 0;
875
+ for (const pair of isolates) {
876
+ emitSegment(cursor, pair.open, parentLevel);
877
+ const innerStart = pair.open + 1;
878
+ const innerEnd = pair.close;
879
+ let innerLevel;
880
+ if (pair.kind === "LRI") innerLevel = 0;
881
+ else if (pair.kind === "RLI") innerLevel = 1;
882
+ else {
883
+ const innerTypes = codePoints.slice(innerStart, innerEnd).map(classifyBidiType);
884
+ innerLevel = detectParagraphLevel(innerTypes);
885
+ }
886
+ if (innerStart < innerEnd) {
887
+ const innerText = text.substring(cpToStr[innerStart], cpToStr[innerEnd]);
888
+ const innerRuns = resolveBidiRunsForced(innerText, innerLevel);
889
+ const baseStrIdx = cpToStr[innerStart];
890
+ for (const r of innerRuns) {
891
+ out.push({ text: r.text, level: r.level, start: r.start + baseStrIdx });
892
+ }
893
+ }
894
+ cursor = pair.close + 1;
895
+ }
896
+ emitSegment(cursor, codePoints.length, parentLevel);
897
+ return out;
898
+ }
899
+ function resolveBidiRunsForced(text, forcedLevel) {
900
+ if (!text) return [];
901
+ const overrideRuns = tryResolveOverrides(text, forcedLevel);
902
+ if (overrideRuns) return overrideRuns;
903
+ const codePoints = [];
904
+ const cpToStr = [];
905
+ for (let i = 0; i < text.length; ) {
906
+ cpToStr.push(i);
907
+ const cp = text.codePointAt(i) ?? 0;
908
+ codePoints.push(cp);
909
+ i += cp > 65535 ? 2 : 1;
910
+ }
911
+ cpToStr.push(text.length);
912
+ const isolates = findOutermostIsolatePairs(codePoints);
913
+ if (isolates.length === 0) {
914
+ return resolveBidiCore(text, codePoints, cpToStr, forcedLevel);
915
+ }
916
+ const out = [];
917
+ const emit = (cpStart, cpEnd, forced) => {
918
+ if (cpStart >= cpEnd) return;
919
+ const segText = text.substring(cpToStr[cpStart], cpToStr[cpEnd]);
920
+ const segCps = codePoints.slice(cpStart, cpEnd);
921
+ const baseStrIdx = cpToStr[cpStart];
922
+ const segCpToStr = cpToStr.slice(cpStart, cpEnd + 1).map((x) => x - baseStrIdx);
923
+ const segRuns = resolveBidiCore(segText, segCps, segCpToStr, forced);
924
+ for (const r of segRuns) {
925
+ out.push({ text: r.text, level: r.level, start: r.start + baseStrIdx });
926
+ }
927
+ };
928
+ let cursor = 0;
929
+ for (const pair of isolates) {
930
+ emit(cursor, pair.open, forcedLevel);
931
+ const innerStart = pair.open + 1;
932
+ const innerEnd = pair.close;
933
+ let innerLevel;
934
+ if (pair.kind === "LRI") innerLevel = 0;
935
+ else if (pair.kind === "RLI") innerLevel = 1;
936
+ else {
937
+ const innerTypes = codePoints.slice(innerStart, innerEnd).map(classifyBidiType);
938
+ innerLevel = detectParagraphLevel(innerTypes);
939
+ }
940
+ if (innerStart < innerEnd) {
941
+ const innerText = text.substring(cpToStr[innerStart], cpToStr[innerEnd]);
942
+ const innerRuns = resolveBidiRunsForced(innerText, innerLevel);
943
+ const baseStrIdx = cpToStr[innerStart];
944
+ for (const r of innerRuns) {
945
+ out.push({ text: r.text, level: r.level, start: r.start + baseStrIdx });
946
+ }
947
+ }
948
+ cursor = pair.close + 1;
949
+ }
950
+ emit(cursor, codePoints.length, forcedLevel);
951
+ return out;
952
+ }
953
+ function resolveBidiCore(text, codePoints, cpToStr, forcedLevel) {
954
+ const len = codePoints.length;
955
+ if (len === 0) return [];
956
+ const types = codePoints.map(classifyBidiType);
957
+ const paraLevel = forcedLevel !== void 0 ? forcedLevel : detectParagraphLevel(types);
958
+ resolveWeakTypes(types, paraLevel);
959
+ resolveNeutralTypes(types, paraLevel);
960
+ if (paraLevel === 1) {
961
+ fixPunctuationAffinity(types, codePoints, len);
962
+ fixBracketPairing(types, codePoints, len);
963
+ }
964
+ const levels = assignLevels(types, paraLevel);
965
+ const runs = [];
966
+ let runStart = 0;
967
+ let runLevel = levels[0];
968
+ for (let i = 1; i <= len; i++) {
969
+ if (i === len || levels[i] !== runLevel) {
970
+ const start = cpToStr[runStart];
971
+ const end = cpToStr[i];
972
+ let runText = text.substring(start, end);
973
+ if (runLevel % 2 === 1) {
974
+ runText = reverseString(runText);
975
+ }
976
+ runs.push({ text: runText, level: runLevel, start });
977
+ if (i < len) {
978
+ runStart = i;
979
+ runLevel = levels[i];
980
+ }
981
+ }
982
+ }
983
+ if (paraLevel === 1 && runs.length > 1) {
984
+ runs.reverse();
985
+ }
986
+ return runs;
987
+ }
988
+ function containsRTL(text) {
989
+ for (let i = 0; i < text.length; ) {
990
+ const cp = text.codePointAt(i) ?? 0;
991
+ if (cp === 8237 || cp === 8238) return true;
992
+ const t = classifyBidiType(cp);
993
+ if (t === "R" || t === "AL") return true;
994
+ i += cp > 65535 ? 2 : 1;
995
+ }
996
+ return false;
997
+ }
998
+ function stripBidiControls(text) {
999
+ if (!text) return text;
1000
+ let needs = false;
1001
+ for (let i = 0; i < text.length; i++) {
1002
+ const c = text.charCodeAt(i);
1003
+ if (c === 8206 || c === 8207 || c >= 8234 && c <= 8238 || c >= 8294 && c <= 8297) {
1004
+ needs = true;
1005
+ break;
1006
+ }
1007
+ }
1008
+ if (!needs) return text;
1009
+ let out = "";
1010
+ for (let i = 0; i < text.length; i++) {
1011
+ const c = text.charCodeAt(i);
1012
+ if (c === 8206 || c === 8207 || c >= 8234 && c <= 8238 || c >= 8294 && c <= 8297) continue;
1013
+ out += text[i];
1014
+ }
1015
+ return out;
1016
+ }
1017
+ function reverseString(str) {
1018
+ const cps = [];
1019
+ for (let i = 0; i < str.length; ) {
1020
+ const cp = str.codePointAt(i) ?? 0;
1021
+ cps.push(cp);
1022
+ i += cp > 65535 ? 2 : 1;
1023
+ }
1024
+ cps.reverse();
1025
+ return String.fromCodePoint(...cps);
1026
+ }
1027
+
1028
+ // src/fonts/encoding.ts
1029
+ function toWinAnsi(str) {
1030
+ if (!str) return "";
1031
+ let r = "";
1032
+ for (let i = 0; i < str.length; i++) {
1033
+ const c = str.charCodeAt(i);
1034
+ if (c >= 32 && c <= 126) r += str[i];
1035
+ else if (c >= 160 && c <= 255) r += str[i];
1036
+ else if (c === 8364) r += "\x80";
1037
+ else if (c === 8218) r += "\x82";
1038
+ else if (c === 402) r += "\x83";
1039
+ else if (c === 8222) r += "\x84";
1040
+ else if (c === 8230) r += "\x85";
1041
+ else if (c === 8224) r += "\x86";
1042
+ else if (c === 8225) r += "\x87";
1043
+ else if (c === 710) r += "\x88";
1044
+ else if (c === 8240) r += "\x89";
1045
+ else if (c === 352) r += "\x8A";
1046
+ else if (c === 8249) r += "\x8B";
1047
+ else if (c === 338) r += "\x8C";
1048
+ else if (c === 381) r += "\x8E";
1049
+ else if (c === 8216) r += "\x91";
1050
+ else if (c === 8217) r += "\x92";
1051
+ else if (c === 8220) r += "\x93";
1052
+ else if (c === 8221) r += "\x94";
1053
+ else if (c === 8226) r += "\x95";
1054
+ else if (c === 8211) r += "\x96";
1055
+ else if (c === 8212) r += "\x97";
1056
+ else if (c === 732) r += "\x98";
1057
+ else if (c === 8482) r += "\x99";
1058
+ else if (c === 353) r += "\x9A";
1059
+ else if (c === 8250) r += "\x9B";
1060
+ else if (c === 339) r += "\x9C";
1061
+ else if (c === 382) r += "\x9E";
1062
+ else if (c === 376) r += "\x9F";
1063
+ else if (c === 160 || c === 8239) r += " ";
1064
+ else if (c === 9 || c === 10 || c === 13) r += " ";
1065
+ else if (c < 32) ; else r += "?";
1066
+ }
1067
+ return r;
1068
+ }
1069
+ function pdfString(str) {
1070
+ const s = toWinAnsi(stripBidiControls(str));
1071
+ return "(" + s.replace(/\\/g, "\\\\").replace(/\(/g, "\\(").replace(/\)/g, "\\)") + ")";
1072
+ }
1073
+ var WINANSI_HIGH_TO_UNICODE = {
1074
+ 128: 8364,
1075
+ 130: 8218,
1076
+ 131: 402,
1077
+ 132: 8222,
1078
+ 133: 8230,
1079
+ 134: 8224,
1080
+ 135: 8225,
1081
+ 136: 710,
1082
+ 137: 8240,
1083
+ 138: 352,
1084
+ 139: 8249,
1085
+ 140: 338,
1086
+ 142: 381,
1087
+ 145: 8216,
1088
+ 146: 8217,
1089
+ 147: 8220,
1090
+ 148: 8221,
1091
+ 149: 8226,
1092
+ 150: 8211,
1093
+ 151: 8212,
1094
+ 152: 732,
1095
+ 153: 8482,
1096
+ 154: 353,
1097
+ 155: 8250,
1098
+ 156: 339,
1099
+ 158: 382,
1100
+ 159: 376
1101
+ };
1102
+ function buildWinAnsiToUnicodeCMap() {
1103
+ const entries = [];
1104
+ const hex2 = (n2) => n2.toString(16).toUpperCase().padStart(2, "0");
1105
+ const hex4 = (n2) => n2.toString(16).toUpperCase().padStart(4, "0");
1106
+ for (let b = 32; b <= 255; b++) {
1107
+ if (b >= 127 && b <= 159) {
1108
+ const u = WINANSI_HIGH_TO_UNICODE[b];
1109
+ if (u === void 0) continue;
1110
+ entries.push(`<${hex2(b)}> <${hex4(u)}>`);
1111
+ } else {
1112
+ entries.push(`<${hex2(b)}> <${hex4(b)}>`);
1113
+ }
1114
+ }
1115
+ const blocks = [];
1116
+ for (let i = 0; i < entries.length; i += 100) {
1117
+ const chunk = entries.slice(i, i + 100);
1118
+ blocks.push(`${chunk.length} beginbfchar
1119
+ ${chunk.join("\n")}
1120
+ endbfchar`);
1121
+ }
1122
+ return "/CIDInit /ProcSet findresource begin\n12 dict begin\nbegincmap\n/CIDSystemInfo << /Registry (Adobe) /Ordering (UCS) /Supplement 0 >> def\n/CMapName /Adobe-Identity-UCS def\n/CMapType 2 def\n1 begincodespacerange\n<20> <FF>\nendcodespacerange\n" + blocks.join("\n") + "\nendcmap\nCMapName currentdict /CMap defineresource pop\nend\nend";
1123
+ }
1124
+ function truncate(str, max) {
1125
+ if (!str || str.length <= max) return str || "";
1126
+ if (max <= 1) return "\u2026";
1127
+ return str.slice(0, max - 1) + "\u2026";
1128
+ }
1129
+ function helveticaWidth(str, sz) {
1130
+ str = stripBidiControls(str);
1131
+ let w = 0;
1132
+ for (let i = 0; i < str.length; i++) {
1133
+ const cp = str.codePointAt(i) ?? 0;
1134
+ if (cp > 65535) i++;
1135
+ if (cp >= 48 && cp <= 57) w += 556;
1136
+ else if (cp >= 65 && cp <= 90) w += 680;
1137
+ else if (cp >= 97 && cp <= 122) w += 500;
1138
+ else if (cp === 32) w += 278;
1139
+ else if (cp === 46 || cp === 44) w += 278;
1140
+ else if (cp === 43) w += 584;
1141
+ else if (cp === 45) w += 333;
1142
+ else if (cp === 47 || cp === 58) w += 278;
1143
+ else if (cp === 8212) w += 1e3;
1144
+ else if (cp === 8211) w += 556;
1145
+ else if (cp === 8230) w += 1e3;
1146
+ else if (cp === 8216 || cp === 8217) w += 222;
1147
+ else if (cp === 8220 || cp === 8221) w += 333;
1148
+ else if (cp === 8364) w += 556;
1149
+ else w += 556;
1150
+ }
1151
+ return w * sz / 1e3;
1152
+ }
1153
+ function helveticaBoldWidth(str, sz) {
1154
+ str = stripBidiControls(str);
1155
+ let w = 0;
1156
+ for (let i = 0; i < str.length; i++) {
1157
+ const cp = str.codePointAt(i) ?? 0;
1158
+ if (cp > 65535) i++;
1159
+ if (cp >= 48 && cp <= 57) w += 556;
1160
+ else if (cp >= 65 && cp <= 90) w += 722;
1161
+ else if (cp >= 97 && cp <= 122) w += 611;
1162
+ else if (cp === 32) w += 278;
1163
+ else if (cp === 46 || cp === 44) w += 278;
1164
+ else if (cp === 43) w += 584;
1165
+ else if (cp === 45) w += 333;
1166
+ else if (cp === 47 || cp === 58) w += 278;
1167
+ else if (cp === 8212) w += 1e3;
1168
+ else if (cp === 8211) w += 556;
1169
+ else if (cp === 8230) w += 1e3;
1170
+ else if (cp === 8216 || cp === 8217) w += 278;
1171
+ else if (cp === 8220 || cp === 8221) w += 500;
1172
+ else if (cp === 8364) w += 556;
1173
+ else w += 611;
1174
+ }
1175
+ return w * sz / 1e3;
1176
+ }
1177
+
1178
+ // src/shaping/gsub-driver.ts
1179
+ function tryLigature(gids, ligatures) {
1180
+ if (!ligatures || gids.length < 2) return null;
1181
+ const firstGid = gids[0];
1182
+ const entries = ligatures[firstGid];
1183
+ if (!entries) return null;
1184
+ for (const entry of entries) {
1185
+ const compCount = entry.length - 1;
1186
+ if (compCount > gids.length - 1) continue;
1187
+ let match = true;
1188
+ for (let ci = 0; ci < compCount; ci++) {
1189
+ if (gids[1 + ci] !== entry[1 + ci]) {
1190
+ match = false;
1191
+ break;
1192
+ }
1193
+ }
1194
+ if (match) return { resultGid: entry[0], consumed: compCount + 1 };
1195
+ }
1196
+ return null;
1197
+ }
1198
+
1199
+ // src/shaping/use-lite.ts
1200
+ function devanagariCategory(cp) {
1201
+ if (cp === 2305 || cp === 2306) return "Mabv";
1202
+ if (cp === 2307) return "Mpst";
1203
+ if (cp >= 2308 && cp <= 2324) return "V";
1204
+ if (cp >= 2325 && cp <= 2361) return "B";
1205
+ if (cp === 2362) return "Mabv";
1206
+ if (cp === 2363) return "Mpst";
1207
+ if (cp === 2364) return "Mblw";
1208
+ if (cp === 2365) return "O";
1209
+ if (cp === 2366) return "Mpst";
1210
+ if (cp >= 2367 && cp <= 2368) return cp === 2367 ? "Mpre" : "Mpst";
1211
+ if (cp >= 2369 && cp <= 2376) return "Mblw";
1212
+ if (cp >= 2377 && cp <= 2380) return "Mpst";
1213
+ if (cp === 2381) return "H";
1214
+ if (cp >= 2382 && cp <= 2383) return "Mpre";
1215
+ if (cp === 2384) return "O";
1216
+ if (cp >= 2385 && cp <= 2391) return "Mabv";
1217
+ if (cp >= 2392 && cp <= 2401) return "B";
1218
+ if (cp >= 2402 && cp <= 2403) return "Mblw";
1219
+ if (cp >= 2406 && cp <= 2415) return "N";
1220
+ return "O";
1221
+ }
1222
+ function bengaliCategory(cp) {
1223
+ if (cp === 2433) return "Mabv";
1224
+ if (cp === 2434) return "Mpst";
1225
+ if (cp === 2435) return "Mpst";
1226
+ if (cp >= 2437 && cp <= 2452) return "V";
1227
+ if (cp >= 2453 && cp <= 2489) return "B";
1228
+ if (cp === 2492) return "Mblw";
1229
+ if (cp === 2493) return "O";
1230
+ if (cp === 2494) return "Mpst";
1231
+ if (cp >= 2495 && cp <= 2496) return cp === 2495 ? "Mpre" : "Mpst";
1232
+ if (cp >= 2497 && cp <= 2500) return "Mblw";
1233
+ if (cp >= 2503 && cp <= 2504) return "Mpre";
1234
+ if (cp >= 2507 && cp <= 2508) return "Mpre";
1235
+ if (cp === 2509) return "H";
1236
+ if (cp === 2510) return "B";
1237
+ if (cp === 2519) return "Mpst";
1238
+ if (cp >= 2524 && cp <= 2527) return "B";
1239
+ if (cp >= 2528 && cp <= 2531) return "O";
1240
+ if (cp >= 2534 && cp <= 2543) return "N";
1241
+ return "O";
1242
+ }
1243
+ function tamilCategory(cp) {
1244
+ if (cp === 2946) return "Mabv";
1245
+ if (cp >= 2949 && cp <= 2964) return "V";
1246
+ if (cp >= 2965 && cp <= 3001) return "B";
1247
+ if (cp === 3006) return "Mpst";
1248
+ if (cp === 3007) return "Mpst";
1249
+ if (cp >= 3008 && cp <= 3010) return "Mpst";
1250
+ if (cp >= 3014 && cp <= 3016) return "Mpre";
1251
+ if (cp >= 3018 && cp <= 3020) return "Mpre";
1252
+ if (cp === 3021) return "H";
1253
+ if (cp === 3031) return "Mpst";
1254
+ if (cp >= 3046 && cp <= 3055) return "N";
1255
+ return "O";
1256
+ }
1257
+ function classifyUseCategory(cp) {
1258
+ if (cp === 8204) return "ZWNJ";
1259
+ if (cp === 8205) return "ZWJ";
1260
+ if (cp >= 2304 && cp <= 2431) return devanagariCategory(cp);
1261
+ if (cp >= 2432 && cp <= 2559) return bengaliCategory(cp);
1262
+ if (cp >= 2944 && cp <= 3071) return tamilCategory(cp);
1263
+ return "O";
1264
+ }
1265
+
1266
+ // src/shaping/bengali-shaper.ts
1267
+ var HALANT = 2509;
454
1268
  var NUKTA = 2492;
455
1269
  var RA = 2480;
456
1270
  function bengaliCharType(cp) {
@@ -475,10 +1289,563 @@ function bengaliCharType(cp) {
475
1289
  if (cp === 2510) return 0;
476
1290
  return -1;
477
1291
  }
478
- function isConsonant(cp) {
479
- return bengaliCharType(cp) === 0;
1292
+ function isConsonant(cp) {
1293
+ return bengaliCharType(cp) === 0;
1294
+ }
1295
+ function buildBengaliClusters(str) {
1296
+ const clusters = [];
1297
+ const cps = [];
1298
+ for (let i2 = 0; i2 < str.length; ) {
1299
+ const cp = str.codePointAt(i2) ?? 0;
1300
+ cps.push(cp);
1301
+ i2 += cp > 65535 ? 2 : 1;
1302
+ }
1303
+ let i = 0;
1304
+ while (i < cps.length) {
1305
+ const cp = cps[i];
1306
+ const type = bengaliCharType(cp);
1307
+ if (classifyUseCategory(cp) === "ZWJ" || classifyUseCategory(cp) === "ZWNJ") {
1308
+ i++;
1309
+ continue;
1310
+ }
1311
+ if (type < 0 || cp < BENGALI_START || cp > BENGALI_END) {
1312
+ clusters.push({ codepoints: [cp], baseIndex: 0, hasReph: false, preBaseMatras: [] });
1313
+ i++;
1314
+ continue;
1315
+ }
1316
+ const syllable = [];
1317
+ let baseIdx = 0;
1318
+ let hasReph = false;
1319
+ const preMatras = [];
1320
+ if (isConsonant(cp) && cp === RA && i + 2 < cps.length && cps[i + 1] === HALANT && isConsonant(cps[i + 2])) {
1321
+ hasReph = true;
1322
+ syllable.push(cp, cps[i + 1]);
1323
+ i += 2;
1324
+ }
1325
+ let lastConsonantIdx = -1;
1326
+ while (i < cps.length) {
1327
+ const cc = cps[i];
1328
+ const ct = bengaliCharType(cc);
1329
+ if (ct === 0) {
1330
+ lastConsonantIdx = syllable.length;
1331
+ syllable.push(cc);
1332
+ i++;
1333
+ if (i < cps.length && cps[i] === NUKTA) {
1334
+ syllable.push(cps[i]);
1335
+ i++;
1336
+ }
1337
+ if (i < cps.length && cps[i] === HALANT) {
1338
+ let j = i + 1;
1339
+ let zwnj = false;
1340
+ if (j < cps.length) {
1341
+ const jc = classifyUseCategory(cps[j]);
1342
+ if (jc === "ZWJ") {
1343
+ j++;
1344
+ } else if (jc === "ZWNJ") {
1345
+ j++;
1346
+ zwnj = true;
1347
+ }
1348
+ }
1349
+ if (!zwnj && j < cps.length && isConsonant(cps[j])) {
1350
+ syllable.push(cps[i]);
1351
+ i = j;
1352
+ continue;
1353
+ } else {
1354
+ syllable.push(cps[i]);
1355
+ i = j;
1356
+ break;
1357
+ }
1358
+ }
1359
+ break;
1360
+ } else {
1361
+ break;
1362
+ }
1363
+ }
1364
+ baseIdx = lastConsonantIdx >= 0 ? lastConsonantIdx : 0;
1365
+ while (i < cps.length) {
1366
+ const ct = bengaliCharType(cps[i]);
1367
+ if (ct >= 2 && ct <= 5) {
1368
+ if (ct === 4) {
1369
+ preMatras.push(syllable.length);
1370
+ }
1371
+ syllable.push(cps[i]);
1372
+ i++;
1373
+ } else {
1374
+ break;
1375
+ }
1376
+ }
1377
+ while (i < cps.length && bengaliCharType(cps[i]) === 6) {
1378
+ syllable.push(cps[i]);
1379
+ i++;
1380
+ }
1381
+ if (syllable.length === 0) {
1382
+ syllable.push(cps[i] ?? 32);
1383
+ i++;
1384
+ }
1385
+ clusters.push({
1386
+ codepoints: syllable,
1387
+ baseIndex: hasReph ? baseIdx + 2 : baseIdx,
1388
+ // Adjust for reph prefix
1389
+ hasReph,
1390
+ preBaseMatras: preMatras
1391
+ });
1392
+ }
1393
+ return clusters;
1394
+ }
1395
+ function shapeBengaliText(str, fontData) {
1396
+ const { cmap, gsub, ligatures, markAnchors, widths, defaultWidth } = fontData;
1397
+ const shaped = [];
1398
+ function resolveGid(cp) {
1399
+ const normCp = cp === 8239 || cp === 160 ? 32 : cp;
1400
+ return cmap[normCp] || 0;
1401
+ }
1402
+ function resolveGidGsub(cp) {
1403
+ const gid = resolveGid(cp);
1404
+ if (gsub[gid] !== void 0) return gsub[gid];
1405
+ return gid;
1406
+ }
1407
+ function tryLig(gids) {
1408
+ return tryLigature(gids, ligatures);
1409
+ }
1410
+ function getAdv(gid) {
1411
+ return widths[gid] !== void 0 ? widths[gid] : defaultWidth;
1412
+ }
1413
+ function getBaseAnchor2(baseGid, markClass) {
1414
+ const base = markAnchors && markAnchors.bases && markAnchors.bases[baseGid];
1415
+ if (!base) return null;
1416
+ return base[markClass] ?? null;
1417
+ }
1418
+ function getMarkAnchor2(markGid) {
1419
+ const mark = markAnchors && markAnchors.marks && markAnchors.marks[markGid];
1420
+ if (!mark) return null;
1421
+ return { classIdx: mark[0], x: mark[1], y: mark[2] };
1422
+ }
1423
+ function emitGlyph(gid, isZero, baseGid) {
1424
+ if (isZero && baseGid !== void 0) {
1425
+ const markAnchor = getMarkAnchor2(gid);
1426
+ if (markAnchor) {
1427
+ const baseAnchorPt = getBaseAnchor2(baseGid, markAnchor.classIdx);
1428
+ if (baseAnchorPt) {
1429
+ const baseAdv = getAdv(baseGid);
1430
+ shaped.push({
1431
+ gid,
1432
+ dx: baseAnchorPt[0] - markAnchor.x - baseAdv,
1433
+ dy: baseAnchorPt[1] - markAnchor.y,
1434
+ isZeroAdvance: true
1435
+ });
1436
+ return;
1437
+ }
1438
+ }
1439
+ shaped.push({ gid, dx: 0, dy: 0, isZeroAdvance: true });
1440
+ } else {
1441
+ shaped.push({ gid, dx: 0, dy: 0, isZeroAdvance: false });
1442
+ }
1443
+ }
1444
+ const clusters = buildBengaliClusters(str);
1445
+ for (const cluster of clusters) {
1446
+ const { codepoints, hasReph, preBaseMatras } = cluster;
1447
+ const baseStart = hasReph ? 2 : 0;
1448
+ let baseGid = 0;
1449
+ for (let ci = baseStart; ci < codepoints.length; ci++) {
1450
+ const ct = bengaliCharType(codepoints[ci]);
1451
+ if (ct === 0) {
1452
+ baseGid = resolveGid(codepoints[ci]);
1453
+ } else if (ct >= 2) {
1454
+ break;
1455
+ }
1456
+ }
1457
+ const splitPostComponents = [];
1458
+ for (const mIdx of preBaseMatras) {
1459
+ if (mIdx < codepoints.length) {
1460
+ const mCp = codepoints[mIdx];
1461
+ if (mCp === 2507) {
1462
+ emitGlyph(resolveGid(2503), false);
1463
+ splitPostComponents.push(2494);
1464
+ } else if (mCp === 2508) {
1465
+ emitGlyph(resolveGid(2503), false);
1466
+ splitPostComponents.push(2519);
1467
+ } else {
1468
+ emitGlyph(resolveGid(mCp), false);
1469
+ }
1470
+ }
1471
+ }
1472
+ const clusterGids = [];
1473
+ const clusterEndIdx = [];
1474
+ let matraStart = codepoints.length;
1475
+ for (let ci = baseStart; ci < codepoints.length; ci++) {
1476
+ const ct = bengaliCharType(codepoints[ci]);
1477
+ if (ct === 0 || ct === 7 || ct === 8) {
1478
+ clusterGids.push(resolveGid(codepoints[ci]));
1479
+ clusterEndIdx.push(ci);
1480
+ } else if (ct < 0 || ct === 1 || ct === 9) {
1481
+ emitGlyph(resolveGid(codepoints[ci]), false);
1482
+ } else {
1483
+ matraStart = ci;
1484
+ break;
1485
+ }
1486
+ }
1487
+ let ligConsumed = 0;
1488
+ const ligResult = tryLig(clusterGids);
1489
+ if (ligResult) {
1490
+ emitGlyph(ligResult.resultGid, false);
1491
+ baseGid = ligResult.resultGid;
1492
+ ligConsumed = ligResult.consumed;
1493
+ let gi = ligConsumed;
1494
+ while (gi < clusterGids.length) {
1495
+ const subSeq = clusterGids.slice(gi);
1496
+ const subLig = tryLig(subSeq);
1497
+ if (subLig) {
1498
+ emitGlyph(subLig.resultGid, false);
1499
+ gi += subLig.consumed;
1500
+ } else {
1501
+ const origCi = clusterEndIdx[gi];
1502
+ const ct = bengaliCharType(codepoints[origCi]);
1503
+ if (ct === 7) {
1504
+ emitGlyph(clusterGids[gi], true, baseGid);
1505
+ } else {
1506
+ emitGlyph(clusterGids[gi], false);
1507
+ }
1508
+ gi++;
1509
+ }
1510
+ }
1511
+ } else {
1512
+ for (let ci = baseStart; ci < matraStart; ci++) {
1513
+ const cp = codepoints[ci];
1514
+ const ct = bengaliCharType(cp);
1515
+ if (ct === 0) {
1516
+ emitGlyph(resolveGid(cp), false);
1517
+ } else if (ct === 7) {
1518
+ emitGlyph(resolveGid(cp), true, baseGid);
1519
+ } else if (ct === 8) {
1520
+ emitGlyph(resolveGid(cp), true, baseGid);
1521
+ }
1522
+ }
1523
+ }
1524
+ for (let ci = matraStart; ci < codepoints.length; ci++) {
1525
+ const cp = codepoints[ci];
1526
+ const ct = bengaliCharType(cp);
1527
+ if (ct === 4) continue;
1528
+ if (ct === 2 || ct === 3) {
1529
+ emitGlyph(resolveGid(cp), true, baseGid);
1530
+ } else if (ct === 5) {
1531
+ emitGlyph(resolveGid(cp), false);
1532
+ } else if (ct === 6) {
1533
+ emitGlyph(resolveGid(cp), true, baseGid);
1534
+ } else if (ct === 9) {
1535
+ emitGlyph(resolveGid(cp), false);
1536
+ } else {
1537
+ emitGlyph(resolveGid(cp), false);
1538
+ }
1539
+ }
1540
+ for (const postCp of splitPostComponents) {
1541
+ emitGlyph(resolveGid(postCp), false);
1542
+ }
1543
+ if (hasReph) {
1544
+ const raGid = resolveGidGsub(RA);
1545
+ const halantGid = resolveGid(HALANT);
1546
+ emitGlyph(raGid, true, baseGid);
1547
+ emitGlyph(halantGid, true, baseGid);
1548
+ }
1549
+ }
1550
+ return shaped;
1551
+ }
1552
+
1553
+ // src/shaping/tamil-shaper.ts
1554
+ var PULLI = 3021;
1555
+ function tamilCharType(cp) {
1556
+ if (cp === PULLI) return 7;
1557
+ if (cp === 2946 || cp === 2947) return 6;
1558
+ if (cp >= 2949 && cp <= 2964) return 1;
1559
+ if (cp >= 2965 && cp <= 3001) return 0;
1560
+ if (cp === 3007) return 4;
1561
+ if (cp === 3014) return 4;
1562
+ if (cp === 3015) return 4;
1563
+ if (cp === 3016) return 4;
1564
+ if (cp === 3018) return 4;
1565
+ if (cp === 3019) return 4;
1566
+ if (cp === 3020) return 4;
1567
+ if (cp === 3006) return 5;
1568
+ if (cp === 3008) return 5;
1569
+ if (cp === 3009 || cp === 3010) return 2;
1570
+ if (cp === 3031) return 5;
1571
+ if (cp >= 3006 && cp <= 3020) return 2;
1572
+ if (cp >= 3046 && cp <= 3055) return 9;
1573
+ if (cp >= 3056 && cp <= 3066) return 9;
1574
+ if (cp === 3024) return 6;
1575
+ return -1;
1576
+ }
1577
+ function isConsonant2(cp) {
1578
+ return tamilCharType(cp) === 0;
1579
+ }
1580
+ function buildTamilClusters(str) {
1581
+ const clusters = [];
1582
+ const cps = [];
1583
+ for (let i2 = 0; i2 < str.length; ) {
1584
+ const cp = str.codePointAt(i2) ?? 0;
1585
+ cps.push(cp);
1586
+ i2 += cp > 65535 ? 2 : 1;
1587
+ }
1588
+ let i = 0;
1589
+ while (i < cps.length) {
1590
+ const cp = cps[i];
1591
+ const type = tamilCharType(cp);
1592
+ if (classifyUseCategory(cp) === "ZWJ" || classifyUseCategory(cp) === "ZWNJ") {
1593
+ i++;
1594
+ continue;
1595
+ }
1596
+ if (type < 0 || cp < TAMIL_START || cp > TAMIL_END) {
1597
+ clusters.push({ codepoints: [cp], baseIndex: 0, preBaseMatras: [] });
1598
+ i++;
1599
+ continue;
1600
+ }
1601
+ const syllable = [];
1602
+ let lastConsonantIdx = -1;
1603
+ const preMatras = [];
1604
+ while (i < cps.length) {
1605
+ const cc = cps[i];
1606
+ const ct = tamilCharType(cc);
1607
+ if (ct === 0) {
1608
+ lastConsonantIdx = syllable.length;
1609
+ syllable.push(cc);
1610
+ i++;
1611
+ if (i < cps.length && cps[i] === PULLI) {
1612
+ let j = i + 1;
1613
+ let zwnj = false;
1614
+ if (j < cps.length) {
1615
+ const jc = classifyUseCategory(cps[j]);
1616
+ if (jc === "ZWJ") {
1617
+ j++;
1618
+ } else if (jc === "ZWNJ") {
1619
+ j++;
1620
+ zwnj = true;
1621
+ }
1622
+ }
1623
+ if (!zwnj && j < cps.length && isConsonant2(cps[j])) {
1624
+ syllable.push(cps[i]);
1625
+ i = j;
1626
+ continue;
1627
+ } else {
1628
+ syllable.push(cps[i]);
1629
+ i = j;
1630
+ break;
1631
+ }
1632
+ }
1633
+ break;
1634
+ } else {
1635
+ break;
1636
+ }
1637
+ }
1638
+ const baseIdx = lastConsonantIdx >= 0 ? lastConsonantIdx : 0;
1639
+ while (i < cps.length) {
1640
+ const ct = tamilCharType(cps[i]);
1641
+ if (ct >= 2 && ct <= 5) {
1642
+ if (ct === 4) {
1643
+ preMatras.push(syllable.length);
1644
+ }
1645
+ syllable.push(cps[i]);
1646
+ i++;
1647
+ } else {
1648
+ break;
1649
+ }
1650
+ }
1651
+ while (i < cps.length && tamilCharType(cps[i]) === 6) {
1652
+ syllable.push(cps[i]);
1653
+ i++;
1654
+ }
1655
+ if (syllable.length === 0) {
1656
+ syllable.push(cps[i] ?? 32);
1657
+ i++;
1658
+ }
1659
+ clusters.push({
1660
+ codepoints: syllable,
1661
+ baseIndex: baseIdx,
1662
+ preBaseMatras: preMatras
1663
+ });
1664
+ }
1665
+ return clusters;
1666
+ }
1667
+ function shapeTamilText(str, fontData) {
1668
+ const { cmap, ligatures, markAnchors, widths, defaultWidth } = fontData;
1669
+ const shaped = [];
1670
+ function resolveGid(cp) {
1671
+ const normCp = cp === 8239 || cp === 160 ? 32 : cp;
1672
+ return cmap[normCp] || 0;
1673
+ }
1674
+ function tryLig(gids) {
1675
+ return tryLigature(gids, ligatures);
1676
+ }
1677
+ function getAdv(gid) {
1678
+ return widths[gid] !== void 0 ? widths[gid] : defaultWidth;
1679
+ }
1680
+ function getBaseAnchor2(baseGid, markClass) {
1681
+ const base = markAnchors && markAnchors.bases && markAnchors.bases[baseGid];
1682
+ if (!base) return null;
1683
+ return base[markClass] ?? null;
1684
+ }
1685
+ function getMarkAnchor2(markGid) {
1686
+ const mark = markAnchors && markAnchors.marks && markAnchors.marks[markGid];
1687
+ if (!mark) return null;
1688
+ return { classIdx: mark[0], x: mark[1], y: mark[2] };
1689
+ }
1690
+ function emitGlyph(gid, isZero, baseGid) {
1691
+ if (isZero && baseGid !== void 0) {
1692
+ const markAnchor = getMarkAnchor2(gid);
1693
+ if (markAnchor) {
1694
+ const baseAnchorPt = getBaseAnchor2(baseGid, markAnchor.classIdx);
1695
+ if (baseAnchorPt) {
1696
+ const baseAdv = getAdv(baseGid);
1697
+ shaped.push({
1698
+ gid,
1699
+ dx: baseAnchorPt[0] - markAnchor.x - baseAdv,
1700
+ dy: baseAnchorPt[1] - markAnchor.y,
1701
+ isZeroAdvance: true
1702
+ });
1703
+ return;
1704
+ }
1705
+ }
1706
+ shaped.push({ gid, dx: 0, dy: 0, isZeroAdvance: true });
1707
+ } else {
1708
+ shaped.push({ gid, dx: 0, dy: 0, isZeroAdvance: false });
1709
+ }
1710
+ }
1711
+ const clusters = buildTamilClusters(str);
1712
+ for (const cluster of clusters) {
1713
+ const { codepoints, preBaseMatras } = cluster;
1714
+ let baseGid = 0;
1715
+ for (let ci = 0; ci < codepoints.length; ci++) {
1716
+ const ct = tamilCharType(codepoints[ci]);
1717
+ if (ct === 0) {
1718
+ baseGid = resolveGid(codepoints[ci]);
1719
+ } else if (ct >= 2) {
1720
+ break;
1721
+ }
1722
+ }
1723
+ const splitPostComponents = [];
1724
+ for (const mIdx of preBaseMatras) {
1725
+ if (mIdx < codepoints.length) {
1726
+ const mCp = codepoints[mIdx];
1727
+ if (mCp === 3018) {
1728
+ emitGlyph(resolveGid(3014), false);
1729
+ splitPostComponents.push(3006);
1730
+ } else if (mCp === 3019) {
1731
+ emitGlyph(resolveGid(3015), false);
1732
+ splitPostComponents.push(3006);
1733
+ } else if (mCp === 3020) {
1734
+ emitGlyph(resolveGid(3014), false);
1735
+ splitPostComponents.push(3031);
1736
+ } else {
1737
+ emitGlyph(resolveGid(mCp), false);
1738
+ }
1739
+ }
1740
+ }
1741
+ const clusterGids = [];
1742
+ const clusterEndIdx = [];
1743
+ let matraStart = codepoints.length;
1744
+ for (let ci = 0; ci < codepoints.length; ci++) {
1745
+ const ct = tamilCharType(codepoints[ci]);
1746
+ if (ct === 0 || ct === 7) {
1747
+ clusterGids.push(resolveGid(codepoints[ci]));
1748
+ clusterEndIdx.push(ci);
1749
+ } else if (ct >= 2) {
1750
+ matraStart = ci;
1751
+ break;
1752
+ } else if (ct < 0) {
1753
+ emitGlyph(resolveGid(codepoints[ci]), false);
1754
+ } else if (ct === 1) {
1755
+ emitGlyph(resolveGid(codepoints[ci]), false);
1756
+ }
1757
+ }
1758
+ let ligConsumed = 0;
1759
+ const ligResult = tryLig(clusterGids);
1760
+ if (ligResult) {
1761
+ emitGlyph(ligResult.resultGid, false);
1762
+ baseGid = ligResult.resultGid;
1763
+ ligConsumed = ligResult.consumed;
1764
+ let gi = ligConsumed;
1765
+ while (gi < clusterGids.length) {
1766
+ const subSeq = clusterGids.slice(gi);
1767
+ const subLig = tryLig(subSeq);
1768
+ if (subLig) {
1769
+ emitGlyph(subLig.resultGid, false);
1770
+ gi += subLig.consumed;
1771
+ } else {
1772
+ const origCi = clusterEndIdx[gi];
1773
+ const ct = tamilCharType(codepoints[origCi]);
1774
+ if (ct === 7) {
1775
+ emitGlyph(clusterGids[gi], true, baseGid);
1776
+ } else {
1777
+ emitGlyph(clusterGids[gi], false);
1778
+ }
1779
+ gi++;
1780
+ }
1781
+ }
1782
+ } else {
1783
+ for (let ci = 0; ci < matraStart; ci++) {
1784
+ const cp = codepoints[ci];
1785
+ const ct = tamilCharType(cp);
1786
+ if (ct === 0) {
1787
+ emitGlyph(resolveGid(cp), false);
1788
+ } else if (ct === 7) {
1789
+ emitGlyph(resolveGid(cp), true, baseGid);
1790
+ }
1791
+ }
1792
+ }
1793
+ for (let ci = matraStart; ci < codepoints.length; ci++) {
1794
+ const cp = codepoints[ci];
1795
+ const ct = tamilCharType(cp);
1796
+ if (ct === 4) continue;
1797
+ if (ct === 2 || ct === 3) {
1798
+ emitGlyph(resolveGid(cp), true, baseGid);
1799
+ } else if (ct === 5) {
1800
+ emitGlyph(resolveGid(cp), false);
1801
+ } else if (ct === 6) {
1802
+ emitGlyph(resolveGid(cp), true, baseGid);
1803
+ } else if (ct === 9) {
1804
+ emitGlyph(resolveGid(cp), false);
1805
+ } else {
1806
+ emitGlyph(resolveGid(cp), false);
1807
+ }
1808
+ }
1809
+ for (const postCp of splitPostComponents) {
1810
+ emitGlyph(resolveGid(postCp), false);
1811
+ }
1812
+ }
1813
+ return shaped;
1814
+ }
1815
+
1816
+ // src/shaping/telugu-shaper.ts
1817
+ var VIRAMA = 3149;
1818
+ function teluguCharType(cp) {
1819
+ if (cp === VIRAMA) return 7;
1820
+ if (cp >= 3072 && cp <= 3076) return 6;
1821
+ if (cp >= 3077 && cp <= 3092) return 1;
1822
+ if (cp === 3133) return 1;
1823
+ if (cp >= 3093 && cp <= 3129) return 0;
1824
+ if (cp >= 3160 && cp <= 3162) return 0;
1825
+ if (cp === 3134) return 5;
1826
+ if (cp === 3135) return 2;
1827
+ if (cp === 3136) return 2;
1828
+ if (cp === 3137) return 5;
1829
+ if (cp === 3138) return 5;
1830
+ if (cp === 3139) return 2;
1831
+ if (cp === 3140) return 2;
1832
+ if (cp === 3142) return 2;
1833
+ if (cp === 3143) return 2;
1834
+ if (cp === 3144) return 2;
1835
+ if (cp === 3146) return 5;
1836
+ if (cp === 3147) return 5;
1837
+ if (cp === 3148) return 5;
1838
+ if (cp === 3170 || cp === 3171) return 3;
1839
+ if (cp === 3157 || cp === 3158) return 6;
1840
+ if (cp === 3168 || cp === 3169) return 1;
1841
+ if (cp >= 3174 && cp <= 3183) return 9;
1842
+ if (cp >= 3192 && cp <= 3199) return 9;
1843
+ return -1;
1844
+ }
1845
+ function isConsonant3(cp) {
1846
+ return teluguCharType(cp) === 0;
480
1847
  }
481
- function buildBengaliClusters(str) {
1848
+ function buildTeluguClusters(str) {
482
1849
  const clusters = [];
483
1850
  const cps = [];
484
1851
  for (let i2 = 0; i2 < str.length; ) {
@@ -489,41 +1856,285 @@ function buildBengaliClusters(str) {
489
1856
  let i = 0;
490
1857
  while (i < cps.length) {
491
1858
  const cp = cps[i];
492
- const type = bengaliCharType(cp);
493
- if (type < 0 || cp < BENGALI_START || cp > BENGALI_END) {
494
- clusters.push({ codepoints: [cp], baseIndex: 0, hasReph: false, preBaseMatras: [] });
1859
+ const type = teluguCharType(cp);
1860
+ if (classifyUseCategory(cp) === "ZWJ" || classifyUseCategory(cp) === "ZWNJ") {
1861
+ i++;
1862
+ continue;
1863
+ }
1864
+ if (type < 0 || cp < TELUGU_START || cp > TELUGU_END) {
1865
+ clusters.push({ codepoints: [cp], baseIndex: 0 });
1866
+ i++;
1867
+ continue;
1868
+ }
1869
+ const syllable = [];
1870
+ let lastConsonantIdx = -1;
1871
+ while (i < cps.length) {
1872
+ const cc = cps[i];
1873
+ const ct = teluguCharType(cc);
1874
+ if (ct === 0) {
1875
+ lastConsonantIdx = syllable.length;
1876
+ syllable.push(cc);
1877
+ i++;
1878
+ if (i < cps.length && cps[i] === VIRAMA) {
1879
+ let j = i + 1;
1880
+ let zwnj = false;
1881
+ if (j < cps.length) {
1882
+ const jc = classifyUseCategory(cps[j]);
1883
+ if (jc === "ZWJ") {
1884
+ j++;
1885
+ } else if (jc === "ZWNJ") {
1886
+ j++;
1887
+ zwnj = true;
1888
+ }
1889
+ }
1890
+ if (!zwnj && j < cps.length && isConsonant3(cps[j])) {
1891
+ syllable.push(cps[i]);
1892
+ i = j;
1893
+ continue;
1894
+ } else {
1895
+ syllable.push(cps[i]);
1896
+ i = j;
1897
+ break;
1898
+ }
1899
+ }
1900
+ break;
1901
+ } else {
1902
+ break;
1903
+ }
1904
+ }
1905
+ const baseIdx = lastConsonantIdx >= 0 ? lastConsonantIdx : 0;
1906
+ while (i < cps.length) {
1907
+ const ct = teluguCharType(cps[i]);
1908
+ if (ct >= 2 && ct <= 5) {
1909
+ syllable.push(cps[i]);
1910
+ i++;
1911
+ } else {
1912
+ break;
1913
+ }
1914
+ }
1915
+ while (i < cps.length && teluguCharType(cps[i]) === 6) {
1916
+ syllable.push(cps[i]);
1917
+ i++;
1918
+ }
1919
+ if (syllable.length === 0) {
1920
+ syllable.push(cps[i] ?? 32);
1921
+ i++;
1922
+ }
1923
+ clusters.push({ codepoints: syllable, baseIndex: baseIdx });
1924
+ }
1925
+ return clusters;
1926
+ }
1927
+ function shapeTeluguText(str, fontData) {
1928
+ const { cmap, ligatures, markAnchors, widths, defaultWidth } = fontData;
1929
+ const shaped = [];
1930
+ function resolveGid(cp) {
1931
+ const normCp = cp === 8239 || cp === 160 ? 32 : cp;
1932
+ return cmap[normCp] || 0;
1933
+ }
1934
+ function tryLig(gids) {
1935
+ return tryLigature(gids, ligatures);
1936
+ }
1937
+ function getAdv(gid) {
1938
+ return widths[gid] !== void 0 ? widths[gid] : defaultWidth;
1939
+ }
1940
+ function getBaseAnchor2(baseGid, markClass) {
1941
+ const base = markAnchors && markAnchors.bases && markAnchors.bases[baseGid];
1942
+ if (!base) return null;
1943
+ return base[markClass] ?? null;
1944
+ }
1945
+ function getMarkAnchor2(markGid) {
1946
+ const mark = markAnchors && markAnchors.marks && markAnchors.marks[markGid];
1947
+ if (!mark) return null;
1948
+ return { classIdx: mark[0], x: mark[1], y: mark[2] };
1949
+ }
1950
+ function emitGlyph(gid, isZero, baseGid) {
1951
+ if (isZero && baseGid !== void 0) {
1952
+ const markAnchor = getMarkAnchor2(gid);
1953
+ if (markAnchor) {
1954
+ const baseAnchorPt = getBaseAnchor2(baseGid, markAnchor.classIdx);
1955
+ if (baseAnchorPt) {
1956
+ const baseAdv = getAdv(baseGid);
1957
+ shaped.push({
1958
+ gid,
1959
+ dx: baseAnchorPt[0] - markAnchor.x - baseAdv,
1960
+ dy: baseAnchorPt[1] - markAnchor.y,
1961
+ isZeroAdvance: true
1962
+ });
1963
+ return;
1964
+ }
1965
+ }
1966
+ shaped.push({ gid, dx: 0, dy: 0, isZeroAdvance: true });
1967
+ } else {
1968
+ shaped.push({ gid, dx: 0, dy: 0, isZeroAdvance: false });
1969
+ }
1970
+ }
1971
+ const clusters = buildTeluguClusters(str);
1972
+ for (const cluster of clusters) {
1973
+ const { codepoints } = cluster;
1974
+ let baseGid = 0;
1975
+ for (let ci = 0; ci < codepoints.length; ci++) {
1976
+ const ct = teluguCharType(codepoints[ci]);
1977
+ if (ct === 0) {
1978
+ baseGid = resolveGid(codepoints[ci]);
1979
+ } else if (ct >= 2) {
1980
+ break;
1981
+ }
1982
+ }
1983
+ const clusterGids = [];
1984
+ const clusterEndIdx = [];
1985
+ let matraStart = codepoints.length;
1986
+ for (let ci = 0; ci < codepoints.length; ci++) {
1987
+ const ct = teluguCharType(codepoints[ci]);
1988
+ if (ct === 0 || ct === 7) {
1989
+ clusterGids.push(resolveGid(codepoints[ci]));
1990
+ clusterEndIdx.push(ci);
1991
+ } else if (ct >= 2) {
1992
+ matraStart = ci;
1993
+ break;
1994
+ } else if (ct < 0 || ct === 1 || ct === 9) {
1995
+ emitGlyph(resolveGid(codepoints[ci]), false);
1996
+ }
1997
+ }
1998
+ const ligResult = tryLig(clusterGids);
1999
+ if (ligResult) {
2000
+ emitGlyph(ligResult.resultGid, false);
2001
+ baseGid = ligResult.resultGid;
2002
+ let gi = ligResult.consumed;
2003
+ while (gi < clusterGids.length) {
2004
+ const subSeq = clusterGids.slice(gi);
2005
+ const subLig = tryLig(subSeq);
2006
+ if (subLig) {
2007
+ emitGlyph(subLig.resultGid, false);
2008
+ gi += subLig.consumed;
2009
+ } else {
2010
+ const origCi = clusterEndIdx[gi];
2011
+ const ct = teluguCharType(codepoints[origCi]);
2012
+ if (ct === 7) {
2013
+ emitGlyph(clusterGids[gi], true, baseGid);
2014
+ } else {
2015
+ emitGlyph(clusterGids[gi], false);
2016
+ }
2017
+ gi++;
2018
+ }
2019
+ }
2020
+ } else {
2021
+ for (let ci = 0; ci < matraStart; ci++) {
2022
+ const cp = codepoints[ci];
2023
+ const ct = teluguCharType(cp);
2024
+ if (ct === 0) {
2025
+ emitGlyph(resolveGid(cp), false);
2026
+ } else if (ct === 7) {
2027
+ emitGlyph(resolveGid(cp), true, baseGid);
2028
+ }
2029
+ }
2030
+ }
2031
+ for (let ci = matraStart; ci < codepoints.length; ci++) {
2032
+ const cp = codepoints[ci];
2033
+ const ct = teluguCharType(cp);
2034
+ if (ct === 2 || ct === 3) {
2035
+ emitGlyph(resolveGid(cp), true, baseGid);
2036
+ } else if (ct === 5) {
2037
+ emitGlyph(resolveGid(cp), false);
2038
+ } else if (ct === 6) {
2039
+ emitGlyph(resolveGid(cp), true, baseGid);
2040
+ } else if (ct === 9) {
2041
+ emitGlyph(resolveGid(cp), false);
2042
+ } else {
2043
+ emitGlyph(resolveGid(cp), false);
2044
+ }
2045
+ }
2046
+ }
2047
+ return shaped;
2048
+ }
2049
+
2050
+ // src/shaping/sinhala-shaper.ts
2051
+ var VIRAMA2 = SINHALA_VIRAMA;
2052
+ var TWO_PART_VOWELS = {
2053
+ 3546: [3545, 3530],
2054
+ // ේ (ee)
2055
+ 3548: [3545, 3535],
2056
+ // ො (o)
2057
+ 3549: [3545, 3535, 3530],
2058
+ // ෝ (oo)
2059
+ 3550: [3545, 3551]
2060
+ // ෞ (au)
2061
+ };
2062
+ function sinhalaCharType(cp) {
2063
+ if (cp === VIRAMA2) return 7;
2064
+ if (cp >= 3458 && cp <= 3460) return 6;
2065
+ if (cp >= 3461 && cp <= 3478) return 1;
2066
+ if (cp >= 3482 && cp <= 3526) return 0;
2067
+ if (cp === 3535) return 5;
2068
+ if (cp === 3536 || cp === 3537) return 5;
2069
+ if (cp === 3538 || cp === 3539) return 2;
2070
+ if (cp === 3540 || cp === 3542) return 3;
2071
+ if (cp === 3544) return 5;
2072
+ if (cp === 3545 || cp === 3547) return 4;
2073
+ if (cp === 3551) return 5;
2074
+ if (cp === 3570 || cp === 3571) return 5;
2075
+ if (cp >= 3558 && cp <= 3567) return 9;
2076
+ return -1;
2077
+ }
2078
+ function isConsonant4(cp) {
2079
+ return sinhalaCharType(cp) === 0;
2080
+ }
2081
+ function buildSinhalaClusters(str) {
2082
+ const clusters = [];
2083
+ const cps = [];
2084
+ for (let i2 = 0; i2 < str.length; ) {
2085
+ const cp = str.codePointAt(i2) ?? 0;
2086
+ const decomp = TWO_PART_VOWELS[cp];
2087
+ if (decomp) {
2088
+ for (const d of decomp) cps.push(d);
2089
+ } else {
2090
+ cps.push(cp);
2091
+ }
2092
+ i2 += cp > 65535 ? 2 : 1;
2093
+ }
2094
+ let i = 0;
2095
+ while (i < cps.length) {
2096
+ const cp = cps[i];
2097
+ const type = sinhalaCharType(cp);
2098
+ if (classifyUseCategory(cp) === "ZWNJ") {
2099
+ i++;
2100
+ continue;
2101
+ }
2102
+ if (type < 0 || cp < SINHALA_START || cp > SINHALA_END) {
2103
+ clusters.push({ codepoints: [cp], baseIndex: 0 });
495
2104
  i++;
496
2105
  continue;
497
2106
  }
498
2107
  const syllable = [];
499
- let baseIdx = 0;
500
- let hasReph = false;
501
- const preMatras = [];
502
- if (isConsonant(cp) && cp === RA && i + 2 < cps.length && cps[i + 1] === HALANT && isConsonant(cps[i + 2])) {
503
- hasReph = true;
504
- syllable.push(cp, cps[i + 1]);
505
- i += 2;
506
- }
507
2108
  let lastConsonantIdx = -1;
508
2109
  while (i < cps.length) {
509
2110
  const cc = cps[i];
510
- const ct = bengaliCharType(cc);
2111
+ const ct = sinhalaCharType(cc);
511
2112
  if (ct === 0) {
512
2113
  lastConsonantIdx = syllable.length;
513
2114
  syllable.push(cc);
514
2115
  i++;
515
- if (i < cps.length && cps[i] === NUKTA) {
516
- syllable.push(cps[i]);
517
- i++;
518
- }
519
- if (i < cps.length && cps[i] === HALANT) {
520
- if (i + 1 < cps.length && isConsonant(cps[i + 1])) {
2116
+ if (i < cps.length && cps[i] === VIRAMA2) {
2117
+ let j = i + 1;
2118
+ let zwnj = false;
2119
+ let zwj = false;
2120
+ if (j < cps.length) {
2121
+ const jc = classifyUseCategory(cps[j]);
2122
+ if (jc === "ZWJ") {
2123
+ j++;
2124
+ zwj = true;
2125
+ } else if (jc === "ZWNJ") {
2126
+ j++;
2127
+ zwnj = true;
2128
+ }
2129
+ }
2130
+ if (!zwnj && j < cps.length && isConsonant4(cps[j])) {
521
2131
  syllable.push(cps[i]);
522
- i++;
2132
+ if (zwj) syllable.push(8205);
2133
+ i = j;
523
2134
  continue;
524
2135
  } else {
525
2136
  syllable.push(cps[i]);
526
- i++;
2137
+ i = j;
527
2138
  break;
528
2139
  }
529
2140
  }
@@ -532,20 +2143,17 @@ function buildBengaliClusters(str) {
532
2143
  break;
533
2144
  }
534
2145
  }
535
- baseIdx = lastConsonantIdx >= 0 ? lastConsonantIdx : 0;
2146
+ const baseIdx = lastConsonantIdx >= 0 ? lastConsonantIdx : 0;
536
2147
  while (i < cps.length) {
537
- const ct = bengaliCharType(cps[i]);
2148
+ const ct = sinhalaCharType(cps[i]);
538
2149
  if (ct >= 2 && ct <= 5) {
539
- if (ct === 4) {
540
- preMatras.push(syllable.length);
541
- }
542
2150
  syllable.push(cps[i]);
543
2151
  i++;
544
2152
  } else {
545
2153
  break;
546
2154
  }
547
2155
  }
548
- while (i < cps.length && bengaliCharType(cps[i]) === 6) {
2156
+ while (i < cps.length && sinhalaCharType(cps[i]) === 6) {
549
2157
  syllable.push(cps[i]);
550
2158
  i++;
551
2159
  }
@@ -553,28 +2161,375 @@ function buildBengaliClusters(str) {
553
2161
  syllable.push(cps[i] ?? 32);
554
2162
  i++;
555
2163
  }
556
- clusters.push({
557
- codepoints: syllable,
558
- baseIndex: hasReph ? baseIdx + 2 : baseIdx,
559
- // Adjust for reph prefix
560
- hasReph,
561
- preBaseMatras: preMatras
562
- });
2164
+ clusters.push({ codepoints: syllable, baseIndex: baseIdx });
563
2165
  }
564
2166
  return clusters;
565
2167
  }
566
- function shapeBengaliText(str, fontData) {
567
- const { cmap, gsub, ligatures, markAnchors, widths, defaultWidth } = fontData;
2168
+ function shapeSinhalaText(str, fontData) {
2169
+ const { cmap, ligatures, markAnchors, widths, defaultWidth } = fontData;
2170
+ const shaped = [];
2171
+ function resolveGid(cp) {
2172
+ const normCp = cp === 8239 || cp === 160 ? 32 : cp;
2173
+ return cmap[normCp] || 0;
2174
+ }
2175
+ function tryLig(gids) {
2176
+ return tryLigature(gids, ligatures);
2177
+ }
2178
+ function getAdv(gid) {
2179
+ return widths[gid] !== void 0 ? widths[gid] : defaultWidth;
2180
+ }
2181
+ function getBaseAnchor2(baseGid, markClass) {
2182
+ const base = markAnchors && markAnchors.bases && markAnchors.bases[baseGid];
2183
+ if (!base) return null;
2184
+ return base[markClass] ?? null;
2185
+ }
2186
+ function getMarkAnchor2(markGid) {
2187
+ const mark = markAnchors && markAnchors.marks && markAnchors.marks[markGid];
2188
+ if (!mark) return null;
2189
+ return { classIdx: mark[0], x: mark[1], y: mark[2] };
2190
+ }
2191
+ function emitGlyph(gid, isZero, baseGid) {
2192
+ if (isZero && baseGid !== void 0) {
2193
+ const markAnchor = getMarkAnchor2(gid);
2194
+ if (markAnchor) {
2195
+ const baseAnchorPt = getBaseAnchor2(baseGid, markAnchor.classIdx);
2196
+ if (baseAnchorPt) {
2197
+ const baseAdv = getAdv(baseGid);
2198
+ shaped.push({
2199
+ gid,
2200
+ dx: baseAnchorPt[0] - markAnchor.x - baseAdv,
2201
+ dy: baseAnchorPt[1] - markAnchor.y,
2202
+ isZeroAdvance: true
2203
+ });
2204
+ return;
2205
+ }
2206
+ }
2207
+ shaped.push({ gid, dx: 0, dy: 0, isZeroAdvance: true });
2208
+ } else {
2209
+ shaped.push({ gid, dx: 0, dy: 0, isZeroAdvance: false });
2210
+ }
2211
+ }
2212
+ const clusters = buildSinhalaClusters(str);
2213
+ for (const cluster of clusters) {
2214
+ const { codepoints } = cluster;
2215
+ let baseGid = 0;
2216
+ for (let ci = 0; ci < codepoints.length; ci++) {
2217
+ const ct = sinhalaCharType(codepoints[ci]);
2218
+ if (ct === 0) {
2219
+ baseGid = resolveGid(codepoints[ci]);
2220
+ } else if (ct >= 2 && ct !== 7) {
2221
+ break;
2222
+ }
2223
+ }
2224
+ for (let ci = 0; ci < codepoints.length; ci++) {
2225
+ if (sinhalaCharType(codepoints[ci]) === 4) {
2226
+ emitGlyph(resolveGid(codepoints[ci]), false);
2227
+ }
2228
+ }
2229
+ const clusterGids = [];
2230
+ const clusterEndIdx = [];
2231
+ let matraStart = codepoints.length;
2232
+ for (let ci = 0; ci < codepoints.length; ci++) {
2233
+ const cp = codepoints[ci];
2234
+ const ct = sinhalaCharType(cp);
2235
+ if (ct === 0 || ct === 7 || cp === 8205) {
2236
+ clusterGids.push(resolveGid(cp));
2237
+ clusterEndIdx.push(ci);
2238
+ } else if (ct >= 2 && ct <= 6) {
2239
+ matraStart = ci;
2240
+ break;
2241
+ } else if (ct < 0 || ct === 1 || ct === 9) {
2242
+ emitGlyph(resolveGid(cp), false);
2243
+ }
2244
+ }
2245
+ const ligResult = tryLig(clusterGids);
2246
+ if (ligResult) {
2247
+ emitGlyph(ligResult.resultGid, false);
2248
+ baseGid = ligResult.resultGid;
2249
+ let gi = ligResult.consumed;
2250
+ while (gi < clusterGids.length) {
2251
+ const subSeq = clusterGids.slice(gi);
2252
+ const subLig = tryLig(subSeq);
2253
+ if (subLig) {
2254
+ emitGlyph(subLig.resultGid, false);
2255
+ gi += subLig.consumed;
2256
+ } else {
2257
+ const origCi = clusterEndIdx[gi];
2258
+ const ct = sinhalaCharType(codepoints[origCi]);
2259
+ if (ct === 7) emitGlyph(clusterGids[gi], true, baseGid);
2260
+ else emitGlyph(clusterGids[gi], false);
2261
+ gi++;
2262
+ }
2263
+ }
2264
+ } else {
2265
+ for (let ci = 0; ci < matraStart; ci++) {
2266
+ const cp = codepoints[ci];
2267
+ const ct = sinhalaCharType(cp);
2268
+ if (ct === 0) emitGlyph(resolveGid(cp), false);
2269
+ else if (ct === 7) emitGlyph(resolveGid(cp), true, baseGid);
2270
+ else ;
2271
+ }
2272
+ }
2273
+ for (let ci = matraStart; ci < codepoints.length; ci++) {
2274
+ const cp = codepoints[ci];
2275
+ const ct = sinhalaCharType(cp);
2276
+ if (ct === 2 || ct === 3 || ct === 6) emitGlyph(resolveGid(cp), true, baseGid);
2277
+ else if (ct === 5) emitGlyph(resolveGid(cp), false);
2278
+ else if (ct === 4) ; else emitGlyph(resolveGid(cp), false);
2279
+ }
2280
+ }
2281
+ return shaped;
2282
+ }
2283
+
2284
+ // src/shaping/tibetan-shaper.ts
2285
+ function tibetanCharType(cp) {
2286
+ if (cp >= 3984 && cp <= 4028) return 8;
2287
+ if (cp === 3972) return 6;
2288
+ if (cp >= 3904 && cp <= 3948) return 0;
2289
+ if (cp === 3954 || cp === 3962 || cp === 3963 || cp === 3964 || cp === 3965 || cp === 3968 || cp === 3969 || cp === 3971 || cp === 3970 || cp === 3966) return 2;
2290
+ if (cp === 3953 || cp === 3956 || cp === 3955 || cp === 3957 || cp === 3864 || cp === 3865 || cp === 3893 || cp === 3895 || cp === 3897) return 3;
2291
+ if (cp === 3967) return 5;
2292
+ if (cp === 3840 || cp >= 3976 && cp <= 3980) return 1;
2293
+ if (cp >= 3872 && cp <= 3891) return 9;
2294
+ if (cp >= 3841 && cp <= 3863) return 9;
2295
+ if (cp >= 3898 && cp <= 3903) return 9;
2296
+ if (cp >= 4030 && cp <= 4047) return 9;
2297
+ if (cp === 3892 || cp === 3894 || cp === 3896) return 9;
2298
+ if (cp === 3973) return 9;
2299
+ return -1;
2300
+ }
2301
+ function buildTibetanStacks(str) {
2302
+ const stacks = [];
2303
+ const cps = [];
2304
+ for (let i2 = 0; i2 < str.length; ) {
2305
+ const cp = str.codePointAt(i2) ?? 0;
2306
+ cps.push(cp);
2307
+ i2 += cp > 65535 ? 2 : 1;
2308
+ }
2309
+ let i = 0;
2310
+ while (i < cps.length) {
2311
+ const cp = cps[i];
2312
+ const type = tibetanCharType(cp);
2313
+ if (type < 0 || cp < TIBETAN_START || cp > TIBETAN_END) {
2314
+ stacks.push({ codepoints: [cp] });
2315
+ i++;
2316
+ continue;
2317
+ }
2318
+ if (type !== 0 && type !== 1) {
2319
+ stacks.push({ codepoints: [cp] });
2320
+ i++;
2321
+ continue;
2322
+ }
2323
+ const stack = [cp];
2324
+ i++;
2325
+ while (i < cps.length && tibetanCharType(cps[i]) === 8) {
2326
+ stack.push(cps[i]);
2327
+ i++;
2328
+ }
2329
+ while (i < cps.length) {
2330
+ const ct = tibetanCharType(cps[i]);
2331
+ if (ct === 2 || ct === 3 || ct === 5 || ct === 6) {
2332
+ stack.push(cps[i]);
2333
+ i++;
2334
+ } else {
2335
+ break;
2336
+ }
2337
+ }
2338
+ stacks.push({ codepoints: stack });
2339
+ }
2340
+ return stacks;
2341
+ }
2342
+ function shapeTibetanText(str, fontData) {
2343
+ const { cmap, ligatures, markAnchors, widths, defaultWidth } = fontData;
2344
+ const shaped = [];
2345
+ function resolveGid(cp) {
2346
+ const normCp = cp === 8239 || cp === 160 ? 32 : cp;
2347
+ return cmap[normCp] || 0;
2348
+ }
2349
+ function tryLig(gids) {
2350
+ return tryLigature(gids, ligatures);
2351
+ }
2352
+ function getAdv(gid) {
2353
+ return widths[gid] !== void 0 ? widths[gid] : defaultWidth;
2354
+ }
2355
+ function getBaseAnchor2(baseGid, markClass) {
2356
+ const base = markAnchors && markAnchors.bases && markAnchors.bases[baseGid];
2357
+ if (!base) return null;
2358
+ return base[markClass] ?? null;
2359
+ }
2360
+ function getMarkAnchor2(markGid) {
2361
+ const mark = markAnchors && markAnchors.marks && markAnchors.marks[markGid];
2362
+ if (!mark) return null;
2363
+ return { classIdx: mark[0], x: mark[1], y: mark[2] };
2364
+ }
2365
+ function emitGlyph(gid, isZero, baseGid) {
2366
+ if (isZero && baseGid !== void 0) {
2367
+ const markAnchor = getMarkAnchor2(gid);
2368
+ if (markAnchor) {
2369
+ const baseAnchorPt = getBaseAnchor2(baseGid, markAnchor.classIdx);
2370
+ if (baseAnchorPt) {
2371
+ const baseAdv = getAdv(baseGid);
2372
+ shaped.push({
2373
+ gid,
2374
+ dx: baseAnchorPt[0] - markAnchor.x - baseAdv,
2375
+ dy: baseAnchorPt[1] - markAnchor.y,
2376
+ isZeroAdvance: true
2377
+ });
2378
+ return;
2379
+ }
2380
+ }
2381
+ shaped.push({ gid, dx: 0, dy: 0, isZeroAdvance: true });
2382
+ } else {
2383
+ shaped.push({ gid, dx: 0, dy: 0, isZeroAdvance: false });
2384
+ }
2385
+ }
2386
+ const stacks = buildTibetanStacks(str);
2387
+ for (const stack of stacks) {
2388
+ const { codepoints } = stack;
2389
+ const stackGids = [];
2390
+ const stackEndIdx = [];
2391
+ let markStart = codepoints.length;
2392
+ for (let ci = 0; ci < codepoints.length; ci++) {
2393
+ const ct = tibetanCharType(codepoints[ci]);
2394
+ if (ct === 0 || ct === 8 || ct === 1 || ct === 6) {
2395
+ stackGids.push(resolveGid(codepoints[ci]));
2396
+ stackEndIdx.push(ci);
2397
+ } else if (ct === 2 || ct === 3 || ct === 5) {
2398
+ markStart = ci;
2399
+ break;
2400
+ } else if (ct < 0 || ct === 9) {
2401
+ emitGlyph(resolveGid(codepoints[ci]), false);
2402
+ }
2403
+ }
2404
+ let baseGid = stackGids.length > 0 ? stackGids[0] : 0;
2405
+ const ligResult = tryLig(stackGids);
2406
+ if (ligResult) {
2407
+ emitGlyph(ligResult.resultGid, false);
2408
+ baseGid = ligResult.resultGid;
2409
+ let gi = ligResult.consumed;
2410
+ while (gi < stackGids.length) {
2411
+ const subSeq = stackGids.slice(gi);
2412
+ const subLig = tryLig(subSeq);
2413
+ if (subLig) {
2414
+ emitGlyph(subLig.resultGid, false);
2415
+ gi += subLig.consumed;
2416
+ } else {
2417
+ const origCi = stackEndIdx[gi];
2418
+ const ct = tibetanCharType(codepoints[origCi]);
2419
+ if (ct === 8 || ct === 6) emitGlyph(stackGids[gi], true, baseGid);
2420
+ else emitGlyph(stackGids[gi], false);
2421
+ gi++;
2422
+ }
2423
+ }
2424
+ } else {
2425
+ for (let gi = 0; gi < stackGids.length; gi++) {
2426
+ const origCi = stackEndIdx[gi];
2427
+ const ct = tibetanCharType(codepoints[origCi]);
2428
+ if (gi === 0) emitGlyph(stackGids[gi], false);
2429
+ else if (ct === 8 || ct === 6) emitGlyph(stackGids[gi], true, baseGid);
2430
+ else emitGlyph(stackGids[gi], false);
2431
+ }
2432
+ }
2433
+ for (let ci = markStart; ci < codepoints.length; ci++) {
2434
+ const cp = codepoints[ci];
2435
+ const ct = tibetanCharType(cp);
2436
+ if (ct === 2 || ct === 3) emitGlyph(resolveGid(cp), true, baseGid);
2437
+ else if (ct === 5) emitGlyph(resolveGid(cp), false);
2438
+ else emitGlyph(resolveGid(cp), false);
2439
+ }
2440
+ }
2441
+ return shaped;
2442
+ }
2443
+
2444
+ // src/shaping/khmer-shaper.ts
2445
+ var COENG = KHMER_COENG;
2446
+ var TWO_PART_VOWELS2 = {
2447
+ 6078: [6081, 6070],
2448
+ // ើ
2449
+ 6079: [6081, 6072],
2450
+ // ឿ (approx: e + y-like)
2451
+ 6080: [6081, 6073],
2452
+ // ៀ (approx)
2453
+ 6084: [6081, 6070],
2454
+ // ោ (e + aa)
2455
+ 6085: [6081, 6070]
2456
+ // ៅ (e + aa-like)
2457
+ };
2458
+ function khmerCharType(cp) {
2459
+ if (cp === COENG) return 7;
2460
+ if (cp >= 6016 && cp <= 6050) return 0;
2461
+ if (cp >= 6051 && cp <= 6069) return 1;
2462
+ if (cp === 6081 || cp === 6082 || cp === 6083) return 4;
2463
+ if (cp === 6071 || cp === 6072 || cp === 6073 || cp === 6074 || cp === 6075 || cp === 6077) return 2;
2464
+ if (cp === 6076) return 3;
2465
+ if (cp === 6070 || cp === 6084 || cp === 6085) return 5;
2466
+ if (cp >= 6086 && cp <= 6097) return 6;
2467
+ if (cp === 6091 || cp === 6093 || cp === 6094 || cp === 6095 || cp === 6096) return 6;
2468
+ if (cp === 6109) return 6;
2469
+ if (cp >= 6112 && cp <= 6121) return 9;
2470
+ if (cp >= 6100 && cp <= 6108) return 9;
2471
+ if (cp >= 6128 && cp <= 6137) return 9;
2472
+ return -1;
2473
+ }
2474
+ function isConsonant5(cp) {
2475
+ return khmerCharType(cp) === 0;
2476
+ }
2477
+ function buildKhmerClusters(str) {
2478
+ const clusters = [];
2479
+ const cps = [];
2480
+ for (let i2 = 0; i2 < str.length; ) {
2481
+ const cp = str.codePointAt(i2) ?? 0;
2482
+ const decomp = TWO_PART_VOWELS2[cp];
2483
+ if (decomp) {
2484
+ for (const d of decomp) cps.push(d);
2485
+ } else cps.push(cp);
2486
+ i2 += cp > 65535 ? 2 : 1;
2487
+ }
2488
+ let i = 0;
2489
+ while (i < cps.length) {
2490
+ const cp = cps[i];
2491
+ const type = khmerCharType(cp);
2492
+ if (type < 0 || cp < KHMER_START || cp > KHMER_END) {
2493
+ clusters.push({ codepoints: [cp] });
2494
+ i++;
2495
+ continue;
2496
+ }
2497
+ if (type !== 0 && type !== 1) {
2498
+ clusters.push({ codepoints: [cp] });
2499
+ i++;
2500
+ continue;
2501
+ }
2502
+ const cluster = [cp];
2503
+ i++;
2504
+ while (i < cps.length && cps[i] === COENG) {
2505
+ if (i + 1 < cps.length && isConsonant5(cps[i + 1])) {
2506
+ cluster.push(cps[i]);
2507
+ cluster.push(cps[i + 1]);
2508
+ i += 2;
2509
+ } else {
2510
+ cluster.push(cps[i]);
2511
+ i++;
2512
+ break;
2513
+ }
2514
+ }
2515
+ while (i < cps.length) {
2516
+ const ct = khmerCharType(cps[i]);
2517
+ if (ct >= 2 && ct <= 6) {
2518
+ cluster.push(cps[i]);
2519
+ i++;
2520
+ } else break;
2521
+ }
2522
+ clusters.push({ codepoints: cluster });
2523
+ }
2524
+ return clusters;
2525
+ }
2526
+ function shapeKhmerText(str, fontData) {
2527
+ const { cmap, ligatures, markAnchors, widths, defaultWidth } = fontData;
568
2528
  const shaped = [];
569
2529
  function resolveGid(cp) {
570
2530
  const normCp = cp === 8239 || cp === 160 ? 32 : cp;
571
2531
  return cmap[normCp] || 0;
572
2532
  }
573
- function resolveGidGsub(cp) {
574
- const gid = resolveGid(cp);
575
- if (gsub[gid] !== void 0) return gsub[gid];
576
- return gid;
577
- }
578
2533
  function tryLig(gids) {
579
2534
  return tryLigature(gids, ligatures);
580
2535
  }
@@ -612,143 +2567,100 @@ function shapeBengaliText(str, fontData) {
612
2567
  shaped.push({ gid, dx: 0, dy: 0, isZeroAdvance: false });
613
2568
  }
614
2569
  }
615
- const clusters = buildBengaliClusters(str);
2570
+ const clusters = buildKhmerClusters(str);
616
2571
  for (const cluster of clusters) {
617
- const { codepoints, hasReph, preBaseMatras } = cluster;
618
- const baseStart = hasReph ? 2 : 0;
2572
+ const { codepoints } = cluster;
619
2573
  let baseGid = 0;
620
- for (let ci = baseStart; ci < codepoints.length; ci++) {
621
- const ct = bengaliCharType(codepoints[ci]);
622
- if (ct === 0) {
2574
+ for (let ci = 0; ci < codepoints.length; ci++) {
2575
+ const ct = khmerCharType(codepoints[ci]);
2576
+ if (ct === 0 || ct === 1) {
623
2577
  baseGid = resolveGid(codepoints[ci]);
624
- } else if (ct >= 2) {
625
2578
  break;
626
2579
  }
627
2580
  }
628
- const splitPostComponents = [];
629
- for (const mIdx of preBaseMatras) {
630
- if (mIdx < codepoints.length) {
631
- const mCp = codepoints[mIdx];
632
- if (mCp === 2507) {
633
- emitGlyph(resolveGid(2503), false);
634
- splitPostComponents.push(2494);
635
- } else if (mCp === 2508) {
636
- emitGlyph(resolveGid(2503), false);
637
- splitPostComponents.push(2519);
638
- } else {
639
- emitGlyph(resolveGid(mCp), false);
640
- }
641
- }
2581
+ for (let ci = 0; ci < codepoints.length; ci++) {
2582
+ if (khmerCharType(codepoints[ci]) === 4) emitGlyph(resolveGid(codepoints[ci]), false);
642
2583
  }
643
- const clusterGids = [];
644
- const clusterEndIdx = [];
645
- let matraStart = codepoints.length;
646
- for (let ci = baseStart; ci < codepoints.length; ci++) {
647
- const ct = bengaliCharType(codepoints[ci]);
648
- if (ct === 0 || ct === 7 || ct === 8) {
649
- clusterGids.push(resolveGid(codepoints[ci]));
650
- clusterEndIdx.push(ci);
651
- } else if (ct < 0 || ct === 1 || ct === 9) {
652
- emitGlyph(resolveGid(codepoints[ci]), false);
653
- } else {
654
- matraStart = ci;
2584
+ const stackGids = [];
2585
+ const stackEndIdx = [];
2586
+ let markStart = codepoints.length;
2587
+ for (let ci = 0; ci < codepoints.length; ci++) {
2588
+ const cp = codepoints[ci];
2589
+ const ct = khmerCharType(cp);
2590
+ if (ct === 0 || ct === 1 || ct === 7) {
2591
+ stackGids.push(resolveGid(cp));
2592
+ stackEndIdx.push(ci);
2593
+ } else if (ct >= 2 && ct <= 6) {
2594
+ markStart = ci;
655
2595
  break;
2596
+ } else {
2597
+ emitGlyph(resolveGid(cp), false);
656
2598
  }
657
2599
  }
658
- let ligConsumed = 0;
659
- const ligResult = tryLig(clusterGids);
2600
+ const ligResult = tryLig(stackGids);
660
2601
  if (ligResult) {
661
2602
  emitGlyph(ligResult.resultGid, false);
662
2603
  baseGid = ligResult.resultGid;
663
- ligConsumed = ligResult.consumed;
664
- let gi = ligConsumed;
665
- while (gi < clusterGids.length) {
666
- const subSeq = clusterGids.slice(gi);
667
- const subLig = tryLig(subSeq);
2604
+ let gi = ligResult.consumed;
2605
+ while (gi < stackGids.length) {
2606
+ const subLig = tryLig(stackGids.slice(gi));
668
2607
  if (subLig) {
669
2608
  emitGlyph(subLig.resultGid, false);
670
2609
  gi += subLig.consumed;
671
2610
  } else {
672
- const origCi = clusterEndIdx[gi];
673
- const ct = bengaliCharType(codepoints[origCi]);
674
- if (ct === 7) {
675
- emitGlyph(clusterGids[gi], true, baseGid);
676
- } else {
677
- emitGlyph(clusterGids[gi], false);
678
- }
2611
+ const origCi = stackEndIdx[gi];
2612
+ const ct = khmerCharType(codepoints[origCi]);
2613
+ if (ct === 7) emitGlyph(stackGids[gi], true, baseGid);
2614
+ else emitGlyph(stackGids[gi], false);
679
2615
  gi++;
680
2616
  }
681
2617
  }
682
2618
  } else {
683
- for (let ci = baseStart; ci < matraStart; ci++) {
684
- const cp = codepoints[ci];
685
- const ct = bengaliCharType(cp);
686
- if (ct === 0) {
687
- emitGlyph(resolveGid(cp), false);
688
- } else if (ct === 7) {
689
- emitGlyph(resolveGid(cp), true, baseGid);
690
- } else if (ct === 8) {
691
- emitGlyph(resolveGid(cp), true, baseGid);
692
- }
2619
+ for (let gi = 0; gi < stackGids.length; gi++) {
2620
+ const origCi = stackEndIdx[gi];
2621
+ const ct = khmerCharType(codepoints[origCi]);
2622
+ if (gi === 0) emitGlyph(stackGids[gi], false);
2623
+ else if (ct === 7) emitGlyph(stackGids[gi], true, baseGid);
2624
+ else emitGlyph(stackGids[gi], true, baseGid);
693
2625
  }
694
2626
  }
695
- for (let ci = matraStart; ci < codepoints.length; ci++) {
2627
+ for (let ci = markStart; ci < codepoints.length; ci++) {
696
2628
  const cp = codepoints[ci];
697
- const ct = bengaliCharType(cp);
698
- if (ct === 4) continue;
699
- if (ct === 2 || ct === 3) {
700
- emitGlyph(resolveGid(cp), true, baseGid);
701
- } else if (ct === 5) {
702
- emitGlyph(resolveGid(cp), false);
703
- } else if (ct === 6) {
704
- emitGlyph(resolveGid(cp), true, baseGid);
705
- } else if (ct === 9) {
706
- emitGlyph(resolveGid(cp), false);
707
- } else {
708
- emitGlyph(resolveGid(cp), false);
709
- }
710
- }
711
- for (const postCp of splitPostComponents) {
712
- emitGlyph(resolveGid(postCp), false);
713
- }
714
- if (hasReph) {
715
- const raGid = resolveGidGsub(RA);
716
- const halantGid = resolveGid(HALANT);
717
- emitGlyph(raGid, true, baseGid);
718
- emitGlyph(halantGid, true, baseGid);
2629
+ const ct = khmerCharType(cp);
2630
+ if (ct === 2 || ct === 3 || ct === 6) emitGlyph(resolveGid(cp), true, baseGid);
2631
+ else if (ct === 5) emitGlyph(resolveGid(cp), false);
2632
+ else if (ct === 4) ; else emitGlyph(resolveGid(cp), false);
719
2633
  }
720
2634
  }
721
2635
  return shaped;
722
2636
  }
723
2637
 
724
- // src/shaping/tamil-shaper.ts
725
- var PULLI = 3021;
726
- function tamilCharType(cp) {
727
- if (cp === PULLI) return 7;
728
- if (cp === 2946 || cp === 2947) return 6;
729
- if (cp >= 2949 && cp <= 2964) return 1;
730
- if (cp >= 2965 && cp <= 3001) return 0;
731
- if (cp === 3007) return 4;
732
- if (cp === 3014) return 4;
733
- if (cp === 3015) return 4;
734
- if (cp === 3016) return 4;
735
- if (cp === 3018) return 4;
736
- if (cp === 3019) return 4;
737
- if (cp === 3020) return 4;
738
- if (cp === 3006) return 5;
739
- if (cp === 3008) return 5;
740
- if (cp === 3009 || cp === 3010) return 2;
741
- if (cp === 3031) return 5;
742
- if (cp >= 3006 && cp <= 3020) return 2;
743
- if (cp >= 3046 && cp <= 3055) return 9;
744
- if (cp >= 3056 && cp <= 3066) return 9;
745
- if (cp === 3024) return 6;
2638
+ // src/shaping/myanmar-shaper.ts
2639
+ var VIRAMA3 = MYANMAR_VIRAMA;
2640
+ var MEDIAL_RA = 4156;
2641
+ function myanmarCharType(cp) {
2642
+ if (cp === VIRAMA3) return 7;
2643
+ if (cp === MEDIAL_RA) return 4;
2644
+ if (cp === 4155 || cp === 4157 || cp === 4158) return 8;
2645
+ if (cp >= 4096 && cp <= 4138) return 0;
2646
+ if (cp === 4159) return 0;
2647
+ if (cp >= 4176 && cp <= 4181) return 0;
2648
+ if (cp === 4141 || cp === 4142 || cp === 4146 || cp === 4150 || cp === 4147 || cp === 4148 || cp === 4149) return 2;
2649
+ if (cp === 4143 || cp === 4144 || cp === 4151) return 3;
2650
+ if (cp === 4139 || cp === 4140) return 5;
2651
+ if (cp === 4145) return 4;
2652
+ if (cp === 4154) return 6;
2653
+ if (cp === 4152) return 5;
2654
+ if (cp >= 4160 && cp <= 4169) return 9;
2655
+ if (cp >= 4240 && cp <= 4249) return 9;
2656
+ if (cp === 4170 || cp === 4171) return 9;
2657
+ if (cp >= 4172 && cp <= 4175) return 9;
746
2658
  return -1;
747
2659
  }
748
- function isConsonant2(cp) {
749
- return tamilCharType(cp) === 0;
2660
+ function isConsonant6(cp) {
2661
+ return myanmarCharType(cp) === 0;
750
2662
  }
751
- function buildTamilClusters(str) {
2663
+ function buildMyanmarClusters(str) {
752
2664
  const clusters = [];
753
2665
  const cps = [];
754
2666
  for (let i2 = 0; i2 < str.length; ) {
@@ -759,68 +2671,46 @@ function buildTamilClusters(str) {
759
2671
  let i = 0;
760
2672
  while (i < cps.length) {
761
2673
  const cp = cps[i];
762
- const type = tamilCharType(cp);
763
- if (type < 0 || cp < TAMIL_START || cp > TAMIL_END) {
764
- clusters.push({ codepoints: [cp], baseIndex: 0, preBaseMatras: [] });
2674
+ const type = myanmarCharType(cp);
2675
+ if (type < 0 || cp < MYANMAR_START || cp > MYANMAR_END) {
2676
+ clusters.push({ codepoints: [cp] });
765
2677
  i++;
766
2678
  continue;
767
2679
  }
768
- const syllable = [];
769
- let lastConsonantIdx = -1;
770
- const preMatras = [];
771
- while (i < cps.length) {
772
- const cc = cps[i];
773
- const ct = tamilCharType(cc);
774
- if (ct === 0) {
775
- lastConsonantIdx = syllable.length;
776
- syllable.push(cc);
777
- i++;
778
- if (i < cps.length && cps[i] === PULLI) {
779
- if (i + 1 < cps.length && isConsonant2(cps[i + 1])) {
780
- syllable.push(cps[i]);
781
- i++;
782
- continue;
783
- } else {
784
- syllable.push(cps[i]);
785
- i++;
786
- break;
787
- }
788
- }
789
- break;
790
- } else {
791
- break;
792
- }
2680
+ if (type !== 0) {
2681
+ clusters.push({ codepoints: [cp] });
2682
+ i++;
2683
+ continue;
793
2684
  }
794
- const baseIdx = lastConsonantIdx >= 0 ? lastConsonantIdx : 0;
795
- while (i < cps.length) {
796
- const ct = tamilCharType(cps[i]);
797
- if (ct >= 2 && ct <= 5) {
798
- if (ct === 4) {
799
- preMatras.push(syllable.length);
800
- }
801
- syllable.push(cps[i]);
802
- i++;
2685
+ const cluster = [cp];
2686
+ i++;
2687
+ while (i < cps.length && cps[i] === VIRAMA3) {
2688
+ if (i + 1 < cps.length && isConsonant6(cps[i + 1])) {
2689
+ cluster.push(cps[i]);
2690
+ cluster.push(cps[i + 1]);
2691
+ i += 2;
803
2692
  } else {
2693
+ cluster.push(cps[i]);
2694
+ i++;
804
2695
  break;
805
2696
  }
806
2697
  }
807
- while (i < cps.length && tamilCharType(cps[i]) === 6) {
808
- syllable.push(cps[i]);
2698
+ while (i < cps.length && myanmarCharType(cps[i]) === 8) {
2699
+ cluster.push(cps[i]);
809
2700
  i++;
810
2701
  }
811
- if (syllable.length === 0) {
812
- syllable.push(cps[i] ?? 32);
813
- i++;
2702
+ while (i < cps.length) {
2703
+ const ct = myanmarCharType(cps[i]);
2704
+ if (ct >= 2 && ct <= 6) {
2705
+ cluster.push(cps[i]);
2706
+ i++;
2707
+ } else break;
814
2708
  }
815
- clusters.push({
816
- codepoints: syllable,
817
- baseIndex: baseIdx,
818
- preBaseMatras: preMatras
819
- });
2709
+ clusters.push({ codepoints: cluster });
820
2710
  }
821
2711
  return clusters;
822
2712
  }
823
- function shapeTamilText(str, fontData) {
2713
+ function shapeMyanmarText(str, fontData) {
824
2714
  const { cmap, ligatures, markAnchors, widths, defaultWidth } = fontData;
825
2715
  const shaped = [];
826
2716
  function resolveGid(cp) {
@@ -864,106 +2754,68 @@ function shapeTamilText(str, fontData) {
864
2754
  shaped.push({ gid, dx: 0, dy: 0, isZeroAdvance: false });
865
2755
  }
866
2756
  }
867
- const clusters = buildTamilClusters(str);
2757
+ const clusters = buildMyanmarClusters(str);
868
2758
  for (const cluster of clusters) {
869
- const { codepoints, preBaseMatras } = cluster;
2759
+ const { codepoints } = cluster;
870
2760
  let baseGid = 0;
871
2761
  for (let ci = 0; ci < codepoints.length; ci++) {
872
- const ct = tamilCharType(codepoints[ci]);
873
- if (ct === 0) {
2762
+ if (myanmarCharType(codepoints[ci]) === 0) {
874
2763
  baseGid = resolveGid(codepoints[ci]);
875
- } else if (ct >= 2) {
876
2764
  break;
877
2765
  }
878
2766
  }
879
- const splitPostComponents = [];
880
- for (const mIdx of preBaseMatras) {
881
- if (mIdx < codepoints.length) {
882
- const mCp = codepoints[mIdx];
883
- if (mCp === 3018) {
884
- emitGlyph(resolveGid(3014), false);
885
- splitPostComponents.push(3006);
886
- } else if (mCp === 3019) {
887
- emitGlyph(resolveGid(3015), false);
888
- splitPostComponents.push(3006);
889
- } else if (mCp === 3020) {
890
- emitGlyph(resolveGid(3014), false);
891
- splitPostComponents.push(3031);
892
- } else {
893
- emitGlyph(resolveGid(mCp), false);
894
- }
895
- }
896
- }
897
- const clusterGids = [];
898
- const clusterEndIdx = [];
899
- let matraStart = codepoints.length;
900
2767
  for (let ci = 0; ci < codepoints.length; ci++) {
901
- const ct = tamilCharType(codepoints[ci]);
902
- if (ct === 0 || ct === 7) {
903
- clusterGids.push(resolveGid(codepoints[ci]));
904
- clusterEndIdx.push(ci);
905
- } else if (ct >= 2) {
906
- matraStart = ci;
907
- break;
908
- } else if (ct < 0) {
909
- emitGlyph(resolveGid(codepoints[ci]), false);
910
- } else if (ct === 1) {
911
- emitGlyph(resolveGid(codepoints[ci]), false);
912
- }
913
- }
914
- let ligConsumed = 0;
915
- const ligResult = tryLig(clusterGids);
916
- if (ligResult) {
917
- emitGlyph(ligResult.resultGid, false);
918
- baseGid = ligResult.resultGid;
919
- ligConsumed = ligResult.consumed;
920
- let gi = ligConsumed;
921
- while (gi < clusterGids.length) {
922
- const subSeq = clusterGids.slice(gi);
923
- const subLig = tryLig(subSeq);
924
- if (subLig) {
925
- emitGlyph(subLig.resultGid, false);
926
- gi += subLig.consumed;
927
- } else {
928
- const origCi = clusterEndIdx[gi];
929
- const ct = tamilCharType(codepoints[origCi]);
930
- if (ct === 7) {
931
- emitGlyph(clusterGids[gi], true, baseGid);
932
- } else {
933
- emitGlyph(clusterGids[gi], false);
934
- }
935
- gi++;
936
- }
937
- }
938
- } else {
939
- for (let ci = 0; ci < matraStart; ci++) {
940
- const cp = codepoints[ci];
941
- const ct = tamilCharType(cp);
942
- if (ct === 0) {
943
- emitGlyph(resolveGid(cp), false);
944
- } else if (ct === 7) {
945
- emitGlyph(resolveGid(cp), true, baseGid);
946
- }
947
- }
2768
+ if (myanmarCharType(codepoints[ci]) === 4) emitGlyph(resolveGid(codepoints[ci]), false);
948
2769
  }
949
- for (let ci = matraStart; ci < codepoints.length; ci++) {
2770
+ const stackGids = [];
2771
+ const stackEndIdx = [];
2772
+ let markStart = codepoints.length;
2773
+ for (let ci = 0; ci < codepoints.length; ci++) {
950
2774
  const cp = codepoints[ci];
951
- const ct = tamilCharType(cp);
952
- if (ct === 4) continue;
953
- if (ct === 2 || ct === 3) {
954
- emitGlyph(resolveGid(cp), true, baseGid);
955
- } else if (ct === 5) {
956
- emitGlyph(resolveGid(cp), false);
957
- } else if (ct === 6) {
958
- emitGlyph(resolveGid(cp), true, baseGid);
959
- } else if (ct === 9) {
960
- emitGlyph(resolveGid(cp), false);
961
- } else {
2775
+ const ct = myanmarCharType(cp);
2776
+ if (ct === 0 || ct === 7 || ct === 8) {
2777
+ stackGids.push(resolveGid(cp));
2778
+ stackEndIdx.push(ci);
2779
+ } else if (ct === 2 || ct === 3 || ct === 5 || ct === 6) {
2780
+ markStart = ci;
2781
+ break;
2782
+ } else if (ct === 4) ; else {
962
2783
  emitGlyph(resolveGid(cp), false);
963
2784
  }
964
2785
  }
965
- for (const postCp of splitPostComponents) {
966
- emitGlyph(resolveGid(postCp), false);
2786
+ const ligResult = tryLig(stackGids);
2787
+ if (ligResult) {
2788
+ emitGlyph(ligResult.resultGid, false);
2789
+ baseGid = ligResult.resultGid;
2790
+ let gi = ligResult.consumed;
2791
+ while (gi < stackGids.length) {
2792
+ const subLig = tryLig(stackGids.slice(gi));
2793
+ if (subLig) {
2794
+ emitGlyph(subLig.resultGid, false);
2795
+ gi += subLig.consumed;
2796
+ } else {
2797
+ const origCi = stackEndIdx[gi];
2798
+ const ct = myanmarCharType(codepoints[origCi]);
2799
+ if (ct === 7 || ct === 8) emitGlyph(stackGids[gi], true, baseGid);
2800
+ else emitGlyph(stackGids[gi], false);
2801
+ gi++;
2802
+ }
2803
+ }
2804
+ } else {
2805
+ for (let gi = 0; gi < stackGids.length; gi++) {
2806
+ const origCi = stackEndIdx[gi];
2807
+ const ct = myanmarCharType(codepoints[origCi]);
2808
+ if (gi === 0) emitGlyph(stackGids[gi], false);
2809
+ else if (ct === 7 || ct === 8) emitGlyph(stackGids[gi], true, baseGid);
2810
+ else emitGlyph(stackGids[gi], false);
2811
+ }
2812
+ }
2813
+ for (let ci = markStart; ci < codepoints.length; ci++) {
2814
+ const cp = codepoints[ci];
2815
+ const ct = myanmarCharType(cp);
2816
+ if (ct === 2 || ct === 3 || ct === 6) emitGlyph(resolveGid(cp), true, baseGid);
2817
+ else if (ct === 5) emitGlyph(resolveGid(cp), false);
2818
+ else if (ct === 4) ; else emitGlyph(resolveGid(cp), false);
967
2819
  }
968
2820
  }
969
2821
  return shaped;
@@ -1015,7 +2867,7 @@ function devanagariCharType(cp) {
1015
2867
  if (cp === 2384) return 1;
1016
2868
  return -1;
1017
2869
  }
1018
- function isConsonant3(cp) {
2870
+ function isConsonant7(cp) {
1019
2871
  return devanagariCharType(cp) === 0;
1020
2872
  }
1021
2873
  function buildDevanagariClusters(str) {
@@ -1030,6 +2882,10 @@ function buildDevanagariClusters(str) {
1030
2882
  while (i < cps.length) {
1031
2883
  const cp = cps[i];
1032
2884
  const type = devanagariCharType(cp);
2885
+ if (classifyUseCategory(cp) === "ZWJ" || classifyUseCategory(cp) === "ZWNJ") {
2886
+ i++;
2887
+ continue;
2888
+ }
1033
2889
  if (type < 0 || cp < DEVANAGARI_START || cp > DEVANAGARI_END) {
1034
2890
  clusters.push({ codepoints: [cp], baseIndex: 0, hasReph: false, preBaseMatras: [] });
1035
2891
  i++;
@@ -1038,7 +2894,7 @@ function buildDevanagariClusters(str) {
1038
2894
  const syllable = [];
1039
2895
  let hasReph = false;
1040
2896
  const preMatras = [];
1041
- if (isConsonant3(cp) && cp === RA2 && i + 2 < cps.length && cps[i + 1] === HALANT2 && isConsonant3(cps[i + 2])) {
2897
+ if (isConsonant7(cp) && cp === RA2 && i + 2 < cps.length && cps[i + 1] === HALANT2 && isConsonant7(cps[i + 2])) {
1042
2898
  hasReph = true;
1043
2899
  syllable.push(cp, cps[i + 1]);
1044
2900
  i += 2;
@@ -1056,13 +2912,24 @@ function buildDevanagariClusters(str) {
1056
2912
  i++;
1057
2913
  }
1058
2914
  if (i < cps.length && cps[i] === HALANT2) {
1059
- if (i + 1 < cps.length && isConsonant3(cps[i + 1])) {
2915
+ let j = i + 1;
2916
+ let zwnj = false;
2917
+ if (j < cps.length) {
2918
+ const jc = classifyUseCategory(cps[j]);
2919
+ if (jc === "ZWJ") {
2920
+ j++;
2921
+ } else if (jc === "ZWNJ") {
2922
+ j++;
2923
+ zwnj = true;
2924
+ }
2925
+ }
2926
+ if (!zwnj && j < cps.length && isConsonant7(cps[j])) {
1060
2927
  syllable.push(cps[i]);
1061
- i++;
2928
+ i = j;
1062
2929
  continue;
1063
2930
  } else {
1064
2931
  syllable.push(cps[i]);
1065
- i++;
2932
+ i = j;
1066
2933
  break;
1067
2934
  }
1068
2935
  }
@@ -1310,533 +3177,514 @@ var ARABIC_PRES_FORMS = /* @__PURE__ */ new Map([
1310
3177
  [1609, { isol: 65263, fina: 65264 }],
1311
3178
  [1610, { isol: 65265, fina: 65266, init: 65267, medi: 65268 }]
1312
3179
  ]);
1313
- var LAM_ALEF_PRES = /* @__PURE__ */ new Map([
1314
- [1570, [65269, 65270]],
1315
- // LAM + ALEF WITH MADDA ABOVE
1316
- [1571, [65271, 65272]],
1317
- // LAM + ALEF WITH HAMZA ABOVE
1318
- [1573, [65273, 65274]],
1319
- // LAM + ALEF WITH HAMZA BELOW
1320
- [1575, [65275, 65276]]
1321
- // LAM + ALEF
1322
- ]);
1323
- function resolvePositionalForms(codePoints) {
1324
- const len = codePoints.length;
1325
- const forms = new Array(len).fill("isol");
1326
- const joining = codePoints.map(getJoiningType);
1327
- for (let i = 0; i < len; i++) {
1328
- if (joining[i] === "T" || joining[i] === "U") continue;
1329
- let prevJoin = "U";
1330
- for (let j = i - 1; j >= 0; j--) {
1331
- if (joining[j] !== "T") {
1332
- prevJoin = joining[j];
1333
- break;
1334
- }
1335
- }
1336
- let nextJoin = "U";
1337
- for (let j = i + 1; j < len; j++) {
1338
- if (joining[j] !== "T") {
1339
- nextJoin = joining[j];
1340
- break;
1341
- }
1342
- }
1343
- const joinsToPrev = prevJoin === "D" || prevJoin === "C";
1344
- const joinsToNext = (nextJoin === "D" || nextJoin === "R" || nextJoin === "C") && (joining[i] === "D" || joining[i] === "C");
1345
- if (joinsToPrev && joinsToNext) {
1346
- forms[i] = "medi";
1347
- } else if (joinsToPrev) {
1348
- forms[i] = "fina";
1349
- } else if (joinsToNext) {
1350
- forms[i] = "init";
1351
- } else {
1352
- forms[i] = "isol";
1353
- }
1354
- }
1355
- return forms;
1356
- }
1357
- var LAM = 1604;
1358
- var ALEF_VARIANTS = /* @__PURE__ */ new Set([1570, 1571, 1573, 1575]);
1359
- function isLamAlef(cp1, cp2) {
1360
- return cp1 === LAM && ALEF_VARIANTS.has(cp2);
1361
- }
1362
- function shapeArabicText(str, fontData) {
1363
- if (!str) return [];
1364
- const codePoints = [];
1365
- for (let i = 0; i < str.length; ) {
1366
- const cp = str.codePointAt(i) ?? 0;
1367
- codePoints.push(cp);
1368
- i += cp > 65535 ? 2 : 1;
1369
- }
1370
- const forms = resolvePositionalForms(codePoints);
1371
- const glyphs = [];
1372
- const cmap = fontData.cmap;
1373
- const widths = fontData.widths;
1374
- const defaultWidth = fontData.defaultWidth;
1375
- const markAnchors = fontData.markAnchors;
1376
- let lastBaseGid = 0;
1377
- for (let i = 0; i < codePoints.length; i++) {
1378
- const cp = codePoints[i];
1379
- if (i < codePoints.length - 1 && isLamAlef(cp, codePoints[i + 1])) {
1380
- const ligForms = LAM_ALEF_PRES.get(codePoints[i + 1]);
1381
- if (ligForms) {
1382
- const isFinal = forms[i] === "medi" || forms[i] === "fina";
1383
- const ligCP = isFinal ? ligForms[1] : ligForms[0];
1384
- const ligGid = cmap[ligCP];
1385
- if (ligGid) {
1386
- glyphs.push({ gid: ligGid, dx: 0, dy: 0, isZeroAdvance: false });
1387
- lastBaseGid = ligGid;
1388
- i++;
1389
- continue;
1390
- }
1391
- }
1392
- }
1393
- let gid = cmap[cp] ?? 0;
1394
- const presForm = ARABIC_PRES_FORMS.get(cp);
1395
- if (presForm) {
1396
- const form = forms[i];
1397
- let presCP;
1398
- if (form === "init") presCP = presForm.init;
1399
- else if (form === "medi") presCP = presForm.medi;
1400
- else if (form === "fina") presCP = presForm.fina;
1401
- else presCP = presForm.isol;
1402
- if (presCP) {
1403
- const presGid = cmap[presCP];
1404
- if (presGid) gid = presGid;
1405
- }
1406
- }
1407
- const joining = getJoiningType(cp);
1408
- const isZeroAdvance = joining === "T";
1409
- if (isZeroAdvance && lastBaseGid !== 0) {
1410
- const baseAdv = widths[lastBaseGid] !== void 0 ? widths[lastBaseGid] : defaultWidth;
1411
- const offset = positionMarkOnBase(markAnchors, gid, lastBaseGid, baseAdv);
1412
- if (offset) {
1413
- glyphs.push({ gid, dx: offset.dx, dy: offset.dy, isZeroAdvance: true });
1414
- continue;
1415
- }
1416
- }
1417
- glyphs.push({ gid, dx: 0, dy: 0, isZeroAdvance });
1418
- if (!isZeroAdvance) lastBaseGid = gid;
1419
- }
1420
- return glyphs;
1421
- }
1422
-
1423
- // src/shaping/bidi.ts
1424
- function classifyBidiType(cp) {
1425
- if (cp >= 768 && cp <= 879 || // Combining Diacritical Marks
1426
- cp >= 1425 && cp <= 1469 || // Hebrew marks
1427
- cp >= 1471 && cp <= 1471 || cp >= 1473 && cp <= 1474 || cp >= 1476 && cp <= 1477 || cp >= 1479 && cp <= 1479 || cp >= 1552 && cp <= 1562 || // Arabic marks
1428
- cp >= 1611 && cp <= 1631 || // Arabic harakat
1429
- cp >= 1648 && cp <= 1648 || // Arabic superscript alef
1430
- cp >= 1750 && cp <= 1756 || cp >= 1759 && cp <= 1764 || cp >= 1767 && cp <= 1768 || cp >= 1770 && cp <= 1773 || cp >= 65056 && cp <= 65071) return "NSM";
1431
- if (cp === 8203 || cp === 8204 || cp === 8205 || cp === 8206 || cp === 8207 || cp === 65279 || cp === 8294 || cp === 8295 || cp === 8296 || cp === 8297) return "BN";
1432
- if (cp >= 1632 && cp <= 1641) return "AN";
1433
- if (cp >= 1776 && cp <= 1785) return "AN";
1434
- if (cp >= 48 && cp <= 57) return "EN";
1435
- if (cp === 43 || cp === 45) return "ES";
1436
- if (cp === 35 || cp === 36 || cp === 37 || cp === 162 || cp === 163 || cp === 164 || cp === 165 || cp === 8364 || cp === 8377 || cp === 8378) return "ET";
1437
- if (cp === 44 || cp === 46 || cp === 47 || cp === 58 || cp === 160) return "CS";
1438
- if (cp === 32 || cp === 9 || cp === 10 || cp === 13 || cp === 12 || cp === 8192 || cp === 8202 || cp === 8232 || cp === 8233 || cp === 8239 || cp === 8287 || cp === 12288) return "WS";
1439
- if (cp >= 1536 && cp <= 1791) return "AL";
1440
- if (cp >= 1872 && cp <= 1919) return "AL";
1441
- if (cp >= 2208 && cp <= 2303) return "AL";
1442
- if (cp >= 64336 && cp <= 65023) return "AL";
1443
- if (cp >= 65136 && cp <= 65278) return "AL";
1444
- if (cp >= 1488 && cp <= 1514) return "R";
1445
- if (cp >= 1520 && cp <= 1524) return "R";
1446
- if (cp >= 1792 && cp <= 1871) return "R";
1447
- if (cp >= 1920 && cp <= 1983) return "R";
1448
- if (cp >= 64285 && cp <= 64335) return "R";
1449
- if (cp >= 65 && cp <= 90) return "L";
1450
- if (cp >= 97 && cp <= 122) return "L";
1451
- if (cp >= 192 && cp <= 591) return "L";
1452
- if (cp >= 880 && cp <= 1023) return "L";
1453
- if (cp >= 1024 && cp <= 1279) return "L";
1454
- if (cp >= 3584 && cp <= 3711) return "L";
1455
- if (cp >= 2304 && cp <= 2431) return "L";
1456
- if (cp >= 12352 && cp <= 12543) return "L";
1457
- if (cp >= 19968 && cp <= 40959) return "L";
1458
- if (cp >= 44032 && cp <= 55215) return "L";
1459
- if (cp >= 33 && cp <= 47) return "ON";
1460
- if (cp >= 58 && cp <= 64) return "ON";
1461
- if (cp >= 91 && cp <= 96) return "ON";
1462
- if (cp >= 123 && cp <= 126) return "ON";
1463
- if (cp >= 161 && cp <= 191) return "ON";
1464
- if (cp >= 8208 && cp <= 8231) return "ON";
1465
- if (cp >= 8240 && cp <= 8286) return "ON";
1466
- return "L";
1467
- }
1468
- function detectParagraphLevel(types) {
1469
- for (const t of types) {
1470
- if (t === "L") return 0;
1471
- if (t === "R" || t === "AL") return 1;
1472
- }
1473
- return 0;
1474
- }
1475
- function resolveWeakTypes(types, paraLevel) {
1476
- const len = types.length;
1477
- let prevType = paraLevel === 0 ? "L" : "R";
1478
- for (let i = 0; i < len; i++) {
1479
- if (types[i] === "BN") continue;
1480
- if (types[i] === "NSM") {
1481
- types[i] = prevType;
1482
- }
1483
- prevType = types[i];
1484
- }
1485
- let lastStrong = paraLevel === 0 ? "L" : "R";
1486
- for (let i = 0; i < len; i++) {
1487
- if (types[i] === "BN") continue;
1488
- if (types[i] === "R" || types[i] === "L" || types[i] === "AL") {
1489
- lastStrong = types[i];
1490
- } else if (types[i] === "EN" && lastStrong === "AL") {
1491
- types[i] = "AN";
1492
- }
1493
- }
1494
- for (let i = 0; i < len; i++) {
1495
- if (types[i] === "AL") types[i] = "R";
1496
- }
1497
- for (let i = 1; i < len - 1; i++) {
1498
- if (types[i] === "BN") continue;
1499
- if (types[i] === "ES" && types[i - 1] === "EN" && types[i + 1] === "EN") {
1500
- types[i] = "EN";
1501
- } else if (types[i] === "CS") {
1502
- if (types[i - 1] === "EN" && types[i + 1] === "EN") types[i] = "EN";
1503
- else if (types[i - 1] === "AN" && types[i + 1] === "AN") types[i] = "AN";
1504
- }
1505
- }
1506
- for (let i = 0; i < len; i++) {
1507
- if (types[i] === "ET") {
1508
- let found = false;
1509
- for (let j = i - 1; j >= 0; j--) {
1510
- if (types[j] === "EN") {
1511
- found = true;
1512
- break;
1513
- }
1514
- if (types[j] !== "ET" && types[j] !== "BN") break;
1515
- }
1516
- if (!found) {
1517
- for (let j = i + 1; j < len; j++) {
1518
- if (types[j] === "EN") {
1519
- found = true;
1520
- break;
1521
- }
1522
- if (types[j] !== "ET" && types[j] !== "BN") break;
1523
- }
1524
- }
1525
- if (found) types[i] = "EN";
1526
- }
1527
- }
1528
- for (let i = 0; i < len; i++) {
1529
- if (types[i] === "ES" || types[i] === "ET" || types[i] === "CS") {
1530
- types[i] = "ON";
1531
- }
1532
- }
1533
- lastStrong = paraLevel === 0 ? "L" : "R";
1534
- for (let i = 0; i < len; i++) {
1535
- if (types[i] === "BN") continue;
1536
- if (types[i] === "L" || types[i] === "R") {
1537
- lastStrong = types[i];
1538
- } else if (types[i] === "EN" && lastStrong === "L") {
1539
- types[i] = "L";
1540
- }
1541
- }
1542
- }
1543
- function resolveNeutralTypes(types, paraLevel) {
1544
- const len = types.length;
1545
- const paraDir = paraLevel === 0 ? "L" : "R";
3180
+ var LAM_ALEF_PRES = /* @__PURE__ */ new Map([
3181
+ [1570, [65269, 65270]],
3182
+ // LAM + ALEF WITH MADDA ABOVE
3183
+ [1571, [65271, 65272]],
3184
+ // LAM + ALEF WITH HAMZA ABOVE
3185
+ [1573, [65273, 65274]],
3186
+ // LAM + ALEF WITH HAMZA BELOW
3187
+ [1575, [65275, 65276]]
3188
+ // LAM + ALEF
3189
+ ]);
3190
+ function resolvePositionalForms(codePoints) {
3191
+ const len = codePoints.length;
3192
+ const forms = new Array(len).fill("isol");
3193
+ const joining = codePoints.map(getJoiningType);
1546
3194
  for (let i = 0; i < len; i++) {
1547
- if (types[i] !== "ON" && types[i] !== "WS" && types[i] !== "BN") continue;
1548
- const start = i;
1549
- while (i < len && (types[i] === "ON" || types[i] === "WS" || types[i] === "BN")) i++;
1550
- const end = i;
1551
- let prevStrong = paraDir;
1552
- for (let j = start - 1; j >= 0; j--) {
1553
- if (types[j] === "L" || types[j] === "R" || types[j] === "EN" || types[j] === "AN") {
1554
- prevStrong = types[j] === "EN" || types[j] === "AN" ? "R" : types[j];
3195
+ if (joining[i] === "T" || joining[i] === "U") continue;
3196
+ let prevJoin = "U";
3197
+ for (let j = i - 1; j >= 0; j--) {
3198
+ if (joining[j] !== "T") {
3199
+ prevJoin = joining[j];
1555
3200
  break;
1556
3201
  }
1557
3202
  }
1558
- let nextStrong = paraDir;
1559
- for (let j = end; j < len; j++) {
1560
- if (types[j] === "L" || types[j] === "R" || types[j] === "EN" || types[j] === "AN") {
1561
- nextStrong = types[j] === "EN" || types[j] === "AN" ? "R" : types[j];
3203
+ let nextJoin = "U";
3204
+ for (let j = i + 1; j < len; j++) {
3205
+ if (joining[j] !== "T") {
3206
+ nextJoin = joining[j];
1562
3207
  break;
1563
3208
  }
1564
3209
  }
1565
- const resolved = prevStrong === nextStrong ? prevStrong : paraDir;
1566
- for (let j = start; j < end; j++) {
1567
- types[j] = resolved;
1568
- }
1569
- }
1570
- }
1571
- function assignLevels(types, paraLevel) {
1572
- const levels = [];
1573
- for (const t of types) {
1574
- if (paraLevel === 0) {
1575
- levels.push(t === "R" || t === "AN" ? 1 : 0);
3210
+ const joinsToPrev = prevJoin === "D" || prevJoin === "C";
3211
+ const joinsToNext = (nextJoin === "D" || nextJoin === "R" || nextJoin === "C") && (joining[i] === "D" || joining[i] === "C");
3212
+ if (joinsToPrev && joinsToNext) {
3213
+ forms[i] = "medi";
3214
+ } else if (joinsToPrev) {
3215
+ forms[i] = "fina";
3216
+ } else if (joinsToNext) {
3217
+ forms[i] = "init";
1576
3218
  } else {
1577
- levels.push(t === "L" ? 2 : 1);
3219
+ forms[i] = "isol";
1578
3220
  }
1579
3221
  }
1580
- return levels;
3222
+ return forms;
1581
3223
  }
1582
- var SENTENCE_PUNCT = /* @__PURE__ */ new Set([
1583
- 46,
1584
- // .
1585
- 44,
1586
- // ,
1587
- 59,
1588
- // ;
1589
- 58,
1590
- // :
1591
- 33,
1592
- // !
1593
- 63
1594
- // ?
1595
- ]);
1596
- function fixPunctuationAffinity(types, codePoints, len) {
1597
- for (let i = 1; i < len; i++) {
1598
- if (types[i] === "R" && SENTENCE_PUNCT.has(codePoints[i])) {
1599
- let prevIdx = i - 1;
1600
- while (prevIdx >= 0 && (types[prevIdx] === "WS" || types[prevIdx] === "BN")) prevIdx--;
1601
- if (prevIdx >= 0 && types[prevIdx] === "L") {
1602
- types[i] = "L";
1603
- }
1604
- }
1605
- }
3224
+ var LAM = 1604;
3225
+ var ALEF_VARIANTS = /* @__PURE__ */ new Set([1570, 1571, 1573, 1575]);
3226
+ function isLamAlef(cp1, cp2) {
3227
+ return cp1 === LAM && ALEF_VARIANTS.has(cp2);
1606
3228
  }
1607
- function fixBracketPairing(types, codePoints, len) {
1608
- const OPEN_BRACKETS = {
1609
- 40: 41,
1610
- // ( )
1611
- 91: 93,
1612
- // [ → ]
1613
- 123: 125
1614
- // { → }
1615
- };
1616
- for (let i = 0; i < len; i++) {
1617
- const closer = OPEN_BRACKETS[codePoints[i]];
1618
- if (closer === void 0) continue;
1619
- let depth = 1;
1620
- let closeIdx = -1;
1621
- for (let j = i + 1; j < len; j++) {
1622
- if (codePoints[j] === codePoints[i]) depth++;
1623
- else if (codePoints[j] === closer) {
1624
- depth--;
1625
- if (depth === 0) {
1626
- closeIdx = j;
1627
- break;
3229
+ function shapeArabicText(str, fontData) {
3230
+ if (!str) return [];
3231
+ const codePoints = [];
3232
+ for (let i = 0; i < str.length; ) {
3233
+ const cp = str.codePointAt(i) ?? 0;
3234
+ codePoints.push(cp);
3235
+ i += cp > 65535 ? 2 : 1;
3236
+ }
3237
+ const forms = resolvePositionalForms(codePoints);
3238
+ const glyphs = [];
3239
+ const cmap = fontData.cmap;
3240
+ const widths = fontData.widths;
3241
+ const defaultWidth = fontData.defaultWidth;
3242
+ const markAnchors = fontData.markAnchors;
3243
+ let lastBaseGid = 0;
3244
+ for (let i = 0; i < codePoints.length; i++) {
3245
+ const cp = codePoints[i];
3246
+ if (i < codePoints.length - 1 && isLamAlef(cp, codePoints[i + 1])) {
3247
+ const ligForms = LAM_ALEF_PRES.get(codePoints[i + 1]);
3248
+ if (ligForms) {
3249
+ const isFinal = forms[i] === "medi" || forms[i] === "fina";
3250
+ const ligCP = isFinal ? ligForms[1] : ligForms[0];
3251
+ const ligGid = cmap[ligCP];
3252
+ if (ligGid) {
3253
+ glyphs.push({ gid: ligGid, dx: 0, dy: 0, isZeroAdvance: false });
3254
+ lastBaseGid = ligGid;
3255
+ i++;
3256
+ continue;
1628
3257
  }
1629
3258
  }
1630
3259
  }
1631
- if (closeIdx === -1) continue;
1632
- let hasL = false;
1633
- for (let j = i + 1; j < closeIdx; j++) {
1634
- if (types[j] === "L") {
1635
- hasL = true;
1636
- break;
3260
+ let gid = cmap[cp] ?? 0;
3261
+ const presForm = ARABIC_PRES_FORMS.get(cp);
3262
+ if (presForm) {
3263
+ const form = forms[i];
3264
+ let presCP;
3265
+ if (form === "init") presCP = presForm.init;
3266
+ else if (form === "medi") presCP = presForm.medi;
3267
+ else if (form === "fina") presCP = presForm.fina;
3268
+ else presCP = presForm.isol;
3269
+ if (presCP) {
3270
+ const presGid = cmap[presCP];
3271
+ if (presGid) gid = presGid;
1637
3272
  }
1638
3273
  }
1639
- if (hasL) {
1640
- types[i] = "L";
1641
- types[closeIdx] = "L";
1642
- }
1643
- }
1644
- }
1645
- function findOutermostIsolatePairs(codePoints) {
1646
- const pairs = [];
1647
- let i = 0;
1648
- while (i < codePoints.length) {
1649
- const cp = codePoints[i];
1650
- if (cp === 8294 || cp === 8295 || cp === 8296) {
1651
- let depth = 1;
1652
- let close = -1;
1653
- for (let j = i + 1; j < codePoints.length; j++) {
1654
- const cj = codePoints[j];
1655
- if (cj === 8294 || cj === 8295 || cj === 8296) depth++;
1656
- else if (cj === 8297) {
1657
- depth--;
1658
- if (depth === 0) {
1659
- close = j;
1660
- break;
1661
- }
1662
- }
1663
- }
1664
- if (close === -1) {
1665
- i++;
3274
+ const joining = getJoiningType(cp);
3275
+ const isZeroAdvance = joining === "T";
3276
+ if (isZeroAdvance && lastBaseGid !== 0) {
3277
+ const baseAdv = widths[lastBaseGid] !== void 0 ? widths[lastBaseGid] : defaultWidth;
3278
+ const offset = positionMarkOnBase(markAnchors, gid, lastBaseGid, baseAdv);
3279
+ if (offset) {
3280
+ glyphs.push({ gid, dx: offset.dx, dy: offset.dy, isZeroAdvance: true });
1666
3281
  continue;
1667
3282
  }
1668
- const kind = cp === 8294 ? "LRI" : cp === 8295 ? "RLI" : "FSI";
1669
- pairs.push({ open: i, close, kind });
1670
- i = close + 1;
1671
- } else {
1672
- i++;
1673
3283
  }
3284
+ glyphs.push({ gid, dx: 0, dy: 0, isZeroAdvance });
3285
+ if (!isZeroAdvance) lastBaseGid = gid;
1674
3286
  }
1675
- return pairs;
3287
+ return glyphs;
1676
3288
  }
1677
- function resolveBidiRuns(text) {
1678
- if (!text) return [];
1679
- const codePoints = [];
1680
- const cpToStr = [];
1681
- for (let i = 0; i < text.length; ) {
1682
- cpToStr.push(i);
1683
- const cp = text.codePointAt(i) ?? 0;
1684
- codePoints.push(cp);
1685
- i += cp > 65535 ? 2 : 1;
1686
- }
1687
- cpToStr.push(text.length);
1688
- const isolates = findOutermostIsolatePairs(codePoints);
1689
- if (isolates.length === 0) {
1690
- return resolveBidiCore(text, codePoints, cpToStr);
1691
- }
1692
- const insideIsolate = new Array(codePoints.length).fill(false);
1693
- for (const p of isolates) {
1694
- for (let k = p.open; k <= p.close; k++) insideIsolate[k] = true;
1695
- }
1696
- const outerTypes = codePoints.map((cp, idx) => insideIsolate[idx] ? "BN" : classifyBidiType(cp));
1697
- const parentLevel = detectParagraphLevel(outerTypes);
1698
- const out = [];
1699
- const emitSegment = (cpStart, cpEnd, forced) => {
1700
- if (cpStart >= cpEnd) return;
1701
- const segText = text.substring(cpToStr[cpStart], cpToStr[cpEnd]);
1702
- const segCps = codePoints.slice(cpStart, cpEnd);
1703
- const baseStrIdx = cpToStr[cpStart];
1704
- const segCpToStr = cpToStr.slice(cpStart, cpEnd + 1).map((x) => x - baseStrIdx);
1705
- const segRuns = forced === void 0 ? resolveBidiRuns(segText) : resolveBidiCore(segText, segCps, segCpToStr, forced);
1706
- for (const r of segRuns) {
1707
- out.push({ text: r.text, level: r.level, start: r.start + baseStrIdx });
1708
- }
1709
- };
1710
- let cursor = 0;
1711
- for (const pair of isolates) {
1712
- emitSegment(cursor, pair.open, parentLevel);
1713
- const innerStart = pair.open + 1;
1714
- const innerEnd = pair.close;
1715
- let innerLevel;
1716
- if (pair.kind === "LRI") innerLevel = 0;
1717
- else if (pair.kind === "RLI") innerLevel = 1;
1718
- else {
1719
- const innerTypes = codePoints.slice(innerStart, innerEnd).map(classifyBidiType);
1720
- innerLevel = detectParagraphLevel(innerTypes);
1721
- }
1722
- if (innerStart < innerEnd) {
1723
- const innerText = text.substring(cpToStr[innerStart], cpToStr[innerEnd]);
1724
- const innerRuns = resolveBidiRunsForced(innerText, innerLevel);
1725
- const baseStrIdx = cpToStr[innerStart];
1726
- for (const r of innerRuns) {
1727
- out.push({ text: r.text, level: r.level, start: r.start + baseStrIdx });
1728
- }
1729
- }
1730
- cursor = pair.close + 1;
3289
+
3290
+ // src/fonts/font-loader.ts
3291
+ var _fontBinaryCache = /* @__PURE__ */ new WeakMap();
3292
+ function getDecodedFontBytes(fontData) {
3293
+ const cached = _fontBinaryCache.get(fontData);
3294
+ if (cached) return cached;
3295
+ let bytes;
3296
+ if (typeof atob === "function") {
3297
+ const binaryStr = atob(fontData.ttfBase64);
3298
+ bytes = new Uint8Array(binaryStr.length);
3299
+ for (let i = 0; i < binaryStr.length; i++) bytes[i] = binaryStr.charCodeAt(i);
3300
+ } else {
3301
+ const buf = globalThis["Buffer"];
3302
+ bytes = buf.from(fontData.ttfBase64, "base64");
1731
3303
  }
1732
- emitSegment(cursor, codePoints.length, parentLevel);
1733
- return out;
3304
+ _fontBinaryCache.set(fontData, bytes);
3305
+ return bytes;
1734
3306
  }
1735
- function resolveBidiRunsForced(text, forcedLevel) {
1736
- if (!text) return [];
1737
- const codePoints = [];
1738
- const cpToStr = [];
1739
- for (let i = 0; i < text.length; ) {
1740
- cpToStr.push(i);
1741
- const cp = text.codePointAt(i) ?? 0;
1742
- codePoints.push(cp);
1743
- i += cp > 65535 ? 2 : 1;
1744
- }
1745
- cpToStr.push(text.length);
1746
- const isolates = findOutermostIsolatePairs(codePoints);
1747
- if (isolates.length === 0) {
1748
- return resolveBidiCore(text, codePoints, cpToStr, forcedLevel);
3307
+
3308
+ // src/fonts/glyf-outline.ts
3309
+ function readTableDirectory(view) {
3310
+ const numTables = view.getUint16(4);
3311
+ const tables = {};
3312
+ for (let i = 0; i < numTables; i++) {
3313
+ const rec = 12 + i * 16;
3314
+ const tag = String.fromCharCode(
3315
+ view.getUint8(rec),
3316
+ view.getUint8(rec + 1),
3317
+ view.getUint8(rec + 2),
3318
+ view.getUint8(rec + 3)
3319
+ );
3320
+ tables[tag] = { offset: view.getUint32(rec + 8), length: view.getUint32(rec + 12) };
1749
3321
  }
1750
- const out = [];
1751
- const emit = (cpStart, cpEnd, forced) => {
1752
- if (cpStart >= cpEnd) return;
1753
- const segText = text.substring(cpToStr[cpStart], cpToStr[cpEnd]);
1754
- const segCps = codePoints.slice(cpStart, cpEnd);
1755
- const baseStrIdx = cpToStr[cpStart];
1756
- const segCpToStr = cpToStr.slice(cpStart, cpEnd + 1).map((x) => x - baseStrIdx);
1757
- const segRuns = resolveBidiCore(segText, segCps, segCpToStr, forced);
1758
- for (const r of segRuns) {
1759
- out.push({ text: r.text, level: r.level, start: r.start + baseStrIdx });
1760
- }
3322
+ return tables;
3323
+ }
3324
+ function parseGlyfFont(bytes) {
3325
+ if (bytes.length < 12) return null;
3326
+ const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
3327
+ const tables = readTableDirectory(view);
3328
+ const head = tables["head"];
3329
+ const maxp = tables["maxp"];
3330
+ const loca = tables["loca"];
3331
+ const glyf = tables["glyf"];
3332
+ if (!head || !maxp || !loca || !glyf) return null;
3333
+ const unitsPerEm = view.getUint16(head.offset + 18) || 1e3;
3334
+ const locaFormat = view.getInt16(head.offset + 50);
3335
+ const numGlyphs = view.getUint16(maxp.offset + 4);
3336
+ const locaOffsets = new Array(numGlyphs + 1);
3337
+ for (let i = 0; i <= numGlyphs; i++) {
3338
+ locaOffsets[i] = locaFormat === 0 ? view.getUint16(loca.offset + i * 2) * 2 : view.getUint32(loca.offset + i * 4);
3339
+ }
3340
+ return { view, glyfOffset: glyf.offset, locaOffsets, unitsPerEm };
3341
+ }
3342
+ function transformPoint(p, a, b, c, d, e, f) {
3343
+ return {
3344
+ x: a * p.x + c * p.y + e,
3345
+ y: b * p.x + d * p.y + f,
3346
+ onCurve: p.onCurve
1761
3347
  };
1762
- let cursor = 0;
1763
- for (const pair of isolates) {
1764
- emit(cursor, pair.open, forcedLevel);
1765
- const innerStart = pair.open + 1;
1766
- const innerEnd = pair.close;
1767
- let innerLevel;
1768
- if (pair.kind === "LRI") innerLevel = 0;
1769
- else if (pair.kind === "RLI") innerLevel = 1;
1770
- else {
1771
- const innerTypes = codePoints.slice(innerStart, innerEnd).map(classifyBidiType);
1772
- innerLevel = detectParagraphLevel(innerTypes);
1773
- }
1774
- if (innerStart < innerEnd) {
1775
- const innerText = text.substring(cpToStr[innerStart], cpToStr[innerEnd]);
1776
- const innerRuns = resolveBidiRunsForced(innerText, innerLevel);
1777
- const baseStrIdx = cpToStr[innerStart];
1778
- for (const r of innerRuns) {
1779
- out.push({ text: r.text, level: r.level, start: r.start + baseStrIdx });
1780
- }
1781
- }
1782
- cursor = pair.close + 1;
3348
+ }
3349
+ function readF2Dot14(view, pos) {
3350
+ return view.getInt16(pos) / 16384;
3351
+ }
3352
+ function extractGlyphContours(font, gid, depth = 0) {
3353
+ if (depth > 8) return [];
3354
+ const { view, glyfOffset, locaOffsets } = font;
3355
+ if (gid < 0 || gid + 1 >= locaOffsets.length) return [];
3356
+ const start = locaOffsets[gid];
3357
+ const end = locaOffsets[gid + 1];
3358
+ if (end <= start) return [];
3359
+ const base = glyfOffset + start;
3360
+ const numberOfContours = view.getInt16(base);
3361
+ if (numberOfContours < 0) {
3362
+ return extractCompositeContours(font, base, depth);
3363
+ }
3364
+ return extractSimpleContours(view, base, numberOfContours);
3365
+ }
3366
+ function extractSimpleContours(view, base, numberOfContours) {
3367
+ let pos = base + 10;
3368
+ const endPts = new Array(numberOfContours);
3369
+ for (let i = 0; i < numberOfContours; i++) {
3370
+ endPts[i] = view.getUint16(pos);
3371
+ pos += 2;
3372
+ }
3373
+ const numPoints = numberOfContours > 0 ? endPts[numberOfContours - 1] + 1 : 0;
3374
+ const instrLen = view.getUint16(pos);
3375
+ pos += 2 + instrLen;
3376
+ const flags = new Array(numPoints);
3377
+ for (let i = 0; i < numPoints; ) {
3378
+ const flag = view.getUint8(pos++);
3379
+ flags[i++] = flag;
3380
+ if (flag & 8) {
3381
+ let repeat = view.getUint8(pos++);
3382
+ while (repeat-- > 0 && i < numPoints) flags[i++] = flag;
3383
+ }
3384
+ }
3385
+ const xs = new Array(numPoints);
3386
+ let x = 0;
3387
+ for (let i = 0; i < numPoints; i++) {
3388
+ const flag = flags[i];
3389
+ if (flag & 2) {
3390
+ const dx = view.getUint8(pos++);
3391
+ x += flag & 16 ? dx : -dx;
3392
+ } else if (!(flag & 16)) {
3393
+ x += view.getInt16(pos);
3394
+ pos += 2;
3395
+ }
3396
+ xs[i] = x;
3397
+ }
3398
+ const ys = new Array(numPoints);
3399
+ let y = 0;
3400
+ for (let i = 0; i < numPoints; i++) {
3401
+ const flag = flags[i];
3402
+ if (flag & 4) {
3403
+ const dy = view.getUint8(pos++);
3404
+ y += flag & 32 ? dy : -dy;
3405
+ } else if (!(flag & 32)) {
3406
+ y += view.getInt16(pos);
3407
+ pos += 2;
3408
+ }
3409
+ ys[i] = y;
3410
+ }
3411
+ const contours = [];
3412
+ let startPt = 0;
3413
+ for (let c = 0; c < numberOfContours; c++) {
3414
+ const endPt = endPts[c];
3415
+ const contour = [];
3416
+ for (let i = startPt; i <= endPt; i++) {
3417
+ contour.push({ x: xs[i], y: ys[i], onCurve: (flags[i] & 1) !== 0 });
3418
+ }
3419
+ if (contour.length > 0) contours.push(contour);
3420
+ startPt = endPt + 1;
3421
+ }
3422
+ return contours;
3423
+ }
3424
+ function extractCompositeContours(font, base, depth) {
3425
+ const { view } = font;
3426
+ let pos = base + 10;
3427
+ const out = [];
3428
+ while (true) {
3429
+ const flags = view.getUint16(pos);
3430
+ pos += 2;
3431
+ const componentGid = view.getUint16(pos);
3432
+ pos += 2;
3433
+ let arg1;
3434
+ let arg2;
3435
+ if (flags & 1) {
3436
+ arg1 = view.getInt16(pos);
3437
+ pos += 2;
3438
+ arg2 = view.getInt16(pos);
3439
+ pos += 2;
3440
+ } else {
3441
+ arg1 = view.getInt8(pos);
3442
+ pos += 1;
3443
+ arg2 = view.getInt8(pos);
3444
+ pos += 1;
3445
+ }
3446
+ let a = 1, b = 0, c = 0, d = 1;
3447
+ if (flags & 8) {
3448
+ a = d = readF2Dot14(view, pos);
3449
+ pos += 2;
3450
+ } else if (flags & 64) {
3451
+ a = readF2Dot14(view, pos);
3452
+ pos += 2;
3453
+ d = readF2Dot14(view, pos);
3454
+ pos += 2;
3455
+ } else if (flags & 128) {
3456
+ a = readF2Dot14(view, pos);
3457
+ pos += 2;
3458
+ b = readF2Dot14(view, pos);
3459
+ pos += 2;
3460
+ c = readF2Dot14(view, pos);
3461
+ pos += 2;
3462
+ d = readF2Dot14(view, pos);
3463
+ pos += 2;
3464
+ }
3465
+ const e = flags & 2 ? arg1 : 0;
3466
+ const f = flags & 2 ? arg2 : 0;
3467
+ const sub = extractGlyphContours(font, componentGid, depth + 1);
3468
+ for (const contour of sub) {
3469
+ out.push(contour.map((p) => transformPoint(p, a, b, c, d, e, f)));
3470
+ }
3471
+ if (!(flags & 32)) break;
1783
3472
  }
1784
- emit(cursor, codePoints.length, forcedLevel);
1785
3473
  return out;
1786
3474
  }
1787
- function resolveBidiCore(text, codePoints, cpToStr, forcedLevel) {
1788
- const len = codePoints.length;
1789
- if (len === 0) return [];
1790
- const types = codePoints.map(classifyBidiType);
1791
- const paraLevel = forcedLevel !== void 0 ? forcedLevel : detectParagraphLevel(types);
1792
- resolveWeakTypes(types, paraLevel);
1793
- resolveNeutralTypes(types, paraLevel);
1794
- if (paraLevel === 1) {
1795
- fixPunctuationAffinity(types, codePoints, len);
1796
- fixBracketPairing(types, codePoints, len);
1797
- }
1798
- const levels = assignLevels(types, paraLevel);
1799
- const runs = [];
1800
- let runStart = 0;
1801
- let runLevel = levels[0];
1802
- for (let i = 1; i <= len; i++) {
1803
- if (i === len || levels[i] !== runLevel) {
1804
- const start = cpToStr[runStart];
1805
- const end = cpToStr[i];
1806
- let runText = text.substring(start, end);
1807
- if (runLevel % 2 === 1) {
1808
- runText = reverseString(runText);
1809
- }
1810
- runs.push({ text: runText, level: runLevel, start });
1811
- if (i < len) {
1812
- runStart = i;
1813
- runLevel = levels[i];
3475
+
3476
+ // src/core/pdf-color-glyph.ts
3477
+ var ID = [1, 0, 0, 1, 0, 0];
3478
+ function n(v) {
3479
+ if (!Number.isFinite(v)) return "0";
3480
+ if (Number.isInteger(v)) return String(v);
3481
+ let s = v.toFixed(3);
3482
+ s = s.replace(/0+$/, "").replace(/\.$/, "");
3483
+ return s === "-0" ? "0" : s;
3484
+ }
3485
+ function tx(m, x, y) {
3486
+ return [m[0] * x + m[2] * y + m[4], m[1] * x + m[3] * y + m[5]];
3487
+ }
3488
+ function ch(v) {
3489
+ return n(Math.max(0, Math.min(1, v / 255)));
3490
+ }
3491
+ function contoursToPath(contours, m = ID) {
3492
+ const ops = [];
3493
+ for (const contour of contours) {
3494
+ if (contour.length === 0) continue;
3495
+ const pts = contour.slice();
3496
+ let startIdx = pts.findIndex((p) => p.onCurve);
3497
+ let start;
3498
+ if (startIdx < 0) {
3499
+ const a = pts[0], b = pts[pts.length - 1];
3500
+ start = [(a.x + b.x) / 2, (a.y + b.y) / 2];
3501
+ startIdx = 0;
3502
+ } else {
3503
+ start = [pts[startIdx].x, pts[startIdx].y];
3504
+ }
3505
+ const [sx, sy] = tx(m, start[0], start[1]);
3506
+ ops.push(`${n(sx)} ${n(sy)} m`);
3507
+ const len = pts.length;
3508
+ let curX = start[0], curY = start[1];
3509
+ let i = 1;
3510
+ while (i <= len) {
3511
+ const p = pts[(startIdx + i) % len];
3512
+ if (p.onCurve) {
3513
+ const [px, py] = tx(m, p.x, p.y);
3514
+ ops.push(`${n(px)} ${n(py)} l`);
3515
+ curX = p.x;
3516
+ curY = p.y;
3517
+ i++;
3518
+ } else {
3519
+ const next = pts[(startIdx + i + 1) % len];
3520
+ let endX, endY;
3521
+ let consumed;
3522
+ if (next.onCurve) {
3523
+ endX = next.x;
3524
+ endY = next.y;
3525
+ consumed = 2;
3526
+ } else {
3527
+ endX = (p.x + next.x) / 2;
3528
+ endY = (p.y + next.y) / 2;
3529
+ consumed = 1;
3530
+ }
3531
+ const c1x = curX + 2 / 3 * (p.x - curX);
3532
+ const c1y = curY + 2 / 3 * (p.y - curY);
3533
+ const c2x = endX + 2 / 3 * (p.x - endX);
3534
+ const c2y = endY + 2 / 3 * (p.y - endY);
3535
+ const [a1, b1] = tx(m, c1x, c1y);
3536
+ const [a2, b2] = tx(m, c2x, c2y);
3537
+ const [ex, ey] = tx(m, endX, endY);
3538
+ ops.push(`${n(a1)} ${n(b1)} ${n(a2)} ${n(b2)} ${n(ex)} ${n(ey)} c`);
3539
+ curX = endX;
3540
+ curY = endY;
3541
+ i += consumed;
1814
3542
  }
1815
3543
  }
3544
+ ops.push("h");
1816
3545
  }
1817
- if (paraLevel === 1 && runs.length > 1) {
1818
- runs.reverse();
1819
- }
1820
- return runs;
3546
+ return ops.join("\n");
1821
3547
  }
1822
- function containsRTL(text) {
1823
- for (let i = 0; i < text.length; ) {
1824
- const cp = text.codePointAt(i) ?? 0;
1825
- const t = classifyBidiType(cp);
1826
- if (t === "R" || t === "AL") return true;
1827
- i += cp > 65535 ? 2 : 1;
3548
+ function buildGradientFunction(stops) {
3549
+ const sorted = stops.slice().sort((a, b) => a.offset - b.offset);
3550
+ if (sorted.length === 0) return "<< /FunctionType 2 /Domain [0 1] /C0 [0 0 0] /C1 [0 0 0] /N 1 >>";
3551
+ if (sorted.length === 1) {
3552
+ const c = sorted[0].color;
3553
+ return `<< /FunctionType 2 /Domain [0 1] /C0 [${ch(c[0])} ${ch(c[1])} ${ch(c[2])}] /C1 [${ch(c[0])} ${ch(c[1])} ${ch(c[2])}] /N 1 >>`;
3554
+ }
3555
+ if (sorted.length === 2) {
3556
+ const a = sorted[0].color, b = sorted[1].color;
3557
+ return `<< /FunctionType 2 /Domain [0 1] /C0 [${ch(a[0])} ${ch(a[1])} ${ch(a[2])}] /C1 [${ch(b[0])} ${ch(b[1])} ${ch(b[2])}] /N 1 >>`;
3558
+ }
3559
+ const subFns = [];
3560
+ const bounds = [];
3561
+ const encode = [];
3562
+ for (let i = 0; i < sorted.length - 1; i++) {
3563
+ const a = sorted[i].color, b = sorted[i + 1].color;
3564
+ subFns.push(`<< /FunctionType 2 /Domain [0 1] /C0 [${ch(a[0])} ${ch(a[1])} ${ch(a[2])}] /C1 [${ch(b[0])} ${ch(b[1])} ${ch(b[2])}] /N 1 >>`);
3565
+ encode.push("0 1");
3566
+ if (i > 0) bounds.push(n(Math.max(0, Math.min(1, sorted[i].offset))));
3567
+ }
3568
+ return `<< /FunctionType 3 /Domain [0 1] /Functions [${subFns.join(" ")}] /Bounds [${bounds.join(" ")}] /Encode [${encode.join(" ")}] >>`;
3569
+ }
3570
+ function extendFlags(extend) {
3571
+ return extend === "pad" ? "[true true]" : "[true true]";
3572
+ }
3573
+ function linearShadingDict(p, m) {
3574
+ const [x0, y0] = tx(m, p.p0[0], p.p0[1]);
3575
+ const [x1, y1] = tx(m, p.p1[0], p.p1[1]);
3576
+ return `<< /ShadingType 2 /ColorSpace /DeviceRGB /Coords [${n(x0)} ${n(y0)} ${n(x1)} ${n(y1)}] /Function ${buildGradientFunction(p.stops)} /Extend ${extendFlags(p.extend)} >>`;
3577
+ }
3578
+ function radialShadingDict(p, m) {
3579
+ const [x0, y0] = tx(m, p.c0[0], p.c0[1]);
3580
+ const [x1, y1] = tx(m, p.c1[0], p.c1[1]);
3581
+ const sx = Math.hypot(m[0], m[1]);
3582
+ const sy = Math.hypot(m[2], m[3]);
3583
+ const s = (sx + sy) / 2 || 1;
3584
+ return `<< /ShadingType 3 /ColorSpace /DeviceRGB /Coords [${n(x0)} ${n(y0)} ${n(p.r0 * s)} ${n(x1)} ${n(y1)} ${n(p.r1 * s)}] /Function ${buildGradientFunction(p.stops)} /Extend ${extendFlags(p.extend)} >>`;
3585
+ }
3586
+ function renderColorGlyph(glyph, outlines, unitsPerEm) {
3587
+ const body = [];
3588
+ const shadings = [];
3589
+ const extGStates = [];
3590
+ const alphaMap = /* @__PURE__ */ new Map();
3591
+ let shadingIdx = 0;
3592
+ let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
3593
+ for (const layer of glyph.layers) {
3594
+ const m = layer.transform ?? ID;
3595
+ const contours = outlines(layer.glyphId);
3596
+ if (contours.length === 0) continue;
3597
+ const path = contoursToPath(contours, m);
3598
+ for (const contour of contours) {
3599
+ for (const pt of contour) {
3600
+ const [px, py] = tx(m, pt.x, pt.y);
3601
+ if (px < minX) minX = px;
3602
+ if (py < minY) minY = py;
3603
+ if (px > maxX) maxX = px;
3604
+ if (py > maxY) maxY = py;
3605
+ }
3606
+ }
3607
+ if (layer.paint.kind === "solid") {
3608
+ const c = layer.paint.color;
3609
+ const alpha = c[3] / 255;
3610
+ body.push("q");
3611
+ if (alpha < 0.999) {
3612
+ let gs = alphaMap.get(c[3]);
3613
+ if (!gs) {
3614
+ gs = `GsA${alphaMap.size}`;
3615
+ alphaMap.set(c[3], gs);
3616
+ extGStates.push({ name: gs, dict: `<< /ca ${n(alpha)} /CA ${n(alpha)} >>` });
3617
+ }
3618
+ body.push(`/${gs} gs`);
3619
+ }
3620
+ body.push(`${ch(c[0])} ${ch(c[1])} ${ch(c[2])} rg`);
3621
+ body.push(path);
3622
+ body.push("f");
3623
+ body.push("Q");
3624
+ } else {
3625
+ const name = `Sh${shadingIdx++}`;
3626
+ const dict = layer.paint.kind === "linear" ? linearShadingDict(layer.paint, m) : radialShadingDict(layer.paint, m);
3627
+ shadings.push({ name, dict });
3628
+ body.push("q");
3629
+ body.push(path);
3630
+ body.push("W n");
3631
+ body.push(`/${name} sh`);
3632
+ body.push("Q");
3633
+ }
1828
3634
  }
1829
- return false;
3635
+ 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];
3636
+ return {
3637
+ content: body.join("\n"),
3638
+ bbox,
3639
+ shadings,
3640
+ extGStates
3641
+ };
1830
3642
  }
1831
- function reverseString(str) {
1832
- const cps = [];
1833
- for (let i = 0; i < str.length; ) {
1834
- const cp = str.codePointAt(i) ?? 0;
1835
- cps.push(cp);
1836
- i += cp > 65535 ? 2 : 1;
3643
+
3644
+ // src/core/color-emoji.ts
3645
+ function createColorEmojiCollector() {
3646
+ const forms = [];
3647
+ const nameByGlyph = /* @__PURE__ */ new WeakMap();
3648
+ const glyfByFont = /* @__PURE__ */ new WeakMap();
3649
+ function glyfFor(fontData) {
3650
+ let g = glyfByFont.get(fontData);
3651
+ if (g === void 0) {
3652
+ g = parseGlyfFont(getDecodedFontBytes(fontData));
3653
+ glyfByFont.set(fontData, g);
3654
+ }
3655
+ return g;
3656
+ }
3657
+ function useGlyph(fontData, gid) {
3658
+ const colorGlyph = fontData.colorGlyphs?.[gid];
3659
+ if (!colorGlyph) return null;
3660
+ let perFont = nameByGlyph.get(fontData);
3661
+ if (!perFont) {
3662
+ perFont = /* @__PURE__ */ new Map();
3663
+ nameByGlyph.set(fontData, perFont);
3664
+ }
3665
+ const cached = perFont.get(gid);
3666
+ if (cached) return cached;
3667
+ const glyf = glyfFor(fontData);
3668
+ if (!glyf) return null;
3669
+ const rendered = renderColorGlyph(
3670
+ colorGlyph,
3671
+ (baseGid) => extractGlyphContours(glyf, baseGid),
3672
+ fontData.metrics.unitsPerEm
3673
+ );
3674
+ if (rendered.content.trim() === "") return null;
3675
+ const name = `CEm${forms.length}`;
3676
+ const resParts = [];
3677
+ if (rendered.shadings.length > 0) {
3678
+ resParts.push(`/Shading << ${rendered.shadings.map((s) => `/${s.name} ${s.dict}`).join(" ")} >>`);
3679
+ }
3680
+ if (rendered.extGStates.length > 0) {
3681
+ resParts.push(`/ExtGState << ${rendered.extGStates.map((g) => `/${g.name} ${g.dict}`).join(" ")} >>`);
3682
+ }
3683
+ forms.push({ name, content: rendered.content, resources: resParts.join(" "), bbox: rendered.bbox });
3684
+ perFont.set(gid, name);
3685
+ return name;
1837
3686
  }
1838
- cps.reverse();
1839
- return String.fromCodePoint(...cps);
3687
+ return { useGlyph, forms };
1840
3688
  }
1841
3689
 
1842
3690
  // src/core/encoding-context.ts
@@ -1943,13 +3791,14 @@ function buildTextRunsWithFallback(text, fontRef, fd, sz, trackGid, pdfA = false
1943
3791
  if (mode === "hel") flushHel();
1944
3792
  return result;
1945
3793
  }
1946
- function createEncodingContext(fontEntries, pdfA = false) {
3794
+ function createEncodingContext(fontEntries, pdfA = false, normalize = false) {
3795
+ const _norm = normalize ? (s) => s.normalize(normalize) : (s) => s;
1947
3796
  if (!fontEntries || fontEntries.length === 0) {
1948
3797
  return {
1949
3798
  isUnicode: false,
1950
3799
  fontEntries: [],
1951
- ps: pdfString,
1952
- tw: helveticaWidth,
3800
+ ps: normalize ? (s) => pdfString(_norm(s)) : pdfString,
3801
+ tw: normalize ? (s, sz) => helveticaWidth(_norm(s), sz) : helveticaWidth,
1953
3802
  textRuns: () => [],
1954
3803
  f1: "/F1",
1955
3804
  f2: "/F2"
@@ -1962,6 +3811,7 @@ function createEncodingContext(fontEntries, pdfA = false) {
1962
3811
  const s = _usedGids.get(fontRef);
1963
3812
  if (s) s.add(gid);
1964
3813
  }
3814
+ const _colorEmoji = fontEntries.some((fe) => fe.fontData.colorGlyphs) ? createColorEmojiCollector() : void 0;
1965
3815
  return {
1966
3816
  isUnicode: true,
1967
3817
  fontEntries,
@@ -1971,7 +3821,11 @@ function createEncodingContext(fontEntries, pdfA = false) {
1971
3821
  getUsedGids() {
1972
3822
  return _usedGids;
1973
3823
  },
3824
+ colorEmoji: _colorEmoji,
1974
3825
  textRuns(str, sz) {
3826
+ if (!str) return [];
3827
+ str = _norm(str);
3828
+ str = stripBidiControls(str);
1975
3829
  if (!str) return [];
1976
3830
  if (containsRTL(str)) {
1977
3831
  const bidiRuns = resolveBidiRuns(str);
@@ -2037,6 +3891,56 @@ function createEncodingContext(fontEntries, pdfA = false) {
2037
3891
  }
2038
3892
  }
2039
3893
  result.push({ text: fRun.text, fontRef, fontData: fd, shaped, hexStr: null, widthPt: designW * sz / upm });
3894
+ } else if (containsTelugu(fRun.text)) {
3895
+ const shaped = shapeTeluguText(fRun.text, fd);
3896
+ let designW = 0;
3897
+ for (const g of shaped) {
3898
+ _trackGid(fontRef, g.gid);
3899
+ if (!g.isZeroAdvance) {
3900
+ designW += fd.widths[g.gid] !== void 0 ? fd.widths[g.gid] : fd.defaultWidth;
3901
+ }
3902
+ }
3903
+ result.push({ text: fRun.text, fontRef, fontData: fd, shaped, hexStr: null, widthPt: designW * sz / upm });
3904
+ } else if (containsSinhala(fRun.text)) {
3905
+ const shaped = shapeSinhalaText(fRun.text, fd);
3906
+ let designW = 0;
3907
+ for (const g of shaped) {
3908
+ _trackGid(fontRef, g.gid);
3909
+ if (!g.isZeroAdvance) {
3910
+ designW += fd.widths[g.gid] !== void 0 ? fd.widths[g.gid] : fd.defaultWidth;
3911
+ }
3912
+ }
3913
+ result.push({ text: fRun.text, fontRef, fontData: fd, shaped, hexStr: null, widthPt: designW * sz / upm });
3914
+ } else if (containsTibetan(fRun.text)) {
3915
+ const shaped = shapeTibetanText(fRun.text, fd);
3916
+ let designW = 0;
3917
+ for (const g of shaped) {
3918
+ _trackGid(fontRef, g.gid);
3919
+ if (!g.isZeroAdvance) {
3920
+ designW += fd.widths[g.gid] !== void 0 ? fd.widths[g.gid] : fd.defaultWidth;
3921
+ }
3922
+ }
3923
+ result.push({ text: fRun.text, fontRef, fontData: fd, shaped, hexStr: null, widthPt: designW * sz / upm });
3924
+ } else if (containsKhmer(fRun.text)) {
3925
+ const shaped = shapeKhmerText(fRun.text, fd);
3926
+ let designW = 0;
3927
+ for (const g of shaped) {
3928
+ _trackGid(fontRef, g.gid);
3929
+ if (!g.isZeroAdvance) {
3930
+ designW += fd.widths[g.gid] !== void 0 ? fd.widths[g.gid] : fd.defaultWidth;
3931
+ }
3932
+ }
3933
+ result.push({ text: fRun.text, fontRef, fontData: fd, shaped, hexStr: null, widthPt: designW * sz / upm });
3934
+ } else if (containsMyanmar(fRun.text)) {
3935
+ const shaped = shapeMyanmarText(fRun.text, fd);
3936
+ let designW = 0;
3937
+ for (const g of shaped) {
3938
+ _trackGid(fontRef, g.gid);
3939
+ if (!g.isZeroAdvance) {
3940
+ designW += fd.widths[g.gid] !== void 0 ? fd.widths[g.gid] : fd.defaultWidth;
3941
+ }
3942
+ }
3943
+ result.push({ text: fRun.text, fontRef, fontData: fd, shaped, hexStr: null, widthPt: designW * sz / upm });
2040
3944
  } else if (containsDevanagari(fRun.text)) {
2041
3945
  const shaped = shapeDevanagariText(fRun.text, fd);
2042
3946
  let designW = 0;
@@ -2094,6 +3998,61 @@ function createEncodingContext(fontEntries, pdfA = false) {
2094
3998
  }
2095
3999
  return [{ text: run.text, fontRef, fontData: fd, shaped, hexStr: null, widthPt: designW * sz / upm }];
2096
4000
  }
4001
+ if (containsTelugu(run.text)) {
4002
+ const shaped = shapeTeluguText(run.text, fd);
4003
+ let designW = 0;
4004
+ for (const g of shaped) {
4005
+ _trackGid(fontRef, g.gid);
4006
+ if (!g.isZeroAdvance) {
4007
+ designW += fd.widths[g.gid] !== void 0 ? fd.widths[g.gid] : fd.defaultWidth;
4008
+ }
4009
+ }
4010
+ return [{ text: run.text, fontRef, fontData: fd, shaped, hexStr: null, widthPt: designW * sz / upm }];
4011
+ }
4012
+ if (containsSinhala(run.text)) {
4013
+ const shaped = shapeSinhalaText(run.text, fd);
4014
+ let designW = 0;
4015
+ for (const g of shaped) {
4016
+ _trackGid(fontRef, g.gid);
4017
+ if (!g.isZeroAdvance) {
4018
+ designW += fd.widths[g.gid] !== void 0 ? fd.widths[g.gid] : fd.defaultWidth;
4019
+ }
4020
+ }
4021
+ return [{ text: run.text, fontRef, fontData: fd, shaped, hexStr: null, widthPt: designW * sz / upm }];
4022
+ }
4023
+ if (containsTibetan(run.text)) {
4024
+ const shaped = shapeTibetanText(run.text, fd);
4025
+ let designW = 0;
4026
+ for (const g of shaped) {
4027
+ _trackGid(fontRef, g.gid);
4028
+ if (!g.isZeroAdvance) {
4029
+ designW += fd.widths[g.gid] !== void 0 ? fd.widths[g.gid] : fd.defaultWidth;
4030
+ }
4031
+ }
4032
+ return [{ text: run.text, fontRef, fontData: fd, shaped, hexStr: null, widthPt: designW * sz / upm }];
4033
+ }
4034
+ if (containsKhmer(run.text)) {
4035
+ const shaped = shapeKhmerText(run.text, fd);
4036
+ let designW = 0;
4037
+ for (const g of shaped) {
4038
+ _trackGid(fontRef, g.gid);
4039
+ if (!g.isZeroAdvance) {
4040
+ designW += fd.widths[g.gid] !== void 0 ? fd.widths[g.gid] : fd.defaultWidth;
4041
+ }
4042
+ }
4043
+ return [{ text: run.text, fontRef, fontData: fd, shaped, hexStr: null, widthPt: designW * sz / upm }];
4044
+ }
4045
+ if (containsMyanmar(run.text)) {
4046
+ const shaped = shapeMyanmarText(run.text, fd);
4047
+ let designW = 0;
4048
+ for (const g of shaped) {
4049
+ _trackGid(fontRef, g.gid);
4050
+ if (!g.isZeroAdvance) {
4051
+ designW += fd.widths[g.gid] !== void 0 ? fd.widths[g.gid] : fd.defaultWidth;
4052
+ }
4053
+ }
4054
+ return [{ text: run.text, fontRef, fontData: fd, shaped, hexStr: null, widthPt: designW * sz / upm }];
4055
+ }
2097
4056
  if (containsDevanagari(run.text)) {
2098
4057
  const shaped = shapeDevanagariText(run.text, fd);
2099
4058
  let designW = 0;
@@ -2109,6 +4068,9 @@ function createEncodingContext(fontEntries, pdfA = false) {
2109
4068
  });
2110
4069
  },
2111
4070
  ps(str) {
4071
+ if (!str) return "<>";
4072
+ str = _norm(str);
4073
+ str = stripBidiControls(str);
2112
4074
  if (!str) return "<>";
2113
4075
  const { cmap } = primary.fontData;
2114
4076
  if (containsRTL(str)) {
@@ -2136,7 +4098,7 @@ function createEncodingContext(fontEntries, pdfA = false) {
2136
4098
  }
2137
4099
  return `<${hex2.toUpperCase()}>`;
2138
4100
  }
2139
- if (!containsThai(str) && !containsBengali(str) && !containsTamil(str) && !containsDevanagari(str)) {
4101
+ if (!containsThai(str) && !containsBengali(str) && !containsTamil(str) && !containsTelugu(str) && !containsSinhala(str) && !containsTibetan(str) && !containsKhmer(str) && !containsMyanmar(str) && !containsDevanagari(str)) {
2140
4102
  let hex2 = "";
2141
4103
  for (let i = 0; i < str.length; i++) {
2142
4104
  const rawCp = str.codePointAt(i) ?? 0;
@@ -2148,7 +4110,7 @@ function createEncodingContext(fontEntries, pdfA = false) {
2148
4110
  }
2149
4111
  return `<${hex2.toUpperCase()}>`;
2150
4112
  }
2151
- const shapeFn = containsThai(str) ? shapeThaiText : containsBengali(str) ? shapeBengaliText : containsTamil(str) ? shapeTamilText : shapeDevanagariText;
4113
+ 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;
2152
4114
  const shaped = shapeFn(str, primary.fontData);
2153
4115
  let hex = "";
2154
4116
  for (const g of shaped) {
@@ -2230,24 +4192,6 @@ function buildSubsetWidthArray(widths, usedGids) {
2230
4192
  return parts.join(" ");
2231
4193
  }
2232
4194
 
2233
- // src/fonts/font-loader.ts
2234
- var _fontBinaryCache = /* @__PURE__ */ new WeakMap();
2235
- function getDecodedFontBytes(fontData) {
2236
- const cached = _fontBinaryCache.get(fontData);
2237
- if (cached) return cached;
2238
- let bytes;
2239
- if (typeof atob === "function") {
2240
- const binaryStr = atob(fontData.ttfBase64);
2241
- bytes = new Uint8Array(binaryStr.length);
2242
- for (let i = 0; i < binaryStr.length; i++) bytes[i] = binaryStr.charCodeAt(i);
2243
- } else {
2244
- const buf = globalThis["Buffer"];
2245
- bytes = buf.from(fontData.ttfBase64, "base64");
2246
- }
2247
- _fontBinaryCache.set(fontData, bytes);
2248
- return bytes;
2249
- }
2250
-
2251
4195
  // src/fonts/font-subsetter.ts
2252
4196
  function subsetTTF(ttfInput, usedGids) {
2253
4197
  try {
@@ -2561,7 +4505,7 @@ function buildStructureTree(root, startObjNum, pageObjToStructParents) {
2561
4505
  };
2562
4506
  }
2563
4507
  function buildPdfMetadata(now = /* @__PURE__ */ new Date()) {
2564
- const pad2 = (n) => String(n).padStart(2, "0");
4508
+ const pad2 = (n2) => String(n2).padStart(2, "0");
2565
4509
  const yyyy = now.getFullYear();
2566
4510
  const mm = pad2(now.getMonth() + 1);
2567
4511
  const dd = pad2(now.getDate());
@@ -2769,45 +4713,64 @@ function txtShaped(shaped, x, y, font, sz, fontData) {
2769
4713
  }
2770
4714
  return parts.join("\n");
2771
4715
  }
4716
+ var fmtScale = (v) => v.toFixed(5).replace(/0+$/, "").replace(/\.$/, "") || "0";
4717
+ function emitColorEmojiRun(parts, run, penX, y, sz, enc) {
4718
+ const fd = run.fontData;
4719
+ const upm = fd.metrics.unitsPerEm;
4720
+ const scale = sz / upm;
4721
+ const s = fmtScale(scale);
4722
+ const hex = (run.hexStr ?? "").replace(/[<>]/g, "");
4723
+ for (let i = 0; i + 4 <= hex.length; i += 4) {
4724
+ const tag = hex.substr(i, 4);
4725
+ const gid = parseInt(tag, 16);
4726
+ const adv = (fd.widths[gid] !== void 0 ? fd.widths[gid] : fd.defaultWidth) * scale;
4727
+ const name = enc.colorEmoji?.useGlyph(fd, gid) ?? null;
4728
+ if (name) {
4729
+ parts.push(`q ${s} 0 0 ${s} ${fmtNum(penX)} ${fmtNum(y)} cm /${name} Do Q`);
4730
+ } else {
4731
+ parts.push(`BT ${run.fontRef} ${sz} Tf ${fmtNum(penX)} ${fmtNum(y)} Td <${tag}> Tj ET`);
4732
+ }
4733
+ penX += adv;
4734
+ }
4735
+ return penX;
4736
+ }
2772
4737
  function txt(str, x, y, font, sz, enc) {
2773
4738
  if (!enc.isUnicode) {
2774
4739
  return `BT ${font} ${sz} Tf ${fmtNum(x)} ${fmtNum(y)} Td ${enc.ps(str)} Tj ET`;
2775
4740
  }
2776
4741
  const runs = enc.textRuns(str, sz);
2777
4742
  if (runs.length === 0) return "";
2778
- if (runs.length === 1) {
2779
- const run = runs[0];
2780
- if (run.shaped) return txtShaped(run.shaped, x, y, run.fontRef, sz, run.fontData);
2781
- return `BT ${run.fontRef} ${sz} Tf ${fmtNum(x)} ${fmtNum(y)} Td ${run.hexStr} Tj ET`;
2782
- }
2783
4743
  const parts = [];
2784
4744
  let penX = x;
2785
4745
  for (const run of runs) {
2786
- if (run.shaped) {
4746
+ if (enc.colorEmoji && run.fontData.colorGlyphs && run.hexStr) {
4747
+ penX = emitColorEmojiRun(parts, run, penX, y, sz, enc);
4748
+ } else if (run.shaped) {
2787
4749
  parts.push(txtShaped(run.shaped, penX, y, run.fontRef, sz, run.fontData));
4750
+ penX += run.widthPt;
2788
4751
  } else {
2789
4752
  parts.push(`BT ${run.fontRef} ${sz} Tf ${fmtNum(penX)} ${fmtNum(y)} Td ${run.hexStr} Tj ET`);
4753
+ penX += run.widthPt;
2790
4754
  }
2791
- penX += run.widthPt;
2792
4755
  }
2793
4756
  return parts.join("\n");
2794
4757
  }
2795
- function txtR(str, rightX, y, font, sz, enc) {
2796
- const width = enc.isUnicode ? enc.tw(str, sz) : helveticaWidth(toWinAnsi(str), sz);
4758
+ function txtR(str, rightX, y, font, sz, enc, bold = false) {
4759
+ const width = enc.isUnicode ? enc.tw(str, sz) : bold ? helveticaBoldWidth(str, sz) : helveticaWidth(toWinAnsi(str), sz);
2797
4760
  return txt(str, rightX - width, y, font, sz, enc);
2798
4761
  }
2799
- function txtC(str, leftX, y, font, sz, colW, enc) {
2800
- const width = enc.isUnicode ? enc.tw(str, sz) : helveticaWidth(toWinAnsi(str), sz);
4762
+ function txtC(str, leftX, y, font, sz, colW, enc, bold = false) {
4763
+ const width = enc.isUnicode ? enc.tw(str, sz) : bold ? helveticaBoldWidth(str, sz) : helveticaWidth(toWinAnsi(str), sz);
2801
4764
  return txt(str, leftX + (colW - width) / 2, y, font, sz, enc);
2802
4765
  }
2803
4766
  function txtTagged(str, x, y, font, sz, enc, mcid) {
2804
4767
  return wrapSpan(txt(str, x, y, font, sz, enc), str, mcid);
2805
4768
  }
2806
- function txtRTagged(str, rightX, y, font, sz, enc, mcid) {
2807
- return wrapSpan(txtR(str, rightX, y, font, sz, enc), str, mcid);
4769
+ function txtRTagged(str, rightX, y, font, sz, enc, mcid, bold = false) {
4770
+ return wrapSpan(txtR(str, rightX, y, font, sz, enc, bold), str, mcid);
2808
4771
  }
2809
- function txtCTagged(str, leftX, y, font, sz, colW, enc, mcid) {
2810
- return wrapSpan(txtC(str, leftX, y, font, sz, colW, enc), str, mcid);
4772
+ function txtCTagged(str, leftX, y, font, sz, colW, enc, mcid, bold = false) {
4773
+ return wrapSpan(txtC(str, leftX, y, font, sz, colW, enc, bold), str, mcid);
2811
4774
  }
2812
4775
  function encodePdfTextString(str) {
2813
4776
  let ascii = true;
@@ -2881,12 +4844,12 @@ var DEFAULT_COLUMNS = [
2881
4844
  { f: 0.18, a: "c", mx: 20, mxH: 20 }
2882
4845
  ];
2883
4846
  function computeColumnPositions(columns, marginLeft, contentWidth) {
2884
- const n = columns.length;
2885
- const cwi = new Array(n).fill(0);
2886
- const fixed = new Array(n).fill(false);
4847
+ const n2 = columns.length;
4848
+ const cwi = new Array(n2).fill(0);
4849
+ const fixed = new Array(n2).fill(false);
2887
4850
  let totalFixed = 0;
2888
4851
  let freeWeight = 0;
2889
- for (let i = 0; i < n; i++) {
4852
+ for (let i = 0; i < n2; i++) {
2890
4853
  const col = columns[i];
2891
4854
  let w = col.f * contentWidth;
2892
4855
  let clamped = false;
@@ -2908,13 +4871,13 @@ function computeColumnPositions(columns, marginLeft, contentWidth) {
2908
4871
  }
2909
4872
  const remaining = contentWidth - totalFixed;
2910
4873
  if (freeWeight > 0) {
2911
- for (let i = 0; i < n; i++) {
4874
+ for (let i = 0; i < n2; i++) {
2912
4875
  if (!fixed[i]) cwi[i] = columns[i].f / freeWeight * remaining;
2913
4876
  }
2914
4877
  }
2915
- const cx = new Array(n);
4878
+ const cx = new Array(n2);
2916
4879
  let x = marginLeft;
2917
- for (let i = 0; i < n; i++) {
4880
+ for (let i = 0; i < n2; i++) {
2918
4881
  cx[i] = x;
2919
4882
  x += cwi[i];
2920
4883
  }
@@ -2940,8 +4903,8 @@ function parseColor(input) {
2940
4903
  `Invalid color format: ${JSON.stringify(input)}. Expected "#RRGGBB", "#RGB", [r, g, b] (0\u2013255), or "R G B" (0.0\u20131.0).`
2941
4904
  );
2942
4905
  }
2943
- function fmtChannel(n) {
2944
- const clamped = Math.max(0, Math.min(1, n));
4906
+ function fmtChannel(n2) {
4907
+ const clamped = Math.max(0, Math.min(1, n2));
2945
4908
  const rounded = Math.round(clamped * 1e3) / 1e3;
2946
4909
  return String(rounded);
2947
4910
  }
@@ -2983,8 +4946,8 @@ function parseTupleColor(tuple) {
2983
4946
  function parsePdfRgbString(str) {
2984
4947
  const parts = str.split(" ");
2985
4948
  for (const p of parts) {
2986
- const n = Number(p);
2987
- if (n < 0 || n > 1) {
4949
+ const n2 = Number(p);
4950
+ if (n2 < 0 || n2 > 1) {
2988
4951
  throw new Error(
2989
4952
  `Invalid PDF RGB value: ${p} in ${JSON.stringify(str)}. Each value must be 0.0\u20131.0.`
2990
4953
  );
@@ -3124,8 +5087,8 @@ var MD5_K = new Uint32Array([
3124
5087
  718787259,
3125
5088
  3951481745
3126
5089
  ]);
3127
- function rotl32(x, n) {
3128
- return (x << n | x >>> 32 - n) >>> 0;
5090
+ function rotl32(x, n2) {
5091
+ return (x << n2 | x >>> 32 - n2) >>> 0;
3129
5092
  }
3130
5093
  function md5(input) {
3131
5094
  const len = input.length;
@@ -3265,17 +5228,17 @@ function _buildTableHeader(y, headers, enc, cx, cwi, columns, cw, mgL, mgR, pgW,
3265
5228
  const thEl = { type: "TH", children: [mcref] };
3266
5229
  thChildren.push(thEl);
3267
5230
  if (columns[i].a === "r") {
3268
- ops.push(txtRTagged(t, cx[i] + cwi[i] - 3, y - TH_H + 4, enc.f2, fs.th, enc, mcid));
5231
+ ops.push(txtRTagged(t, cx[i] + cwi[i] - 3, y - TH_H + 4, enc.f2, fs.th, enc, mcid, true));
3269
5232
  } else if (columns[i].a === "c") {
3270
- ops.push(txtCTagged(t, cx[i], y - TH_H + 4, enc.f2, fs.th, cwi[i], enc, mcid));
5233
+ ops.push(txtCTagged(t, cx[i], y - TH_H + 4, enc.f2, fs.th, cwi[i], enc, mcid, true));
3271
5234
  } else {
3272
5235
  ops.push(txtTagged(t, cx[i] + 3, y - TH_H + 4, enc.f2, fs.th, enc, mcid));
3273
5236
  }
3274
5237
  } else {
3275
5238
  if (columns[i].a === "r") {
3276
- ops.push(txtR(t, cx[i] + cwi[i] - 3, y - TH_H + 4, enc.f2, fs.th, enc));
5239
+ ops.push(txtR(t, cx[i] + cwi[i] - 3, y - TH_H + 4, enc.f2, fs.th, enc, true));
3277
5240
  } else if (columns[i].a === "c") {
3278
- ops.push(txtC(t, cx[i], y - TH_H + 4, enc.f2, fs.th, cwi[i], enc));
5241
+ ops.push(txtC(t, cx[i], y - TH_H + 4, enc.f2, fs.th, cwi[i], enc, true));
3279
5242
  } else {
3280
5243
  ops.push(txt(t, cx[i] + 3, y - TH_H + 4, enc.f2, fs.th, enc));
3281
5244
  }
@@ -3363,6 +5326,9 @@ function _buildPageTemplate(template, page, pages, title, date, y, enc, mgL, mgR
3363
5326
  return { ops, structEls };
3364
5327
  }
3365
5328
  function buildPDF(params, layoutOptions) {
5329
+ return assembleTableParts(params, layoutOptions).join("");
5330
+ }
5331
+ function assembleTableParts(params, layoutOptions) {
3366
5332
  if (!params || typeof params !== "object") {
3367
5333
  throw new Error("buildPDF: params is required and must be an object");
3368
5334
  }
@@ -3388,14 +5354,14 @@ function buildPDF(params, layoutOptions) {
3388
5354
  const fontEntries = params.fontEntries || (fontData ? [{ fontData, fontRef: "/F3", lang: "unknown" }] : []);
3389
5355
  const pdfaConfig = resolvePdfAConfig();
3390
5356
  const tagged = pdfaConfig.enabled;
3391
- const enc = createEncodingContext(fontEntries, tagged);
5357
+ const enc = createEncodingContext(fontEntries, tagged, false);
3392
5358
  const footerTpl = {
3393
5359
  left: footerText || void 0,
3394
5360
  right: "{page}/{pages}"
3395
5361
  };
3396
5362
  const headerH = 0;
3397
5363
  const dateNow = /* @__PURE__ */ new Date();
3398
- const pad2d = (n) => String(n).padStart(2, "0");
5364
+ const pad2d = (n2) => String(n2).padStart(2, "0");
3399
5365
  const dateStr = `${dateNow.getFullYear()}-${pad2d(dateNow.getMonth() + 1)}-${pad2d(dateNow.getDate())}`;
3400
5366
  const infoCount = infoItems.length;
3401
5367
  const page1Header = TITLE_LN + 16 + infoCount * INFO_LN + 8 + BAL_H + 10;
@@ -3417,6 +5383,9 @@ function buildPDF(params, layoutOptions) {
3417
5383
  const pageStreams = [];
3418
5384
  let rowIdx = 0;
3419
5385
  const prePageObjStart = enc.isUnicode && fontEntries.length > 0 ? 5 + fontEntries.length * 5 + wmExtraObjs : 5 + wmExtraObjs;
5386
+ const preBaseObjCount = enc.isUnicode && fontEntries.length > 0 ? 4 + fontEntries.length * 5 + wmExtraObjs + totalPages * 2 : 4 + wmExtraObjs + totalPages * 2;
5387
+ const latinToUniObjNum = tagged ? 0 : preBaseObjCount + 2;
5388
+ const baseFontToUniRef = latinToUniObjNum ? ` /ToUnicode ${latinToUniObjNum} 0 R` : "";
3420
5389
  const pageObjToStructParents = /* @__PURE__ */ new Map();
3421
5390
  for (let p = 0; p < totalPages; p++) {
3422
5391
  const pageObjNum = prePageObjStart + p * 2;
@@ -3546,8 +5515,8 @@ function buildPDF(params, layoutOptions) {
3546
5515
  emitObj(3, refDict);
3547
5516
  emitObj(4, refDict);
3548
5517
  } else {
3549
- emitObj(3, "<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica /Encoding /WinAnsiEncoding >>");
3550
- emitObj(4, "<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding >>");
5518
+ emitObj(3, `<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica /Encoding /WinAnsiEncoding${baseFontToUniRef} >>`);
5519
+ emitObj(4, `<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding${baseFontToUniRef} >>`);
3551
5520
  }
3552
5521
  for (let fi = 0; fi < fontEntries.length; fi++) {
3553
5522
  const fe = fontEntries[fi];
@@ -3605,8 +5574,8 @@ function buildPDF(params, layoutOptions) {
3605
5574
  kids.push(`${pageObjStart + p * 2} 0 R`);
3606
5575
  }
3607
5576
  emitObj(2, `<< /Type /Pages /Kids [${kids.join(" ")}] /Count ${totalPages} >>`);
3608
- emitObj(3, "<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica /Encoding /WinAnsiEncoding >>");
3609
- emitObj(4, "<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding >>");
5577
+ emitObj(3, `<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica /Encoding /WinAnsiEncoding${baseFontToUniRef} >>`);
5578
+ emitObj(4, `<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding${baseFontToUniRef} >>`);
3610
5579
  let wmGsResLatin = "";
3611
5580
  let wmImgResLatin = "";
3612
5581
  for (let p = 0; p < totalPages; p++) {
@@ -3623,13 +5592,18 @@ function buildPDF(params, layoutOptions) {
3623
5592
  }
3624
5593
  const baseObjCount = enc.isUnicode ? 4 + fontEntries.length * 5 + wmExtraObjs + totalPages * 2 : 4 + wmExtraObjs + totalPages * 2;
3625
5594
  const infoObjNum = baseObjCount + 1;
3626
- const { pdfDate, xmpDate: isoDate } = buildPdfMetadata();
5595
+ const { pdfDate, xmpDate: isoDate } = buildPdfMetadata(layoutOptions?.creationDate);
3627
5596
  const infoTitle = params.docTitle || title || "";
3628
5597
  emitObj(
3629
5598
  infoObjNum,
3630
5599
  `<< /Title ${encodePdfTextString(infoTitle)} /Producer (pdfnative) /CreationDate (${pdfDate}) >>`
3631
5600
  );
3632
5601
  let totalObjs = infoObjNum;
5602
+ if (latinToUniObjNum) {
5603
+ const cmap = buildWinAnsiToUnicodeCMap();
5604
+ emitStreamObj(latinToUniObjNum, `<< /Length ${cmap.length}`, cmap);
5605
+ totalObjs = latinToUniObjNum;
5606
+ }
3633
5607
  let xmpObjNum = 0;
3634
5608
  let outputIntentObjNum = 0;
3635
5609
  if (tagged) {
@@ -3685,7 +5659,7 @@ endobj
3685
5659
  }
3686
5660
  const writer = { emit, emitObj, emitStreamObj, offset: getOffset, adjustOffset, objOffsets, parts };
3687
5661
  writeXrefTrailer(writer, totalObjs, infoObjNum, encState, `${infoTitle}|${pdfDate}`);
3688
- return parts.join("");
5662
+ return parts;
3689
5663
  }
3690
5664
 
3691
5665
  // src/worker/pdf-worker.ts