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.
@@ -88,12 +88,12 @@ function shapeThaiText(str, fontData) {
88
88
  function getAdv(gid) {
89
89
  return widths[gid] !== void 0 ? widths[gid] : defaultWidth;
90
90
  }
91
- function getBaseAnchor(baseGid, markClass) {
91
+ function getBaseAnchor2(baseGid, markClass) {
92
92
  const base = markAnchors && markAnchors.bases && markAnchors.bases[baseGid];
93
93
  if (!base) return null;
94
94
  return base[markClass] ?? null;
95
95
  }
96
- function getMarkAnchor(markGid) {
96
+ function getMarkAnchor2(markGid) {
97
97
  const mark = markAnchors && markAnchors.marks && markAnchors.marks[markGid];
98
98
  if (!mark) return null;
99
99
  return { classIdx: mark[0], x: mark[1], y: mark[2] };
@@ -130,7 +130,7 @@ function shapeThaiText(str, fontData) {
130
130
  for (let ai = 0; ai < cluster.aboves.length; ai++) {
131
131
  const abvCp = cluster.aboves[ai];
132
132
  const markGid = resolveMarkGid(abvCp, isTallBase);
133
- const markAnchor = getMarkAnchor(markGid);
133
+ const markAnchor = getMarkAnchor2(markGid);
134
134
  let dx = 0;
135
135
  let dy = 0;
136
136
  if (ai > 0 && prevAboveMarkGid !== null) {
@@ -139,7 +139,7 @@ function shapeThaiText(str, fontData) {
139
139
  dx = prevAboveMarkDx + m2mOffset.dx;
140
140
  dy = prevAboveMarkDy + m2mOffset.dy;
141
141
  } else if (markAnchor) {
142
- const baseAnchor = getBaseAnchor(baseGid, markAnchor.classIdx);
142
+ const baseAnchor = getBaseAnchor2(baseGid, markAnchor.classIdx);
143
143
  if (baseAnchor) {
144
144
  dx = baseAnchor[0] - markAnchor.x - baseAdv;
145
145
  dy = baseAnchor[1] - markAnchor.y;
@@ -147,7 +147,7 @@ function shapeThaiText(str, fontData) {
147
147
  }
148
148
  } else {
149
149
  if (markAnchor) {
150
- const baseAnchor = getBaseAnchor(baseGid, markAnchor.classIdx);
150
+ const baseAnchor = getBaseAnchor2(baseGid, markAnchor.classIdx);
151
151
  if (baseAnchor) {
152
152
  dx = baseAnchor[0] - markAnchor.x - baseAdv;
153
153
  dy = baseAnchor[1] - markAnchor.y;
@@ -161,11 +161,11 @@ function shapeThaiText(str, fontData) {
161
161
  }
162
162
  for (const blwCp of cluster.belows) {
163
163
  const markGid = cmap[blwCp] || 0;
164
- const markAnchor = getMarkAnchor(markGid);
164
+ const markAnchor = getMarkAnchor2(markGid);
165
165
  let dx = 0;
166
166
  let dy = 0;
167
167
  if (markAnchor) {
168
- const baseAnchor = getBaseAnchor(baseGid, markAnchor.classIdx);
168
+ const baseAnchor = getBaseAnchor2(baseGid, markAnchor.classIdx);
169
169
  if (baseAnchor) {
170
170
  dx = baseAnchor[0] - markAnchor.x - baseAdv;
171
171
  dy = baseAnchor[1] - markAnchor.y;
@@ -177,6 +177,112 @@ function shapeThaiText(str, fontData) {
177
177
  return shaped;
178
178
  }
179
179
 
180
+ // src/shaping/script-registry.ts
181
+ var ARABIC_START = 1536;
182
+ var ARABIC_END = 1791;
183
+ var ARABIC_SUPPLEMENT_START = 1872;
184
+ var ARABIC_SUPPLEMENT_END = 1919;
185
+ var ARABIC_EXTENDED_A_START = 2208;
186
+ var ARABIC_EXTENDED_A_END = 2303;
187
+ var ARABIC_PRES_A_START = 64336;
188
+ var ARABIC_PRES_A_END = 65023;
189
+ var ARABIC_PRES_B_START = 65136;
190
+ var ARABIC_PRES_B_END = 65279;
191
+ var THAI_START = 3584;
192
+ var THAI_END = 3711;
193
+ var DEVANAGARI_START = 2304;
194
+ var DEVANAGARI_END = 2431;
195
+ var DEVANAGARI_EXT_START = 43232;
196
+ var DEVANAGARI_EXT_END = 43263;
197
+ var BENGALI_START = 2432;
198
+ var BENGALI_END = 2559;
199
+ var TAMIL_START = 2944;
200
+ var TAMIL_END = 3071;
201
+ var EMOJI_RANGES = [
202
+ [127744, 128511],
203
+ // Miscellaneous Symbols and Pictographs
204
+ [128512, 128591],
205
+ // Emoticons
206
+ [128640, 128767],
207
+ // Transport and Map Symbols
208
+ [128768, 128895],
209
+ // Alchemical Symbols (partial)
210
+ [128896, 129023],
211
+ // Geometric Shapes Extended
212
+ [129024, 129279],
213
+ // Supplemental Arrows-C
214
+ [129280, 129535],
215
+ // Supplemental Symbols and Pictographs
216
+ [129536, 129647],
217
+ // Chess Symbols
218
+ [129648, 129791],
219
+ // Symbols and Pictographs Extended-A
220
+ [9728, 9983],
221
+ // Miscellaneous Symbols
222
+ [9984, 10175],
223
+ // Dingbats
224
+ [126976, 127023],
225
+ // Mahjong Tiles
226
+ [127136, 127231]
227
+ // Playing Cards
228
+ ];
229
+ var FITZPATRICK_START = 127995;
230
+ var FITZPATRICK_END = 127999;
231
+ function isArabicCodepoint(cp) {
232
+ return cp >= ARABIC_START && cp <= ARABIC_END || cp >= ARABIC_SUPPLEMENT_START && cp <= ARABIC_SUPPLEMENT_END || cp >= ARABIC_EXTENDED_A_START && cp <= ARABIC_EXTENDED_A_END || cp >= ARABIC_PRES_A_START && cp <= ARABIC_PRES_A_END || cp >= ARABIC_PRES_B_START && cp <= ARABIC_PRES_B_END;
233
+ }
234
+ function isThaiCodepoint(cp) {
235
+ return cp >= THAI_START && cp <= THAI_END;
236
+ }
237
+ function isBengaliCodepoint(cp) {
238
+ return cp >= BENGALI_START && cp <= BENGALI_END;
239
+ }
240
+ function isTamilCodepoint(cp) {
241
+ return cp >= TAMIL_START && cp <= TAMIL_END;
242
+ }
243
+ function isDevanagariCodepoint(cp) {
244
+ return cp >= DEVANAGARI_START && cp <= DEVANAGARI_END || cp >= DEVANAGARI_EXT_START && cp <= DEVANAGARI_EXT_END;
245
+ }
246
+ function isEmojiCodepoint(cp) {
247
+ if (cp >= FITZPATRICK_START && cp <= FITZPATRICK_END) return true;
248
+ for (const [lo, hi] of EMOJI_RANGES) {
249
+ if (cp >= lo && cp <= hi) return true;
250
+ }
251
+ return false;
252
+ }
253
+ function containsArabic(text) {
254
+ for (let i = 0; i < text.length; ) {
255
+ const cp = text.codePointAt(i) ?? 0;
256
+ if (isArabicCodepoint(cp)) return true;
257
+ i += cp > 65535 ? 2 : 1;
258
+ }
259
+ return false;
260
+ }
261
+ function containsThai(str) {
262
+ for (let i = 0; i < str.length; i++) {
263
+ if (isThaiCodepoint(str.charCodeAt(i))) return true;
264
+ }
265
+ return false;
266
+ }
267
+ function containsBengali(str) {
268
+ for (let i = 0; i < str.length; i++) {
269
+ if (isBengaliCodepoint(str.charCodeAt(i))) return true;
270
+ }
271
+ return false;
272
+ }
273
+ function containsTamil(str) {
274
+ for (let i = 0; i < str.length; i++) {
275
+ if (isTamilCodepoint(str.charCodeAt(i))) return true;
276
+ }
277
+ return false;
278
+ }
279
+ function containsDevanagari(str) {
280
+ for (let i = 0; i < str.length; i++) {
281
+ if (isDevanagariCodepoint(str.charCodeAt(i))) return true;
282
+ }
283
+ return false;
284
+ }
285
+
180
286
  // src/shaping/script-detect.ts
181
287
  function detectCharLang(cp) {
182
288
  if (cp >= 880 && cp <= 1023 || cp >= 7936 && cp <= 8191) return "el";
@@ -193,6 +299,7 @@ function detectCharLang(cp) {
193
299
  if (cp >= 1024 && cp <= 1279 || cp >= 1280 && cp <= 1327) return "ru";
194
300
  if (cp >= 4256 && cp <= 4351 || cp >= 11520 && cp <= 11567) return "ka";
195
301
  if (cp >= 1328 && cp <= 1423 || cp >= 64275 && cp <= 64279) return "hy";
302
+ if (isEmojiCodepoint(cp)) return "emoji";
196
303
  return null;
197
304
  }
198
305
 
@@ -292,8 +399,8 @@ function pdfString(str) {
292
399
  }
293
400
  function truncate(str, max) {
294
401
  if (!str || str.length <= max) return str || "";
295
- if (max <= 2) return "..";
296
- return str.slice(0, max - 2) + "..";
402
+ if (max <= 1) return "\u2026";
403
+ return str.slice(0, max - 1) + "\u2026";
297
404
  }
298
405
  function helveticaWidth(str, sz) {
299
406
  let w = 0;
@@ -319,73 +426,25 @@ function helveticaWidth(str, sz) {
319
426
  return w * sz / 1e3;
320
427
  }
321
428
 
322
- // src/shaping/script-registry.ts
323
- var ARABIC_START = 1536;
324
- var ARABIC_END = 1791;
325
- var ARABIC_SUPPLEMENT_START = 1872;
326
- var ARABIC_SUPPLEMENT_END = 1919;
327
- var ARABIC_EXTENDED_A_START = 2208;
328
- var ARABIC_EXTENDED_A_END = 2303;
329
- var ARABIC_PRES_A_START = 64336;
330
- var ARABIC_PRES_A_END = 65023;
331
- var ARABIC_PRES_B_START = 65136;
332
- var ARABIC_PRES_B_END = 65279;
333
- var THAI_START = 3584;
334
- var THAI_END = 3711;
335
- var DEVANAGARI_START = 2304;
336
- var DEVANAGARI_END = 2431;
337
- var DEVANAGARI_EXT_START = 43232;
338
- var DEVANAGARI_EXT_END = 43263;
339
- var BENGALI_START = 2432;
340
- var BENGALI_END = 2559;
341
- var TAMIL_START = 2944;
342
- var TAMIL_END = 3071;
343
- function isArabicCodepoint(cp) {
344
- 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;
345
- }
346
- function isThaiCodepoint(cp) {
347
- return cp >= THAI_START && cp <= THAI_END;
348
- }
349
- function isBengaliCodepoint(cp) {
350
- return cp >= BENGALI_START && cp <= BENGALI_END;
351
- }
352
- function isTamilCodepoint(cp) {
353
- return cp >= TAMIL_START && cp <= TAMIL_END;
354
- }
355
- function isDevanagariCodepoint(cp) {
356
- return cp >= DEVANAGARI_START && cp <= DEVANAGARI_END || cp >= DEVANAGARI_EXT_START && cp <= DEVANAGARI_EXT_END;
357
- }
358
- function containsArabic(text) {
359
- for (let i = 0; i < text.length; ) {
360
- const cp = text.codePointAt(i) ?? 0;
361
- if (isArabicCodepoint(cp)) return true;
362
- i += cp > 65535 ? 2 : 1;
363
- }
364
- return false;
365
- }
366
- function containsThai(str) {
367
- for (let i = 0; i < str.length; i++) {
368
- if (isThaiCodepoint(str.charCodeAt(i))) return true;
369
- }
370
- return false;
371
- }
372
- function containsBengali(str) {
373
- for (let i = 0; i < str.length; i++) {
374
- if (isBengaliCodepoint(str.charCodeAt(i))) return true;
375
- }
376
- return false;
377
- }
378
- function containsTamil(str) {
379
- for (let i = 0; i < str.length; i++) {
380
- if (isTamilCodepoint(str.charCodeAt(i))) return true;
381
- }
382
- return false;
383
- }
384
- function containsDevanagari(str) {
385
- for (let i = 0; i < str.length; i++) {
386
- if (isDevanagariCodepoint(str.charCodeAt(i))) return true;
429
+ // src/shaping/gsub-driver.ts
430
+ function tryLigature(gids, ligatures) {
431
+ if (!ligatures || gids.length < 2) return null;
432
+ const firstGid = gids[0];
433
+ const entries = ligatures[firstGid];
434
+ if (!entries) return null;
435
+ for (const entry of entries) {
436
+ const compCount = entry.length - 1;
437
+ if (compCount > gids.length - 1) continue;
438
+ let match = true;
439
+ for (let ci = 0; ci < compCount; ci++) {
440
+ if (gids[1 + ci] !== entry[1 + ci]) {
441
+ match = false;
442
+ break;
443
+ }
444
+ }
445
+ if (match) return { resultGid: entry[0], consumed: compCount + 1 };
387
446
  }
388
- return false;
447
+ return null;
389
448
  }
390
449
 
391
450
  // src/shaping/bengali-shaper.ts
@@ -514,43 +573,27 @@ function shapeBengaliText(str, fontData) {
514
573
  if (gsub[gid] !== void 0) return gsub[gid];
515
574
  return gid;
516
575
  }
517
- function tryLigature(gids) {
518
- if (!ligatures || gids.length < 2) return null;
519
- const firstGid = gids[0];
520
- const entries = ligatures[firstGid];
521
- if (!entries) return null;
522
- for (const entry of entries) {
523
- const compCount = entry.length - 1;
524
- if (compCount > gids.length - 1) continue;
525
- let match = true;
526
- for (let ci = 0; ci < compCount; ci++) {
527
- if (gids[1 + ci] !== entry[1 + ci]) {
528
- match = false;
529
- break;
530
- }
531
- }
532
- if (match) return { resultGid: entry[0], consumed: compCount + 1 };
533
- }
534
- return null;
576
+ function tryLig(gids) {
577
+ return tryLigature(gids, ligatures);
535
578
  }
536
579
  function getAdv(gid) {
537
580
  return widths[gid] !== void 0 ? widths[gid] : defaultWidth;
538
581
  }
539
- function getBaseAnchor(baseGid, markClass) {
582
+ function getBaseAnchor2(baseGid, markClass) {
540
583
  const base = markAnchors && markAnchors.bases && markAnchors.bases[baseGid];
541
584
  if (!base) return null;
542
585
  return base[markClass] ?? null;
543
586
  }
544
- function getMarkAnchor(markGid) {
587
+ function getMarkAnchor2(markGid) {
545
588
  const mark = markAnchors && markAnchors.marks && markAnchors.marks[markGid];
546
589
  if (!mark) return null;
547
590
  return { classIdx: mark[0], x: mark[1], y: mark[2] };
548
591
  }
549
592
  function emitGlyph(gid, isZero, baseGid) {
550
593
  if (isZero && baseGid !== void 0) {
551
- const markAnchor = getMarkAnchor(gid);
594
+ const markAnchor = getMarkAnchor2(gid);
552
595
  if (markAnchor) {
553
- const baseAnchorPt = getBaseAnchor(baseGid, markAnchor.classIdx);
596
+ const baseAnchorPt = getBaseAnchor2(baseGid, markAnchor.classIdx);
554
597
  if (baseAnchorPt) {
555
598
  const baseAdv = getAdv(baseGid);
556
599
  shaped.push({
@@ -611,7 +654,7 @@ function shapeBengaliText(str, fontData) {
611
654
  }
612
655
  }
613
656
  let ligConsumed = 0;
614
- const ligResult = tryLigature(clusterGids);
657
+ const ligResult = tryLig(clusterGids);
615
658
  if (ligResult) {
616
659
  emitGlyph(ligResult.resultGid, false);
617
660
  baseGid = ligResult.resultGid;
@@ -619,7 +662,7 @@ function shapeBengaliText(str, fontData) {
619
662
  let gi = ligConsumed;
620
663
  while (gi < clusterGids.length) {
621
664
  const subSeq = clusterGids.slice(gi);
622
- const subLig = tryLigature(subSeq);
665
+ const subLig = tryLig(subSeq);
623
666
  if (subLig) {
624
667
  emitGlyph(subLig.resultGid, false);
625
668
  gi += subLig.consumed;
@@ -782,43 +825,27 @@ function shapeTamilText(str, fontData) {
782
825
  const normCp = cp === 8239 || cp === 160 ? 32 : cp;
783
826
  return cmap[normCp] || 0;
784
827
  }
785
- function tryLigature(gids) {
786
- if (!ligatures || gids.length < 2) return null;
787
- const firstGid = gids[0];
788
- const entries = ligatures[firstGid];
789
- if (!entries) return null;
790
- for (const entry of entries) {
791
- const compCount = entry.length - 1;
792
- if (compCount > gids.length - 1) continue;
793
- let match = true;
794
- for (let ci = 0; ci < compCount; ci++) {
795
- if (gids[1 + ci] !== entry[1 + ci]) {
796
- match = false;
797
- break;
798
- }
799
- }
800
- if (match) return { resultGid: entry[0], consumed: compCount + 1 };
801
- }
802
- return null;
828
+ function tryLig(gids) {
829
+ return tryLigature(gids, ligatures);
803
830
  }
804
831
  function getAdv(gid) {
805
832
  return widths[gid] !== void 0 ? widths[gid] : defaultWidth;
806
833
  }
807
- function getBaseAnchor(baseGid, markClass) {
834
+ function getBaseAnchor2(baseGid, markClass) {
808
835
  const base = markAnchors && markAnchors.bases && markAnchors.bases[baseGid];
809
836
  if (!base) return null;
810
837
  return base[markClass] ?? null;
811
838
  }
812
- function getMarkAnchor(markGid) {
839
+ function getMarkAnchor2(markGid) {
813
840
  const mark = markAnchors && markAnchors.marks && markAnchors.marks[markGid];
814
841
  if (!mark) return null;
815
842
  return { classIdx: mark[0], x: mark[1], y: mark[2] };
816
843
  }
817
844
  function emitGlyph(gid, isZero, baseGid) {
818
845
  if (isZero && baseGid !== void 0) {
819
- const markAnchor = getMarkAnchor(gid);
846
+ const markAnchor = getMarkAnchor2(gid);
820
847
  if (markAnchor) {
821
- const baseAnchorPt = getBaseAnchor(baseGid, markAnchor.classIdx);
848
+ const baseAnchorPt = getBaseAnchor2(baseGid, markAnchor.classIdx);
822
849
  if (baseAnchorPt) {
823
850
  const baseAdv = getAdv(baseGid);
824
851
  shaped.push({
@@ -883,7 +910,7 @@ function shapeTamilText(str, fontData) {
883
910
  }
884
911
  }
885
912
  let ligConsumed = 0;
886
- const ligResult = tryLigature(clusterGids);
913
+ const ligResult = tryLig(clusterGids);
887
914
  if (ligResult) {
888
915
  emitGlyph(ligResult.resultGid, false);
889
916
  baseGid = ligResult.resultGid;
@@ -891,7 +918,7 @@ function shapeTamilText(str, fontData) {
891
918
  let gi = ligConsumed;
892
919
  while (gi < clusterGids.length) {
893
920
  const subSeq = clusterGids.slice(gi);
894
- const subLig = tryLigature(subSeq);
921
+ const subLig = tryLig(subSeq);
895
922
  if (subLig) {
896
923
  emitGlyph(subLig.resultGid, false);
897
924
  gi += subLig.consumed;
@@ -940,6 +967,30 @@ function shapeTamilText(str, fontData) {
940
967
  return shaped;
941
968
  }
942
969
 
970
+ // src/shaping/gpos-positioner.ts
971
+ function getBaseAnchor(markAnchors, baseGid, markClass) {
972
+ if (!markAnchors) return null;
973
+ const base = markAnchors.bases[baseGid];
974
+ if (!base) return null;
975
+ return base[markClass] ?? null;
976
+ }
977
+ function getMarkAnchor(markAnchors, markGid) {
978
+ if (!markAnchors) return null;
979
+ const mark = markAnchors.marks[markGid];
980
+ if (!mark) return null;
981
+ return { classIdx: mark[0], x: mark[1], y: mark[2] };
982
+ }
983
+ function positionMarkOnBase(markAnchors, markGid, baseGid, baseAdv) {
984
+ const mark = getMarkAnchor(markAnchors, markGid);
985
+ if (!mark) return null;
986
+ const base = getBaseAnchor(markAnchors, baseGid, mark.classIdx);
987
+ if (!base) return null;
988
+ return {
989
+ dx: base[0] - mark.x - baseAdv,
990
+ dy: base[1] - mark.y
991
+ };
992
+ }
993
+
943
994
  // src/shaping/devanagari-shaper.ts
944
995
  var HALANT2 = 2381;
945
996
  var NUKTA2 = 2364;
@@ -1055,43 +1106,17 @@ function shapeDevanagariText(str, fontData) {
1055
1106
  const normCp = cp === 8239 || cp === 160 ? 32 : cp;
1056
1107
  return cmap[normCp] || 0;
1057
1108
  }
1058
- function tryLigature(gids) {
1059
- if (!ligatures || gids.length < 2) return null;
1060
- const firstGid = gids[0];
1061
- const entries = ligatures[firstGid];
1062
- if (!entries) return null;
1063
- for (const entry of entries) {
1064
- const compCount = entry.length - 1;
1065
- if (compCount > gids.length - 1) continue;
1066
- let match = true;
1067
- for (let ci = 0; ci < compCount; ci++) {
1068
- if (gids[1 + ci] !== entry[1 + ci]) {
1069
- match = false;
1070
- break;
1071
- }
1072
- }
1073
- if (match) return { resultGid: entry[0], consumed: compCount + 1 };
1074
- }
1075
- return null;
1109
+ function tryLig(gids) {
1110
+ return tryLigature(gids, ligatures);
1076
1111
  }
1077
1112
  function getAdv(gid) {
1078
1113
  return widths[gid] !== void 0 ? widths[gid] : defaultWidth;
1079
1114
  }
1080
- function getBaseAnchor(baseGid, markClass) {
1081
- const base = markAnchors && markAnchors.bases && markAnchors.bases[baseGid];
1082
- if (!base) return null;
1083
- return base[markClass] ?? null;
1084
- }
1085
- function getMarkAnchor(markGid) {
1086
- const mark = markAnchors && markAnchors.marks && markAnchors.marks[markGid];
1087
- if (!mark) return null;
1088
- return { classIdx: mark[0], x: mark[1], y: mark[2] };
1089
- }
1090
1115
  function emitGlyph(gid, isZero, baseGid) {
1091
1116
  if (isZero && baseGid !== void 0) {
1092
- const markAnchor = getMarkAnchor(gid);
1117
+ const markAnchor = getMarkAnchor(markAnchors, gid);
1093
1118
  if (markAnchor) {
1094
- const baseAnchorPt = getBaseAnchor(baseGid, markAnchor.classIdx);
1119
+ const baseAnchorPt = getBaseAnchor(markAnchors, baseGid, markAnchor.classIdx);
1095
1120
  if (baseAnchorPt) {
1096
1121
  const baseAdv = getAdv(baseGid);
1097
1122
  shaped.push({
@@ -1139,7 +1164,7 @@ function shapeDevanagariText(str, fontData) {
1139
1164
  if (hasReph) {
1140
1165
  const raGid = resolveGid(RA2);
1141
1166
  const halantGid = resolveGid(HALANT2);
1142
- const rephLig = tryLigature([raGid, halantGid]);
1167
+ const rephLig = tryLig([raGid, halantGid]);
1143
1168
  if (rephLig) {
1144
1169
  emitGlyph(rephLig.resultGid, true, baseGid);
1145
1170
  } else {
@@ -1162,14 +1187,14 @@ function shapeDevanagariText(str, fontData) {
1162
1187
  break;
1163
1188
  }
1164
1189
  }
1165
- const ligResult = tryLigature(clusterGids);
1190
+ const ligResult = tryLig(clusterGids);
1166
1191
  if (ligResult) {
1167
1192
  emitGlyph(ligResult.resultGid, false);
1168
1193
  baseGid = ligResult.resultGid;
1169
1194
  let gi = ligResult.consumed;
1170
1195
  while (gi < clusterGids.length) {
1171
1196
  const subSeq = clusterGids.slice(gi);
1172
- const subLig = tryLigature(subSeq);
1197
+ const subLig = tryLig(subSeq);
1173
1198
  if (subLig) {
1174
1199
  emitGlyph(subLig.resultGid, false);
1175
1200
  gi += subLig.consumed;
@@ -1343,6 +1368,10 @@ function shapeArabicText(str, fontData) {
1343
1368
  const forms = resolvePositionalForms(codePoints);
1344
1369
  const glyphs = [];
1345
1370
  const cmap = fontData.cmap;
1371
+ const widths = fontData.widths;
1372
+ const defaultWidth = fontData.defaultWidth;
1373
+ const markAnchors = fontData.markAnchors;
1374
+ let lastBaseGid = 0;
1346
1375
  for (let i = 0; i < codePoints.length; i++) {
1347
1376
  const cp = codePoints[i];
1348
1377
  if (i < codePoints.length - 1 && isLamAlef(cp, codePoints[i + 1])) {
@@ -1353,6 +1382,7 @@ function shapeArabicText(str, fontData) {
1353
1382
  const ligGid = cmap[ligCP];
1354
1383
  if (ligGid) {
1355
1384
  glyphs.push({ gid: ligGid, dx: 0, dy: 0, isZeroAdvance: false });
1385
+ lastBaseGid = ligGid;
1356
1386
  i++;
1357
1387
  continue;
1358
1388
  }
@@ -1374,7 +1404,16 @@ function shapeArabicText(str, fontData) {
1374
1404
  }
1375
1405
  const joining = getJoiningType(cp);
1376
1406
  const isZeroAdvance = joining === "T";
1407
+ if (isZeroAdvance && lastBaseGid !== 0) {
1408
+ const baseAdv = widths[lastBaseGid] !== void 0 ? widths[lastBaseGid] : defaultWidth;
1409
+ const offset = positionMarkOnBase(markAnchors, gid, lastBaseGid, baseAdv);
1410
+ if (offset) {
1411
+ glyphs.push({ gid, dx: offset.dx, dy: offset.dy, isZeroAdvance: true });
1412
+ continue;
1413
+ }
1414
+ }
1377
1415
  glyphs.push({ gid, dx: 0, dy: 0, isZeroAdvance });
1416
+ if (!isZeroAdvance) lastBaseGid = gid;
1378
1417
  }
1379
1418
  return glyphs;
1380
1419
  }
@@ -1387,7 +1426,7 @@ function classifyBidiType(cp) {
1387
1426
  cp >= 1611 && cp <= 1631 || // Arabic harakat
1388
1427
  cp >= 1648 && cp <= 1648 || // Arabic superscript alef
1389
1428
  cp >= 1750 && cp <= 1756 || cp >= 1759 && cp <= 1764 || cp >= 1767 && cp <= 1768 || cp >= 1770 && cp <= 1773 || cp >= 65056 && cp <= 65071) return "NSM";
1390
- if (cp === 8203 || cp === 8204 || cp === 8205 || cp === 8206 || cp === 8207 || cp === 65279) return "BN";
1429
+ if (cp === 8203 || cp === 8204 || cp === 8205 || cp === 8206 || cp === 8207 || cp === 65279 || cp === 8294 || cp === 8295 || cp === 8296 || cp === 8297) return "BN";
1391
1430
  if (cp >= 1632 && cp <= 1641) return "AN";
1392
1431
  if (cp >= 1776 && cp <= 1785) return "AN";
1393
1432
  if (cp >= 48 && cp <= 57) return "EN";
@@ -1601,18 +1640,153 @@ function fixBracketPairing(types, codePoints, len) {
1601
1640
  }
1602
1641
  }
1603
1642
  }
1643
+ function findOutermostIsolatePairs(codePoints) {
1644
+ const pairs = [];
1645
+ let i = 0;
1646
+ while (i < codePoints.length) {
1647
+ const cp = codePoints[i];
1648
+ if (cp === 8294 || cp === 8295 || cp === 8296) {
1649
+ let depth = 1;
1650
+ let close = -1;
1651
+ for (let j = i + 1; j < codePoints.length; j++) {
1652
+ const cj = codePoints[j];
1653
+ if (cj === 8294 || cj === 8295 || cj === 8296) depth++;
1654
+ else if (cj === 8297) {
1655
+ depth--;
1656
+ if (depth === 0) {
1657
+ close = j;
1658
+ break;
1659
+ }
1660
+ }
1661
+ }
1662
+ if (close === -1) {
1663
+ i++;
1664
+ continue;
1665
+ }
1666
+ const kind = cp === 8294 ? "LRI" : cp === 8295 ? "RLI" : "FSI";
1667
+ pairs.push({ open: i, close, kind });
1668
+ i = close + 1;
1669
+ } else {
1670
+ i++;
1671
+ }
1672
+ }
1673
+ return pairs;
1674
+ }
1604
1675
  function resolveBidiRuns(text) {
1605
1676
  if (!text) return [];
1606
1677
  const codePoints = [];
1678
+ const cpToStr = [];
1679
+ for (let i = 0; i < text.length; ) {
1680
+ cpToStr.push(i);
1681
+ const cp = text.codePointAt(i) ?? 0;
1682
+ codePoints.push(cp);
1683
+ i += cp > 65535 ? 2 : 1;
1684
+ }
1685
+ cpToStr.push(text.length);
1686
+ const isolates = findOutermostIsolatePairs(codePoints);
1687
+ if (isolates.length === 0) {
1688
+ return resolveBidiCore(text, codePoints, cpToStr);
1689
+ }
1690
+ const insideIsolate = new Array(codePoints.length).fill(false);
1691
+ for (const p of isolates) {
1692
+ for (let k = p.open; k <= p.close; k++) insideIsolate[k] = true;
1693
+ }
1694
+ const outerTypes = codePoints.map((cp, idx) => insideIsolate[idx] ? "BN" : classifyBidiType(cp));
1695
+ const parentLevel = detectParagraphLevel(outerTypes);
1696
+ const out = [];
1697
+ const emitSegment = (cpStart, cpEnd, forced) => {
1698
+ if (cpStart >= cpEnd) return;
1699
+ const segText = text.substring(cpToStr[cpStart], cpToStr[cpEnd]);
1700
+ const segCps = codePoints.slice(cpStart, cpEnd);
1701
+ const baseStrIdx = cpToStr[cpStart];
1702
+ const segCpToStr = cpToStr.slice(cpStart, cpEnd + 1).map((x) => x - baseStrIdx);
1703
+ const segRuns = forced === void 0 ? resolveBidiRuns(segText) : resolveBidiCore(segText, segCps, segCpToStr, forced);
1704
+ for (const r of segRuns) {
1705
+ out.push({ text: r.text, level: r.level, start: r.start + baseStrIdx });
1706
+ }
1707
+ };
1708
+ let cursor = 0;
1709
+ for (const pair of isolates) {
1710
+ emitSegment(cursor, pair.open, parentLevel);
1711
+ const innerStart = pair.open + 1;
1712
+ const innerEnd = pair.close;
1713
+ let innerLevel;
1714
+ if (pair.kind === "LRI") innerLevel = 0;
1715
+ else if (pair.kind === "RLI") innerLevel = 1;
1716
+ else {
1717
+ const innerTypes = codePoints.slice(innerStart, innerEnd).map(classifyBidiType);
1718
+ innerLevel = detectParagraphLevel(innerTypes);
1719
+ }
1720
+ if (innerStart < innerEnd) {
1721
+ const innerText = text.substring(cpToStr[innerStart], cpToStr[innerEnd]);
1722
+ const innerRuns = resolveBidiRunsForced(innerText, innerLevel);
1723
+ const baseStrIdx = cpToStr[innerStart];
1724
+ for (const r of innerRuns) {
1725
+ out.push({ text: r.text, level: r.level, start: r.start + baseStrIdx });
1726
+ }
1727
+ }
1728
+ cursor = pair.close + 1;
1729
+ }
1730
+ emitSegment(cursor, codePoints.length, parentLevel);
1731
+ return out;
1732
+ }
1733
+ function resolveBidiRunsForced(text, forcedLevel) {
1734
+ if (!text) return [];
1735
+ const codePoints = [];
1736
+ const cpToStr = [];
1607
1737
  for (let i = 0; i < text.length; ) {
1738
+ cpToStr.push(i);
1608
1739
  const cp = text.codePointAt(i) ?? 0;
1609
1740
  codePoints.push(cp);
1610
1741
  i += cp > 65535 ? 2 : 1;
1611
1742
  }
1743
+ cpToStr.push(text.length);
1744
+ const isolates = findOutermostIsolatePairs(codePoints);
1745
+ if (isolates.length === 0) {
1746
+ return resolveBidiCore(text, codePoints, cpToStr, forcedLevel);
1747
+ }
1748
+ const out = [];
1749
+ const emit = (cpStart, cpEnd, forced) => {
1750
+ if (cpStart >= cpEnd) return;
1751
+ const segText = text.substring(cpToStr[cpStart], cpToStr[cpEnd]);
1752
+ const segCps = codePoints.slice(cpStart, cpEnd);
1753
+ const baseStrIdx = cpToStr[cpStart];
1754
+ const segCpToStr = cpToStr.slice(cpStart, cpEnd + 1).map((x) => x - baseStrIdx);
1755
+ const segRuns = resolveBidiCore(segText, segCps, segCpToStr, forced);
1756
+ for (const r of segRuns) {
1757
+ out.push({ text: r.text, level: r.level, start: r.start + baseStrIdx });
1758
+ }
1759
+ };
1760
+ let cursor = 0;
1761
+ for (const pair of isolates) {
1762
+ emit(cursor, pair.open, forcedLevel);
1763
+ const innerStart = pair.open + 1;
1764
+ const innerEnd = pair.close;
1765
+ let innerLevel;
1766
+ if (pair.kind === "LRI") innerLevel = 0;
1767
+ else if (pair.kind === "RLI") innerLevel = 1;
1768
+ else {
1769
+ const innerTypes = codePoints.slice(innerStart, innerEnd).map(classifyBidiType);
1770
+ innerLevel = detectParagraphLevel(innerTypes);
1771
+ }
1772
+ if (innerStart < innerEnd) {
1773
+ const innerText = text.substring(cpToStr[innerStart], cpToStr[innerEnd]);
1774
+ const innerRuns = resolveBidiRunsForced(innerText, innerLevel);
1775
+ const baseStrIdx = cpToStr[innerStart];
1776
+ for (const r of innerRuns) {
1777
+ out.push({ text: r.text, level: r.level, start: r.start + baseStrIdx });
1778
+ }
1779
+ }
1780
+ cursor = pair.close + 1;
1781
+ }
1782
+ emit(cursor, codePoints.length, forcedLevel);
1783
+ return out;
1784
+ }
1785
+ function resolveBidiCore(text, codePoints, cpToStr, forcedLevel) {
1612
1786
  const len = codePoints.length;
1613
1787
  if (len === 0) return [];
1614
1788
  const types = codePoints.map(classifyBidiType);
1615
- const paraLevel = detectParagraphLevel(types);
1789
+ const paraLevel = forcedLevel !== void 0 ? forcedLevel : detectParagraphLevel(types);
1616
1790
  resolveWeakTypes(types, paraLevel);
1617
1791
  resolveNeutralTypes(types, paraLevel);
1618
1792
  if (paraLevel === 1) {
@@ -1623,13 +1797,6 @@ function resolveBidiRuns(text) {
1623
1797
  const runs = [];
1624
1798
  let runStart = 0;
1625
1799
  let runLevel = levels[0];
1626
- const cpToStr = [];
1627
- let strIdx = 0;
1628
- for (let i = 0; i < len; i++) {
1629
- cpToStr.push(strIdx);
1630
- strIdx += codePoints[i] > 65535 ? 2 : 1;
1631
- }
1632
- cpToStr.push(strIdx);
1633
1800
  for (let i = 1; i <= len; i++) {
1634
1801
  if (i === len || levels[i] !== runLevel) {
1635
1802
  const start = cpToStr[runStart];
@@ -1713,7 +1880,7 @@ function splitArabicNonArabic(text, fd) {
1713
1880
  if (cur) segments.push({ text: cur, arabic: curArabic });
1714
1881
  return segments;
1715
1882
  }
1716
- function buildTextRunsWithFallback(text, fontRef, fd, sz, trackGid) {
1883
+ function buildTextRunsWithFallback(text, fontRef, fd, sz, trackGid, pdfA = false) {
1717
1884
  const upm = fd.metrics.unitsPerEm;
1718
1885
  const result = [];
1719
1886
  let mode = null;
@@ -1753,11 +1920,11 @@ function buildTextRunsWithFallback(text, fontRef, fd, sz, trackGid) {
1753
1920
  const cp = rawCp === 8239 || rawCp === 160 ? 32 : rawCp;
1754
1921
  const char = text.substring(i, i + charLen);
1755
1922
  const gid = fd.cmap[cp] ?? 0;
1756
- if (gid === 0 && isWinAnsi(cp)) {
1923
+ if (gid === 0 && isWinAnsi(cp) && !pdfA) {
1757
1924
  if (mode === "cid") flushCid();
1758
1925
  mode = "hel";
1759
1926
  helChars += char;
1760
- } else if (mode === "hel" && isWinAnsi(cp)) {
1927
+ } else if (mode === "hel" && isWinAnsi(cp) && !pdfA) {
1761
1928
  helChars += char;
1762
1929
  } else {
1763
1930
  if (mode === "hel") flushHel();
@@ -1774,7 +1941,7 @@ function buildTextRunsWithFallback(text, fontRef, fd, sz, trackGid) {
1774
1941
  if (mode === "hel") flushHel();
1775
1942
  return result;
1776
1943
  }
1777
- function createEncodingContext(fontEntries) {
1944
+ function createEncodingContext(fontEntries, pdfA = false) {
1778
1945
  if (!fontEntries || fontEntries.length === 0) {
1779
1946
  return {
1780
1947
  isUnicode: false,
@@ -1830,12 +1997,12 @@ function createEncodingContext(fontEntries) {
1830
1997
  }
1831
1998
  result.push({ text: seg.text, fontRef, fontData: fd, shaped: visual, hexStr: null, widthPt: designW * sz / upm });
1832
1999
  } else {
1833
- const subRuns = buildTextRunsWithFallback(seg.text, fontRef, fd, sz, _trackGid);
2000
+ const subRuns = buildTextRunsWithFallback(seg.text, fontRef, fd, sz, _trackGid, pdfA);
1834
2001
  result.push(...subRuns);
1835
2002
  }
1836
2003
  }
1837
2004
  } else if (isRTL) {
1838
- const subRuns = buildTextRunsWithFallback(fRun.text, fontRef, fd, sz, _trackGid);
2005
+ const subRuns = buildTextRunsWithFallback(fRun.text, fontRef, fd, sz, _trackGid, pdfA);
1839
2006
  result.push(...subRuns);
1840
2007
  } else {
1841
2008
  if (containsThai(fRun.text)) {
@@ -1879,7 +2046,7 @@ function createEncodingContext(fontEntries) {
1879
2046
  }
1880
2047
  result.push({ text: fRun.text, fontRef, fontData: fd, shaped, hexStr: null, widthPt: designW * sz / upm });
1881
2048
  } else {
1882
- const subRuns = buildTextRunsWithFallback(fRun.text, fontRef, fd, sz, _trackGid);
2049
+ const subRuns = buildTextRunsWithFallback(fRun.text, fontRef, fd, sz, _trackGid, pdfA);
1883
2050
  result.push(...subRuns);
1884
2051
  }
1885
2052
  }
@@ -1936,7 +2103,7 @@ function createEncodingContext(fontEntries) {
1936
2103
  }
1937
2104
  return [{ text: run.text, fontRef, fontData: fd, shaped, hexStr: null, widthPt: designW * sz / upm }];
1938
2105
  }
1939
- return buildTextRunsWithFallback(run.text, fontRef, fd, sz, _trackGid);
2106
+ return buildTextRunsWithFallback(run.text, fontRef, fd, sz, _trackGid, pdfA);
1940
2107
  });
1941
2108
  },
1942
2109
  ps(str) {
@@ -2408,7 +2575,7 @@ function buildPdfMetadata(now = /* @__PURE__ */ new Date()) {
2408
2575
  const xmpDate = `${yyyy}-${mm}-${dd}T${hh}:${mi}:${ss}${tzSign}${tzH}:${tzM}`;
2409
2576
  return { pdfDate, xmpDate };
2410
2577
  }
2411
- function buildXMPMetadata(title, createDate, pdfaPart = 2, pdfaConformance = "B", author) {
2578
+ function buildXMPMetadata(title, createDate, pdfaPart = 2, pdfaConformance = "B", author, subject, keywords) {
2412
2579
  const escapedTitle = escapeXml(title);
2413
2580
  const lines = [
2414
2581
  '<?xpacket begin="\xEF\xBB\xBF" id="W5M0MpCehiHzreSzNTczkc9d"?>',
@@ -2427,7 +2594,9 @@ function buildXMPMetadata(title, createDate, pdfaPart = 2, pdfaConformance = "B"
2427
2594
  ` <xmp:ModifyDate>${createDate}</xmp:ModifyDate>`,
2428
2595
  ` <xmp:MetadataDate>${createDate}</xmp:MetadataDate>`,
2429
2596
  ` <pdfaid:part>${pdfaPart}</pdfaid:part>`,
2430
- ` <pdfaid:conformance>${pdfaConformance}</pdfaid:conformance>`,
2597
+ ` <pdfaid:conformance>${pdfaConformance}</pdfaid:conformance>`
2598
+ );
2599
+ lines.push(
2431
2600
  " </rdf:Description>",
2432
2601
  " </rdf:RDF>",
2433
2602
  "</x:xmpmeta>",
@@ -2435,6 +2604,35 @@ function buildXMPMetadata(title, createDate, pdfaPart = 2, pdfaConformance = "B"
2435
2604
  );
2436
2605
  return lines.join("\n");
2437
2606
  }
2607
+ function utf8EncodeBinaryString(str) {
2608
+ let out = "";
2609
+ for (let i = 0; i < str.length; i++) {
2610
+ let cp = str.charCodeAt(i);
2611
+ if (cp >= 55296 && cp <= 56319 && i + 1 < str.length) {
2612
+ const lo = str.charCodeAt(i + 1);
2613
+ if (lo >= 56320 && lo <= 57343) {
2614
+ cp = (cp - 55296 << 10) + (lo - 56320) + 65536;
2615
+ i++;
2616
+ }
2617
+ }
2618
+ if (cp < 128) {
2619
+ out += String.fromCharCode(cp);
2620
+ } else if (cp < 2048) {
2621
+ out += String.fromCharCode(192 | cp >> 6);
2622
+ out += String.fromCharCode(128 | cp & 63);
2623
+ } else if (cp < 65536) {
2624
+ out += String.fromCharCode(224 | cp >> 12);
2625
+ out += String.fromCharCode(128 | cp >> 6 & 63);
2626
+ out += String.fromCharCode(128 | cp & 63);
2627
+ } else {
2628
+ out += String.fromCharCode(240 | cp >> 18);
2629
+ out += String.fromCharCode(128 | cp >> 12 & 63);
2630
+ out += String.fromCharCode(128 | cp >> 6 & 63);
2631
+ out += String.fromCharCode(128 | cp & 63);
2632
+ }
2633
+ }
2634
+ return out;
2635
+ }
2438
2636
  function buildOutputIntentDict(iccStreamObjNum, subtype = "GTS_PDFA1") {
2439
2637
  return `<< /Type /OutputIntent /S /${subtype} /OutputConditionIdentifier (sRGB IEC61966-2.1) /RegistryName (http://www.color.org) /DestOutputProfile ${iccStreamObjNum} 0 R >>`;
2440
2638
  }
@@ -2681,13 +2879,42 @@ var DEFAULT_COLUMNS = [
2681
2879
  { f: 0.18, a: "c", mx: 20, mxH: 20 }
2682
2880
  ];
2683
2881
  function computeColumnPositions(columns, marginLeft, contentWidth) {
2684
- const cx = [];
2685
- const cwi = [];
2882
+ const n = columns.length;
2883
+ const cwi = new Array(n).fill(0);
2884
+ const fixed = new Array(n).fill(false);
2885
+ let totalFixed = 0;
2886
+ let freeWeight = 0;
2887
+ for (let i = 0; i < n; i++) {
2888
+ const col = columns[i];
2889
+ let w = col.f * contentWidth;
2890
+ let clamped = false;
2891
+ if (col.minWidth !== void 0 && w < col.minWidth) {
2892
+ w = col.minWidth;
2893
+ clamped = true;
2894
+ }
2895
+ if (col.maxWidth !== void 0 && w > col.maxWidth) {
2896
+ w = col.maxWidth;
2897
+ clamped = true;
2898
+ }
2899
+ if (clamped) {
2900
+ cwi[i] = w;
2901
+ fixed[i] = true;
2902
+ totalFixed += w;
2903
+ } else {
2904
+ freeWeight += col.f;
2905
+ }
2906
+ }
2907
+ const remaining = contentWidth - totalFixed;
2908
+ if (freeWeight > 0) {
2909
+ for (let i = 0; i < n; i++) {
2910
+ if (!fixed[i]) cwi[i] = columns[i].f / freeWeight * remaining;
2911
+ }
2912
+ }
2913
+ const cx = new Array(n);
2686
2914
  let x = marginLeft;
2687
- for (const col of columns) {
2688
- cx.push(x);
2689
- cwi.push(col.f * contentWidth);
2690
- x += col.f * contentWidth;
2915
+ for (let i = 0; i < n; i++) {
2916
+ cx[i] = x;
2917
+ x += cwi[i];
2691
2918
  }
2692
2919
  return { cx, cwi };
2693
2920
  }
@@ -3157,7 +3384,9 @@ function buildPDF(params, layoutOptions) {
3157
3384
  const fs = DEFAULT_FONT_SIZES;
3158
3385
  const { cx, cwi } = computeColumnPositions(columns, mg.l, cw);
3159
3386
  const fontEntries = params.fontEntries || (fontData ? [{ fontData, fontRef: "/F3", lang: "unknown" }] : []);
3160
- const enc = createEncodingContext(fontEntries);
3387
+ const pdfaConfig = resolvePdfAConfig();
3388
+ const tagged = pdfaConfig.enabled;
3389
+ const enc = createEncodingContext(fontEntries, tagged);
3161
3390
  const footerTpl = {
3162
3391
  left: footerText || void 0,
3163
3392
  right: "{page}/{pages}"
@@ -3178,8 +3407,6 @@ function buildPDF(params, layoutOptions) {
3178
3407
  totalPages = 1 + Math.ceil((totalRows - rowsPage1) / rowsPerPage);
3179
3408
  }
3180
3409
  if (totalPages < 1) totalPages = 1;
3181
- const pdfaConfig = resolvePdfAConfig();
3182
- const tagged = pdfaConfig.enabled;
3183
3410
  const encState = null;
3184
3411
  const wmExtraObjs = 0;
3185
3412
  const mcidAlloc = tagged ? createMCIDAllocator() : void 0;
@@ -3309,8 +3536,17 @@ function buildPDF(params, layoutOptions) {
3309
3536
  kids.push(`${pageObjStart + p * 2} 0 R`);
3310
3537
  }
3311
3538
  emitObj(2, `<< /Type /Pages /Kids [${kids.join(" ")}] /Count ${totalPages} >>`);
3312
- emitObj(3, "<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica /Encoding /WinAnsiEncoding >>");
3313
- emitObj(4, "<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding >>");
3539
+ if (tagged) {
3540
+ const pf = fontEntries[0];
3541
+ const bfName = `/${pf.fontData.fontName.replace(/[^A-Za-z0-9-]/g, "")}`;
3542
+ const primaryBase = 5;
3543
+ const refDict = `<< /Type /Font /Subtype /Type0 /BaseFont ${bfName} /Encoding /Identity-H /DescendantFonts [${primaryBase + 1} 0 R] /ToUnicode ${primaryBase + 4} 0 R >>`;
3544
+ emitObj(3, refDict);
3545
+ emitObj(4, refDict);
3546
+ } else {
3547
+ emitObj(3, "<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica /Encoding /WinAnsiEncoding >>");
3548
+ emitObj(4, "<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding >>");
3549
+ }
3314
3550
  for (let fi = 0; fi < fontEntries.length; fi++) {
3315
3551
  const fe = fontEntries[fi];
3316
3552
  const fd = fe.fontData;
@@ -3404,7 +3640,7 @@ function buildPDF(params, layoutOptions) {
3404
3640
  structTreeRootObjNum = tree.structTreeRootObjNum;
3405
3641
  totalObjs = treeStart + tree.totalObjects - 1;
3406
3642
  xmpObjNum = totalObjs + 1;
3407
- const xmpContent = buildXMPMetadata(infoTitle, isoDate, pdfaConfig.pdfaPart, pdfaConfig.pdfaConformance);
3643
+ const xmpContent = utf8EncodeBinaryString(buildXMPMetadata(infoTitle, isoDate, pdfaConfig.pdfaPart, pdfaConfig.pdfaConformance));
3408
3644
  emitStreamObj(
3409
3645
  xmpObjNum,
3410
3646
  `<< /Type /Metadata /Subtype /XML /Length ${xmpContent.length}`,