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