pdfnative 1.0.4 → 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
  }
@@ -5499,6 +5752,8 @@ var DEFAULT_TEXT_COLOR = "0.75 0.75 0.75";
5499
5752
  var DEFAULT_TEXT_OPACITY = 0.15;
5500
5753
  var DEFAULT_TEXT_ANGLE = -45;
5501
5754
  var DEFAULT_IMAGE_OPACITY = 0.1;
5755
+ var DEFAULT_CAP_HEIGHT_RATIO = 0.718;
5756
+ var WATERMARK_SAFETY_MARGIN = 24;
5502
5757
  function validateWatermark(watermark, pdfaLevel) {
5503
5758
  if (pdfaLevel === "pdfa1b") {
5504
5759
  const textOpacity = watermark.text?.opacity ?? DEFAULT_TEXT_OPACITY;
@@ -5544,19 +5799,35 @@ function buildWatermarkState(watermark, pgW, pgH, enc) {
5544
5799
  };
5545
5800
  }
5546
5801
  function _buildTextWatermarkOps(wm, pgW, pgH, enc, gsName) {
5547
- const sz = wm.fontSize ?? DEFAULT_TEXT_FONT_SIZE;
5802
+ let sz = wm.fontSize ?? DEFAULT_TEXT_FONT_SIZE;
5548
5803
  const color = parseColor(wm.color ?? DEFAULT_TEXT_COLOR);
5549
5804
  const angle = (wm.angle ?? DEFAULT_TEXT_ANGLE) * DEG_TO_RAD;
5550
5805
  const cos = Math.cos(angle);
5551
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
+ }
5552
5823
  const cx = pgW / 2;
5553
5824
  const cy = pgH / 2;
5554
5825
  const textWidth = enc.tw(wm.text, sz);
5555
5826
  const offsetX = -textWidth / 2;
5556
- const offsetY = -sz / 2;
5827
+ const offsetY = -sz * capRatio / 2;
5557
5828
  const tx = cx + offsetX * cos - offsetY * sin;
5558
5829
  const ty = cy + offsetX * sin + offsetY * cos;
5559
- const escapedText = pdfString(wm.text);
5830
+ const escapedText = enc.ps(wm.text);
5560
5831
  const ops = [
5561
5832
  "q",
5562
5833
  `${gsName} gs`,
@@ -5741,7 +6012,9 @@ function buildPDF(params, layoutOptions) {
5741
6012
  const fs = layoutOptions?.fontSizes ? { ...DEFAULT_FONT_SIZES, ...layoutOptions.fontSizes } : DEFAULT_FONT_SIZES;
5742
6013
  const { cx, cwi } = computeColumnPositions(columns, mg.l, cw);
5743
6014
  const fontEntries = params.fontEntries || (fontData ? [{ fontData, fontRef: "/F3", lang: "unknown" }] : []);
5744
- const enc = createEncodingContext(fontEntries);
6015
+ const pdfaConfig = resolvePdfAConfig(layoutOptions?.tagged);
6016
+ const tagged = pdfaConfig.enabled;
6017
+ const enc = createEncodingContext(fontEntries, tagged);
5745
6018
  const footerTpl = layoutOptions?.footerTemplate ?? {
5746
6019
  left: footerText || void 0,
5747
6020
  right: "{page}/{pages}"
@@ -5763,8 +6036,6 @@ function buildPDF(params, layoutOptions) {
5763
6036
  totalPages = 1 + Math.ceil((totalRows - rowsPage1) / rowsPerPage);
5764
6037
  }
5765
6038
  if (totalPages < 1) totalPages = 1;
5766
- const pdfaConfig = resolvePdfAConfig(layoutOptions?.tagged);
5767
- const tagged = pdfaConfig.enabled;
5768
6039
  const encryptionOpts = layoutOptions?.encryption;
5769
6040
  if (tagged && encryptionOpts) {
5770
6041
  throw new Error("PDF/A and encryption are mutually exclusive (ISO 19005-1 \xA76.3.2)");
@@ -5933,8 +6204,17 @@ function buildPDF(params, layoutOptions) {
5933
6204
  kids.push(`${pageObjStart + p * 2} 0 R`);
5934
6205
  }
5935
6206
  emitObj(2, `<< /Type /Pages /Kids [${kids.join(" ")}] /Count ${totalPages} >>`);
5936
- emitObj(3, "<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica /Encoding /WinAnsiEncoding >>");
5937
- 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
+ }
5938
6218
  for (let fi = 0; fi < fontEntries.length; fi++) {
5939
6219
  const fe = fontEntries[fi];
5940
6220
  const fd = fe.fontData;
@@ -6059,7 +6339,7 @@ function buildPDF(params, layoutOptions) {
6059
6339
  structTreeRootObjNum = tree.structTreeRootObjNum;
6060
6340
  totalObjs = treeStart + tree.totalObjects - 1;
6061
6341
  xmpObjNum = totalObjs + 1;
6062
- const xmpContent = buildXMPMetadata(infoTitle, isoDate, pdfaConfig.pdfaPart, pdfaConfig.pdfaConformance);
6342
+ const xmpContent = utf8EncodeBinaryString(buildXMPMetadata(infoTitle, isoDate, pdfaConfig.pdfaPart, pdfaConfig.pdfaConformance));
6063
6343
  emitStreamObj(
6064
6344
  xmpObjNum,
6065
6345
  `<< /Type /Metadata /Subtype /XML /Length ${xmpContent.length}`,
@@ -6374,7 +6654,8 @@ function buildFormWidget(field, apObjNum, radioCtx) {
6374
6654
  if (field.required) ff |= FF_REQUIRED;
6375
6655
  const parts = [
6376
6656
  "<< /Type /Annot /Subtype /Widget",
6377
- `/Rect [${fmtNum2(x1)} ${fmtNum2(y1)} ${fmtNum2(x2)} ${fmtNum2(y2)}]`
6657
+ `/Rect [${fmtNum2(x1)} ${fmtNum2(y1)} ${fmtNum2(x2)} ${fmtNum2(y2)}]`,
6658
+ "/F 4"
6378
6659
  ];
6379
6660
  if (radioCtx) {
6380
6661
  parts.push(`/Parent ${radioCtx.parentObjNum} 0 R`);
@@ -6473,13 +6754,13 @@ function buildLinkAnnotation(annot, objNum) {
6473
6754
  const [x1, y1, x2, y2] = annot.rect;
6474
6755
  const escapedUrl = escapeUrlForPdf(annot.url);
6475
6756
  return `${objNum} 0 obj
6476
- << /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}) >> >>
6477
6758
  endobj`;
6478
6759
  }
6479
6760
  function buildInternalLinkAnnotation(annot, pageObjNum, objNum) {
6480
6761
  const [x1, y1, x2, y2] = annot.rect;
6481
6762
  return `${objNum} 0 obj
6482
- << /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] >> >>
6483
6764
  endobj`;
6484
6765
  }
6485
6766
  function isLinkAnnotation(annot) {
@@ -8411,6 +8692,39 @@ function renderSvg(data, x, y, w, h, options) {
8411
8692
  return allOps.join("\n");
8412
8693
  }
8413
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
+
8414
8728
  // src/core/pdf-renderers.ts
8415
8729
  var HEADING_SIZES = { 1: 18, 2: 14, 3: 11 };
8416
8730
  var HEADING_SPACING = {
@@ -8630,10 +8944,15 @@ function renderList(block, y, enc, mgL, cw, tagCtx, documentChildren) {
8630
8944
  }
8631
8945
  function renderTable(block, y, enc, mgL, mgR, pgW, cw, tagCtx, documentChildren) {
8632
8946
  const ops = [];
8633
- const columns = block.columns ? [...block.columns] : DEFAULT_COLUMNS;
8947
+ const baseColumns = block.columns ? [...block.columns] : DEFAULT_COLUMNS;
8634
8948
  const fs = DEFAULT_FONT_SIZES;
8635
8949
  const colors = DEFAULT_COLORS;
8950
+ const columns = block.autoFitColumns ? computeAutoFitColumns(baseColumns, block.headers, block.rows, enc, fs.th, fs.td) : baseColumns;
8636
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;
8637
8956
  const tableRows = [];
8638
8957
  ops.push(`${colors.thBg} rg`);
8639
8958
  ops.push(`${fmtNum(mgL)} ${fmtNum(y - TH_H)} ${fmtNum(cw)} ${fmtNum(TH_H)} re f`);
@@ -8647,19 +8966,19 @@ function renderTable(block, y, enc, mgL, mgR, pgW, cw, tagCtx, documentChildren)
8647
8966
  const mcid = tagCtx.mcidAlloc.next(tagCtx.pageObjNum);
8648
8967
  thChildren.push({ type: "TH", children: [{ mcid, pageObjNum: tagCtx.pageObjNum }] });
8649
8968
  if (columns[i].a === "r") {
8650
- 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));
8651
8970
  } else if (columns[i].a === "c") {
8652
- 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));
8653
8972
  } else {
8654
- 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));
8655
8974
  }
8656
8975
  } else {
8657
8976
  if (columns[i].a === "r") {
8658
- 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));
8659
8978
  } else if (columns[i].a === "c") {
8660
- 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));
8661
8980
  } else {
8662
- 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));
8663
8982
  }
8664
8983
  }
8665
8984
  }
@@ -8679,19 +8998,19 @@ function renderTable(block, y, enc, mgL, mgR, pgW, cw, tagCtx, documentChildren)
8679
8998
  const mcid = tagCtx.mcidAlloc.next(tagCtx.pageObjNum);
8680
8999
  tdChildren.push({ type: "TD", children: [{ mcid, pageObjNum: tagCtx.pageObjNum }] });
8681
9000
  if (columns[i].a === "r") {
8682
- 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));
8683
9002
  } else if (columns[i].a === "c") {
8684
- 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));
8685
9004
  } else {
8686
- 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));
8687
9006
  }
8688
9007
  } else {
8689
9008
  if (columns[i].a === "r") {
8690
- 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));
8691
9010
  } else if (columns[i].a === "c") {
8692
- 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));
8693
9012
  } else {
8694
- 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));
8695
9014
  }
8696
9015
  }
8697
9016
  }
@@ -8870,10 +9189,11 @@ function renderToc(tocBlock, headings, y, enc, mgL, cw, pageIndex, pageAnnotatio
8870
9189
  const availTextW = dotLeaderEnd - entryX - 8;
8871
9190
  let displayText = heading.text;
8872
9191
  if (measureText(displayText, sz, enc) > availTextW) {
8873
- while (displayText.length > 1 && measureText(displayText + "...", sz, enc) > availTextW) {
9192
+ const ell = "\u2026";
9193
+ while (displayText.length > 1 && measureText(displayText + ell, sz, enc) > availTextW) {
8874
9194
  displayText = displayText.slice(0, -1);
8875
9195
  }
8876
- displayText += "...";
9196
+ displayText += ell;
8877
9197
  }
8878
9198
  const textW = measureText(displayText, sz, enc);
8879
9199
  const textY = y - sz;
@@ -9118,9 +9438,9 @@ function buildDocumentPDF(params, layoutOptions) {
9118
9438
  const mg = layout?.margins ?? { ...DEFAULT_MARGINS };
9119
9439
  const cw = pgW - mg.l - mg.r;
9120
9440
  const fontEntries = params.fontEntries ? [...params.fontEntries] : [];
9121
- const enc = createEncodingContext(fontEntries);
9122
9441
  const pdfaConfig = resolvePdfAConfig(layout?.tagged);
9123
9442
  const tagged = pdfaConfig.enabled;
9443
+ const enc = createEncodingContext(fontEntries, tagged);
9124
9444
  const encryptionOpts = layout?.encryption;
9125
9445
  if (tagged && encryptionOpts) {
9126
9446
  throw new Error("PDF/A and encryption are mutually exclusive (ISO 19005-1 \xA76.3.2)");
@@ -9451,8 +9771,17 @@ function buildDocumentPDF(params, layoutOptions) {
9451
9771
  kids.push(`${pageObjStart + p * 2} 0 R`);
9452
9772
  }
9453
9773
  emitObj(2, `<< /Type /Pages /Kids [${kids.join(" ")}] /Count ${totalPages} >>`);
9454
- emitObj(3, "<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica /Encoding /WinAnsiEncoding >>");
9455
- 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
+ }
9456
9785
  for (let fi = 0; fi < fontEntries.length; fi++) {
9457
9786
  const fe = fontEntries[fi];
9458
9787
  const fd = fe.fontData;
@@ -9546,13 +9875,13 @@ function buildDocumentPDF(params, layoutOptions) {
9546
9875
  const destName = pa.annot.url.slice(1);
9547
9876
  emitObj(
9548
9877
  objNum,
9549
- `<< /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} >>`
9550
9879
  );
9551
9880
  } else {
9552
9881
  const escapedUrl = pa.annot.url.replace(/\\/g, "\\\\").replace(/\(/g, "\\(").replace(/\)/g, "\\)");
9553
9882
  emitObj(
9554
9883
  objNum,
9555
- `<< /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}) >> >>`
9556
9885
  );
9557
9886
  }
9558
9887
  annotIdx++;
@@ -9669,13 +9998,13 @@ function buildDocumentPDF(params, layoutOptions) {
9669
9998
  const destName = pa.annot.url.slice(1);
9670
9999
  emitObj(
9671
10000
  objNum,
9672
- `<< /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} >>`
9673
10002
  );
9674
10003
  } else {
9675
10004
  const escapedUrl = pa.annot.url.replace(/\\/g, "\\\\").replace(/\(/g, "\\(").replace(/\)/g, "\\)");
9676
10005
  emitObj(
9677
10006
  objNum,
9678
- `<< /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}) >> >>`
9679
10008
  );
9680
10009
  }
9681
10010
  annotIdx++;
@@ -9752,7 +10081,7 @@ function buildDocumentPDF(params, layoutOptions) {
9752
10081
  structTreeRootObjNum = tree.structTreeRootObjNum;
9753
10082
  totalObjs = treeStart + tree.totalObjects - 1;
9754
10083
  xmpObjNum = totalObjs + 1;
9755
- 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));
9756
10085
  emitStreamObj(
9757
10086
  xmpObjNum,
9758
10087
  `<< /Type /Metadata /Subtype /XML /Length ${xmpContent.length}`,
@@ -11250,6 +11579,185 @@ function getTrailerRef(trailer, key) {
11250
11579
  return void 0;
11251
11580
  }
11252
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
+
11253
11761
  // src/parser/pdf-reader.ts
11254
11762
  function openPdf(bytes) {
11255
11763
  const xref = parseXrefTable(bytes);
@@ -11309,10 +11817,15 @@ function openPdf(bytes) {
11309
11817
  data = decodePNGPredictor(data, decodeParms);
11310
11818
  }
11311
11819
  }
11820
+ } else if (filterName !== void 0 && KNOWN_DECODE_FILTERS.has(filterName)) {
11821
+ data = applyDecodeFilter(filterName, data);
11312
11822
  } else if (filter !== void 0 && isArray(filter)) {
11313
11823
  for (const f of filter) {
11314
- if (isName(f) && f.value === "FlateDecode") {
11824
+ if (!isName(f)) continue;
11825
+ if (f.value === "FlateDecode") {
11315
11826
  data = inflateSync(data);
11827
+ } else if (KNOWN_DECODE_FILTERS.has(f.value)) {
11828
+ data = applyDecodeFilter(f.value, data);
11316
11829
  }
11317
11830
  }
11318
11831
  }
@@ -11681,6 +12194,6 @@ async function createPDF(pdfParams, options) {
11681
12194
  return generatePDFMainThread(pdfParams, options?.layoutOptions);
11682
12195
  }
11683
12196
 
11684
- 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 };
11685
12198
  //# sourceMappingURL=index.js.map
11686
12199
  //# sourceMappingURL=index.js.map