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.js
CHANGED
|
@@ -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
|
|
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
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
164
|
+
const markAnchor = getMarkAnchor2(markGid);
|
|
165
165
|
let dx = 0;
|
|
166
166
|
let dy = 0;
|
|
167
167
|
if (markAnchor) {
|
|
168
|
-
const baseAnchor =
|
|
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 <=
|
|
296
|
-
return str.slice(0, max -
|
|
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/
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
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
|
|
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
|
|
518
|
-
|
|
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
|
|
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
|
|
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 =
|
|
594
|
+
const markAnchor = getMarkAnchor2(gid);
|
|
552
595
|
if (markAnchor) {
|
|
553
|
-
const baseAnchorPt =
|
|
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 =
|
|
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 =
|
|
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
|
|
786
|
-
|
|
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
|
|
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
|
|
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 =
|
|
846
|
+
const markAnchor = getMarkAnchor2(gid);
|
|
820
847
|
if (markAnchor) {
|
|
821
|
-
const baseAnchorPt =
|
|
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 =
|
|
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 =
|
|
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
|
|
1059
|
-
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
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 (
|
|
2688
|
-
cx
|
|
2689
|
-
|
|
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
|
|
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
|
-
|
|
3313
|
-
|
|
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}`,
|