pdfnative 1.0.5 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -2153,12 +2153,12 @@ function shapeThaiText(str, fontData) {
2153
2153
  function getAdv(gid) {
2154
2154
  return widths[gid] !== void 0 ? widths[gid] : defaultWidth;
2155
2155
  }
2156
- function getBaseAnchor(baseGid, markClass) {
2156
+ function getBaseAnchor2(baseGid, markClass) {
2157
2157
  const base = markAnchors && markAnchors.bases && markAnchors.bases[baseGid];
2158
2158
  if (!base) return null;
2159
2159
  return base[markClass] ?? null;
2160
2160
  }
2161
- function getMarkAnchor(markGid) {
2161
+ function getMarkAnchor2(markGid) {
2162
2162
  const mark = markAnchors && markAnchors.marks && markAnchors.marks[markGid];
2163
2163
  if (!mark) return null;
2164
2164
  return { classIdx: mark[0], x: mark[1], y: mark[2] };
@@ -2195,7 +2195,7 @@ function shapeThaiText(str, fontData) {
2195
2195
  for (let ai = 0; ai < cluster.aboves.length; ai++) {
2196
2196
  const abvCp = cluster.aboves[ai];
2197
2197
  const markGid = resolveMarkGid(abvCp, isTallBase);
2198
- const markAnchor = getMarkAnchor(markGid);
2198
+ const markAnchor = getMarkAnchor2(markGid);
2199
2199
  let dx = 0;
2200
2200
  let dy = 0;
2201
2201
  if (ai > 0 && prevAboveMarkGid !== null) {
@@ -2204,7 +2204,7 @@ function shapeThaiText(str, fontData) {
2204
2204
  dx = prevAboveMarkDx + m2mOffset.dx;
2205
2205
  dy = prevAboveMarkDy + m2mOffset.dy;
2206
2206
  } else if (markAnchor) {
2207
- const baseAnchor = getBaseAnchor(baseGid, markAnchor.classIdx);
2207
+ const baseAnchor = getBaseAnchor2(baseGid, markAnchor.classIdx);
2208
2208
  if (baseAnchor) {
2209
2209
  dx = baseAnchor[0] - markAnchor.x - baseAdv;
2210
2210
  dy = baseAnchor[1] - markAnchor.y;
@@ -2212,7 +2212,7 @@ function shapeThaiText(str, fontData) {
2212
2212
  }
2213
2213
  } else {
2214
2214
  if (markAnchor) {
2215
- const baseAnchor = getBaseAnchor(baseGid, markAnchor.classIdx);
2215
+ const baseAnchor = getBaseAnchor2(baseGid, markAnchor.classIdx);
2216
2216
  if (baseAnchor) {
2217
2217
  dx = baseAnchor[0] - markAnchor.x - baseAdv;
2218
2218
  dy = baseAnchor[1] - markAnchor.y;
@@ -2226,11 +2226,11 @@ function shapeThaiText(str, fontData) {
2226
2226
  }
2227
2227
  for (const blwCp of cluster.belows) {
2228
2228
  const markGid = cmap[blwCp] || 0;
2229
- const markAnchor = getMarkAnchor(markGid);
2229
+ const markAnchor = getMarkAnchor2(markGid);
2230
2230
  let dx = 0;
2231
2231
  let dy = 0;
2232
2232
  if (markAnchor) {
2233
- const baseAnchor = getBaseAnchor(baseGid, markAnchor.classIdx);
2233
+ const baseAnchor = getBaseAnchor2(baseGid, markAnchor.classIdx);
2234
2234
  if (baseAnchor) {
2235
2235
  dx = baseAnchor[0] - markAnchor.x - baseAdv;
2236
2236
  dy = baseAnchor[1] - markAnchor.y;
@@ -2242,9 +2242,155 @@ function shapeThaiText(str, fontData) {
2242
2242
  return shaped;
2243
2243
  }
2244
2244
 
2245
+ // src/shaping/script-registry.ts
2246
+ var ARABIC_START = 1536;
2247
+ var ARABIC_END = 1791;
2248
+ var ARABIC_SUPPLEMENT_START = 1872;
2249
+ var ARABIC_SUPPLEMENT_END = 1919;
2250
+ var ARABIC_EXTENDED_A_START = 2208;
2251
+ var ARABIC_EXTENDED_A_END = 2303;
2252
+ var ARABIC_PRES_A_START = 64336;
2253
+ var ARABIC_PRES_A_END = 65023;
2254
+ var ARABIC_PRES_B_START = 65136;
2255
+ var ARABIC_PRES_B_END = 65279;
2256
+ var HEBREW_START = 1424;
2257
+ var HEBREW_END = 1535;
2258
+ var HEBREW_PRES_START = 64285;
2259
+ var HEBREW_PRES_END = 64335;
2260
+ var THAI_START = 3584;
2261
+ var THAI_END = 3711;
2262
+ var DEVANAGARI_START = 2304;
2263
+ var DEVANAGARI_END = 2431;
2264
+ var DEVANAGARI_EXT_START = 43232;
2265
+ var DEVANAGARI_EXT_END = 43263;
2266
+ var CYRILLIC_START = 1024;
2267
+ var CYRILLIC_END = 1279;
2268
+ var CYRILLIC_SUPPLEMENT_START = 1280;
2269
+ var CYRILLIC_SUPPLEMENT_END = 1327;
2270
+ var CYRILLIC_EXT_A_START = 11744;
2271
+ var CYRILLIC_EXT_A_END = 11775;
2272
+ var CYRILLIC_EXT_B_START = 42560;
2273
+ var CYRILLIC_EXT_B_END = 42655;
2274
+ var GEORGIAN_START = 4256;
2275
+ var GEORGIAN_END = 4351;
2276
+ var GEORGIAN_SUPPLEMENT_START = 11520;
2277
+ var GEORGIAN_SUPPLEMENT_END = 11567;
2278
+ var ARMENIAN_START = 1328;
2279
+ var ARMENIAN_END = 1423;
2280
+ var ARMENIAN_LIGATURES_START = 64275;
2281
+ var ARMENIAN_LIGATURES_END = 64279;
2282
+ var BENGALI_START = 2432;
2283
+ var BENGALI_END = 2559;
2284
+ var TAMIL_START = 2944;
2285
+ var TAMIL_END = 3071;
2286
+ var EMOJI_RANGES = [
2287
+ [127744, 128511],
2288
+ // Miscellaneous Symbols and Pictographs
2289
+ [128512, 128591],
2290
+ // Emoticons
2291
+ [128640, 128767],
2292
+ // Transport and Map Symbols
2293
+ [128768, 128895],
2294
+ // Alchemical Symbols (partial)
2295
+ [128896, 129023],
2296
+ // Geometric Shapes Extended
2297
+ [129024, 129279],
2298
+ // Supplemental Arrows-C
2299
+ [129280, 129535],
2300
+ // Supplemental Symbols and Pictographs
2301
+ [129536, 129647],
2302
+ // Chess Symbols
2303
+ [129648, 129791],
2304
+ // Symbols and Pictographs Extended-A
2305
+ [9728, 9983],
2306
+ // Miscellaneous Symbols
2307
+ [9984, 10175],
2308
+ // Dingbats
2309
+ [126976, 127023],
2310
+ // Mahjong Tiles
2311
+ [127136, 127231]
2312
+ // Playing Cards
2313
+ ];
2314
+ var FITZPATRICK_START = 127995;
2315
+ var FITZPATRICK_END = 127999;
2316
+ function isArabicCodepoint(cp) {
2317
+ 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;
2318
+ }
2319
+ function isHebrewCodepoint(cp) {
2320
+ return cp >= HEBREW_START && cp <= HEBREW_END || cp >= HEBREW_PRES_START && cp <= HEBREW_PRES_END;
2321
+ }
2322
+ function isThaiCodepoint(cp) {
2323
+ return cp >= THAI_START && cp <= THAI_END;
2324
+ }
2325
+ function isCyrillicCodepoint(cp) {
2326
+ return cp >= CYRILLIC_START && cp <= CYRILLIC_END || cp >= CYRILLIC_SUPPLEMENT_START && cp <= CYRILLIC_SUPPLEMENT_END || cp >= CYRILLIC_EXT_A_START && cp <= CYRILLIC_EXT_A_END || cp >= CYRILLIC_EXT_B_START && cp <= CYRILLIC_EXT_B_END;
2327
+ }
2328
+ function isGeorgianCodepoint(cp) {
2329
+ return cp >= GEORGIAN_START && cp <= GEORGIAN_END || cp >= GEORGIAN_SUPPLEMENT_START && cp <= GEORGIAN_SUPPLEMENT_END;
2330
+ }
2331
+ function isArmenianCodepoint(cp) {
2332
+ return cp >= ARMENIAN_START && cp <= ARMENIAN_END || cp >= ARMENIAN_LIGATURES_START && cp <= ARMENIAN_LIGATURES_END;
2333
+ }
2334
+ function isBengaliCodepoint(cp) {
2335
+ return cp >= BENGALI_START && cp <= BENGALI_END;
2336
+ }
2337
+ function isTamilCodepoint(cp) {
2338
+ return cp >= TAMIL_START && cp <= TAMIL_END;
2339
+ }
2340
+ function isDevanagariCodepoint(cp) {
2341
+ return cp >= DEVANAGARI_START && cp <= DEVANAGARI_END || cp >= DEVANAGARI_EXT_START && cp <= DEVANAGARI_EXT_END;
2342
+ }
2343
+ function isEmojiCodepoint(cp) {
2344
+ if (cp >= FITZPATRICK_START && cp <= FITZPATRICK_END) return true;
2345
+ for (const [lo, hi] of EMOJI_RANGES) {
2346
+ if (cp >= lo && cp <= hi) return true;
2347
+ }
2348
+ return false;
2349
+ }
2350
+ function containsArabic(text) {
2351
+ for (let i = 0; i < text.length; ) {
2352
+ const cp = text.codePointAt(i) ?? 0;
2353
+ if (isArabicCodepoint(cp)) return true;
2354
+ i += cp > 65535 ? 2 : 1;
2355
+ }
2356
+ return false;
2357
+ }
2358
+ function containsHebrew(text) {
2359
+ for (let i = 0; i < text.length; ) {
2360
+ const cp = text.codePointAt(i) ?? 0;
2361
+ if (isHebrewCodepoint(cp)) return true;
2362
+ i += cp > 65535 ? 2 : 1;
2363
+ }
2364
+ return false;
2365
+ }
2366
+ function containsThai(str) {
2367
+ for (let i = 0; i < str.length; i++) {
2368
+ if (isThaiCodepoint(str.charCodeAt(i))) return true;
2369
+ }
2370
+ return false;
2371
+ }
2372
+ function containsBengali(str) {
2373
+ for (let i = 0; i < str.length; i++) {
2374
+ if (isBengaliCodepoint(str.charCodeAt(i))) return true;
2375
+ }
2376
+ return false;
2377
+ }
2378
+ function containsTamil(str) {
2379
+ for (let i = 0; i < str.length; i++) {
2380
+ if (isTamilCodepoint(str.charCodeAt(i))) return true;
2381
+ }
2382
+ return false;
2383
+ }
2384
+ function containsDevanagari(str) {
2385
+ for (let i = 0; i < str.length; i++) {
2386
+ if (isDevanagariCodepoint(str.charCodeAt(i))) return true;
2387
+ }
2388
+ return false;
2389
+ }
2390
+
2245
2391
  // src/shaping/script-detect.ts
2246
2392
  function needsUnicodeFont(lang) {
2247
- return ["th", "ja", "zh", "ko", "el", "hi", "tr", "vi", "pl", "ar", "he", "ru", "ka", "hy"].includes(lang);
2393
+ return ["th", "ja", "zh", "ko", "el", "hi", "tr", "vi", "pl", "ar", "he", "ru", "ka", "hy", "emoji"].includes(lang);
2248
2394
  }
2249
2395
  function detectFallbackLangs(texts, primaryLang) {
2250
2396
  const needed = /* @__PURE__ */ new Set();
@@ -2321,6 +2467,10 @@ function detectFallbackLangs(texts, primaryLang) {
2321
2467
  needed.add("hy");
2322
2468
  continue;
2323
2469
  }
2470
+ if (isEmojiCodepoint(cp)) {
2471
+ needed.add("emoji");
2472
+ continue;
2473
+ }
2324
2474
  }
2325
2475
  }
2326
2476
  needed.delete(primaryLang);
@@ -2341,6 +2491,7 @@ function detectCharLang(cp) {
2341
2491
  if (cp >= 1024 && cp <= 1279 || cp >= 1280 && cp <= 1327) return "ru";
2342
2492
  if (cp >= 4256 && cp <= 4351 || cp >= 11520 && cp <= 11567) return "ka";
2343
2493
  if (cp >= 1328 && cp <= 1423 || cp >= 64275 && cp <= 64279) return "hy";
2494
+ if (isEmojiCodepoint(cp)) return "emoji";
2344
2495
  return null;
2345
2496
  }
2346
2497
 
@@ -2440,8 +2591,24 @@ function pdfString(str) {
2440
2591
  }
2441
2592
  function truncate(str, max) {
2442
2593
  if (!str || str.length <= max) return str || "";
2443
- if (max <= 2) return "..";
2444
- return str.slice(0, max - 2) + "..";
2594
+ if (max <= 1) return "\u2026";
2595
+ return str.slice(0, max - 1) + "\u2026";
2596
+ }
2597
+ function truncateToWidth(str, maxWidthPt, sz, enc) {
2598
+ if (!str) return "";
2599
+ const measure = (s) => enc.isUnicode ? enc.tw(s, sz) : helveticaWidth(s, sz);
2600
+ if (measure(str) <= maxWidthPt) return str;
2601
+ const ell = "\u2026";
2602
+ const ellW = measure(ell);
2603
+ if (maxWidthPt <= ellW) return ell;
2604
+ let lo = 0;
2605
+ let hi = str.length;
2606
+ while (lo < hi) {
2607
+ const mid = lo + hi + 1 >> 1;
2608
+ if (measure(str.slice(0, mid) + ell) <= maxWidthPt) lo = mid;
2609
+ else hi = mid - 1;
2610
+ }
2611
+ return lo <= 0 ? ell : str.slice(0, lo) + ell;
2445
2612
  }
2446
2613
  function helveticaWidth(str, sz) {
2447
2614
  let w = 0;
@@ -2467,113 +2634,25 @@ function helveticaWidth(str, sz) {
2467
2634
  return w * sz / 1e3;
2468
2635
  }
2469
2636
 
2470
- // src/shaping/script-registry.ts
2471
- var ARABIC_START = 1536;
2472
- var ARABIC_END = 1791;
2473
- var ARABIC_SUPPLEMENT_START = 1872;
2474
- var ARABIC_SUPPLEMENT_END = 1919;
2475
- var ARABIC_EXTENDED_A_START = 2208;
2476
- var ARABIC_EXTENDED_A_END = 2303;
2477
- var ARABIC_PRES_A_START = 64336;
2478
- var ARABIC_PRES_A_END = 65023;
2479
- var ARABIC_PRES_B_START = 65136;
2480
- var ARABIC_PRES_B_END = 65279;
2481
- var HEBREW_START = 1424;
2482
- var HEBREW_END = 1535;
2483
- var HEBREW_PRES_START = 64285;
2484
- var HEBREW_PRES_END = 64335;
2485
- var THAI_START = 3584;
2486
- var THAI_END = 3711;
2487
- var DEVANAGARI_START = 2304;
2488
- var DEVANAGARI_END = 2431;
2489
- var DEVANAGARI_EXT_START = 43232;
2490
- var DEVANAGARI_EXT_END = 43263;
2491
- var CYRILLIC_START = 1024;
2492
- var CYRILLIC_END = 1279;
2493
- var CYRILLIC_SUPPLEMENT_START = 1280;
2494
- var CYRILLIC_SUPPLEMENT_END = 1327;
2495
- var CYRILLIC_EXT_A_START = 11744;
2496
- var CYRILLIC_EXT_A_END = 11775;
2497
- var CYRILLIC_EXT_B_START = 42560;
2498
- var CYRILLIC_EXT_B_END = 42655;
2499
- var GEORGIAN_START = 4256;
2500
- var GEORGIAN_END = 4351;
2501
- var GEORGIAN_SUPPLEMENT_START = 11520;
2502
- var GEORGIAN_SUPPLEMENT_END = 11567;
2503
- var ARMENIAN_START = 1328;
2504
- var ARMENIAN_END = 1423;
2505
- var ARMENIAN_LIGATURES_START = 64275;
2506
- var ARMENIAN_LIGATURES_END = 64279;
2507
- var BENGALI_START = 2432;
2508
- var BENGALI_END = 2559;
2509
- var TAMIL_START = 2944;
2510
- var TAMIL_END = 3071;
2511
- function isArabicCodepoint(cp) {
2512
- 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;
2513
- }
2514
- function isHebrewCodepoint(cp) {
2515
- return cp >= HEBREW_START && cp <= HEBREW_END || cp >= HEBREW_PRES_START && cp <= HEBREW_PRES_END;
2516
- }
2517
- function isThaiCodepoint(cp) {
2518
- return cp >= THAI_START && cp <= THAI_END;
2519
- }
2520
- function isCyrillicCodepoint(cp) {
2521
- return cp >= CYRILLIC_START && cp <= CYRILLIC_END || cp >= CYRILLIC_SUPPLEMENT_START && cp <= CYRILLIC_SUPPLEMENT_END || cp >= CYRILLIC_EXT_A_START && cp <= CYRILLIC_EXT_A_END || cp >= CYRILLIC_EXT_B_START && cp <= CYRILLIC_EXT_B_END;
2522
- }
2523
- function isGeorgianCodepoint(cp) {
2524
- return cp >= GEORGIAN_START && cp <= GEORGIAN_END || cp >= GEORGIAN_SUPPLEMENT_START && cp <= GEORGIAN_SUPPLEMENT_END;
2525
- }
2526
- function isArmenianCodepoint(cp) {
2527
- return cp >= ARMENIAN_START && cp <= ARMENIAN_END || cp >= ARMENIAN_LIGATURES_START && cp <= ARMENIAN_LIGATURES_END;
2528
- }
2529
- function isBengaliCodepoint(cp) {
2530
- return cp >= BENGALI_START && cp <= BENGALI_END;
2531
- }
2532
- function isTamilCodepoint(cp) {
2533
- return cp >= TAMIL_START && cp <= TAMIL_END;
2534
- }
2535
- function isDevanagariCodepoint(cp) {
2536
- return cp >= DEVANAGARI_START && cp <= DEVANAGARI_END || cp >= DEVANAGARI_EXT_START && cp <= DEVANAGARI_EXT_END;
2537
- }
2538
- function containsArabic(text) {
2539
- for (let i = 0; i < text.length; ) {
2540
- const cp = text.codePointAt(i) ?? 0;
2541
- if (isArabicCodepoint(cp)) return true;
2542
- i += cp > 65535 ? 2 : 1;
2543
- }
2544
- return false;
2545
- }
2546
- function containsHebrew(text) {
2547
- for (let i = 0; i < text.length; ) {
2548
- const cp = text.codePointAt(i) ?? 0;
2549
- if (isHebrewCodepoint(cp)) return true;
2550
- i += cp > 65535 ? 2 : 1;
2551
- }
2552
- return false;
2553
- }
2554
- function containsThai(str) {
2555
- for (let i = 0; i < str.length; i++) {
2556
- if (isThaiCodepoint(str.charCodeAt(i))) return true;
2557
- }
2558
- return false;
2559
- }
2560
- function containsBengali(str) {
2561
- for (let i = 0; i < str.length; i++) {
2562
- if (isBengaliCodepoint(str.charCodeAt(i))) return true;
2563
- }
2564
- return false;
2565
- }
2566
- function containsTamil(str) {
2567
- for (let i = 0; i < str.length; i++) {
2568
- if (isTamilCodepoint(str.charCodeAt(i))) return true;
2569
- }
2570
- return false;
2571
- }
2572
- function containsDevanagari(str) {
2573
- for (let i = 0; i < str.length; i++) {
2574
- if (isDevanagariCodepoint(str.charCodeAt(i))) return true;
2637
+ // src/shaping/gsub-driver.ts
2638
+ function tryLigature(gids, ligatures) {
2639
+ if (!ligatures || gids.length < 2) return null;
2640
+ const firstGid = gids[0];
2641
+ const entries = ligatures[firstGid];
2642
+ if (!entries) return null;
2643
+ for (const entry of entries) {
2644
+ const compCount = entry.length - 1;
2645
+ if (compCount > gids.length - 1) continue;
2646
+ let match = true;
2647
+ for (let ci = 0; ci < compCount; ci++) {
2648
+ if (gids[1 + ci] !== entry[1 + ci]) {
2649
+ match = false;
2650
+ break;
2651
+ }
2652
+ }
2653
+ if (match) return { resultGid: entry[0], consumed: compCount + 1 };
2575
2654
  }
2576
- return false;
2655
+ return null;
2577
2656
  }
2578
2657
 
2579
2658
  // src/shaping/bengali-shaper.ts
@@ -2702,43 +2781,27 @@ function shapeBengaliText(str, fontData) {
2702
2781
  if (gsub[gid] !== void 0) return gsub[gid];
2703
2782
  return gid;
2704
2783
  }
2705
- function tryLigature(gids) {
2706
- if (!ligatures || gids.length < 2) return null;
2707
- const firstGid = gids[0];
2708
- const entries = ligatures[firstGid];
2709
- if (!entries) return null;
2710
- for (const entry of entries) {
2711
- const compCount = entry.length - 1;
2712
- if (compCount > gids.length - 1) continue;
2713
- let match = true;
2714
- for (let ci = 0; ci < compCount; ci++) {
2715
- if (gids[1 + ci] !== entry[1 + ci]) {
2716
- match = false;
2717
- break;
2718
- }
2719
- }
2720
- if (match) return { resultGid: entry[0], consumed: compCount + 1 };
2721
- }
2722
- return null;
2784
+ function tryLig(gids) {
2785
+ return tryLigature(gids, ligatures);
2723
2786
  }
2724
2787
  function getAdv(gid) {
2725
2788
  return widths[gid] !== void 0 ? widths[gid] : defaultWidth;
2726
2789
  }
2727
- function getBaseAnchor(baseGid, markClass) {
2790
+ function getBaseAnchor2(baseGid, markClass) {
2728
2791
  const base = markAnchors && markAnchors.bases && markAnchors.bases[baseGid];
2729
2792
  if (!base) return null;
2730
2793
  return base[markClass] ?? null;
2731
2794
  }
2732
- function getMarkAnchor(markGid) {
2795
+ function getMarkAnchor2(markGid) {
2733
2796
  const mark = markAnchors && markAnchors.marks && markAnchors.marks[markGid];
2734
2797
  if (!mark) return null;
2735
2798
  return { classIdx: mark[0], x: mark[1], y: mark[2] };
2736
2799
  }
2737
2800
  function emitGlyph(gid, isZero, baseGid) {
2738
2801
  if (isZero && baseGid !== void 0) {
2739
- const markAnchor = getMarkAnchor(gid);
2802
+ const markAnchor = getMarkAnchor2(gid);
2740
2803
  if (markAnchor) {
2741
- const baseAnchorPt = getBaseAnchor(baseGid, markAnchor.classIdx);
2804
+ const baseAnchorPt = getBaseAnchor2(baseGid, markAnchor.classIdx);
2742
2805
  if (baseAnchorPt) {
2743
2806
  const baseAdv = getAdv(baseGid);
2744
2807
  shaped.push({
@@ -2799,7 +2862,7 @@ function shapeBengaliText(str, fontData) {
2799
2862
  }
2800
2863
  }
2801
2864
  let ligConsumed = 0;
2802
- const ligResult = tryLigature(clusterGids);
2865
+ const ligResult = tryLig(clusterGids);
2803
2866
  if (ligResult) {
2804
2867
  emitGlyph(ligResult.resultGid, false);
2805
2868
  baseGid = ligResult.resultGid;
@@ -2807,7 +2870,7 @@ function shapeBengaliText(str, fontData) {
2807
2870
  let gi = ligConsumed;
2808
2871
  while (gi < clusterGids.length) {
2809
2872
  const subSeq = clusterGids.slice(gi);
2810
- const subLig = tryLigature(subSeq);
2873
+ const subLig = tryLig(subSeq);
2811
2874
  if (subLig) {
2812
2875
  emitGlyph(subLig.resultGid, false);
2813
2876
  gi += subLig.consumed;
@@ -2970,43 +3033,27 @@ function shapeTamilText(str, fontData) {
2970
3033
  const normCp = cp === 8239 || cp === 160 ? 32 : cp;
2971
3034
  return cmap[normCp] || 0;
2972
3035
  }
2973
- function tryLigature(gids) {
2974
- if (!ligatures || gids.length < 2) return null;
2975
- const firstGid = gids[0];
2976
- const entries = ligatures[firstGid];
2977
- if (!entries) return null;
2978
- for (const entry of entries) {
2979
- const compCount = entry.length - 1;
2980
- if (compCount > gids.length - 1) continue;
2981
- let match = true;
2982
- for (let ci = 0; ci < compCount; ci++) {
2983
- if (gids[1 + ci] !== entry[1 + ci]) {
2984
- match = false;
2985
- break;
2986
- }
2987
- }
2988
- if (match) return { resultGid: entry[0], consumed: compCount + 1 };
2989
- }
2990
- return null;
3036
+ function tryLig(gids) {
3037
+ return tryLigature(gids, ligatures);
2991
3038
  }
2992
3039
  function getAdv(gid) {
2993
3040
  return widths[gid] !== void 0 ? widths[gid] : defaultWidth;
2994
3041
  }
2995
- function getBaseAnchor(baseGid, markClass) {
3042
+ function getBaseAnchor2(baseGid, markClass) {
2996
3043
  const base = markAnchors && markAnchors.bases && markAnchors.bases[baseGid];
2997
3044
  if (!base) return null;
2998
3045
  return base[markClass] ?? null;
2999
3046
  }
3000
- function getMarkAnchor(markGid) {
3047
+ function getMarkAnchor2(markGid) {
3001
3048
  const mark = markAnchors && markAnchors.marks && markAnchors.marks[markGid];
3002
3049
  if (!mark) return null;
3003
3050
  return { classIdx: mark[0], x: mark[1], y: mark[2] };
3004
3051
  }
3005
3052
  function emitGlyph(gid, isZero, baseGid) {
3006
3053
  if (isZero && baseGid !== void 0) {
3007
- const markAnchor = getMarkAnchor(gid);
3054
+ const markAnchor = getMarkAnchor2(gid);
3008
3055
  if (markAnchor) {
3009
- const baseAnchorPt = getBaseAnchor(baseGid, markAnchor.classIdx);
3056
+ const baseAnchorPt = getBaseAnchor2(baseGid, markAnchor.classIdx);
3010
3057
  if (baseAnchorPt) {
3011
3058
  const baseAdv = getAdv(baseGid);
3012
3059
  shaped.push({
@@ -3071,7 +3118,7 @@ function shapeTamilText(str, fontData) {
3071
3118
  }
3072
3119
  }
3073
3120
  let ligConsumed = 0;
3074
- const ligResult = tryLigature(clusterGids);
3121
+ const ligResult = tryLig(clusterGids);
3075
3122
  if (ligResult) {
3076
3123
  emitGlyph(ligResult.resultGid, false);
3077
3124
  baseGid = ligResult.resultGid;
@@ -3079,7 +3126,7 @@ function shapeTamilText(str, fontData) {
3079
3126
  let gi = ligConsumed;
3080
3127
  while (gi < clusterGids.length) {
3081
3128
  const subSeq = clusterGids.slice(gi);
3082
- const subLig = tryLigature(subSeq);
3129
+ const subLig = tryLig(subSeq);
3083
3130
  if (subLig) {
3084
3131
  emitGlyph(subLig.resultGid, false);
3085
3132
  gi += subLig.consumed;
@@ -3128,6 +3175,30 @@ function shapeTamilText(str, fontData) {
3128
3175
  return shaped;
3129
3176
  }
3130
3177
 
3178
+ // src/shaping/gpos-positioner.ts
3179
+ function getBaseAnchor(markAnchors, baseGid, markClass) {
3180
+ if (!markAnchors) return null;
3181
+ const base = markAnchors.bases[baseGid];
3182
+ if (!base) return null;
3183
+ return base[markClass] ?? null;
3184
+ }
3185
+ function getMarkAnchor(markAnchors, markGid) {
3186
+ if (!markAnchors) return null;
3187
+ const mark = markAnchors.marks[markGid];
3188
+ if (!mark) return null;
3189
+ return { classIdx: mark[0], x: mark[1], y: mark[2] };
3190
+ }
3191
+ function positionMarkOnBase(markAnchors, markGid, baseGid, baseAdv) {
3192
+ const mark = getMarkAnchor(markAnchors, markGid);
3193
+ if (!mark) return null;
3194
+ const base = getBaseAnchor(markAnchors, baseGid, mark.classIdx);
3195
+ if (!base) return null;
3196
+ return {
3197
+ dx: base[0] - mark.x - baseAdv,
3198
+ dy: base[1] - mark.y
3199
+ };
3200
+ }
3201
+
3131
3202
  // src/shaping/devanagari-shaper.ts
3132
3203
  var HALANT2 = 2381;
3133
3204
  var NUKTA2 = 2364;
@@ -3243,43 +3314,17 @@ function shapeDevanagariText(str, fontData) {
3243
3314
  const normCp = cp === 8239 || cp === 160 ? 32 : cp;
3244
3315
  return cmap[normCp] || 0;
3245
3316
  }
3246
- function tryLigature(gids) {
3247
- if (!ligatures || gids.length < 2) return null;
3248
- const firstGid = gids[0];
3249
- const entries = ligatures[firstGid];
3250
- if (!entries) return null;
3251
- for (const entry of entries) {
3252
- const compCount = entry.length - 1;
3253
- if (compCount > gids.length - 1) continue;
3254
- let match = true;
3255
- for (let ci = 0; ci < compCount; ci++) {
3256
- if (gids[1 + ci] !== entry[1 + ci]) {
3257
- match = false;
3258
- break;
3259
- }
3260
- }
3261
- if (match) return { resultGid: entry[0], consumed: compCount + 1 };
3262
- }
3263
- return null;
3317
+ function tryLig(gids) {
3318
+ return tryLigature(gids, ligatures);
3264
3319
  }
3265
3320
  function getAdv(gid) {
3266
3321
  return widths[gid] !== void 0 ? widths[gid] : defaultWidth;
3267
3322
  }
3268
- function getBaseAnchor(baseGid, markClass) {
3269
- const base = markAnchors && markAnchors.bases && markAnchors.bases[baseGid];
3270
- if (!base) return null;
3271
- return base[markClass] ?? null;
3272
- }
3273
- function getMarkAnchor(markGid) {
3274
- const mark = markAnchors && markAnchors.marks && markAnchors.marks[markGid];
3275
- if (!mark) return null;
3276
- return { classIdx: mark[0], x: mark[1], y: mark[2] };
3277
- }
3278
3323
  function emitGlyph(gid, isZero, baseGid) {
3279
3324
  if (isZero && baseGid !== void 0) {
3280
- const markAnchor = getMarkAnchor(gid);
3325
+ const markAnchor = getMarkAnchor(markAnchors, gid);
3281
3326
  if (markAnchor) {
3282
- const baseAnchorPt = getBaseAnchor(baseGid, markAnchor.classIdx);
3327
+ const baseAnchorPt = getBaseAnchor(markAnchors, baseGid, markAnchor.classIdx);
3283
3328
  if (baseAnchorPt) {
3284
3329
  const baseAdv = getAdv(baseGid);
3285
3330
  shaped.push({
@@ -3327,7 +3372,7 @@ function shapeDevanagariText(str, fontData) {
3327
3372
  if (hasReph) {
3328
3373
  const raGid = resolveGid(RA2);
3329
3374
  const halantGid = resolveGid(HALANT2);
3330
- const rephLig = tryLigature([raGid, halantGid]);
3375
+ const rephLig = tryLig([raGid, halantGid]);
3331
3376
  if (rephLig) {
3332
3377
  emitGlyph(rephLig.resultGid, true, baseGid);
3333
3378
  } else {
@@ -3350,14 +3395,14 @@ function shapeDevanagariText(str, fontData) {
3350
3395
  break;
3351
3396
  }
3352
3397
  }
3353
- const ligResult = tryLigature(clusterGids);
3398
+ const ligResult = tryLig(clusterGids);
3354
3399
  if (ligResult) {
3355
3400
  emitGlyph(ligResult.resultGid, false);
3356
3401
  baseGid = ligResult.resultGid;
3357
3402
  let gi = ligResult.consumed;
3358
3403
  while (gi < clusterGids.length) {
3359
3404
  const subSeq = clusterGids.slice(gi);
3360
- const subLig = tryLigature(subSeq);
3405
+ const subLig = tryLig(subSeq);
3361
3406
  if (subLig) {
3362
3407
  emitGlyph(subLig.resultGid, false);
3363
3408
  gi += subLig.consumed;
@@ -3531,6 +3576,10 @@ function shapeArabicText(str, fontData) {
3531
3576
  const forms = resolvePositionalForms(codePoints);
3532
3577
  const glyphs = [];
3533
3578
  const cmap = fontData.cmap;
3579
+ const widths = fontData.widths;
3580
+ const defaultWidth = fontData.defaultWidth;
3581
+ const markAnchors = fontData.markAnchors;
3582
+ let lastBaseGid = 0;
3534
3583
  for (let i = 0; i < codePoints.length; i++) {
3535
3584
  const cp = codePoints[i];
3536
3585
  if (i < codePoints.length - 1 && isLamAlef(cp, codePoints[i + 1])) {
@@ -3541,6 +3590,7 @@ function shapeArabicText(str, fontData) {
3541
3590
  const ligGid = cmap[ligCP];
3542
3591
  if (ligGid) {
3543
3592
  glyphs.push({ gid: ligGid, dx: 0, dy: 0, isZeroAdvance: false });
3593
+ lastBaseGid = ligGid;
3544
3594
  i++;
3545
3595
  continue;
3546
3596
  }
@@ -3562,7 +3612,16 @@ function shapeArabicText(str, fontData) {
3562
3612
  }
3563
3613
  const joining = getJoiningType(cp);
3564
3614
  const isZeroAdvance = joining === "T";
3615
+ if (isZeroAdvance && lastBaseGid !== 0) {
3616
+ const baseAdv = widths[lastBaseGid] !== void 0 ? widths[lastBaseGid] : defaultWidth;
3617
+ const offset = positionMarkOnBase(markAnchors, gid, lastBaseGid, baseAdv);
3618
+ if (offset) {
3619
+ glyphs.push({ gid, dx: offset.dx, dy: offset.dy, isZeroAdvance: true });
3620
+ continue;
3621
+ }
3622
+ }
3565
3623
  glyphs.push({ gid, dx: 0, dy: 0, isZeroAdvance });
3624
+ if (!isZeroAdvance) lastBaseGid = gid;
3566
3625
  }
3567
3626
  return glyphs;
3568
3627
  }
@@ -3575,7 +3634,7 @@ function classifyBidiType(cp) {
3575
3634
  cp >= 1611 && cp <= 1631 || // Arabic harakat
3576
3635
  cp >= 1648 && cp <= 1648 || // Arabic superscript alef
3577
3636
  cp >= 1750 && cp <= 1756 || cp >= 1759 && cp <= 1764 || cp >= 1767 && cp <= 1768 || cp >= 1770 && cp <= 1773 || cp >= 65056 && cp <= 65071) return "NSM";
3578
- if (cp === 8203 || cp === 8204 || cp === 8205 || cp === 8206 || cp === 8207 || cp === 65279) return "BN";
3637
+ if (cp === 8203 || cp === 8204 || cp === 8205 || cp === 8206 || cp === 8207 || cp === 65279 || cp === 8294 || cp === 8295 || cp === 8296 || cp === 8297) return "BN";
3579
3638
  if (cp >= 1632 && cp <= 1641) return "AN";
3580
3639
  if (cp >= 1776 && cp <= 1785) return "AN";
3581
3640
  if (cp >= 48 && cp <= 57) return "EN";
@@ -3789,18 +3848,153 @@ function fixBracketPairing(types, codePoints, len) {
3789
3848
  }
3790
3849
  }
3791
3850
  }
3851
+ function findOutermostIsolatePairs(codePoints) {
3852
+ const pairs = [];
3853
+ let i = 0;
3854
+ while (i < codePoints.length) {
3855
+ const cp = codePoints[i];
3856
+ if (cp === 8294 || cp === 8295 || cp === 8296) {
3857
+ let depth = 1;
3858
+ let close = -1;
3859
+ for (let j = i + 1; j < codePoints.length; j++) {
3860
+ const cj = codePoints[j];
3861
+ if (cj === 8294 || cj === 8295 || cj === 8296) depth++;
3862
+ else if (cj === 8297) {
3863
+ depth--;
3864
+ if (depth === 0) {
3865
+ close = j;
3866
+ break;
3867
+ }
3868
+ }
3869
+ }
3870
+ if (close === -1) {
3871
+ i++;
3872
+ continue;
3873
+ }
3874
+ const kind = cp === 8294 ? "LRI" : cp === 8295 ? "RLI" : "FSI";
3875
+ pairs.push({ open: i, close, kind });
3876
+ i = close + 1;
3877
+ } else {
3878
+ i++;
3879
+ }
3880
+ }
3881
+ return pairs;
3882
+ }
3792
3883
  function resolveBidiRuns(text) {
3793
3884
  if (!text) return [];
3794
3885
  const codePoints = [];
3886
+ const cpToStr = [];
3887
+ for (let i = 0; i < text.length; ) {
3888
+ cpToStr.push(i);
3889
+ const cp = text.codePointAt(i) ?? 0;
3890
+ codePoints.push(cp);
3891
+ i += cp > 65535 ? 2 : 1;
3892
+ }
3893
+ cpToStr.push(text.length);
3894
+ const isolates = findOutermostIsolatePairs(codePoints);
3895
+ if (isolates.length === 0) {
3896
+ return resolveBidiCore(text, codePoints, cpToStr);
3897
+ }
3898
+ const insideIsolate = new Array(codePoints.length).fill(false);
3899
+ for (const p of isolates) {
3900
+ for (let k = p.open; k <= p.close; k++) insideIsolate[k] = true;
3901
+ }
3902
+ const outerTypes = codePoints.map((cp, idx) => insideIsolate[idx] ? "BN" : classifyBidiType(cp));
3903
+ const parentLevel = detectParagraphLevel(outerTypes);
3904
+ const out = [];
3905
+ const emitSegment = (cpStart, cpEnd, forced) => {
3906
+ if (cpStart >= cpEnd) return;
3907
+ const segText = text.substring(cpToStr[cpStart], cpToStr[cpEnd]);
3908
+ const segCps = codePoints.slice(cpStart, cpEnd);
3909
+ const baseStrIdx = cpToStr[cpStart];
3910
+ const segCpToStr = cpToStr.slice(cpStart, cpEnd + 1).map((x) => x - baseStrIdx);
3911
+ const segRuns = forced === void 0 ? resolveBidiRuns(segText) : resolveBidiCore(segText, segCps, segCpToStr, forced);
3912
+ for (const r of segRuns) {
3913
+ out.push({ text: r.text, level: r.level, start: r.start + baseStrIdx });
3914
+ }
3915
+ };
3916
+ let cursor = 0;
3917
+ for (const pair of isolates) {
3918
+ emitSegment(cursor, pair.open, parentLevel);
3919
+ const innerStart = pair.open + 1;
3920
+ const innerEnd = pair.close;
3921
+ let innerLevel;
3922
+ if (pair.kind === "LRI") innerLevel = 0;
3923
+ else if (pair.kind === "RLI") innerLevel = 1;
3924
+ else {
3925
+ const innerTypes = codePoints.slice(innerStart, innerEnd).map(classifyBidiType);
3926
+ innerLevel = detectParagraphLevel(innerTypes);
3927
+ }
3928
+ if (innerStart < innerEnd) {
3929
+ const innerText = text.substring(cpToStr[innerStart], cpToStr[innerEnd]);
3930
+ const innerRuns = resolveBidiRunsForced(innerText, innerLevel);
3931
+ const baseStrIdx = cpToStr[innerStart];
3932
+ for (const r of innerRuns) {
3933
+ out.push({ text: r.text, level: r.level, start: r.start + baseStrIdx });
3934
+ }
3935
+ }
3936
+ cursor = pair.close + 1;
3937
+ }
3938
+ emitSegment(cursor, codePoints.length, parentLevel);
3939
+ return out;
3940
+ }
3941
+ function resolveBidiRunsForced(text, forcedLevel) {
3942
+ if (!text) return [];
3943
+ const codePoints = [];
3944
+ const cpToStr = [];
3795
3945
  for (let i = 0; i < text.length; ) {
3946
+ cpToStr.push(i);
3796
3947
  const cp = text.codePointAt(i) ?? 0;
3797
3948
  codePoints.push(cp);
3798
3949
  i += cp > 65535 ? 2 : 1;
3799
3950
  }
3951
+ cpToStr.push(text.length);
3952
+ const isolates = findOutermostIsolatePairs(codePoints);
3953
+ if (isolates.length === 0) {
3954
+ return resolveBidiCore(text, codePoints, cpToStr, forcedLevel);
3955
+ }
3956
+ const out = [];
3957
+ const emit = (cpStart, cpEnd, forced) => {
3958
+ if (cpStart >= cpEnd) return;
3959
+ const segText = text.substring(cpToStr[cpStart], cpToStr[cpEnd]);
3960
+ const segCps = codePoints.slice(cpStart, cpEnd);
3961
+ const baseStrIdx = cpToStr[cpStart];
3962
+ const segCpToStr = cpToStr.slice(cpStart, cpEnd + 1).map((x) => x - baseStrIdx);
3963
+ const segRuns = resolveBidiCore(segText, segCps, segCpToStr, forced);
3964
+ for (const r of segRuns) {
3965
+ out.push({ text: r.text, level: r.level, start: r.start + baseStrIdx });
3966
+ }
3967
+ };
3968
+ let cursor = 0;
3969
+ for (const pair of isolates) {
3970
+ emit(cursor, pair.open, forcedLevel);
3971
+ const innerStart = pair.open + 1;
3972
+ const innerEnd = pair.close;
3973
+ let innerLevel;
3974
+ if (pair.kind === "LRI") innerLevel = 0;
3975
+ else if (pair.kind === "RLI") innerLevel = 1;
3976
+ else {
3977
+ const innerTypes = codePoints.slice(innerStart, innerEnd).map(classifyBidiType);
3978
+ innerLevel = detectParagraphLevel(innerTypes);
3979
+ }
3980
+ if (innerStart < innerEnd) {
3981
+ const innerText = text.substring(cpToStr[innerStart], cpToStr[innerEnd]);
3982
+ const innerRuns = resolveBidiRunsForced(innerText, innerLevel);
3983
+ const baseStrIdx = cpToStr[innerStart];
3984
+ for (const r of innerRuns) {
3985
+ out.push({ text: r.text, level: r.level, start: r.start + baseStrIdx });
3986
+ }
3987
+ }
3988
+ cursor = pair.close + 1;
3989
+ }
3990
+ emit(cursor, codePoints.length, forcedLevel);
3991
+ return out;
3992
+ }
3993
+ function resolveBidiCore(text, codePoints, cpToStr, forcedLevel) {
3800
3994
  const len = codePoints.length;
3801
3995
  if (len === 0) return [];
3802
3996
  const types = codePoints.map(classifyBidiType);
3803
- const paraLevel = detectParagraphLevel(types);
3997
+ const paraLevel = forcedLevel !== void 0 ? forcedLevel : detectParagraphLevel(types);
3804
3998
  resolveWeakTypes(types, paraLevel);
3805
3999
  resolveNeutralTypes(types, paraLevel);
3806
4000
  if (paraLevel === 1) {
@@ -3811,13 +4005,6 @@ function resolveBidiRuns(text) {
3811
4005
  const runs = [];
3812
4006
  let runStart = 0;
3813
4007
  let runLevel = levels[0];
3814
- const cpToStr = [];
3815
- let strIdx = 0;
3816
- for (let i = 0; i < len; i++) {
3817
- cpToStr.push(strIdx);
3818
- strIdx += codePoints[i] > 65535 ? 2 : 1;
3819
- }
3820
- cpToStr.push(strIdx);
3821
4008
  for (let i = 1; i <= len; i++) {
3822
4009
  if (i === len || levels[i] !== runLevel) {
3823
4010
  const start = cpToStr[runStart];
@@ -3901,7 +4088,7 @@ function splitArabicNonArabic(text, fd) {
3901
4088
  if (cur) segments.push({ text: cur, arabic: curArabic });
3902
4089
  return segments;
3903
4090
  }
3904
- function buildTextRunsWithFallback(text, fontRef, fd, sz, trackGid) {
4091
+ function buildTextRunsWithFallback(text, fontRef, fd, sz, trackGid, pdfA = false) {
3905
4092
  const upm = fd.metrics.unitsPerEm;
3906
4093
  const result = [];
3907
4094
  let mode = null;
@@ -3941,11 +4128,11 @@ function buildTextRunsWithFallback(text, fontRef, fd, sz, trackGid) {
3941
4128
  const cp = rawCp === 8239 || rawCp === 160 ? 32 : rawCp;
3942
4129
  const char = text.substring(i, i + charLen);
3943
4130
  const gid = fd.cmap[cp] ?? 0;
3944
- if (gid === 0 && isWinAnsi(cp)) {
4131
+ if (gid === 0 && isWinAnsi(cp) && !pdfA) {
3945
4132
  if (mode === "cid") flushCid();
3946
4133
  mode = "hel";
3947
4134
  helChars += char;
3948
- } else if (mode === "hel" && isWinAnsi(cp)) {
4135
+ } else if (mode === "hel" && isWinAnsi(cp) && !pdfA) {
3949
4136
  helChars += char;
3950
4137
  } else {
3951
4138
  if (mode === "hel") flushHel();
@@ -3962,7 +4149,7 @@ function buildTextRunsWithFallback(text, fontRef, fd, sz, trackGid) {
3962
4149
  if (mode === "hel") flushHel();
3963
4150
  return result;
3964
4151
  }
3965
- function createEncodingContext(fontEntries) {
4152
+ function createEncodingContext(fontEntries, pdfA = false) {
3966
4153
  if (!fontEntries || fontEntries.length === 0) {
3967
4154
  return {
3968
4155
  isUnicode: false,
@@ -4018,12 +4205,12 @@ function createEncodingContext(fontEntries) {
4018
4205
  }
4019
4206
  result.push({ text: seg.text, fontRef, fontData: fd, shaped: visual, hexStr: null, widthPt: designW * sz / upm });
4020
4207
  } else {
4021
- const subRuns = buildTextRunsWithFallback(seg.text, fontRef, fd, sz, _trackGid);
4208
+ const subRuns = buildTextRunsWithFallback(seg.text, fontRef, fd, sz, _trackGid, pdfA);
4022
4209
  result.push(...subRuns);
4023
4210
  }
4024
4211
  }
4025
4212
  } else if (isRTL) {
4026
- const subRuns = buildTextRunsWithFallback(fRun.text, fontRef, fd, sz, _trackGid);
4213
+ const subRuns = buildTextRunsWithFallback(fRun.text, fontRef, fd, sz, _trackGid, pdfA);
4027
4214
  result.push(...subRuns);
4028
4215
  } else {
4029
4216
  if (containsThai(fRun.text)) {
@@ -4067,7 +4254,7 @@ function createEncodingContext(fontEntries) {
4067
4254
  }
4068
4255
  result.push({ text: fRun.text, fontRef, fontData: fd, shaped, hexStr: null, widthPt: designW * sz / upm });
4069
4256
  } else {
4070
- const subRuns = buildTextRunsWithFallback(fRun.text, fontRef, fd, sz, _trackGid);
4257
+ const subRuns = buildTextRunsWithFallback(fRun.text, fontRef, fd, sz, _trackGid, pdfA);
4071
4258
  result.push(...subRuns);
4072
4259
  }
4073
4260
  }
@@ -4124,7 +4311,7 @@ function createEncodingContext(fontEntries) {
4124
4311
  }
4125
4312
  return [{ text: run.text, fontRef, fontData: fd, shaped, hexStr: null, widthPt: designW * sz / upm }];
4126
4313
  }
4127
- return buildTextRunsWithFallback(run.text, fontRef, fd, sz, _trackGid);
4314
+ return buildTextRunsWithFallback(run.text, fontRef, fd, sz, _trackGid, pdfA);
4128
4315
  });
4129
4316
  },
4130
4317
  ps(str) {
@@ -4638,7 +4825,7 @@ function buildPdfMetadata(now = /* @__PURE__ */ new Date()) {
4638
4825
  const xmpDate = `${yyyy}-${mm}-${dd}T${hh}:${mi}:${ss}${tzSign}${tzH}:${tzM}`;
4639
4826
  return { pdfDate, xmpDate };
4640
4827
  }
4641
- function buildXMPMetadata(title, createDate, pdfaPart = 2, pdfaConformance = "B", author) {
4828
+ function buildXMPMetadata(title, createDate, pdfaPart = 2, pdfaConformance = "B", author, subject, keywords) {
4642
4829
  const escapedTitle = escapeXml(title);
4643
4830
  const lines = [
4644
4831
  '<?xpacket begin="\xEF\xBB\xBF" id="W5M0MpCehiHzreSzNTczkc9d"?>',
@@ -4654,13 +4841,21 @@ function buildXMPMetadata(title, createDate, pdfaPart = 2, pdfaConformance = "B"
4654
4841
  if (author !== void 0 && author !== "") {
4655
4842
  lines.push(` <dc:creator><rdf:Seq><rdf:li>${escapeXml(author)}</rdf:li></rdf:Seq></dc:creator>`);
4656
4843
  }
4844
+ if (subject !== void 0 && subject !== "") {
4845
+ lines.push(` <dc:description><rdf:Alt><rdf:li xml:lang="x-default">${escapeXml(subject)}</rdf:li></rdf:Alt></dc:description>`);
4846
+ }
4657
4847
  lines.push(
4658
4848
  " <pdf:Producer>pdfnative</pdf:Producer>",
4659
4849
  ` <xmp:CreateDate>${createDate}</xmp:CreateDate>`,
4660
4850
  ` <xmp:ModifyDate>${createDate}</xmp:ModifyDate>`,
4661
4851
  ` <xmp:MetadataDate>${createDate}</xmp:MetadataDate>`,
4662
4852
  ` <pdfaid:part>${pdfaPart}</pdfaid:part>`,
4663
- ` <pdfaid:conformance>${pdfaConformance}</pdfaid:conformance>`,
4853
+ ` <pdfaid:conformance>${pdfaConformance}</pdfaid:conformance>`
4854
+ );
4855
+ if (keywords !== void 0 && keywords !== "") {
4856
+ lines.push(` <pdf:Keywords>${escapeXml(keywords)}</pdf:Keywords>`);
4857
+ }
4858
+ lines.push(
4664
4859
  " </rdf:Description>",
4665
4860
  " </rdf:RDF>",
4666
4861
  "</x:xmpmeta>",
@@ -4668,6 +4863,35 @@ function buildXMPMetadata(title, createDate, pdfaPart = 2, pdfaConformance = "B"
4668
4863
  );
4669
4864
  return lines.join("\n");
4670
4865
  }
4866
+ function utf8EncodeBinaryString(str) {
4867
+ let out = "";
4868
+ for (let i = 0; i < str.length; i++) {
4869
+ let cp = str.charCodeAt(i);
4870
+ if (cp >= 55296 && cp <= 56319 && i + 1 < str.length) {
4871
+ const lo = str.charCodeAt(i + 1);
4872
+ if (lo >= 56320 && lo <= 57343) {
4873
+ cp = (cp - 55296 << 10) + (lo - 56320) + 65536;
4874
+ i++;
4875
+ }
4876
+ }
4877
+ if (cp < 128) {
4878
+ out += String.fromCharCode(cp);
4879
+ } else if (cp < 2048) {
4880
+ out += String.fromCharCode(192 | cp >> 6);
4881
+ out += String.fromCharCode(128 | cp & 63);
4882
+ } else if (cp < 65536) {
4883
+ out += String.fromCharCode(224 | cp >> 12);
4884
+ out += String.fromCharCode(128 | cp >> 6 & 63);
4885
+ out += String.fromCharCode(128 | cp & 63);
4886
+ } else {
4887
+ out += String.fromCharCode(240 | cp >> 18);
4888
+ out += String.fromCharCode(128 | cp >> 12 & 63);
4889
+ out += String.fromCharCode(128 | cp >> 6 & 63);
4890
+ out += String.fromCharCode(128 | cp & 63);
4891
+ }
4892
+ }
4893
+ return out;
4894
+ }
4671
4895
  function buildOutputIntentDict(iccStreamObjNum, subtype = "GTS_PDFA1") {
4672
4896
  return `<< /Type /OutputIntent /S /${subtype} /OutputConditionIdentifier (sRGB IEC61966-2.1) /RegistryName (http://www.color.org) /DestOutputProfile ${iccStreamObjNum} 0 R >>`;
4673
4897
  }
@@ -5002,13 +5226,42 @@ var DEFAULT_COLUMNS = [
5002
5226
  { f: 0.18, a: "c", mx: 20, mxH: 20 }
5003
5227
  ];
5004
5228
  function computeColumnPositions(columns, marginLeft, contentWidth) {
5005
- const cx = [];
5006
- const cwi = [];
5229
+ const n = columns.length;
5230
+ const cwi = new Array(n).fill(0);
5231
+ const fixed = new Array(n).fill(false);
5232
+ let totalFixed = 0;
5233
+ let freeWeight = 0;
5234
+ for (let i = 0; i < n; i++) {
5235
+ const col = columns[i];
5236
+ let w = col.f * contentWidth;
5237
+ let clamped = false;
5238
+ if (col.minWidth !== void 0 && w < col.minWidth) {
5239
+ w = col.minWidth;
5240
+ clamped = true;
5241
+ }
5242
+ if (col.maxWidth !== void 0 && w > col.maxWidth) {
5243
+ w = col.maxWidth;
5244
+ clamped = true;
5245
+ }
5246
+ if (clamped) {
5247
+ cwi[i] = w;
5248
+ fixed[i] = true;
5249
+ totalFixed += w;
5250
+ } else {
5251
+ freeWeight += col.f;
5252
+ }
5253
+ }
5254
+ const remaining = contentWidth - totalFixed;
5255
+ if (freeWeight > 0) {
5256
+ for (let i = 0; i < n; i++) {
5257
+ if (!fixed[i]) cwi[i] = columns[i].f / freeWeight * remaining;
5258
+ }
5259
+ }
5260
+ const cx = new Array(n);
5007
5261
  let x = marginLeft;
5008
- for (const col of columns) {
5009
- cx.push(x);
5010
- cwi.push(col.f * contentWidth);
5011
- x += col.f * contentWidth;
5262
+ for (let i = 0; i < n; i++) {
5263
+ cx[i] = x;
5264
+ x += cwi[i];
5012
5265
  }
5013
5266
  return { cx, cwi };
5014
5267
  }
@@ -5500,6 +5753,7 @@ var DEFAULT_TEXT_OPACITY = 0.15;
5500
5753
  var DEFAULT_TEXT_ANGLE = -45;
5501
5754
  var DEFAULT_IMAGE_OPACITY = 0.1;
5502
5755
  var DEFAULT_CAP_HEIGHT_RATIO = 0.718;
5756
+ var WATERMARK_SAFETY_MARGIN = 24;
5503
5757
  function validateWatermark(watermark, pdfaLevel) {
5504
5758
  if (pdfaLevel === "pdfa1b") {
5505
5759
  const textOpacity = watermark.text?.opacity ?? DEFAULT_TEXT_OPACITY;
@@ -5545,18 +5799,32 @@ function buildWatermarkState(watermark, pgW, pgH, enc) {
5545
5799
  };
5546
5800
  }
5547
5801
  function _buildTextWatermarkOps(wm, pgW, pgH, enc, gsName) {
5548
- const sz = wm.fontSize ?? DEFAULT_TEXT_FONT_SIZE;
5802
+ let sz = wm.fontSize ?? DEFAULT_TEXT_FONT_SIZE;
5549
5803
  const color = parseColor(wm.color ?? DEFAULT_TEXT_COLOR);
5550
5804
  const angle = (wm.angle ?? DEFAULT_TEXT_ANGLE) * DEG_TO_RAD;
5551
5805
  const cos = Math.cos(angle);
5552
5806
  const sin = Math.sin(angle);
5807
+ const fdMetrics = enc.fontData?.metrics;
5808
+ const capRatio = fdMetrics ? fdMetrics.capHeight / fdMetrics.unitsPerEm : DEFAULT_CAP_HEIGHT_RATIO;
5809
+ if (wm.autoFit !== false) {
5810
+ const textW = enc.tw(wm.text, sz);
5811
+ const textH = sz * capRatio;
5812
+ const aCos = Math.abs(cos);
5813
+ const aSin = Math.abs(sin);
5814
+ const rotW = textW * aCos + textH * aSin;
5815
+ const rotH = textW * aSin + textH * aCos;
5816
+ const safeW = pgW - WATERMARK_SAFETY_MARGIN * 2;
5817
+ const safeH = pgH - WATERMARK_SAFETY_MARGIN * 2;
5818
+ if (rotW > safeW || rotH > safeH) {
5819
+ const scale = Math.min(safeW / rotW, safeH / rotH);
5820
+ sz *= scale;
5821
+ }
5822
+ }
5553
5823
  const cx = pgW / 2;
5554
5824
  const cy = pgH / 2;
5555
5825
  const textWidth = enc.tw(wm.text, sz);
5556
5826
  const offsetX = -textWidth / 2;
5557
- const fd = enc.fontData;
5558
- const capHeightRatio = fd ? fd.metrics.capHeight / fd.metrics.unitsPerEm : DEFAULT_CAP_HEIGHT_RATIO;
5559
- const offsetY = -sz * capHeightRatio / 2;
5827
+ const offsetY = -sz * capRatio / 2;
5560
5828
  const tx = cx + offsetX * cos - offsetY * sin;
5561
5829
  const ty = cy + offsetX * sin + offsetY * cos;
5562
5830
  const escapedText = enc.ps(wm.text);
@@ -5744,7 +6012,9 @@ function buildPDF(params, layoutOptions) {
5744
6012
  const fs = layoutOptions?.fontSizes ? { ...DEFAULT_FONT_SIZES, ...layoutOptions.fontSizes } : DEFAULT_FONT_SIZES;
5745
6013
  const { cx, cwi } = computeColumnPositions(columns, mg.l, cw);
5746
6014
  const fontEntries = params.fontEntries || (fontData ? [{ fontData, fontRef: "/F3", lang: "unknown" }] : []);
5747
- const enc = createEncodingContext(fontEntries);
6015
+ const pdfaConfig = resolvePdfAConfig(layoutOptions?.tagged);
6016
+ const tagged = pdfaConfig.enabled;
6017
+ const enc = createEncodingContext(fontEntries, tagged);
5748
6018
  const footerTpl = layoutOptions?.footerTemplate ?? {
5749
6019
  left: footerText || void 0,
5750
6020
  right: "{page}/{pages}"
@@ -5766,8 +6036,6 @@ function buildPDF(params, layoutOptions) {
5766
6036
  totalPages = 1 + Math.ceil((totalRows - rowsPage1) / rowsPerPage);
5767
6037
  }
5768
6038
  if (totalPages < 1) totalPages = 1;
5769
- const pdfaConfig = resolvePdfAConfig(layoutOptions?.tagged);
5770
- const tagged = pdfaConfig.enabled;
5771
6039
  const encryptionOpts = layoutOptions?.encryption;
5772
6040
  if (tagged && encryptionOpts) {
5773
6041
  throw new Error("PDF/A and encryption are mutually exclusive (ISO 19005-1 \xA76.3.2)");
@@ -5936,8 +6204,17 @@ function buildPDF(params, layoutOptions) {
5936
6204
  kids.push(`${pageObjStart + p * 2} 0 R`);
5937
6205
  }
5938
6206
  emitObj(2, `<< /Type /Pages /Kids [${kids.join(" ")}] /Count ${totalPages} >>`);
5939
- emitObj(3, "<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica /Encoding /WinAnsiEncoding >>");
5940
- emitObj(4, "<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding >>");
6207
+ if (tagged) {
6208
+ const pf = fontEntries[0];
6209
+ const bfName = `/${pf.fontData.fontName.replace(/[^A-Za-z0-9-]/g, "")}`;
6210
+ const primaryBase = 5;
6211
+ const refDict = `<< /Type /Font /Subtype /Type0 /BaseFont ${bfName} /Encoding /Identity-H /DescendantFonts [${primaryBase + 1} 0 R] /ToUnicode ${primaryBase + 4} 0 R >>`;
6212
+ emitObj(3, refDict);
6213
+ emitObj(4, refDict);
6214
+ } else {
6215
+ emitObj(3, "<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica /Encoding /WinAnsiEncoding >>");
6216
+ emitObj(4, "<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding >>");
6217
+ }
5941
6218
  for (let fi = 0; fi < fontEntries.length; fi++) {
5942
6219
  const fe = fontEntries[fi];
5943
6220
  const fd = fe.fontData;
@@ -6062,7 +6339,7 @@ function buildPDF(params, layoutOptions) {
6062
6339
  structTreeRootObjNum = tree.structTreeRootObjNum;
6063
6340
  totalObjs = treeStart + tree.totalObjects - 1;
6064
6341
  xmpObjNum = totalObjs + 1;
6065
- const xmpContent = buildXMPMetadata(infoTitle, isoDate, pdfaConfig.pdfaPart, pdfaConfig.pdfaConformance);
6342
+ const xmpContent = utf8EncodeBinaryString(buildXMPMetadata(infoTitle, isoDate, pdfaConfig.pdfaPart, pdfaConfig.pdfaConformance));
6066
6343
  emitStreamObj(
6067
6344
  xmpObjNum,
6068
6345
  `<< /Type /Metadata /Subtype /XML /Length ${xmpContent.length}`,
@@ -6377,7 +6654,8 @@ function buildFormWidget(field, apObjNum, radioCtx) {
6377
6654
  if (field.required) ff |= FF_REQUIRED;
6378
6655
  const parts = [
6379
6656
  "<< /Type /Annot /Subtype /Widget",
6380
- `/Rect [${fmtNum2(x1)} ${fmtNum2(y1)} ${fmtNum2(x2)} ${fmtNum2(y2)}]`
6657
+ `/Rect [${fmtNum2(x1)} ${fmtNum2(y1)} ${fmtNum2(x2)} ${fmtNum2(y2)}]`,
6658
+ "/F 4"
6381
6659
  ];
6382
6660
  if (radioCtx) {
6383
6661
  parts.push(`/Parent ${radioCtx.parentObjNum} 0 R`);
@@ -6476,13 +6754,13 @@ function buildLinkAnnotation(annot, objNum) {
6476
6754
  const [x1, y1, x2, y2] = annot.rect;
6477
6755
  const escapedUrl = escapeUrlForPdf(annot.url);
6478
6756
  return `${objNum} 0 obj
6479
- << /Type /Annot /Subtype /Link /Rect [${fmtNum(x1)} ${fmtNum(y1)} ${fmtNum(x2)} ${fmtNum(y2)}] /Border [0 0 0] /A << /Type /Action /S /URI /URI (${escapedUrl}) >> >>
6757
+ << /Type /Annot /Subtype /Link /Rect [${fmtNum(x1)} ${fmtNum(y1)} ${fmtNum(x2)} ${fmtNum(y2)}] /Border [0 0 0] /F 4 /A << /Type /Action /S /URI /URI (${escapedUrl}) >> >>
6480
6758
  endobj`;
6481
6759
  }
6482
6760
  function buildInternalLinkAnnotation(annot, pageObjNum, objNum) {
6483
6761
  const [x1, y1, x2, y2] = annot.rect;
6484
6762
  return `${objNum} 0 obj
6485
- << /Type /Annot /Subtype /Link /Rect [${fmtNum(x1)} ${fmtNum(y1)} ${fmtNum(x2)} ${fmtNum(y2)}] /Border [0 0 0] /A << /Type /Action /S /GoTo /D [${pageObjNum} 0 R /Fit] >> >>
6763
+ << /Type /Annot /Subtype /Link /Rect [${fmtNum(x1)} ${fmtNum(y1)} ${fmtNum(x2)} ${fmtNum(y2)}] /Border [0 0 0] /F 4 /A << /Type /Action /S /GoTo /D [${pageObjNum} 0 R /Fit] >> >>
6486
6764
  endobj`;
6487
6765
  }
6488
6766
  function isLinkAnnotation(annot) {
@@ -8414,6 +8692,39 @@ function renderSvg(data, x, y, w, h, options) {
8414
8692
  return allOps.join("\n");
8415
8693
  }
8416
8694
 
8695
+ // src/core/pdf-column-fit.ts
8696
+ var CELL_PAD_LEFT = 3;
8697
+ var CELL_PAD_RIGHT = 3;
8698
+ var CELL_PAD_TOTAL = CELL_PAD_LEFT + CELL_PAD_RIGHT;
8699
+ function computeAutoFitColumns(columns, headers, rows, enc, thSize, tdSize) {
8700
+ const n = columns.length;
8701
+ if (n === 0) return [];
8702
+ const desired = new Array(n).fill(0);
8703
+ for (let i = 0; i < n; i++) {
8704
+ let max = 0;
8705
+ const hdr = headers[i];
8706
+ if (hdr) {
8707
+ const w = enc.tw(hdr, thSize);
8708
+ if (w > max) max = w;
8709
+ }
8710
+ for (const row of rows) {
8711
+ const cell = row.cells[i];
8712
+ if (!cell) continue;
8713
+ const w = enc.tw(cell, tdSize);
8714
+ if (w > max) max = w;
8715
+ }
8716
+ desired[i] = max + CELL_PAD_TOTAL;
8717
+ }
8718
+ let total = 0;
8719
+ for (let i = 0; i < n; i++) total += desired[i];
8720
+ if (total <= 0) return columns.slice();
8721
+ const out = new Array(n);
8722
+ for (let i = 0; i < n; i++) {
8723
+ out[i] = { ...columns[i], f: desired[i] / total };
8724
+ }
8725
+ return out;
8726
+ }
8727
+
8417
8728
  // src/core/pdf-renderers.ts
8418
8729
  var HEADING_SIZES = { 1: 18, 2: 14, 3: 11 };
8419
8730
  var HEADING_SPACING = {
@@ -8633,10 +8944,15 @@ function renderList(block, y, enc, mgL, cw, tagCtx, documentChildren) {
8633
8944
  }
8634
8945
  function renderTable(block, y, enc, mgL, mgR, pgW, cw, tagCtx, documentChildren) {
8635
8946
  const ops = [];
8636
- const columns = block.columns ? [...block.columns] : DEFAULT_COLUMNS;
8947
+ const baseColumns = block.columns ? [...block.columns] : DEFAULT_COLUMNS;
8637
8948
  const fs = DEFAULT_FONT_SIZES;
8638
8949
  const colors = DEFAULT_COLORS;
8950
+ const columns = block.autoFitColumns ? computeAutoFitColumns(baseColumns, block.headers, block.rows, enc, fs.th, fs.td) : baseColumns;
8639
8951
  const { cx, cwi } = computeColumnPositions(columns, mgL, cw);
8952
+ const clip = block.clipCells !== false;
8953
+ const clipCell = (op, i, top, h) => clip ? `q ${fmtNum(cx[i])} ${fmtNum(top - h)} ${fmtNum(cwi[i])} ${fmtNum(h)} re W n
8954
+ ${op}
8955
+ Q` : op;
8640
8956
  const tableRows = [];
8641
8957
  ops.push(`${colors.thBg} rg`);
8642
8958
  ops.push(`${fmtNum(mgL)} ${fmtNum(y - TH_H)} ${fmtNum(cw)} ${fmtNum(TH_H)} re f`);
@@ -8650,19 +8966,19 @@ function renderTable(block, y, enc, mgL, mgR, pgW, cw, tagCtx, documentChildren)
8650
8966
  const mcid = tagCtx.mcidAlloc.next(tagCtx.pageObjNum);
8651
8967
  thChildren.push({ type: "TH", children: [{ mcid, pageObjNum: tagCtx.pageObjNum }] });
8652
8968
  if (columns[i].a === "r") {
8653
- ops.push(txtRTagged(t, cx[i] + cwi[i] - 3, y - TH_H + 4, enc.f2, fs.th, enc, mcid));
8969
+ ops.push(clipCell(txtRTagged(t, cx[i] + cwi[i] - 3, y - TH_H + 4, enc.f2, fs.th, enc, mcid), i, y, TH_H));
8654
8970
  } else if (columns[i].a === "c") {
8655
- ops.push(txtCTagged(t, cx[i], y - TH_H + 4, enc.f2, fs.th, cwi[i], enc, mcid));
8971
+ ops.push(clipCell(txtCTagged(t, cx[i], y - TH_H + 4, enc.f2, fs.th, cwi[i], enc, mcid), i, y, TH_H));
8656
8972
  } else {
8657
- ops.push(txtTagged(t, cx[i] + 3, y - TH_H + 4, enc.f2, fs.th, enc, mcid));
8973
+ ops.push(clipCell(txtTagged(t, cx[i] + 3, y - TH_H + 4, enc.f2, fs.th, enc, mcid), i, y, TH_H));
8658
8974
  }
8659
8975
  } else {
8660
8976
  if (columns[i].a === "r") {
8661
- ops.push(txtR(t, cx[i] + cwi[i] - 3, y - TH_H + 4, enc.f2, fs.th, enc));
8977
+ ops.push(clipCell(txtR(t, cx[i] + cwi[i] - 3, y - TH_H + 4, enc.f2, fs.th, enc), i, y, TH_H));
8662
8978
  } else if (columns[i].a === "c") {
8663
- ops.push(txtC(t, cx[i], y - TH_H + 4, enc.f2, fs.th, cwi[i], enc));
8979
+ ops.push(clipCell(txtC(t, cx[i], y - TH_H + 4, enc.f2, fs.th, cwi[i], enc), i, y, TH_H));
8664
8980
  } else {
8665
- ops.push(txt(t, cx[i] + 3, y - TH_H + 4, enc.f2, fs.th, enc));
8981
+ ops.push(clipCell(txt(t, cx[i] + 3, y - TH_H + 4, enc.f2, fs.th, enc), i, y, TH_H));
8666
8982
  }
8667
8983
  }
8668
8984
  }
@@ -8682,19 +8998,19 @@ function renderTable(block, y, enc, mgL, mgR, pgW, cw, tagCtx, documentChildren)
8682
8998
  const mcid = tagCtx.mcidAlloc.next(tagCtx.pageObjNum);
8683
8999
  tdChildren.push({ type: "TD", children: [{ mcid, pageObjNum: tagCtx.pageObjNum }] });
8684
9000
  if (columns[i].a === "r") {
8685
- ops.push(txtRTagged(t, cx[i] + cwi[i] - 3, y - ROW_H + 3, font, fs.td, enc, mcid));
9001
+ ops.push(clipCell(txtRTagged(t, cx[i] + cwi[i] - 3, y - ROW_H + 3, font, fs.td, enc, mcid), i, y, ROW_H));
8686
9002
  } else if (columns[i].a === "c") {
8687
- ops.push(txtCTagged(t, cx[i], y - ROW_H + 3, font, fs.td, cwi[i], enc, mcid));
9003
+ ops.push(clipCell(txtCTagged(t, cx[i], y - ROW_H + 3, font, fs.td, cwi[i], enc, mcid), i, y, ROW_H));
8688
9004
  } else {
8689
- ops.push(txtTagged(t, cx[i] + 3, y - ROW_H + 3, font, fs.td, enc, mcid));
9005
+ ops.push(clipCell(txtTagged(t, cx[i] + 3, y - ROW_H + 3, font, fs.td, enc, mcid), i, y, ROW_H));
8690
9006
  }
8691
9007
  } else {
8692
9008
  if (columns[i].a === "r") {
8693
- ops.push(txtR(t, cx[i] + cwi[i] - 3, y - ROW_H + 3, font, fs.td, enc));
9009
+ ops.push(clipCell(txtR(t, cx[i] + cwi[i] - 3, y - ROW_H + 3, font, fs.td, enc), i, y, ROW_H));
8694
9010
  } else if (columns[i].a === "c") {
8695
- ops.push(txtC(t, cx[i], y - ROW_H + 3, font, fs.td, cwi[i], enc));
9011
+ ops.push(clipCell(txtC(t, cx[i], y - ROW_H + 3, font, fs.td, cwi[i], enc), i, y, ROW_H));
8696
9012
  } else {
8697
- ops.push(txt(t, cx[i] + 3, y - ROW_H + 3, font, fs.td, enc));
9013
+ ops.push(clipCell(txt(t, cx[i] + 3, y - ROW_H + 3, font, fs.td, enc), i, y, ROW_H));
8698
9014
  }
8699
9015
  }
8700
9016
  }
@@ -8873,10 +9189,11 @@ function renderToc(tocBlock, headings, y, enc, mgL, cw, pageIndex, pageAnnotatio
8873
9189
  const availTextW = dotLeaderEnd - entryX - 8;
8874
9190
  let displayText = heading.text;
8875
9191
  if (measureText(displayText, sz, enc) > availTextW) {
8876
- while (displayText.length > 1 && measureText(displayText + "...", sz, enc) > availTextW) {
9192
+ const ell = "\u2026";
9193
+ while (displayText.length > 1 && measureText(displayText + ell, sz, enc) > availTextW) {
8877
9194
  displayText = displayText.slice(0, -1);
8878
9195
  }
8879
- displayText += "...";
9196
+ displayText += ell;
8880
9197
  }
8881
9198
  const textW = measureText(displayText, sz, enc);
8882
9199
  const textY = y - sz;
@@ -9121,9 +9438,9 @@ function buildDocumentPDF(params, layoutOptions) {
9121
9438
  const mg = layout?.margins ?? { ...DEFAULT_MARGINS };
9122
9439
  const cw = pgW - mg.l - mg.r;
9123
9440
  const fontEntries = params.fontEntries ? [...params.fontEntries] : [];
9124
- const enc = createEncodingContext(fontEntries);
9125
9441
  const pdfaConfig = resolvePdfAConfig(layout?.tagged);
9126
9442
  const tagged = pdfaConfig.enabled;
9443
+ const enc = createEncodingContext(fontEntries, tagged);
9127
9444
  const encryptionOpts = layout?.encryption;
9128
9445
  if (tagged && encryptionOpts) {
9129
9446
  throw new Error("PDF/A and encryption are mutually exclusive (ISO 19005-1 \xA76.3.2)");
@@ -9454,8 +9771,17 @@ function buildDocumentPDF(params, layoutOptions) {
9454
9771
  kids.push(`${pageObjStart + p * 2} 0 R`);
9455
9772
  }
9456
9773
  emitObj(2, `<< /Type /Pages /Kids [${kids.join(" ")}] /Count ${totalPages} >>`);
9457
- emitObj(3, "<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica /Encoding /WinAnsiEncoding >>");
9458
- emitObj(4, "<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding >>");
9774
+ if (tagged) {
9775
+ const pf = fontEntries[0];
9776
+ const bfName = `/${pf.fontData.fontName.replace(/[^A-Za-z0-9-]/g, "")}`;
9777
+ const primaryBase = 5;
9778
+ const refDict = `<< /Type /Font /Subtype /Type0 /BaseFont ${bfName} /Encoding /Identity-H /DescendantFonts [${primaryBase + 1} 0 R] /ToUnicode ${primaryBase + 4} 0 R >>`;
9779
+ emitObj(3, refDict);
9780
+ emitObj(4, refDict);
9781
+ } else {
9782
+ emitObj(3, "<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica /Encoding /WinAnsiEncoding >>");
9783
+ emitObj(4, "<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding >>");
9784
+ }
9459
9785
  for (let fi = 0; fi < fontEntries.length; fi++) {
9460
9786
  const fe = fontEntries[fi];
9461
9787
  const fd = fe.fontData;
@@ -9549,13 +9875,13 @@ function buildDocumentPDF(params, layoutOptions) {
9549
9875
  const destName = pa.annot.url.slice(1);
9550
9876
  emitObj(
9551
9877
  objNum,
9552
- `<< /Type /Annot /Subtype /Link /Rect [${fmtNum(x1)} ${fmtNum(y1)} ${fmtNum(x2)} ${fmtNum(y2)}] /Border [0 0 0] /Dest /${destName} >>`
9878
+ `<< /Type /Annot /Subtype /Link /Rect [${fmtNum(x1)} ${fmtNum(y1)} ${fmtNum(x2)} ${fmtNum(y2)}] /Border [0 0 0] /F 4 /Dest /${destName} >>`
9553
9879
  );
9554
9880
  } else {
9555
9881
  const escapedUrl = pa.annot.url.replace(/\\/g, "\\\\").replace(/\(/g, "\\(").replace(/\)/g, "\\)");
9556
9882
  emitObj(
9557
9883
  objNum,
9558
- `<< /Type /Annot /Subtype /Link /Rect [${fmtNum(x1)} ${fmtNum(y1)} ${fmtNum(x2)} ${fmtNum(y2)}] /Border [0 0 0] /A << /Type /Action /S /URI /URI (${escapedUrl}) >> >>`
9884
+ `<< /Type /Annot /Subtype /Link /Rect [${fmtNum(x1)} ${fmtNum(y1)} ${fmtNum(x2)} ${fmtNum(y2)}] /Border [0 0 0] /F 4 /A << /Type /Action /S /URI /URI (${escapedUrl}) >> >>`
9559
9885
  );
9560
9886
  }
9561
9887
  annotIdx++;
@@ -9672,13 +9998,13 @@ function buildDocumentPDF(params, layoutOptions) {
9672
9998
  const destName = pa.annot.url.slice(1);
9673
9999
  emitObj(
9674
10000
  objNum,
9675
- `<< /Type /Annot /Subtype /Link /Rect [${fmtNum(x1)} ${fmtNum(y1)} ${fmtNum(x2)} ${fmtNum(y2)}] /Border [0 0 0] /Dest /${destName} >>`
10001
+ `<< /Type /Annot /Subtype /Link /Rect [${fmtNum(x1)} ${fmtNum(y1)} ${fmtNum(x2)} ${fmtNum(y2)}] /Border [0 0 0] /F 4 /Dest /${destName} >>`
9676
10002
  );
9677
10003
  } else {
9678
10004
  const escapedUrl = pa.annot.url.replace(/\\/g, "\\\\").replace(/\(/g, "\\(").replace(/\)/g, "\\)");
9679
10005
  emitObj(
9680
10006
  objNum,
9681
- `<< /Type /Annot /Subtype /Link /Rect [${fmtNum(x1)} ${fmtNum(y1)} ${fmtNum(x2)} ${fmtNum(y2)}] /Border [0 0 0] /A << /Type /Action /S /URI /URI (${escapedUrl}) >> >>`
10007
+ `<< /Type /Annot /Subtype /Link /Rect [${fmtNum(x1)} ${fmtNum(y1)} ${fmtNum(x2)} ${fmtNum(y2)}] /Border [0 0 0] /F 4 /A << /Type /Action /S /URI /URI (${escapedUrl}) >> >>`
9682
10008
  );
9683
10009
  }
9684
10010
  annotIdx++;
@@ -9755,7 +10081,7 @@ function buildDocumentPDF(params, layoutOptions) {
9755
10081
  structTreeRootObjNum = tree.structTreeRootObjNum;
9756
10082
  totalObjs = treeStart + tree.totalObjects - 1;
9757
10083
  xmpObjNum = totalObjs + 1;
9758
- const xmpContent = buildXMPMetadata(infoTitle, isoDate, pdfaConfig.pdfaPart, pdfaConfig.pdfaConformance, params.metadata?.author);
10084
+ const xmpContent = utf8EncodeBinaryString(buildXMPMetadata(infoTitle, isoDate, pdfaConfig.pdfaPart, pdfaConfig.pdfaConformance, params.metadata?.author, params.metadata?.subject, params.metadata?.keywords));
9759
10085
  emitStreamObj(
9760
10086
  xmpObjNum,
9761
10087
  `<< /Type /Metadata /Subtype /XML /Length ${xmpContent.length}`,
@@ -11253,6 +11579,185 @@ function getTrailerRef(trailer, key) {
11253
11579
  return void 0;
11254
11580
  }
11255
11581
 
11582
+ // src/parser/pdf-decode-filters.ts
11583
+ var MAX_DECODE_OUTPUT = 256 * 1024 * 1024;
11584
+ function checkOutputSize(n, filter) {
11585
+ if (n > MAX_DECODE_OUTPUT) {
11586
+ throw new Error(
11587
+ `${filter} output exceeds ${MAX_DECODE_OUTPUT} bytes (possible zip-bomb)`
11588
+ );
11589
+ }
11590
+ }
11591
+ function decodeASCIIHex(data) {
11592
+ const out = [];
11593
+ let nibble = -1;
11594
+ for (let i = 0; i < data.length; i++) {
11595
+ const c = data[i];
11596
+ if (c === 62) break;
11597
+ if (c === 0 || c === 9 || c === 10 || c === 12 || c === 13 || c === 32) continue;
11598
+ let v;
11599
+ if (c >= 48 && c <= 57) v = c - 48;
11600
+ else if (c >= 65 && c <= 70) v = c - 65 + 10;
11601
+ else if (c >= 97 && c <= 102) v = c - 97 + 10;
11602
+ else throw new Error(`ASCIIHexDecode: invalid character 0x${c.toString(16)}`);
11603
+ if (nibble < 0) {
11604
+ nibble = v;
11605
+ } else {
11606
+ out.push(nibble << 4 | v);
11607
+ checkOutputSize(out.length, "ASCIIHexDecode");
11608
+ nibble = -1;
11609
+ }
11610
+ }
11611
+ if (nibble >= 0) out.push(nibble << 4);
11612
+ return Uint8Array.from(out);
11613
+ }
11614
+ function decodeASCII85(data) {
11615
+ const out = [];
11616
+ let group = 0;
11617
+ let count = 0;
11618
+ for (let i = 0; i < data.length; i++) {
11619
+ const c = data[i];
11620
+ if (c === 126) {
11621
+ if (i + 1 < data.length && data[i + 1] === 62) break;
11622
+ throw new Error("ASCII85Decode: lone ~ without >");
11623
+ }
11624
+ if (c === 62) break;
11625
+ if (c === 0 || c === 9 || c === 10 || c === 12 || c === 13 || c === 32) continue;
11626
+ if (c === 122) {
11627
+ if (count !== 0) throw new Error("ASCII85Decode: z inside group");
11628
+ out.push(0, 0, 0, 0);
11629
+ checkOutputSize(out.length, "ASCII85Decode");
11630
+ continue;
11631
+ }
11632
+ if (c < 33 || c > 117) throw new Error(`ASCII85Decode: invalid char 0x${c.toString(16)}`);
11633
+ group = group * 85 + (c - 33);
11634
+ count++;
11635
+ if (count === 5) {
11636
+ if (group > 4294967295) throw new Error("ASCII85Decode: group overflow");
11637
+ out.push(group >>> 24 & 255, group >>> 16 & 255, group >>> 8 & 255, group & 255);
11638
+ checkOutputSize(out.length, "ASCII85Decode");
11639
+ group = 0;
11640
+ count = 0;
11641
+ }
11642
+ }
11643
+ if (count > 0) {
11644
+ for (let k = count; k < 5; k++) group = group * 85 + 84;
11645
+ if (group > 4294967295) throw new Error("ASCII85Decode: trailing group overflow");
11646
+ const tail = [group >>> 24 & 255, group >>> 16 & 255, group >>> 8 & 255, group & 255];
11647
+ for (let k = 0; k < count - 1; k++) out.push(tail[k]);
11648
+ checkOutputSize(out.length, "ASCII85Decode");
11649
+ }
11650
+ return Uint8Array.from(out);
11651
+ }
11652
+ var LZW_CLEAR_CODE = 256;
11653
+ var LZW_EOD_CODE = 257;
11654
+ function decodeLZW(data) {
11655
+ const out = [];
11656
+ let bitBuf = 0;
11657
+ let bitCount = 0;
11658
+ let p = 0;
11659
+ let codeSize = 9;
11660
+ let dict = [];
11661
+ let prev = null;
11662
+ const resetDict = () => {
11663
+ dict = new Array(258);
11664
+ for (let i = 0; i < 256; i++) dict[i] = Uint8Array.of(i);
11665
+ codeSize = 9;
11666
+ prev = null;
11667
+ };
11668
+ resetDict();
11669
+ const readCode = () => {
11670
+ while (bitCount < codeSize) {
11671
+ if (p >= data.length) return -1;
11672
+ bitBuf = bitBuf << 8 | data[p++];
11673
+ bitCount += 8;
11674
+ }
11675
+ const shift = bitCount - codeSize;
11676
+ const code = bitBuf >>> shift & (1 << codeSize) - 1;
11677
+ bitBuf &= (1 << shift) - 1;
11678
+ bitCount = shift;
11679
+ return code;
11680
+ };
11681
+ for (; ; ) {
11682
+ const code = readCode();
11683
+ if (code < 0 || code === LZW_EOD_CODE) break;
11684
+ if (code === LZW_CLEAR_CODE) {
11685
+ resetDict();
11686
+ continue;
11687
+ }
11688
+ let entry;
11689
+ if (code < dict.length) {
11690
+ entry = dict[code];
11691
+ } else if (code === dict.length && prev) {
11692
+ entry = new Uint8Array(prev.length + 1);
11693
+ entry.set(prev);
11694
+ entry[prev.length] = prev[0];
11695
+ } else {
11696
+ throw new Error(`LZWDecode: invalid code ${code}`);
11697
+ }
11698
+ for (let i = 0; i < entry.length; i++) out.push(entry[i]);
11699
+ checkOutputSize(out.length, "LZWDecode");
11700
+ if (prev) {
11701
+ const next = new Uint8Array(prev.length + 1);
11702
+ next.set(prev);
11703
+ next[prev.length] = entry[0];
11704
+ dict.push(next);
11705
+ if (dict.length === (1 << codeSize) - 1 && codeSize < 12) codeSize++;
11706
+ }
11707
+ prev = entry;
11708
+ }
11709
+ return Uint8Array.from(out);
11710
+ }
11711
+ function decodeRunLength(data) {
11712
+ const out = [];
11713
+ let p = 0;
11714
+ while (p < data.length) {
11715
+ const n = data[p++];
11716
+ if (n === 128) break;
11717
+ if (n < 128) {
11718
+ const len = n + 1;
11719
+ if (p + len > data.length) throw new Error("RunLengthDecode: truncated literal");
11720
+ for (let i = 0; i < len; i++) out.push(data[p + i]);
11721
+ p += len;
11722
+ } else {
11723
+ if (p >= data.length) throw new Error("RunLengthDecode: truncated repeat");
11724
+ const v = data[p++];
11725
+ const len = 257 - n;
11726
+ for (let i = 0; i < len; i++) out.push(v);
11727
+ }
11728
+ checkOutputSize(out.length, "RunLengthDecode");
11729
+ }
11730
+ return Uint8Array.from(out);
11731
+ }
11732
+ function applyDecodeFilter(name, data) {
11733
+ switch (name) {
11734
+ case "ASCIIHexDecode":
11735
+ case "AHx":
11736
+ return decodeASCIIHex(data);
11737
+ case "ASCII85Decode":
11738
+ case "A85":
11739
+ return decodeASCII85(data);
11740
+ case "LZWDecode":
11741
+ case "LZW":
11742
+ return decodeLZW(data);
11743
+ case "RunLengthDecode":
11744
+ case "RL":
11745
+ return decodeRunLength(data);
11746
+ default:
11747
+ return data;
11748
+ }
11749
+ }
11750
+ var KNOWN_DECODE_FILTERS = /* @__PURE__ */ new Set([
11751
+ "ASCIIHexDecode",
11752
+ "AHx",
11753
+ "ASCII85Decode",
11754
+ "A85",
11755
+ "LZWDecode",
11756
+ "LZW",
11757
+ "RunLengthDecode",
11758
+ "RL"
11759
+ ]);
11760
+
11256
11761
  // src/parser/pdf-reader.ts
11257
11762
  function openPdf(bytes) {
11258
11763
  const xref = parseXrefTable(bytes);
@@ -11312,10 +11817,15 @@ function openPdf(bytes) {
11312
11817
  data = decodePNGPredictor(data, decodeParms);
11313
11818
  }
11314
11819
  }
11820
+ } else if (filterName !== void 0 && KNOWN_DECODE_FILTERS.has(filterName)) {
11821
+ data = applyDecodeFilter(filterName, data);
11315
11822
  } else if (filter !== void 0 && isArray(filter)) {
11316
11823
  for (const f of filter) {
11317
- if (isName(f) && f.value === "FlateDecode") {
11824
+ if (!isName(f)) continue;
11825
+ if (f.value === "FlateDecode") {
11318
11826
  data = inflateSync(data);
11827
+ } else if (KNOWN_DECODE_FILTERS.has(f.value)) {
11828
+ data = applyDecodeFilter(f.value, data);
11319
11829
  }
11320
11830
  }
11321
11831
  }
@@ -11684,6 +12194,6 @@ async function createPDF(pdfParams, options) {
11684
12194
  return generatePDFMainThread(pdfParams, options?.layoutOptions);
11685
12195
  }
11686
12196
 
11687
- export { BAL_H, DEFAULT_COLORS, DEFAULT_COLUMNS, DEFAULT_CW, DEFAULT_FONT_SIZES, DEFAULT_MARGINS, DEFAULT_MAX_INFLATE_OUTPUT, FT_H, HEADER_H, INFO_LN, MAX_PARSE_DEPTH, MAX_XREF_CHAIN, PAGE_SIZES, PG_H, PG_W, ROW_H, TH_H, TITLE_LN, WORKER_THRESHOLD, WORKER_TIMEOUT_MS, buildAcroFormDict, buildAppearanceStreamDict, buildCmsSignedData, buildDocumentPDF, buildDocumentPDFBytes, buildDocumentPDFStream, buildEmbeddedFiles, buildFormWidget, buildImageOperators, buildImageXObject, buildInternalLinkAnnotation, buildLinkAnnotation, buildPDF, buildPDFBytes, buildPDFStream, buildRadioGroupParent, buildSMaskXObject, buildSigDict, buildWatermarkState, chunkBinaryString, clearFontCache, computeColumnPositions, concatChunks, containsArabic, containsBengali, containsDevanagari, containsHebrew, containsRTL, containsTamil, containsThai, createEncodingContext, createModifier, createPDF, createTokenizer, decodeEcPublicKey, defaultFieldHeight, derBitString, derDecode, derInteger, derOctetString, derOid, derSequence, detectCharLang, detectFallbackLangs, detectImageFormat, dictGet, dictGetArray, dictGetDict, dictGetName, dictGetNum, dictGetRef, downloadBlob, ean13CheckDigit, ecPublicKeyFromPrivate, ecdsaSign, ecdsaVerify, encodeCode128, encodeEcPublicKey, encodePDF417, encodePdfTextString, estimateCmsSize, estimateContentsSize, findStartxref, generateDataMatrix, generatePDFInWorker, generatePDFMainThread, generateQR, getMaxInflateOutputSize, getRegisteredLangs, getTrailerRef, getTrailerValue, hasFontLoader, helveticaWidth, hmacSha256, inflateSync, initCrypto, initNodeCompression, initNodeDecompression as initNodeDecompression_parser, isArmenianCodepoint, isArray, isBengaliCodepoint, isCyrillicCodepoint, isDevanagariCodepoint, isDict, isGeorgianCodepoint, isLinkAnnotation, isName, isRef, isSelfSigned, isStream, isTamilCodepoint, isValidPdfRgb, loadFontData, nameValue, needsUnicodeFont, normalizeColors, openPdf, parseCertificate, parseColor, parseImage, parseIndirectObject, parseJPEG, parsePNG, parseRsaPrivateKey, parseRsaPublicKey, parseSvgPath, parseValue, parseXrefTable, pdfString, registerFont, registerFonts, renderBarcode, renderCode128, renderDataMatrix, renderEAN13, renderPDF417, renderQR, renderSvg, resetFontRegistry, resolveBidiRuns, resolveLayout, resolvePdfAConfig, resolveTemplate, rsaSign, rsaSignHash, rsaVerify, rsaVerifyHash, setDeflateImpl, setInflateImpl, setMaxInflateOutputSize, sha384, sha512, shapeArabicText, shapeBengaliText, shapeDevanagariText, shapeTamilText, shapeThaiText, signPdfBytes, slugify, splitTextByFont, streamByteLength, toBytes, toWinAnsi, truncate, validateAttachments, validateDocumentStreamable, validateTableStreamable, validateURL, validateWatermark, verifyCertSignature, wrapText };
12197
+ export { BAL_H, DEFAULT_COLORS, DEFAULT_COLUMNS, DEFAULT_CW, DEFAULT_FONT_SIZES, DEFAULT_MARGINS, DEFAULT_MAX_INFLATE_OUTPUT, FT_H, HEADER_H, INFO_LN, KNOWN_DECODE_FILTERS, MAX_PARSE_DEPTH, MAX_XREF_CHAIN, PAGE_SIZES, PG_H, PG_W, ROW_H, TH_H, TITLE_LN, WORKER_THRESHOLD, WORKER_TIMEOUT_MS, applyDecodeFilter, buildAcroFormDict, buildAppearanceStreamDict, buildCmsSignedData, buildDocumentPDF, buildDocumentPDFBytes, buildDocumentPDFStream, buildEmbeddedFiles, buildFormWidget, buildImageOperators, buildImageXObject, buildInternalLinkAnnotation, buildLinkAnnotation, buildPDF, buildPDFBytes, buildPDFStream, buildRadioGroupParent, buildSMaskXObject, buildSigDict, buildWatermarkState, chunkBinaryString, clearFontCache, computeColumnPositions, concatChunks, containsArabic, containsBengali, containsDevanagari, containsHebrew, containsRTL, containsTamil, containsThai, createEncodingContext, createModifier, createPDF, createTokenizer, decodeASCII85, decodeASCIIHex, decodeEcPublicKey, decodeLZW, decodeRunLength, defaultFieldHeight, derBitString, derDecode, derInteger, derOctetString, derOid, derSequence, detectCharLang, detectFallbackLangs, detectImageFormat, dictGet, dictGetArray, dictGetDict, dictGetName, dictGetNum, dictGetRef, downloadBlob, ean13CheckDigit, ecPublicKeyFromPrivate, ecdsaSign, ecdsaVerify, encodeCode128, encodeEcPublicKey, encodePDF417, encodePdfTextString, estimateCmsSize, estimateContentsSize, findStartxref, generateDataMatrix, generatePDFInWorker, generatePDFMainThread, generateQR, getMaxInflateOutputSize, getRegisteredLangs, getTrailerRef, getTrailerValue, hasFontLoader, helveticaWidth, hmacSha256, inflateSync, initCrypto, initNodeCompression, initNodeDecompression as initNodeDecompression_parser, isArmenianCodepoint, isArray, isBengaliCodepoint, isCyrillicCodepoint, isDevanagariCodepoint, isDict, isGeorgianCodepoint, isLinkAnnotation, isName, isRef, isSelfSigned, isStream, isTamilCodepoint, isValidPdfRgb, loadFontData, nameValue, needsUnicodeFont, normalizeColors, openPdf, parseCertificate, parseColor, parseImage, parseIndirectObject, parseJPEG, parsePNG, parseRsaPrivateKey, parseRsaPublicKey, parseSvgPath, parseValue, parseXrefTable, pdfString, registerFont, registerFonts, renderBarcode, renderCode128, renderDataMatrix, renderEAN13, renderPDF417, renderQR, renderSvg, resetFontRegistry, resolveBidiRuns, resolveLayout, resolvePdfAConfig, resolveTemplate, rsaSign, rsaSignHash, rsaVerify, rsaVerifyHash, setDeflateImpl, setInflateImpl, setMaxInflateOutputSize, sha384, sha512, shapeArabicText, shapeBengaliText, shapeDevanagariText, shapeTamilText, shapeThaiText, signPdfBytes, slugify, splitTextByFont, streamByteLength, toBytes, toWinAnsi, truncate, truncateToWidth, validateAttachments, validateDocumentStreamable, validateTableStreamable, validateURL, validateWatermark, verifyCertSignature, wrapText };
11688
12198
  //# sourceMappingURL=index.js.map
11689
12199
  //# sourceMappingURL=index.js.map