pdfnative 1.0.5 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +51 -24
- package/dist/index.cjs +776 -259
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +146 -4
- package/dist/index.d.ts +146 -4
- package/dist/index.js +770 -260
- package/dist/index.js.map +1 -1
- package/dist/worker/index.cjs +423 -187
- package/dist/worker/index.cjs.map +1 -1
- package/dist/worker/index.js +423 -187
- package/dist/worker/index.js.map +1 -1
- package/fonts/noto-emoji-data.d.ts +27 -0
- package/fonts/noto-emoji-data.js +64 -0
- package/fonts/noto-sans-data.d.ts +22 -0
- package/fonts/noto-sans-data.js +64 -0
- package/package.json +2 -1
package/dist/worker/index.cjs
CHANGED
|
@@ -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
|
|
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
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
166
|
+
const markAnchor = getMarkAnchor2(markGid);
|
|
167
167
|
let dx = 0;
|
|
168
168
|
let dy = 0;
|
|
169
169
|
if (markAnchor) {
|
|
170
|
-
const baseAnchor =
|
|
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 <=
|
|
298
|
-
return str.slice(0, max -
|
|
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/
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
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
|
|
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
|
|
520
|
-
|
|
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
|
|
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
|
|
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 =
|
|
596
|
+
const markAnchor = getMarkAnchor2(gid);
|
|
554
597
|
if (markAnchor) {
|
|
555
|
-
const baseAnchorPt =
|
|
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 =
|
|
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 =
|
|
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
|
|
788
|
-
|
|
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
|
|
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
|
|
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 =
|
|
848
|
+
const markAnchor = getMarkAnchor2(gid);
|
|
822
849
|
if (markAnchor) {
|
|
823
|
-
const baseAnchorPt =
|
|
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 =
|
|
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 =
|
|
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
|
|
1061
|
-
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
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 (
|
|
2690
|
-
cx
|
|
2691
|
-
|
|
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
|
|
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
|
-
|
|
3315
|
-
|
|
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}`,
|