pdfnative 1.1.0 → 1.3.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 +71 -19
- package/dist/index.cjs +6165 -3146
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +995 -22
- package/dist/index.d.ts +995 -22
- package/dist/index.js +6130 -3147
- package/dist/index.js.map +1 -1
- package/dist/worker/index.cjs +2905 -931
- package/dist/worker/index.cjs.map +1 -1
- package/dist/worker/index.js +2905 -931
- package/dist/worker/index.js.map +1 -1
- package/fonts/noto-color-emoji-data.d.ts +28 -0
- package/fonts/noto-color-emoji-data.js +26 -0
- package/fonts/noto-ethiopic-data.d.ts +13 -0
- package/fonts/noto-ethiopic-data.js +64 -0
- package/fonts/noto-khmer-data.d.ts +13 -0
- package/fonts/noto-khmer-data.js +64 -0
- package/fonts/noto-myanmar-data.d.ts +13 -0
- package/fonts/noto-myanmar-data.js +64 -0
- package/fonts/noto-sinhala-data.d.ts +13 -0
- package/fonts/noto-sinhala-data.js +64 -0
- package/fonts/noto-telugu-data.d.ts +13 -0
- package/fonts/noto-telugu-data.js +64 -0
- package/fonts/noto-tibetan-data.d.ts +13 -0
- package/fonts/noto-tibetan-data.js +64 -0
- package/package.json +22 -5
package/dist/worker/index.cjs
CHANGED
|
@@ -200,6 +200,33 @@ var BENGALI_START = 2432;
|
|
|
200
200
|
var BENGALI_END = 2559;
|
|
201
201
|
var TAMIL_START = 2944;
|
|
202
202
|
var TAMIL_END = 3071;
|
|
203
|
+
var TELUGU_START = 3072;
|
|
204
|
+
var TELUGU_END = 3199;
|
|
205
|
+
var ETHIOPIC_START = 4608;
|
|
206
|
+
var ETHIOPIC_END = 4991;
|
|
207
|
+
var ETHIOPIC_SUPPLEMENT_START = 4992;
|
|
208
|
+
var ETHIOPIC_SUPPLEMENT_END = 5023;
|
|
209
|
+
var ETHIOPIC_EXTENDED_START = 11648;
|
|
210
|
+
var ETHIOPIC_EXTENDED_END = 11743;
|
|
211
|
+
var ETHIOPIC_EXTENDED_A_START = 43776;
|
|
212
|
+
var ETHIOPIC_EXTENDED_A_END = 43823;
|
|
213
|
+
var SINHALA_START = 3456;
|
|
214
|
+
var SINHALA_END = 3583;
|
|
215
|
+
var SINHALA_VIRAMA = 3530;
|
|
216
|
+
var TIBETAN_START = 3840;
|
|
217
|
+
var TIBETAN_END = 4095;
|
|
218
|
+
var KHMER_START = 6016;
|
|
219
|
+
var KHMER_END = 6143;
|
|
220
|
+
var KHMER_SYMBOLS_START = 6624;
|
|
221
|
+
var KHMER_SYMBOLS_END = 6655;
|
|
222
|
+
var KHMER_COENG = 6098;
|
|
223
|
+
var MYANMAR_START = 4096;
|
|
224
|
+
var MYANMAR_END = 4255;
|
|
225
|
+
var MYANMAR_EXTENDED_A_START = 43616;
|
|
226
|
+
var MYANMAR_EXTENDED_A_END = 43647;
|
|
227
|
+
var MYANMAR_EXTENDED_B_START = 43488;
|
|
228
|
+
var MYANMAR_EXTENDED_B_END = 43519;
|
|
229
|
+
var MYANMAR_VIRAMA = 4153;
|
|
203
230
|
var EMOJI_RANGES = [
|
|
204
231
|
[127744, 128511],
|
|
205
232
|
// Miscellaneous Symbols and Pictographs
|
|
@@ -230,6 +257,9 @@ var EMOJI_RANGES = [
|
|
|
230
257
|
];
|
|
231
258
|
var FITZPATRICK_START = 127995;
|
|
232
259
|
var FITZPATRICK_END = 127999;
|
|
260
|
+
var ZWJ = 8205;
|
|
261
|
+
var VS15 = 65038;
|
|
262
|
+
var VS16 = 65039;
|
|
233
263
|
function isArabicCodepoint(cp) {
|
|
234
264
|
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
265
|
}
|
|
@@ -242,6 +272,24 @@ function isBengaliCodepoint(cp) {
|
|
|
242
272
|
function isTamilCodepoint(cp) {
|
|
243
273
|
return cp >= TAMIL_START && cp <= TAMIL_END;
|
|
244
274
|
}
|
|
275
|
+
function isTeluguCodepoint(cp) {
|
|
276
|
+
return cp >= TELUGU_START && cp <= TELUGU_END;
|
|
277
|
+
}
|
|
278
|
+
function isEthiopicCodepoint(cp) {
|
|
279
|
+
return cp >= ETHIOPIC_START && cp <= ETHIOPIC_END || cp >= ETHIOPIC_SUPPLEMENT_START && cp <= ETHIOPIC_SUPPLEMENT_END || cp >= ETHIOPIC_EXTENDED_START && cp <= ETHIOPIC_EXTENDED_END || cp >= ETHIOPIC_EXTENDED_A_START && cp <= ETHIOPIC_EXTENDED_A_END;
|
|
280
|
+
}
|
|
281
|
+
function isSinhalaCodepoint(cp) {
|
|
282
|
+
return cp >= SINHALA_START && cp <= SINHALA_END;
|
|
283
|
+
}
|
|
284
|
+
function isTibetanCodepoint(cp) {
|
|
285
|
+
return cp >= TIBETAN_START && cp <= TIBETAN_END;
|
|
286
|
+
}
|
|
287
|
+
function isKhmerCodepoint(cp) {
|
|
288
|
+
return cp >= KHMER_START && cp <= KHMER_END || cp >= KHMER_SYMBOLS_START && cp <= KHMER_SYMBOLS_END;
|
|
289
|
+
}
|
|
290
|
+
function isMyanmarCodepoint(cp) {
|
|
291
|
+
return cp >= MYANMAR_START && cp <= MYANMAR_END || cp >= MYANMAR_EXTENDED_A_START && cp <= MYANMAR_EXTENDED_A_END || cp >= MYANMAR_EXTENDED_B_START && cp <= MYANMAR_EXTENDED_B_END;
|
|
292
|
+
}
|
|
245
293
|
function isDevanagariCodepoint(cp) {
|
|
246
294
|
return cp >= DEVANAGARI_START && cp <= DEVANAGARI_END || cp >= DEVANAGARI_EXT_START && cp <= DEVANAGARI_EXT_END;
|
|
247
295
|
}
|
|
@@ -252,6 +300,9 @@ function isEmojiCodepoint(cp) {
|
|
|
252
300
|
}
|
|
253
301
|
return false;
|
|
254
302
|
}
|
|
303
|
+
function isZeroWidthFormat(cp) {
|
|
304
|
+
return cp === ZWJ || cp === 8204 || cp === VS15 || cp === VS16 || cp >= FITZPATRICK_START && cp <= FITZPATRICK_END;
|
|
305
|
+
}
|
|
255
306
|
function containsArabic(text) {
|
|
256
307
|
for (let i = 0; i < text.length; ) {
|
|
257
308
|
const cp = text.codePointAt(i) ?? 0;
|
|
@@ -278,6 +329,36 @@ function containsTamil(str) {
|
|
|
278
329
|
}
|
|
279
330
|
return false;
|
|
280
331
|
}
|
|
332
|
+
function containsTelugu(str) {
|
|
333
|
+
for (let i = 0; i < str.length; i++) {
|
|
334
|
+
if (isTeluguCodepoint(str.charCodeAt(i))) return true;
|
|
335
|
+
}
|
|
336
|
+
return false;
|
|
337
|
+
}
|
|
338
|
+
function containsSinhala(str) {
|
|
339
|
+
for (let i = 0; i < str.length; i++) {
|
|
340
|
+
if (isSinhalaCodepoint(str.charCodeAt(i))) return true;
|
|
341
|
+
}
|
|
342
|
+
return false;
|
|
343
|
+
}
|
|
344
|
+
function containsTibetan(str) {
|
|
345
|
+
for (let i = 0; i < str.length; i++) {
|
|
346
|
+
if (isTibetanCodepoint(str.charCodeAt(i))) return true;
|
|
347
|
+
}
|
|
348
|
+
return false;
|
|
349
|
+
}
|
|
350
|
+
function containsKhmer(str) {
|
|
351
|
+
for (let i = 0; i < str.length; i++) {
|
|
352
|
+
if (isKhmerCodepoint(str.charCodeAt(i))) return true;
|
|
353
|
+
}
|
|
354
|
+
return false;
|
|
355
|
+
}
|
|
356
|
+
function containsMyanmar(str) {
|
|
357
|
+
for (let i = 0; i < str.length; i++) {
|
|
358
|
+
if (isMyanmarCodepoint(str.charCodeAt(i))) return true;
|
|
359
|
+
}
|
|
360
|
+
return false;
|
|
361
|
+
}
|
|
281
362
|
function containsDevanagari(str) {
|
|
282
363
|
for (let i = 0; i < str.length; i++) {
|
|
283
364
|
if (isDevanagariCodepoint(str.charCodeAt(i))) return true;
|
|
@@ -289,6 +370,12 @@ function containsDevanagari(str) {
|
|
|
289
370
|
function detectCharLang(cp) {
|
|
290
371
|
if (cp >= 880 && cp <= 1023 || cp >= 7936 && cp <= 8191) return "el";
|
|
291
372
|
if (cp >= 2304 && cp <= 2431 || cp >= 43232 && cp <= 43263) return "hi";
|
|
373
|
+
if (cp >= 3072 && cp <= 3199) return "te";
|
|
374
|
+
if (isSinhalaCodepoint(cp)) return "si";
|
|
375
|
+
if (isTibetanCodepoint(cp)) return "bo";
|
|
376
|
+
if (isKhmerCodepoint(cp)) return "km";
|
|
377
|
+
if (isMyanmarCodepoint(cp)) return "my";
|
|
378
|
+
if (isEthiopicCodepoint(cp)) return "am";
|
|
292
379
|
if (cp >= 3584 && cp <= 3711) return "th";
|
|
293
380
|
if (cp >= 12352 && cp <= 12543) return "ja";
|
|
294
381
|
if (cp >= 44032 && cp <= 55215 || cp >= 4352 && cp <= 4607 || cp >= 12592 && cp <= 12687) return "ko";
|
|
@@ -322,6 +409,10 @@ function splitTextByFont(str, fontEntries) {
|
|
|
322
409
|
i += charLen;
|
|
323
410
|
continue;
|
|
324
411
|
}
|
|
412
|
+
if (isZeroWidthFormat(normCp) && !fontEntries.some((fe) => fe.fontData.cmap[normCp])) {
|
|
413
|
+
i += charLen;
|
|
414
|
+
continue;
|
|
415
|
+
}
|
|
325
416
|
let newEntry = null;
|
|
326
417
|
const charLang = detectCharLang(normCp);
|
|
327
418
|
if (charLang) {
|
|
@@ -354,103 +445,826 @@ function splitTextByFont(str, fontEntries) {
|
|
|
354
445
|
return runs;
|
|
355
446
|
}
|
|
356
447
|
|
|
357
|
-
// src/
|
|
358
|
-
function
|
|
359
|
-
if (
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
return
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
return "
|
|
448
|
+
// src/shaping/bidi.ts
|
|
449
|
+
function classifyBidiType(cp) {
|
|
450
|
+
if (cp >= 768 && cp <= 879 || // Combining Diacritical Marks
|
|
451
|
+
cp >= 1425 && cp <= 1469 || // Hebrew marks
|
|
452
|
+
cp >= 1471 && cp <= 1471 || cp >= 1473 && cp <= 1474 || cp >= 1476 && cp <= 1477 || cp >= 1479 && cp <= 1479 || cp >= 1552 && cp <= 1562 || // Arabic marks
|
|
453
|
+
cp >= 1611 && cp <= 1631 || // Arabic harakat
|
|
454
|
+
cp >= 1648 && cp <= 1648 || // Arabic superscript alef
|
|
455
|
+
cp >= 1750 && cp <= 1756 || cp >= 1759 && cp <= 1764 || cp >= 1767 && cp <= 1768 || cp >= 1770 && cp <= 1773 || cp >= 65056 && cp <= 65071) return "NSM";
|
|
456
|
+
if (cp === 8203 || cp === 8204 || cp === 8205 || cp === 8206 || cp === 8207 || cp === 65279 || cp === 8294 || cp === 8295 || cp === 8296 || cp === 8297) return "BN";
|
|
457
|
+
if (cp >= 1632 && cp <= 1641) return "AN";
|
|
458
|
+
if (cp >= 1776 && cp <= 1785) return "AN";
|
|
459
|
+
if (cp >= 48 && cp <= 57) return "EN";
|
|
460
|
+
if (cp === 43 || cp === 45) return "ES";
|
|
461
|
+
if (cp === 35 || cp === 36 || cp === 37 || cp === 162 || cp === 163 || cp === 164 || cp === 165 || cp === 8364 || cp === 8377 || cp === 8378) return "ET";
|
|
462
|
+
if (cp === 44 || cp === 46 || cp === 47 || cp === 58 || cp === 160) return "CS";
|
|
463
|
+
if (cp === 32 || cp === 9 || cp === 10 || cp === 13 || cp === 12 || cp === 8192 || cp === 8202 || cp === 8232 || cp === 8233 || cp === 8239 || cp === 8287 || cp === 12288) return "WS";
|
|
464
|
+
if (cp >= 1536 && cp <= 1791) return "AL";
|
|
465
|
+
if (cp >= 1872 && cp <= 1919) return "AL";
|
|
466
|
+
if (cp >= 2208 && cp <= 2303) return "AL";
|
|
467
|
+
if (cp >= 64336 && cp <= 65023) return "AL";
|
|
468
|
+
if (cp >= 65136 && cp <= 65278) return "AL";
|
|
469
|
+
if (cp >= 1488 && cp <= 1514) return "R";
|
|
470
|
+
if (cp >= 1520 && cp <= 1524) return "R";
|
|
471
|
+
if (cp >= 1792 && cp <= 1871) return "R";
|
|
472
|
+
if (cp >= 1920 && cp <= 1983) return "R";
|
|
473
|
+
if (cp >= 64285 && cp <= 64335) return "R";
|
|
474
|
+
if (cp >= 65 && cp <= 90) return "L";
|
|
475
|
+
if (cp >= 97 && cp <= 122) return "L";
|
|
476
|
+
if (cp >= 192 && cp <= 591) return "L";
|
|
477
|
+
if (cp >= 880 && cp <= 1023) return "L";
|
|
478
|
+
if (cp >= 1024 && cp <= 1279) return "L";
|
|
479
|
+
if (cp >= 3584 && cp <= 3711) return "L";
|
|
480
|
+
if (cp >= 2304 && cp <= 2431) return "L";
|
|
481
|
+
if (cp >= 12352 && cp <= 12543) return "L";
|
|
482
|
+
if (cp >= 19968 && cp <= 40959) return "L";
|
|
483
|
+
if (cp >= 44032 && cp <= 55215) return "L";
|
|
484
|
+
if (cp >= 33 && cp <= 47) return "ON";
|
|
485
|
+
if (cp >= 58 && cp <= 64) return "ON";
|
|
486
|
+
if (cp >= 91 && cp <= 96) return "ON";
|
|
487
|
+
if (cp >= 123 && cp <= 126) return "ON";
|
|
488
|
+
if (cp >= 161 && cp <= 191) return "ON";
|
|
489
|
+
if (cp >= 8208 && cp <= 8231) return "ON";
|
|
490
|
+
if (cp >= 8240 && cp <= 8286) return "ON";
|
|
491
|
+
return "L";
|
|
401
492
|
}
|
|
402
|
-
function
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
493
|
+
function detectParagraphLevel(types) {
|
|
494
|
+
for (const t of types) {
|
|
495
|
+
if (t === "L") return 0;
|
|
496
|
+
if (t === "R" || t === "AL") return 1;
|
|
497
|
+
}
|
|
498
|
+
return 0;
|
|
406
499
|
}
|
|
407
|
-
function
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
if (
|
|
412
|
-
if (
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
else if (
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
500
|
+
function resolveWeakTypes(types, paraLevel) {
|
|
501
|
+
const len = types.length;
|
|
502
|
+
let prevType = paraLevel === 0 ? "L" : "R";
|
|
503
|
+
for (let i = 0; i < len; i++) {
|
|
504
|
+
if (types[i] === "BN") continue;
|
|
505
|
+
if (types[i] === "NSM") {
|
|
506
|
+
types[i] = prevType;
|
|
507
|
+
}
|
|
508
|
+
prevType = types[i];
|
|
509
|
+
}
|
|
510
|
+
let lastStrong = paraLevel === 0 ? "L" : "R";
|
|
511
|
+
for (let i = 0; i < len; i++) {
|
|
512
|
+
if (types[i] === "BN") continue;
|
|
513
|
+
if (types[i] === "R" || types[i] === "L" || types[i] === "AL") {
|
|
514
|
+
lastStrong = types[i];
|
|
515
|
+
} else if (types[i] === "EN" && lastStrong === "AL") {
|
|
516
|
+
types[i] = "AN";
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
for (let i = 0; i < len; i++) {
|
|
520
|
+
if (types[i] === "AL") types[i] = "R";
|
|
521
|
+
}
|
|
522
|
+
for (let i = 1; i < len - 1; i++) {
|
|
523
|
+
if (types[i] === "BN") continue;
|
|
524
|
+
if (types[i] === "ES" && types[i - 1] === "EN" && types[i + 1] === "EN") {
|
|
525
|
+
types[i] = "EN";
|
|
526
|
+
} else if (types[i] === "CS") {
|
|
527
|
+
if (types[i - 1] === "EN" && types[i + 1] === "EN") types[i] = "EN";
|
|
528
|
+
else if (types[i - 1] === "AN" && types[i + 1] === "AN") types[i] = "AN";
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
for (let i = 0; i < len; i++) {
|
|
532
|
+
if (types[i] === "ET") {
|
|
533
|
+
let found = false;
|
|
534
|
+
for (let j = i - 1; j >= 0; j--) {
|
|
535
|
+
if (types[j] === "EN") {
|
|
536
|
+
found = true;
|
|
537
|
+
break;
|
|
538
|
+
}
|
|
539
|
+
if (types[j] !== "ET" && types[j] !== "BN") break;
|
|
540
|
+
}
|
|
541
|
+
if (!found) {
|
|
542
|
+
for (let j = i + 1; j < len; j++) {
|
|
543
|
+
if (types[j] === "EN") {
|
|
544
|
+
found = true;
|
|
545
|
+
break;
|
|
546
|
+
}
|
|
547
|
+
if (types[j] !== "ET" && types[j] !== "BN") break;
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
if (found) types[i] = "EN";
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
for (let i = 0; i < len; i++) {
|
|
554
|
+
if (types[i] === "ES" || types[i] === "ET" || types[i] === "CS") {
|
|
555
|
+
types[i] = "ON";
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
lastStrong = paraLevel === 0 ? "L" : "R";
|
|
559
|
+
for (let i = 0; i < len; i++) {
|
|
560
|
+
if (types[i] === "BN") continue;
|
|
561
|
+
if (types[i] === "L" || types[i] === "R") {
|
|
562
|
+
lastStrong = types[i];
|
|
563
|
+
} else if (types[i] === "EN" && lastStrong === "L") {
|
|
564
|
+
types[i] = "L";
|
|
565
|
+
}
|
|
427
566
|
}
|
|
428
|
-
return w * sz / 1e3;
|
|
429
567
|
}
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
if (gids[1 + ci] !== entry[1 + ci]) {
|
|
443
|
-
match = false;
|
|
568
|
+
function resolveNeutralTypes(types, paraLevel) {
|
|
569
|
+
const len = types.length;
|
|
570
|
+
const paraDir = paraLevel === 0 ? "L" : "R";
|
|
571
|
+
for (let i = 0; i < len; i++) {
|
|
572
|
+
if (types[i] !== "ON" && types[i] !== "WS" && types[i] !== "BN") continue;
|
|
573
|
+
const start = i;
|
|
574
|
+
while (i < len && (types[i] === "ON" || types[i] === "WS" || types[i] === "BN")) i++;
|
|
575
|
+
const end = i;
|
|
576
|
+
let prevStrong = paraDir;
|
|
577
|
+
for (let j = start - 1; j >= 0; j--) {
|
|
578
|
+
if (types[j] === "L" || types[j] === "R" || types[j] === "EN" || types[j] === "AN") {
|
|
579
|
+
prevStrong = types[j] === "EN" || types[j] === "AN" ? "R" : types[j];
|
|
444
580
|
break;
|
|
445
581
|
}
|
|
446
582
|
}
|
|
447
|
-
|
|
583
|
+
let nextStrong = paraDir;
|
|
584
|
+
for (let j = end; j < len; j++) {
|
|
585
|
+
if (types[j] === "L" || types[j] === "R" || types[j] === "EN" || types[j] === "AN") {
|
|
586
|
+
nextStrong = types[j] === "EN" || types[j] === "AN" ? "R" : types[j];
|
|
587
|
+
break;
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
const resolved = prevStrong === nextStrong ? prevStrong : paraDir;
|
|
591
|
+
for (let j = start; j < end; j++) {
|
|
592
|
+
types[j] = resolved;
|
|
593
|
+
}
|
|
448
594
|
}
|
|
449
|
-
return null;
|
|
450
595
|
}
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
596
|
+
function assignLevels(types, paraLevel) {
|
|
597
|
+
const levels = [];
|
|
598
|
+
for (const t of types) {
|
|
599
|
+
if (paraLevel === 0) {
|
|
600
|
+
levels.push(t === "R" || t === "AN" ? 1 : 0);
|
|
601
|
+
} else {
|
|
602
|
+
levels.push(t === "L" ? 2 : 1);
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
return levels;
|
|
606
|
+
}
|
|
607
|
+
var SENTENCE_PUNCT = /* @__PURE__ */ new Set([
|
|
608
|
+
46,
|
|
609
|
+
// .
|
|
610
|
+
44,
|
|
611
|
+
// ,
|
|
612
|
+
59,
|
|
613
|
+
// ;
|
|
614
|
+
58,
|
|
615
|
+
// :
|
|
616
|
+
33,
|
|
617
|
+
// !
|
|
618
|
+
63
|
|
619
|
+
// ?
|
|
620
|
+
]);
|
|
621
|
+
function fixPunctuationAffinity(types, codePoints, len) {
|
|
622
|
+
for (let i = 1; i < len; i++) {
|
|
623
|
+
if (types[i] === "R" && SENTENCE_PUNCT.has(codePoints[i])) {
|
|
624
|
+
let prevIdx = i - 1;
|
|
625
|
+
while (prevIdx >= 0 && (types[prevIdx] === "WS" || types[prevIdx] === "BN")) prevIdx--;
|
|
626
|
+
if (prevIdx >= 0 && types[prevIdx] === "L") {
|
|
627
|
+
types[i] = "L";
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
function fixBracketPairing(types, codePoints, len) {
|
|
633
|
+
const OPEN_BRACKETS = {
|
|
634
|
+
40: 41,
|
|
635
|
+
// ( → )
|
|
636
|
+
91: 93,
|
|
637
|
+
// [ → ]
|
|
638
|
+
123: 125
|
|
639
|
+
// { → }
|
|
640
|
+
};
|
|
641
|
+
for (let i = 0; i < len; i++) {
|
|
642
|
+
const closer = OPEN_BRACKETS[codePoints[i]];
|
|
643
|
+
if (closer === void 0) continue;
|
|
644
|
+
let depth = 1;
|
|
645
|
+
let closeIdx = -1;
|
|
646
|
+
for (let j = i + 1; j < len; j++) {
|
|
647
|
+
if (codePoints[j] === codePoints[i]) depth++;
|
|
648
|
+
else if (codePoints[j] === closer) {
|
|
649
|
+
depth--;
|
|
650
|
+
if (depth === 0) {
|
|
651
|
+
closeIdx = j;
|
|
652
|
+
break;
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
if (closeIdx === -1) continue;
|
|
657
|
+
let hasL = false;
|
|
658
|
+
for (let j = i + 1; j < closeIdx; j++) {
|
|
659
|
+
if (types[j] === "L") {
|
|
660
|
+
hasL = true;
|
|
661
|
+
break;
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
if (hasL) {
|
|
665
|
+
types[i] = "L";
|
|
666
|
+
types[closeIdx] = "L";
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
function findOutermostIsolatePairs(codePoints) {
|
|
671
|
+
const pairs = [];
|
|
672
|
+
let i = 0;
|
|
673
|
+
while (i < codePoints.length) {
|
|
674
|
+
const cp = codePoints[i];
|
|
675
|
+
if (cp === 8294 || cp === 8295 || cp === 8296) {
|
|
676
|
+
let depth = 1;
|
|
677
|
+
let close = -1;
|
|
678
|
+
for (let j = i + 1; j < codePoints.length; j++) {
|
|
679
|
+
const cj = codePoints[j];
|
|
680
|
+
if (cj === 8294 || cj === 8295 || cj === 8296) depth++;
|
|
681
|
+
else if (cj === 8297) {
|
|
682
|
+
depth--;
|
|
683
|
+
if (depth === 0) {
|
|
684
|
+
close = j;
|
|
685
|
+
break;
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
if (close === -1) {
|
|
690
|
+
i++;
|
|
691
|
+
continue;
|
|
692
|
+
}
|
|
693
|
+
const kind = cp === 8294 ? "LRI" : cp === 8295 ? "RLI" : "FSI";
|
|
694
|
+
pairs.push({ open: i, close, kind });
|
|
695
|
+
i = close + 1;
|
|
696
|
+
} else {
|
|
697
|
+
i++;
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
return pairs;
|
|
701
|
+
}
|
|
702
|
+
function normalizeBidiEmbeddings(text) {
|
|
703
|
+
const LRE = 8234, RLE = 8235, PDF_CP = 8236, LRO = 8237, RLO = 8238;
|
|
704
|
+
const LRI = 8294, RLI = 8295, PDI = 8297;
|
|
705
|
+
let hasEmbed = false;
|
|
706
|
+
for (let i = 0; i < text.length; i++) {
|
|
707
|
+
const c = text.charCodeAt(i);
|
|
708
|
+
if (c === LRE || c === RLE || c === PDF_CP || c === LRO || c === RLO) {
|
|
709
|
+
hasEmbed = true;
|
|
710
|
+
break;
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
if (!hasEmbed) return text;
|
|
714
|
+
const stack = [];
|
|
715
|
+
const out = [];
|
|
716
|
+
const MAX_DEPTH = 125;
|
|
717
|
+
for (let i = 0; i < text.length; ) {
|
|
718
|
+
const cp = text.codePointAt(i) ?? 0;
|
|
719
|
+
const cpLen = cp > 65535 ? 2 : 1;
|
|
720
|
+
if (cp === LRE || cp === RLE) {
|
|
721
|
+
if (stack.length >= MAX_DEPTH) {
|
|
722
|
+
i += cpLen;
|
|
723
|
+
continue;
|
|
724
|
+
}
|
|
725
|
+
stack.push("E");
|
|
726
|
+
out.push(cp === LRE ? LRI : RLI);
|
|
727
|
+
i += cpLen;
|
|
728
|
+
} else if (cp === LRO || cp === RLO) {
|
|
729
|
+
if (stack.length >= MAX_DEPTH) {
|
|
730
|
+
i += cpLen;
|
|
731
|
+
continue;
|
|
732
|
+
}
|
|
733
|
+
stack.push("O");
|
|
734
|
+
out.push(cp);
|
|
735
|
+
i += cpLen;
|
|
736
|
+
} else if (cp === PDF_CP) {
|
|
737
|
+
const frame = stack.pop();
|
|
738
|
+
if (frame === "E") out.push(PDI);
|
|
739
|
+
else if (frame === "O") out.push(PDF_CP);
|
|
740
|
+
i += cpLen;
|
|
741
|
+
} else {
|
|
742
|
+
out.push(cp);
|
|
743
|
+
i += cpLen;
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
let result = "";
|
|
747
|
+
for (let i = 0; i < out.length; i++) result += String.fromCodePoint(out[i]);
|
|
748
|
+
return result;
|
|
749
|
+
}
|
|
750
|
+
function matchingPdfIndex(codePoints, openCp) {
|
|
751
|
+
let depth = 1;
|
|
752
|
+
for (let j = openCp + 1; j < codePoints.length; j++) {
|
|
753
|
+
const cj = codePoints[j];
|
|
754
|
+
if (cj === 8234 || cj === 8235 || cj === 8237 || cj === 8238) depth++;
|
|
755
|
+
else if (cj === 8236) {
|
|
756
|
+
depth--;
|
|
757
|
+
if (depth === 0) return j;
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
return -1;
|
|
761
|
+
}
|
|
762
|
+
function matchingPdiIndex(codePoints, openCp) {
|
|
763
|
+
let depth = 1;
|
|
764
|
+
for (let j = openCp + 1; j < codePoints.length; j++) {
|
|
765
|
+
const cj = codePoints[j];
|
|
766
|
+
if (cj === 8294 || cj === 8295 || cj === 8296) depth++;
|
|
767
|
+
else if (cj === 8297) {
|
|
768
|
+
depth--;
|
|
769
|
+
if (depth === 0) return j;
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
return -1;
|
|
773
|
+
}
|
|
774
|
+
function tryResolveOverrides(text, forcedLevel) {
|
|
775
|
+
const LRE = 8234, RLE = 8235, LRO = 8237, RLO = 8238;
|
|
776
|
+
if (text.indexOf("\u202D") === -1 && text.indexOf("\u202E") === -1) return null;
|
|
777
|
+
const codePoints = [];
|
|
778
|
+
const cpToStr = [];
|
|
779
|
+
for (let i2 = 0; i2 < text.length; ) {
|
|
780
|
+
cpToStr.push(i2);
|
|
781
|
+
const cp = text.codePointAt(i2) ?? 0;
|
|
782
|
+
codePoints.push(cp);
|
|
783
|
+
i2 += cp > 65535 ? 2 : 1;
|
|
784
|
+
}
|
|
785
|
+
cpToStr.push(text.length);
|
|
786
|
+
const len = codePoints.length;
|
|
787
|
+
const spans = [];
|
|
788
|
+
let i = 0;
|
|
789
|
+
while (i < len) {
|
|
790
|
+
const cp = codePoints[i];
|
|
791
|
+
if (cp === LRO || cp === RLO) {
|
|
792
|
+
const close = matchingPdfIndex(codePoints, i);
|
|
793
|
+
if (close === -1) {
|
|
794
|
+
i++;
|
|
795
|
+
continue;
|
|
796
|
+
}
|
|
797
|
+
spans.push({ open: i, close, rtl: cp === RLO });
|
|
798
|
+
i = close + 1;
|
|
799
|
+
} else if (cp === LRE || cp === RLE) {
|
|
800
|
+
const close = matchingPdfIndex(codePoints, i);
|
|
801
|
+
i = close === -1 ? i + 1 : close + 1;
|
|
802
|
+
} else if (cp === 8294 || cp === 8295 || cp === 8296) {
|
|
803
|
+
const close = matchingPdiIndex(codePoints, i);
|
|
804
|
+
i = close === -1 ? i + 1 : close + 1;
|
|
805
|
+
} else {
|
|
806
|
+
i++;
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
if (spans.length === 0) return null;
|
|
810
|
+
const out = [];
|
|
811
|
+
const emitGap = (cpStart, cpEnd) => {
|
|
812
|
+
if (cpStart >= cpEnd) return;
|
|
813
|
+
const segText = text.substring(cpToStr[cpStart], cpToStr[cpEnd]);
|
|
814
|
+
const baseStrIdx = cpToStr[cpStart];
|
|
815
|
+
const segRuns = forcedLevel === void 0 ? resolveBidiRuns(segText) : resolveBidiRunsForced(segText, forcedLevel);
|
|
816
|
+
for (const r of segRuns) {
|
|
817
|
+
out.push({ text: r.text, level: r.level, start: r.start + baseStrIdx });
|
|
818
|
+
}
|
|
819
|
+
};
|
|
820
|
+
let cursor = 0;
|
|
821
|
+
for (const span of spans) {
|
|
822
|
+
emitGap(cursor, span.open);
|
|
823
|
+
const innerRaw = text.substring(cpToStr[span.open + 1], cpToStr[span.close]);
|
|
824
|
+
const inner = stripBidiControls(innerRaw);
|
|
825
|
+
if (inner) {
|
|
826
|
+
const level = span.rtl ? 1 : 0;
|
|
827
|
+
const runText = span.rtl ? reverseString(inner) : inner;
|
|
828
|
+
out.push({ text: runText, level, start: cpToStr[span.open + 1] });
|
|
829
|
+
}
|
|
830
|
+
cursor = span.close + 1;
|
|
831
|
+
}
|
|
832
|
+
emitGap(cursor, len);
|
|
833
|
+
return out;
|
|
834
|
+
}
|
|
835
|
+
function resolveBidiRuns(text) {
|
|
836
|
+
if (!text) return [];
|
|
837
|
+
const overrideRuns = tryResolveOverrides(text);
|
|
838
|
+
if (overrideRuns) return overrideRuns;
|
|
839
|
+
const normalized = normalizeBidiEmbeddings(text);
|
|
840
|
+
if (normalized !== text) {
|
|
841
|
+
return resolveBidiRuns(normalized);
|
|
842
|
+
}
|
|
843
|
+
const codePoints = [];
|
|
844
|
+
const cpToStr = [];
|
|
845
|
+
for (let i = 0; i < text.length; ) {
|
|
846
|
+
cpToStr.push(i);
|
|
847
|
+
const cp = text.codePointAt(i) ?? 0;
|
|
848
|
+
codePoints.push(cp);
|
|
849
|
+
i += cp > 65535 ? 2 : 1;
|
|
850
|
+
}
|
|
851
|
+
cpToStr.push(text.length);
|
|
852
|
+
const isolates = findOutermostIsolatePairs(codePoints);
|
|
853
|
+
if (isolates.length === 0) {
|
|
854
|
+
return resolveBidiCore(text, codePoints, cpToStr);
|
|
855
|
+
}
|
|
856
|
+
const insideIsolate = new Array(codePoints.length).fill(false);
|
|
857
|
+
for (const p of isolates) {
|
|
858
|
+
for (let k = p.open; k <= p.close; k++) insideIsolate[k] = true;
|
|
859
|
+
}
|
|
860
|
+
const outerTypes = codePoints.map((cp, idx) => insideIsolate[idx] ? "BN" : classifyBidiType(cp));
|
|
861
|
+
const parentLevel = detectParagraphLevel(outerTypes);
|
|
862
|
+
const out = [];
|
|
863
|
+
const emitSegment = (cpStart, cpEnd, forced) => {
|
|
864
|
+
if (cpStart >= cpEnd) return;
|
|
865
|
+
const segText = text.substring(cpToStr[cpStart], cpToStr[cpEnd]);
|
|
866
|
+
const segCps = codePoints.slice(cpStart, cpEnd);
|
|
867
|
+
const baseStrIdx = cpToStr[cpStart];
|
|
868
|
+
const segCpToStr = cpToStr.slice(cpStart, cpEnd + 1).map((x) => x - baseStrIdx);
|
|
869
|
+
const segRuns = forced === void 0 ? resolveBidiRuns(segText) : resolveBidiCore(segText, segCps, segCpToStr, forced);
|
|
870
|
+
for (const r of segRuns) {
|
|
871
|
+
out.push({ text: r.text, level: r.level, start: r.start + baseStrIdx });
|
|
872
|
+
}
|
|
873
|
+
};
|
|
874
|
+
let cursor = 0;
|
|
875
|
+
for (const pair of isolates) {
|
|
876
|
+
emitSegment(cursor, pair.open, parentLevel);
|
|
877
|
+
const innerStart = pair.open + 1;
|
|
878
|
+
const innerEnd = pair.close;
|
|
879
|
+
let innerLevel;
|
|
880
|
+
if (pair.kind === "LRI") innerLevel = 0;
|
|
881
|
+
else if (pair.kind === "RLI") innerLevel = 1;
|
|
882
|
+
else {
|
|
883
|
+
const innerTypes = codePoints.slice(innerStart, innerEnd).map(classifyBidiType);
|
|
884
|
+
innerLevel = detectParagraphLevel(innerTypes);
|
|
885
|
+
}
|
|
886
|
+
if (innerStart < innerEnd) {
|
|
887
|
+
const innerText = text.substring(cpToStr[innerStart], cpToStr[innerEnd]);
|
|
888
|
+
const innerRuns = resolveBidiRunsForced(innerText, innerLevel);
|
|
889
|
+
const baseStrIdx = cpToStr[innerStart];
|
|
890
|
+
for (const r of innerRuns) {
|
|
891
|
+
out.push({ text: r.text, level: r.level, start: r.start + baseStrIdx });
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
cursor = pair.close + 1;
|
|
895
|
+
}
|
|
896
|
+
emitSegment(cursor, codePoints.length, parentLevel);
|
|
897
|
+
return out;
|
|
898
|
+
}
|
|
899
|
+
function resolveBidiRunsForced(text, forcedLevel) {
|
|
900
|
+
if (!text) return [];
|
|
901
|
+
const overrideRuns = tryResolveOverrides(text, forcedLevel);
|
|
902
|
+
if (overrideRuns) return overrideRuns;
|
|
903
|
+
const codePoints = [];
|
|
904
|
+
const cpToStr = [];
|
|
905
|
+
for (let i = 0; i < text.length; ) {
|
|
906
|
+
cpToStr.push(i);
|
|
907
|
+
const cp = text.codePointAt(i) ?? 0;
|
|
908
|
+
codePoints.push(cp);
|
|
909
|
+
i += cp > 65535 ? 2 : 1;
|
|
910
|
+
}
|
|
911
|
+
cpToStr.push(text.length);
|
|
912
|
+
const isolates = findOutermostIsolatePairs(codePoints);
|
|
913
|
+
if (isolates.length === 0) {
|
|
914
|
+
return resolveBidiCore(text, codePoints, cpToStr, forcedLevel);
|
|
915
|
+
}
|
|
916
|
+
const out = [];
|
|
917
|
+
const emit = (cpStart, cpEnd, forced) => {
|
|
918
|
+
if (cpStart >= cpEnd) return;
|
|
919
|
+
const segText = text.substring(cpToStr[cpStart], cpToStr[cpEnd]);
|
|
920
|
+
const segCps = codePoints.slice(cpStart, cpEnd);
|
|
921
|
+
const baseStrIdx = cpToStr[cpStart];
|
|
922
|
+
const segCpToStr = cpToStr.slice(cpStart, cpEnd + 1).map((x) => x - baseStrIdx);
|
|
923
|
+
const segRuns = resolveBidiCore(segText, segCps, segCpToStr, forced);
|
|
924
|
+
for (const r of segRuns) {
|
|
925
|
+
out.push({ text: r.text, level: r.level, start: r.start + baseStrIdx });
|
|
926
|
+
}
|
|
927
|
+
};
|
|
928
|
+
let cursor = 0;
|
|
929
|
+
for (const pair of isolates) {
|
|
930
|
+
emit(cursor, pair.open, forcedLevel);
|
|
931
|
+
const innerStart = pair.open + 1;
|
|
932
|
+
const innerEnd = pair.close;
|
|
933
|
+
let innerLevel;
|
|
934
|
+
if (pair.kind === "LRI") innerLevel = 0;
|
|
935
|
+
else if (pair.kind === "RLI") innerLevel = 1;
|
|
936
|
+
else {
|
|
937
|
+
const innerTypes = codePoints.slice(innerStart, innerEnd).map(classifyBidiType);
|
|
938
|
+
innerLevel = detectParagraphLevel(innerTypes);
|
|
939
|
+
}
|
|
940
|
+
if (innerStart < innerEnd) {
|
|
941
|
+
const innerText = text.substring(cpToStr[innerStart], cpToStr[innerEnd]);
|
|
942
|
+
const innerRuns = resolveBidiRunsForced(innerText, innerLevel);
|
|
943
|
+
const baseStrIdx = cpToStr[innerStart];
|
|
944
|
+
for (const r of innerRuns) {
|
|
945
|
+
out.push({ text: r.text, level: r.level, start: r.start + baseStrIdx });
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
cursor = pair.close + 1;
|
|
949
|
+
}
|
|
950
|
+
emit(cursor, codePoints.length, forcedLevel);
|
|
951
|
+
return out;
|
|
952
|
+
}
|
|
953
|
+
function resolveBidiCore(text, codePoints, cpToStr, forcedLevel) {
|
|
954
|
+
const len = codePoints.length;
|
|
955
|
+
if (len === 0) return [];
|
|
956
|
+
const types = codePoints.map(classifyBidiType);
|
|
957
|
+
const paraLevel = forcedLevel !== void 0 ? forcedLevel : detectParagraphLevel(types);
|
|
958
|
+
resolveWeakTypes(types, paraLevel);
|
|
959
|
+
resolveNeutralTypes(types, paraLevel);
|
|
960
|
+
if (paraLevel === 1) {
|
|
961
|
+
fixPunctuationAffinity(types, codePoints, len);
|
|
962
|
+
fixBracketPairing(types, codePoints, len);
|
|
963
|
+
}
|
|
964
|
+
const levels = assignLevels(types, paraLevel);
|
|
965
|
+
const runs = [];
|
|
966
|
+
let runStart = 0;
|
|
967
|
+
let runLevel = levels[0];
|
|
968
|
+
for (let i = 1; i <= len; i++) {
|
|
969
|
+
if (i === len || levels[i] !== runLevel) {
|
|
970
|
+
const start = cpToStr[runStart];
|
|
971
|
+
const end = cpToStr[i];
|
|
972
|
+
let runText = text.substring(start, end);
|
|
973
|
+
if (runLevel % 2 === 1) {
|
|
974
|
+
runText = reverseString(runText);
|
|
975
|
+
}
|
|
976
|
+
runs.push({ text: runText, level: runLevel, start });
|
|
977
|
+
if (i < len) {
|
|
978
|
+
runStart = i;
|
|
979
|
+
runLevel = levels[i];
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
if (paraLevel === 1 && runs.length > 1) {
|
|
984
|
+
runs.reverse();
|
|
985
|
+
}
|
|
986
|
+
return runs;
|
|
987
|
+
}
|
|
988
|
+
function containsRTL(text) {
|
|
989
|
+
for (let i = 0; i < text.length; ) {
|
|
990
|
+
const cp = text.codePointAt(i) ?? 0;
|
|
991
|
+
if (cp === 8237 || cp === 8238) return true;
|
|
992
|
+
const t = classifyBidiType(cp);
|
|
993
|
+
if (t === "R" || t === "AL") return true;
|
|
994
|
+
i += cp > 65535 ? 2 : 1;
|
|
995
|
+
}
|
|
996
|
+
return false;
|
|
997
|
+
}
|
|
998
|
+
function stripBidiControls(text) {
|
|
999
|
+
if (!text) return text;
|
|
1000
|
+
let needs = false;
|
|
1001
|
+
for (let i = 0; i < text.length; i++) {
|
|
1002
|
+
const c = text.charCodeAt(i);
|
|
1003
|
+
if (c === 8206 || c === 8207 || c >= 8234 && c <= 8238 || c >= 8294 && c <= 8297) {
|
|
1004
|
+
needs = true;
|
|
1005
|
+
break;
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
if (!needs) return text;
|
|
1009
|
+
let out = "";
|
|
1010
|
+
for (let i = 0; i < text.length; i++) {
|
|
1011
|
+
const c = text.charCodeAt(i);
|
|
1012
|
+
if (c === 8206 || c === 8207 || c >= 8234 && c <= 8238 || c >= 8294 && c <= 8297) continue;
|
|
1013
|
+
out += text[i];
|
|
1014
|
+
}
|
|
1015
|
+
return out;
|
|
1016
|
+
}
|
|
1017
|
+
function reverseString(str) {
|
|
1018
|
+
const cps = [];
|
|
1019
|
+
for (let i = 0; i < str.length; ) {
|
|
1020
|
+
const cp = str.codePointAt(i) ?? 0;
|
|
1021
|
+
cps.push(cp);
|
|
1022
|
+
i += cp > 65535 ? 2 : 1;
|
|
1023
|
+
}
|
|
1024
|
+
cps.reverse();
|
|
1025
|
+
return String.fromCodePoint(...cps);
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
// src/fonts/encoding.ts
|
|
1029
|
+
function toWinAnsi(str) {
|
|
1030
|
+
if (!str) return "";
|
|
1031
|
+
let r = "";
|
|
1032
|
+
for (let i = 0; i < str.length; i++) {
|
|
1033
|
+
const c = str.charCodeAt(i);
|
|
1034
|
+
if (c >= 32 && c <= 126) r += str[i];
|
|
1035
|
+
else if (c >= 160 && c <= 255) r += str[i];
|
|
1036
|
+
else if (c === 8364) r += "\x80";
|
|
1037
|
+
else if (c === 8218) r += "\x82";
|
|
1038
|
+
else if (c === 402) r += "\x83";
|
|
1039
|
+
else if (c === 8222) r += "\x84";
|
|
1040
|
+
else if (c === 8230) r += "\x85";
|
|
1041
|
+
else if (c === 8224) r += "\x86";
|
|
1042
|
+
else if (c === 8225) r += "\x87";
|
|
1043
|
+
else if (c === 710) r += "\x88";
|
|
1044
|
+
else if (c === 8240) r += "\x89";
|
|
1045
|
+
else if (c === 352) r += "\x8A";
|
|
1046
|
+
else if (c === 8249) r += "\x8B";
|
|
1047
|
+
else if (c === 338) r += "\x8C";
|
|
1048
|
+
else if (c === 381) r += "\x8E";
|
|
1049
|
+
else if (c === 8216) r += "\x91";
|
|
1050
|
+
else if (c === 8217) r += "\x92";
|
|
1051
|
+
else if (c === 8220) r += "\x93";
|
|
1052
|
+
else if (c === 8221) r += "\x94";
|
|
1053
|
+
else if (c === 8226) r += "\x95";
|
|
1054
|
+
else if (c === 8211) r += "\x96";
|
|
1055
|
+
else if (c === 8212) r += "\x97";
|
|
1056
|
+
else if (c === 732) r += "\x98";
|
|
1057
|
+
else if (c === 8482) r += "\x99";
|
|
1058
|
+
else if (c === 353) r += "\x9A";
|
|
1059
|
+
else if (c === 8250) r += "\x9B";
|
|
1060
|
+
else if (c === 339) r += "\x9C";
|
|
1061
|
+
else if (c === 382) r += "\x9E";
|
|
1062
|
+
else if (c === 376) r += "\x9F";
|
|
1063
|
+
else if (c === 160 || c === 8239) r += " ";
|
|
1064
|
+
else if (c === 9 || c === 10 || c === 13) r += " ";
|
|
1065
|
+
else if (c < 32) ; else r += "?";
|
|
1066
|
+
}
|
|
1067
|
+
return r;
|
|
1068
|
+
}
|
|
1069
|
+
function pdfString(str) {
|
|
1070
|
+
const s = toWinAnsi(stripBidiControls(str));
|
|
1071
|
+
return "(" + s.replace(/\\/g, "\\\\").replace(/\(/g, "\\(").replace(/\)/g, "\\)") + ")";
|
|
1072
|
+
}
|
|
1073
|
+
var WINANSI_HIGH_TO_UNICODE = {
|
|
1074
|
+
128: 8364,
|
|
1075
|
+
130: 8218,
|
|
1076
|
+
131: 402,
|
|
1077
|
+
132: 8222,
|
|
1078
|
+
133: 8230,
|
|
1079
|
+
134: 8224,
|
|
1080
|
+
135: 8225,
|
|
1081
|
+
136: 710,
|
|
1082
|
+
137: 8240,
|
|
1083
|
+
138: 352,
|
|
1084
|
+
139: 8249,
|
|
1085
|
+
140: 338,
|
|
1086
|
+
142: 381,
|
|
1087
|
+
145: 8216,
|
|
1088
|
+
146: 8217,
|
|
1089
|
+
147: 8220,
|
|
1090
|
+
148: 8221,
|
|
1091
|
+
149: 8226,
|
|
1092
|
+
150: 8211,
|
|
1093
|
+
151: 8212,
|
|
1094
|
+
152: 732,
|
|
1095
|
+
153: 8482,
|
|
1096
|
+
154: 353,
|
|
1097
|
+
155: 8250,
|
|
1098
|
+
156: 339,
|
|
1099
|
+
158: 382,
|
|
1100
|
+
159: 376
|
|
1101
|
+
};
|
|
1102
|
+
function buildWinAnsiToUnicodeCMap() {
|
|
1103
|
+
const entries = [];
|
|
1104
|
+
const hex2 = (n2) => n2.toString(16).toUpperCase().padStart(2, "0");
|
|
1105
|
+
const hex4 = (n2) => n2.toString(16).toUpperCase().padStart(4, "0");
|
|
1106
|
+
for (let b = 32; b <= 255; b++) {
|
|
1107
|
+
if (b >= 127 && b <= 159) {
|
|
1108
|
+
const u = WINANSI_HIGH_TO_UNICODE[b];
|
|
1109
|
+
if (u === void 0) continue;
|
|
1110
|
+
entries.push(`<${hex2(b)}> <${hex4(u)}>`);
|
|
1111
|
+
} else {
|
|
1112
|
+
entries.push(`<${hex2(b)}> <${hex4(b)}>`);
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
const blocks = [];
|
|
1116
|
+
for (let i = 0; i < entries.length; i += 100) {
|
|
1117
|
+
const chunk = entries.slice(i, i + 100);
|
|
1118
|
+
blocks.push(`${chunk.length} beginbfchar
|
|
1119
|
+
${chunk.join("\n")}
|
|
1120
|
+
endbfchar`);
|
|
1121
|
+
}
|
|
1122
|
+
return "/CIDInit /ProcSet findresource begin\n12 dict begin\nbegincmap\n/CIDSystemInfo << /Registry (Adobe) /Ordering (UCS) /Supplement 0 >> def\n/CMapName /Adobe-Identity-UCS def\n/CMapType 2 def\n1 begincodespacerange\n<20> <FF>\nendcodespacerange\n" + blocks.join("\n") + "\nendcmap\nCMapName currentdict /CMap defineresource pop\nend\nend";
|
|
1123
|
+
}
|
|
1124
|
+
function truncate(str, max) {
|
|
1125
|
+
if (!str || str.length <= max) return str || "";
|
|
1126
|
+
if (max <= 1) return "\u2026";
|
|
1127
|
+
return str.slice(0, max - 1) + "\u2026";
|
|
1128
|
+
}
|
|
1129
|
+
function helveticaWidth(str, sz) {
|
|
1130
|
+
str = stripBidiControls(str);
|
|
1131
|
+
let w = 0;
|
|
1132
|
+
for (let i = 0; i < str.length; i++) {
|
|
1133
|
+
const cp = str.codePointAt(i) ?? 0;
|
|
1134
|
+
if (cp > 65535) i++;
|
|
1135
|
+
if (cp >= 48 && cp <= 57) w += 556;
|
|
1136
|
+
else if (cp >= 65 && cp <= 90) w += 680;
|
|
1137
|
+
else if (cp >= 97 && cp <= 122) w += 500;
|
|
1138
|
+
else if (cp === 32) w += 278;
|
|
1139
|
+
else if (cp === 46 || cp === 44) w += 278;
|
|
1140
|
+
else if (cp === 43) w += 584;
|
|
1141
|
+
else if (cp === 45) w += 333;
|
|
1142
|
+
else if (cp === 47 || cp === 58) w += 278;
|
|
1143
|
+
else if (cp === 8212) w += 1e3;
|
|
1144
|
+
else if (cp === 8211) w += 556;
|
|
1145
|
+
else if (cp === 8230) w += 1e3;
|
|
1146
|
+
else if (cp === 8216 || cp === 8217) w += 222;
|
|
1147
|
+
else if (cp === 8220 || cp === 8221) w += 333;
|
|
1148
|
+
else if (cp === 8364) w += 556;
|
|
1149
|
+
else w += 556;
|
|
1150
|
+
}
|
|
1151
|
+
return w * sz / 1e3;
|
|
1152
|
+
}
|
|
1153
|
+
function helveticaBoldWidth(str, sz) {
|
|
1154
|
+
str = stripBidiControls(str);
|
|
1155
|
+
let w = 0;
|
|
1156
|
+
for (let i = 0; i < str.length; i++) {
|
|
1157
|
+
const cp = str.codePointAt(i) ?? 0;
|
|
1158
|
+
if (cp > 65535) i++;
|
|
1159
|
+
if (cp >= 48 && cp <= 57) w += 556;
|
|
1160
|
+
else if (cp >= 65 && cp <= 90) w += 722;
|
|
1161
|
+
else if (cp >= 97 && cp <= 122) w += 611;
|
|
1162
|
+
else if (cp === 32) w += 278;
|
|
1163
|
+
else if (cp === 46 || cp === 44) w += 278;
|
|
1164
|
+
else if (cp === 43) w += 584;
|
|
1165
|
+
else if (cp === 45) w += 333;
|
|
1166
|
+
else if (cp === 47 || cp === 58) w += 278;
|
|
1167
|
+
else if (cp === 8212) w += 1e3;
|
|
1168
|
+
else if (cp === 8211) w += 556;
|
|
1169
|
+
else if (cp === 8230) w += 1e3;
|
|
1170
|
+
else if (cp === 8216 || cp === 8217) w += 278;
|
|
1171
|
+
else if (cp === 8220 || cp === 8221) w += 500;
|
|
1172
|
+
else if (cp === 8364) w += 556;
|
|
1173
|
+
else w += 611;
|
|
1174
|
+
}
|
|
1175
|
+
return w * sz / 1e3;
|
|
1176
|
+
}
|
|
1177
|
+
|
|
1178
|
+
// src/shaping/gsub-driver.ts
|
|
1179
|
+
function tryLigature(gids, ligatures) {
|
|
1180
|
+
if (!ligatures || gids.length < 2) return null;
|
|
1181
|
+
const firstGid = gids[0];
|
|
1182
|
+
const entries = ligatures[firstGid];
|
|
1183
|
+
if (!entries) return null;
|
|
1184
|
+
for (const entry of entries) {
|
|
1185
|
+
const compCount = entry.length - 1;
|
|
1186
|
+
if (compCount > gids.length - 1) continue;
|
|
1187
|
+
let match = true;
|
|
1188
|
+
for (let ci = 0; ci < compCount; ci++) {
|
|
1189
|
+
if (gids[1 + ci] !== entry[1 + ci]) {
|
|
1190
|
+
match = false;
|
|
1191
|
+
break;
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1194
|
+
if (match) return { resultGid: entry[0], consumed: compCount + 1 };
|
|
1195
|
+
}
|
|
1196
|
+
return null;
|
|
1197
|
+
}
|
|
1198
|
+
|
|
1199
|
+
// src/shaping/use-lite.ts
|
|
1200
|
+
function devanagariCategory(cp) {
|
|
1201
|
+
if (cp === 2305 || cp === 2306) return "Mabv";
|
|
1202
|
+
if (cp === 2307) return "Mpst";
|
|
1203
|
+
if (cp >= 2308 && cp <= 2324) return "V";
|
|
1204
|
+
if (cp >= 2325 && cp <= 2361) return "B";
|
|
1205
|
+
if (cp === 2362) return "Mabv";
|
|
1206
|
+
if (cp === 2363) return "Mpst";
|
|
1207
|
+
if (cp === 2364) return "Mblw";
|
|
1208
|
+
if (cp === 2365) return "O";
|
|
1209
|
+
if (cp === 2366) return "Mpst";
|
|
1210
|
+
if (cp >= 2367 && cp <= 2368) return cp === 2367 ? "Mpre" : "Mpst";
|
|
1211
|
+
if (cp >= 2369 && cp <= 2376) return "Mblw";
|
|
1212
|
+
if (cp >= 2377 && cp <= 2380) return "Mpst";
|
|
1213
|
+
if (cp === 2381) return "H";
|
|
1214
|
+
if (cp >= 2382 && cp <= 2383) return "Mpre";
|
|
1215
|
+
if (cp === 2384) return "O";
|
|
1216
|
+
if (cp >= 2385 && cp <= 2391) return "Mabv";
|
|
1217
|
+
if (cp >= 2392 && cp <= 2401) return "B";
|
|
1218
|
+
if (cp >= 2402 && cp <= 2403) return "Mblw";
|
|
1219
|
+
if (cp >= 2406 && cp <= 2415) return "N";
|
|
1220
|
+
return "O";
|
|
1221
|
+
}
|
|
1222
|
+
function bengaliCategory(cp) {
|
|
1223
|
+
if (cp === 2433) return "Mabv";
|
|
1224
|
+
if (cp === 2434) return "Mpst";
|
|
1225
|
+
if (cp === 2435) return "Mpst";
|
|
1226
|
+
if (cp >= 2437 && cp <= 2452) return "V";
|
|
1227
|
+
if (cp >= 2453 && cp <= 2489) return "B";
|
|
1228
|
+
if (cp === 2492) return "Mblw";
|
|
1229
|
+
if (cp === 2493) return "O";
|
|
1230
|
+
if (cp === 2494) return "Mpst";
|
|
1231
|
+
if (cp >= 2495 && cp <= 2496) return cp === 2495 ? "Mpre" : "Mpst";
|
|
1232
|
+
if (cp >= 2497 && cp <= 2500) return "Mblw";
|
|
1233
|
+
if (cp >= 2503 && cp <= 2504) return "Mpre";
|
|
1234
|
+
if (cp >= 2507 && cp <= 2508) return "Mpre";
|
|
1235
|
+
if (cp === 2509) return "H";
|
|
1236
|
+
if (cp === 2510) return "B";
|
|
1237
|
+
if (cp === 2519) return "Mpst";
|
|
1238
|
+
if (cp >= 2524 && cp <= 2527) return "B";
|
|
1239
|
+
if (cp >= 2528 && cp <= 2531) return "O";
|
|
1240
|
+
if (cp >= 2534 && cp <= 2543) return "N";
|
|
1241
|
+
return "O";
|
|
1242
|
+
}
|
|
1243
|
+
function tamilCategory(cp) {
|
|
1244
|
+
if (cp === 2946) return "Mabv";
|
|
1245
|
+
if (cp >= 2949 && cp <= 2964) return "V";
|
|
1246
|
+
if (cp >= 2965 && cp <= 3001) return "B";
|
|
1247
|
+
if (cp === 3006) return "Mpst";
|
|
1248
|
+
if (cp === 3007) return "Mpst";
|
|
1249
|
+
if (cp >= 3008 && cp <= 3010) return "Mpst";
|
|
1250
|
+
if (cp >= 3014 && cp <= 3016) return "Mpre";
|
|
1251
|
+
if (cp >= 3018 && cp <= 3020) return "Mpre";
|
|
1252
|
+
if (cp === 3021) return "H";
|
|
1253
|
+
if (cp === 3031) return "Mpst";
|
|
1254
|
+
if (cp >= 3046 && cp <= 3055) return "N";
|
|
1255
|
+
return "O";
|
|
1256
|
+
}
|
|
1257
|
+
function classifyUseCategory(cp) {
|
|
1258
|
+
if (cp === 8204) return "ZWNJ";
|
|
1259
|
+
if (cp === 8205) return "ZWJ";
|
|
1260
|
+
if (cp >= 2304 && cp <= 2431) return devanagariCategory(cp);
|
|
1261
|
+
if (cp >= 2432 && cp <= 2559) return bengaliCategory(cp);
|
|
1262
|
+
if (cp >= 2944 && cp <= 3071) return tamilCategory(cp);
|
|
1263
|
+
return "O";
|
|
1264
|
+
}
|
|
1265
|
+
|
|
1266
|
+
// src/shaping/bengali-shaper.ts
|
|
1267
|
+
var HALANT = 2509;
|
|
454
1268
|
var NUKTA = 2492;
|
|
455
1269
|
var RA = 2480;
|
|
456
1270
|
function bengaliCharType(cp) {
|
|
@@ -475,10 +1289,563 @@ function bengaliCharType(cp) {
|
|
|
475
1289
|
if (cp === 2510) return 0;
|
|
476
1290
|
return -1;
|
|
477
1291
|
}
|
|
478
|
-
function isConsonant(cp) {
|
|
479
|
-
return bengaliCharType(cp) === 0;
|
|
1292
|
+
function isConsonant(cp) {
|
|
1293
|
+
return bengaliCharType(cp) === 0;
|
|
1294
|
+
}
|
|
1295
|
+
function buildBengaliClusters(str) {
|
|
1296
|
+
const clusters = [];
|
|
1297
|
+
const cps = [];
|
|
1298
|
+
for (let i2 = 0; i2 < str.length; ) {
|
|
1299
|
+
const cp = str.codePointAt(i2) ?? 0;
|
|
1300
|
+
cps.push(cp);
|
|
1301
|
+
i2 += cp > 65535 ? 2 : 1;
|
|
1302
|
+
}
|
|
1303
|
+
let i = 0;
|
|
1304
|
+
while (i < cps.length) {
|
|
1305
|
+
const cp = cps[i];
|
|
1306
|
+
const type = bengaliCharType(cp);
|
|
1307
|
+
if (classifyUseCategory(cp) === "ZWJ" || classifyUseCategory(cp) === "ZWNJ") {
|
|
1308
|
+
i++;
|
|
1309
|
+
continue;
|
|
1310
|
+
}
|
|
1311
|
+
if (type < 0 || cp < BENGALI_START || cp > BENGALI_END) {
|
|
1312
|
+
clusters.push({ codepoints: [cp], baseIndex: 0, hasReph: false, preBaseMatras: [] });
|
|
1313
|
+
i++;
|
|
1314
|
+
continue;
|
|
1315
|
+
}
|
|
1316
|
+
const syllable = [];
|
|
1317
|
+
let baseIdx = 0;
|
|
1318
|
+
let hasReph = false;
|
|
1319
|
+
const preMatras = [];
|
|
1320
|
+
if (isConsonant(cp) && cp === RA && i + 2 < cps.length && cps[i + 1] === HALANT && isConsonant(cps[i + 2])) {
|
|
1321
|
+
hasReph = true;
|
|
1322
|
+
syllable.push(cp, cps[i + 1]);
|
|
1323
|
+
i += 2;
|
|
1324
|
+
}
|
|
1325
|
+
let lastConsonantIdx = -1;
|
|
1326
|
+
while (i < cps.length) {
|
|
1327
|
+
const cc = cps[i];
|
|
1328
|
+
const ct = bengaliCharType(cc);
|
|
1329
|
+
if (ct === 0) {
|
|
1330
|
+
lastConsonantIdx = syllable.length;
|
|
1331
|
+
syllable.push(cc);
|
|
1332
|
+
i++;
|
|
1333
|
+
if (i < cps.length && cps[i] === NUKTA) {
|
|
1334
|
+
syllable.push(cps[i]);
|
|
1335
|
+
i++;
|
|
1336
|
+
}
|
|
1337
|
+
if (i < cps.length && cps[i] === HALANT) {
|
|
1338
|
+
let j = i + 1;
|
|
1339
|
+
let zwnj = false;
|
|
1340
|
+
if (j < cps.length) {
|
|
1341
|
+
const jc = classifyUseCategory(cps[j]);
|
|
1342
|
+
if (jc === "ZWJ") {
|
|
1343
|
+
j++;
|
|
1344
|
+
} else if (jc === "ZWNJ") {
|
|
1345
|
+
j++;
|
|
1346
|
+
zwnj = true;
|
|
1347
|
+
}
|
|
1348
|
+
}
|
|
1349
|
+
if (!zwnj && j < cps.length && isConsonant(cps[j])) {
|
|
1350
|
+
syllable.push(cps[i]);
|
|
1351
|
+
i = j;
|
|
1352
|
+
continue;
|
|
1353
|
+
} else {
|
|
1354
|
+
syllable.push(cps[i]);
|
|
1355
|
+
i = j;
|
|
1356
|
+
break;
|
|
1357
|
+
}
|
|
1358
|
+
}
|
|
1359
|
+
break;
|
|
1360
|
+
} else {
|
|
1361
|
+
break;
|
|
1362
|
+
}
|
|
1363
|
+
}
|
|
1364
|
+
baseIdx = lastConsonantIdx >= 0 ? lastConsonantIdx : 0;
|
|
1365
|
+
while (i < cps.length) {
|
|
1366
|
+
const ct = bengaliCharType(cps[i]);
|
|
1367
|
+
if (ct >= 2 && ct <= 5) {
|
|
1368
|
+
if (ct === 4) {
|
|
1369
|
+
preMatras.push(syllable.length);
|
|
1370
|
+
}
|
|
1371
|
+
syllable.push(cps[i]);
|
|
1372
|
+
i++;
|
|
1373
|
+
} else {
|
|
1374
|
+
break;
|
|
1375
|
+
}
|
|
1376
|
+
}
|
|
1377
|
+
while (i < cps.length && bengaliCharType(cps[i]) === 6) {
|
|
1378
|
+
syllable.push(cps[i]);
|
|
1379
|
+
i++;
|
|
1380
|
+
}
|
|
1381
|
+
if (syllable.length === 0) {
|
|
1382
|
+
syllable.push(cps[i] ?? 32);
|
|
1383
|
+
i++;
|
|
1384
|
+
}
|
|
1385
|
+
clusters.push({
|
|
1386
|
+
codepoints: syllable,
|
|
1387
|
+
baseIndex: hasReph ? baseIdx + 2 : baseIdx,
|
|
1388
|
+
// Adjust for reph prefix
|
|
1389
|
+
hasReph,
|
|
1390
|
+
preBaseMatras: preMatras
|
|
1391
|
+
});
|
|
1392
|
+
}
|
|
1393
|
+
return clusters;
|
|
1394
|
+
}
|
|
1395
|
+
function shapeBengaliText(str, fontData) {
|
|
1396
|
+
const { cmap, gsub, ligatures, markAnchors, widths, defaultWidth } = fontData;
|
|
1397
|
+
const shaped = [];
|
|
1398
|
+
function resolveGid(cp) {
|
|
1399
|
+
const normCp = cp === 8239 || cp === 160 ? 32 : cp;
|
|
1400
|
+
return cmap[normCp] || 0;
|
|
1401
|
+
}
|
|
1402
|
+
function resolveGidGsub(cp) {
|
|
1403
|
+
const gid = resolveGid(cp);
|
|
1404
|
+
if (gsub[gid] !== void 0) return gsub[gid];
|
|
1405
|
+
return gid;
|
|
1406
|
+
}
|
|
1407
|
+
function tryLig(gids) {
|
|
1408
|
+
return tryLigature(gids, ligatures);
|
|
1409
|
+
}
|
|
1410
|
+
function getAdv(gid) {
|
|
1411
|
+
return widths[gid] !== void 0 ? widths[gid] : defaultWidth;
|
|
1412
|
+
}
|
|
1413
|
+
function getBaseAnchor2(baseGid, markClass) {
|
|
1414
|
+
const base = markAnchors && markAnchors.bases && markAnchors.bases[baseGid];
|
|
1415
|
+
if (!base) return null;
|
|
1416
|
+
return base[markClass] ?? null;
|
|
1417
|
+
}
|
|
1418
|
+
function getMarkAnchor2(markGid) {
|
|
1419
|
+
const mark = markAnchors && markAnchors.marks && markAnchors.marks[markGid];
|
|
1420
|
+
if (!mark) return null;
|
|
1421
|
+
return { classIdx: mark[0], x: mark[1], y: mark[2] };
|
|
1422
|
+
}
|
|
1423
|
+
function emitGlyph(gid, isZero, baseGid) {
|
|
1424
|
+
if (isZero && baseGid !== void 0) {
|
|
1425
|
+
const markAnchor = getMarkAnchor2(gid);
|
|
1426
|
+
if (markAnchor) {
|
|
1427
|
+
const baseAnchorPt = getBaseAnchor2(baseGid, markAnchor.classIdx);
|
|
1428
|
+
if (baseAnchorPt) {
|
|
1429
|
+
const baseAdv = getAdv(baseGid);
|
|
1430
|
+
shaped.push({
|
|
1431
|
+
gid,
|
|
1432
|
+
dx: baseAnchorPt[0] - markAnchor.x - baseAdv,
|
|
1433
|
+
dy: baseAnchorPt[1] - markAnchor.y,
|
|
1434
|
+
isZeroAdvance: true
|
|
1435
|
+
});
|
|
1436
|
+
return;
|
|
1437
|
+
}
|
|
1438
|
+
}
|
|
1439
|
+
shaped.push({ gid, dx: 0, dy: 0, isZeroAdvance: true });
|
|
1440
|
+
} else {
|
|
1441
|
+
shaped.push({ gid, dx: 0, dy: 0, isZeroAdvance: false });
|
|
1442
|
+
}
|
|
1443
|
+
}
|
|
1444
|
+
const clusters = buildBengaliClusters(str);
|
|
1445
|
+
for (const cluster of clusters) {
|
|
1446
|
+
const { codepoints, hasReph, preBaseMatras } = cluster;
|
|
1447
|
+
const baseStart = hasReph ? 2 : 0;
|
|
1448
|
+
let baseGid = 0;
|
|
1449
|
+
for (let ci = baseStart; ci < codepoints.length; ci++) {
|
|
1450
|
+
const ct = bengaliCharType(codepoints[ci]);
|
|
1451
|
+
if (ct === 0) {
|
|
1452
|
+
baseGid = resolveGid(codepoints[ci]);
|
|
1453
|
+
} else if (ct >= 2) {
|
|
1454
|
+
break;
|
|
1455
|
+
}
|
|
1456
|
+
}
|
|
1457
|
+
const splitPostComponents = [];
|
|
1458
|
+
for (const mIdx of preBaseMatras) {
|
|
1459
|
+
if (mIdx < codepoints.length) {
|
|
1460
|
+
const mCp = codepoints[mIdx];
|
|
1461
|
+
if (mCp === 2507) {
|
|
1462
|
+
emitGlyph(resolveGid(2503), false);
|
|
1463
|
+
splitPostComponents.push(2494);
|
|
1464
|
+
} else if (mCp === 2508) {
|
|
1465
|
+
emitGlyph(resolveGid(2503), false);
|
|
1466
|
+
splitPostComponents.push(2519);
|
|
1467
|
+
} else {
|
|
1468
|
+
emitGlyph(resolveGid(mCp), false);
|
|
1469
|
+
}
|
|
1470
|
+
}
|
|
1471
|
+
}
|
|
1472
|
+
const clusterGids = [];
|
|
1473
|
+
const clusterEndIdx = [];
|
|
1474
|
+
let matraStart = codepoints.length;
|
|
1475
|
+
for (let ci = baseStart; ci < codepoints.length; ci++) {
|
|
1476
|
+
const ct = bengaliCharType(codepoints[ci]);
|
|
1477
|
+
if (ct === 0 || ct === 7 || ct === 8) {
|
|
1478
|
+
clusterGids.push(resolveGid(codepoints[ci]));
|
|
1479
|
+
clusterEndIdx.push(ci);
|
|
1480
|
+
} else if (ct < 0 || ct === 1 || ct === 9) {
|
|
1481
|
+
emitGlyph(resolveGid(codepoints[ci]), false);
|
|
1482
|
+
} else {
|
|
1483
|
+
matraStart = ci;
|
|
1484
|
+
break;
|
|
1485
|
+
}
|
|
1486
|
+
}
|
|
1487
|
+
let ligConsumed = 0;
|
|
1488
|
+
const ligResult = tryLig(clusterGids);
|
|
1489
|
+
if (ligResult) {
|
|
1490
|
+
emitGlyph(ligResult.resultGid, false);
|
|
1491
|
+
baseGid = ligResult.resultGid;
|
|
1492
|
+
ligConsumed = ligResult.consumed;
|
|
1493
|
+
let gi = ligConsumed;
|
|
1494
|
+
while (gi < clusterGids.length) {
|
|
1495
|
+
const subSeq = clusterGids.slice(gi);
|
|
1496
|
+
const subLig = tryLig(subSeq);
|
|
1497
|
+
if (subLig) {
|
|
1498
|
+
emitGlyph(subLig.resultGid, false);
|
|
1499
|
+
gi += subLig.consumed;
|
|
1500
|
+
} else {
|
|
1501
|
+
const origCi = clusterEndIdx[gi];
|
|
1502
|
+
const ct = bengaliCharType(codepoints[origCi]);
|
|
1503
|
+
if (ct === 7) {
|
|
1504
|
+
emitGlyph(clusterGids[gi], true, baseGid);
|
|
1505
|
+
} else {
|
|
1506
|
+
emitGlyph(clusterGids[gi], false);
|
|
1507
|
+
}
|
|
1508
|
+
gi++;
|
|
1509
|
+
}
|
|
1510
|
+
}
|
|
1511
|
+
} else {
|
|
1512
|
+
for (let ci = baseStart; ci < matraStart; ci++) {
|
|
1513
|
+
const cp = codepoints[ci];
|
|
1514
|
+
const ct = bengaliCharType(cp);
|
|
1515
|
+
if (ct === 0) {
|
|
1516
|
+
emitGlyph(resolveGid(cp), false);
|
|
1517
|
+
} else if (ct === 7) {
|
|
1518
|
+
emitGlyph(resolveGid(cp), true, baseGid);
|
|
1519
|
+
} else if (ct === 8) {
|
|
1520
|
+
emitGlyph(resolveGid(cp), true, baseGid);
|
|
1521
|
+
}
|
|
1522
|
+
}
|
|
1523
|
+
}
|
|
1524
|
+
for (let ci = matraStart; ci < codepoints.length; ci++) {
|
|
1525
|
+
const cp = codepoints[ci];
|
|
1526
|
+
const ct = bengaliCharType(cp);
|
|
1527
|
+
if (ct === 4) continue;
|
|
1528
|
+
if (ct === 2 || ct === 3) {
|
|
1529
|
+
emitGlyph(resolveGid(cp), true, baseGid);
|
|
1530
|
+
} else if (ct === 5) {
|
|
1531
|
+
emitGlyph(resolveGid(cp), false);
|
|
1532
|
+
} else if (ct === 6) {
|
|
1533
|
+
emitGlyph(resolveGid(cp), true, baseGid);
|
|
1534
|
+
} else if (ct === 9) {
|
|
1535
|
+
emitGlyph(resolveGid(cp), false);
|
|
1536
|
+
} else {
|
|
1537
|
+
emitGlyph(resolveGid(cp), false);
|
|
1538
|
+
}
|
|
1539
|
+
}
|
|
1540
|
+
for (const postCp of splitPostComponents) {
|
|
1541
|
+
emitGlyph(resolveGid(postCp), false);
|
|
1542
|
+
}
|
|
1543
|
+
if (hasReph) {
|
|
1544
|
+
const raGid = resolveGidGsub(RA);
|
|
1545
|
+
const halantGid = resolveGid(HALANT);
|
|
1546
|
+
emitGlyph(raGid, true, baseGid);
|
|
1547
|
+
emitGlyph(halantGid, true, baseGid);
|
|
1548
|
+
}
|
|
1549
|
+
}
|
|
1550
|
+
return shaped;
|
|
1551
|
+
}
|
|
1552
|
+
|
|
1553
|
+
// src/shaping/tamil-shaper.ts
|
|
1554
|
+
var PULLI = 3021;
|
|
1555
|
+
function tamilCharType(cp) {
|
|
1556
|
+
if (cp === PULLI) return 7;
|
|
1557
|
+
if (cp === 2946 || cp === 2947) return 6;
|
|
1558
|
+
if (cp >= 2949 && cp <= 2964) return 1;
|
|
1559
|
+
if (cp >= 2965 && cp <= 3001) return 0;
|
|
1560
|
+
if (cp === 3007) return 4;
|
|
1561
|
+
if (cp === 3014) return 4;
|
|
1562
|
+
if (cp === 3015) return 4;
|
|
1563
|
+
if (cp === 3016) return 4;
|
|
1564
|
+
if (cp === 3018) return 4;
|
|
1565
|
+
if (cp === 3019) return 4;
|
|
1566
|
+
if (cp === 3020) return 4;
|
|
1567
|
+
if (cp === 3006) return 5;
|
|
1568
|
+
if (cp === 3008) return 5;
|
|
1569
|
+
if (cp === 3009 || cp === 3010) return 2;
|
|
1570
|
+
if (cp === 3031) return 5;
|
|
1571
|
+
if (cp >= 3006 && cp <= 3020) return 2;
|
|
1572
|
+
if (cp >= 3046 && cp <= 3055) return 9;
|
|
1573
|
+
if (cp >= 3056 && cp <= 3066) return 9;
|
|
1574
|
+
if (cp === 3024) return 6;
|
|
1575
|
+
return -1;
|
|
1576
|
+
}
|
|
1577
|
+
function isConsonant2(cp) {
|
|
1578
|
+
return tamilCharType(cp) === 0;
|
|
1579
|
+
}
|
|
1580
|
+
function buildTamilClusters(str) {
|
|
1581
|
+
const clusters = [];
|
|
1582
|
+
const cps = [];
|
|
1583
|
+
for (let i2 = 0; i2 < str.length; ) {
|
|
1584
|
+
const cp = str.codePointAt(i2) ?? 0;
|
|
1585
|
+
cps.push(cp);
|
|
1586
|
+
i2 += cp > 65535 ? 2 : 1;
|
|
1587
|
+
}
|
|
1588
|
+
let i = 0;
|
|
1589
|
+
while (i < cps.length) {
|
|
1590
|
+
const cp = cps[i];
|
|
1591
|
+
const type = tamilCharType(cp);
|
|
1592
|
+
if (classifyUseCategory(cp) === "ZWJ" || classifyUseCategory(cp) === "ZWNJ") {
|
|
1593
|
+
i++;
|
|
1594
|
+
continue;
|
|
1595
|
+
}
|
|
1596
|
+
if (type < 0 || cp < TAMIL_START || cp > TAMIL_END) {
|
|
1597
|
+
clusters.push({ codepoints: [cp], baseIndex: 0, preBaseMatras: [] });
|
|
1598
|
+
i++;
|
|
1599
|
+
continue;
|
|
1600
|
+
}
|
|
1601
|
+
const syllable = [];
|
|
1602
|
+
let lastConsonantIdx = -1;
|
|
1603
|
+
const preMatras = [];
|
|
1604
|
+
while (i < cps.length) {
|
|
1605
|
+
const cc = cps[i];
|
|
1606
|
+
const ct = tamilCharType(cc);
|
|
1607
|
+
if (ct === 0) {
|
|
1608
|
+
lastConsonantIdx = syllable.length;
|
|
1609
|
+
syllable.push(cc);
|
|
1610
|
+
i++;
|
|
1611
|
+
if (i < cps.length && cps[i] === PULLI) {
|
|
1612
|
+
let j = i + 1;
|
|
1613
|
+
let zwnj = false;
|
|
1614
|
+
if (j < cps.length) {
|
|
1615
|
+
const jc = classifyUseCategory(cps[j]);
|
|
1616
|
+
if (jc === "ZWJ") {
|
|
1617
|
+
j++;
|
|
1618
|
+
} else if (jc === "ZWNJ") {
|
|
1619
|
+
j++;
|
|
1620
|
+
zwnj = true;
|
|
1621
|
+
}
|
|
1622
|
+
}
|
|
1623
|
+
if (!zwnj && j < cps.length && isConsonant2(cps[j])) {
|
|
1624
|
+
syllable.push(cps[i]);
|
|
1625
|
+
i = j;
|
|
1626
|
+
continue;
|
|
1627
|
+
} else {
|
|
1628
|
+
syllable.push(cps[i]);
|
|
1629
|
+
i = j;
|
|
1630
|
+
break;
|
|
1631
|
+
}
|
|
1632
|
+
}
|
|
1633
|
+
break;
|
|
1634
|
+
} else {
|
|
1635
|
+
break;
|
|
1636
|
+
}
|
|
1637
|
+
}
|
|
1638
|
+
const baseIdx = lastConsonantIdx >= 0 ? lastConsonantIdx : 0;
|
|
1639
|
+
while (i < cps.length) {
|
|
1640
|
+
const ct = tamilCharType(cps[i]);
|
|
1641
|
+
if (ct >= 2 && ct <= 5) {
|
|
1642
|
+
if (ct === 4) {
|
|
1643
|
+
preMatras.push(syllable.length);
|
|
1644
|
+
}
|
|
1645
|
+
syllable.push(cps[i]);
|
|
1646
|
+
i++;
|
|
1647
|
+
} else {
|
|
1648
|
+
break;
|
|
1649
|
+
}
|
|
1650
|
+
}
|
|
1651
|
+
while (i < cps.length && tamilCharType(cps[i]) === 6) {
|
|
1652
|
+
syllable.push(cps[i]);
|
|
1653
|
+
i++;
|
|
1654
|
+
}
|
|
1655
|
+
if (syllable.length === 0) {
|
|
1656
|
+
syllable.push(cps[i] ?? 32);
|
|
1657
|
+
i++;
|
|
1658
|
+
}
|
|
1659
|
+
clusters.push({
|
|
1660
|
+
codepoints: syllable,
|
|
1661
|
+
baseIndex: baseIdx,
|
|
1662
|
+
preBaseMatras: preMatras
|
|
1663
|
+
});
|
|
1664
|
+
}
|
|
1665
|
+
return clusters;
|
|
1666
|
+
}
|
|
1667
|
+
function shapeTamilText(str, fontData) {
|
|
1668
|
+
const { cmap, ligatures, markAnchors, widths, defaultWidth } = fontData;
|
|
1669
|
+
const shaped = [];
|
|
1670
|
+
function resolveGid(cp) {
|
|
1671
|
+
const normCp = cp === 8239 || cp === 160 ? 32 : cp;
|
|
1672
|
+
return cmap[normCp] || 0;
|
|
1673
|
+
}
|
|
1674
|
+
function tryLig(gids) {
|
|
1675
|
+
return tryLigature(gids, ligatures);
|
|
1676
|
+
}
|
|
1677
|
+
function getAdv(gid) {
|
|
1678
|
+
return widths[gid] !== void 0 ? widths[gid] : defaultWidth;
|
|
1679
|
+
}
|
|
1680
|
+
function getBaseAnchor2(baseGid, markClass) {
|
|
1681
|
+
const base = markAnchors && markAnchors.bases && markAnchors.bases[baseGid];
|
|
1682
|
+
if (!base) return null;
|
|
1683
|
+
return base[markClass] ?? null;
|
|
1684
|
+
}
|
|
1685
|
+
function getMarkAnchor2(markGid) {
|
|
1686
|
+
const mark = markAnchors && markAnchors.marks && markAnchors.marks[markGid];
|
|
1687
|
+
if (!mark) return null;
|
|
1688
|
+
return { classIdx: mark[0], x: mark[1], y: mark[2] };
|
|
1689
|
+
}
|
|
1690
|
+
function emitGlyph(gid, isZero, baseGid) {
|
|
1691
|
+
if (isZero && baseGid !== void 0) {
|
|
1692
|
+
const markAnchor = getMarkAnchor2(gid);
|
|
1693
|
+
if (markAnchor) {
|
|
1694
|
+
const baseAnchorPt = getBaseAnchor2(baseGid, markAnchor.classIdx);
|
|
1695
|
+
if (baseAnchorPt) {
|
|
1696
|
+
const baseAdv = getAdv(baseGid);
|
|
1697
|
+
shaped.push({
|
|
1698
|
+
gid,
|
|
1699
|
+
dx: baseAnchorPt[0] - markAnchor.x - baseAdv,
|
|
1700
|
+
dy: baseAnchorPt[1] - markAnchor.y,
|
|
1701
|
+
isZeroAdvance: true
|
|
1702
|
+
});
|
|
1703
|
+
return;
|
|
1704
|
+
}
|
|
1705
|
+
}
|
|
1706
|
+
shaped.push({ gid, dx: 0, dy: 0, isZeroAdvance: true });
|
|
1707
|
+
} else {
|
|
1708
|
+
shaped.push({ gid, dx: 0, dy: 0, isZeroAdvance: false });
|
|
1709
|
+
}
|
|
1710
|
+
}
|
|
1711
|
+
const clusters = buildTamilClusters(str);
|
|
1712
|
+
for (const cluster of clusters) {
|
|
1713
|
+
const { codepoints, preBaseMatras } = cluster;
|
|
1714
|
+
let baseGid = 0;
|
|
1715
|
+
for (let ci = 0; ci < codepoints.length; ci++) {
|
|
1716
|
+
const ct = tamilCharType(codepoints[ci]);
|
|
1717
|
+
if (ct === 0) {
|
|
1718
|
+
baseGid = resolveGid(codepoints[ci]);
|
|
1719
|
+
} else if (ct >= 2) {
|
|
1720
|
+
break;
|
|
1721
|
+
}
|
|
1722
|
+
}
|
|
1723
|
+
const splitPostComponents = [];
|
|
1724
|
+
for (const mIdx of preBaseMatras) {
|
|
1725
|
+
if (mIdx < codepoints.length) {
|
|
1726
|
+
const mCp = codepoints[mIdx];
|
|
1727
|
+
if (mCp === 3018) {
|
|
1728
|
+
emitGlyph(resolveGid(3014), false);
|
|
1729
|
+
splitPostComponents.push(3006);
|
|
1730
|
+
} else if (mCp === 3019) {
|
|
1731
|
+
emitGlyph(resolveGid(3015), false);
|
|
1732
|
+
splitPostComponents.push(3006);
|
|
1733
|
+
} else if (mCp === 3020) {
|
|
1734
|
+
emitGlyph(resolveGid(3014), false);
|
|
1735
|
+
splitPostComponents.push(3031);
|
|
1736
|
+
} else {
|
|
1737
|
+
emitGlyph(resolveGid(mCp), false);
|
|
1738
|
+
}
|
|
1739
|
+
}
|
|
1740
|
+
}
|
|
1741
|
+
const clusterGids = [];
|
|
1742
|
+
const clusterEndIdx = [];
|
|
1743
|
+
let matraStart = codepoints.length;
|
|
1744
|
+
for (let ci = 0; ci < codepoints.length; ci++) {
|
|
1745
|
+
const ct = tamilCharType(codepoints[ci]);
|
|
1746
|
+
if (ct === 0 || ct === 7) {
|
|
1747
|
+
clusterGids.push(resolveGid(codepoints[ci]));
|
|
1748
|
+
clusterEndIdx.push(ci);
|
|
1749
|
+
} else if (ct >= 2) {
|
|
1750
|
+
matraStart = ci;
|
|
1751
|
+
break;
|
|
1752
|
+
} else if (ct < 0) {
|
|
1753
|
+
emitGlyph(resolveGid(codepoints[ci]), false);
|
|
1754
|
+
} else if (ct === 1) {
|
|
1755
|
+
emitGlyph(resolveGid(codepoints[ci]), false);
|
|
1756
|
+
}
|
|
1757
|
+
}
|
|
1758
|
+
let ligConsumed = 0;
|
|
1759
|
+
const ligResult = tryLig(clusterGids);
|
|
1760
|
+
if (ligResult) {
|
|
1761
|
+
emitGlyph(ligResult.resultGid, false);
|
|
1762
|
+
baseGid = ligResult.resultGid;
|
|
1763
|
+
ligConsumed = ligResult.consumed;
|
|
1764
|
+
let gi = ligConsumed;
|
|
1765
|
+
while (gi < clusterGids.length) {
|
|
1766
|
+
const subSeq = clusterGids.slice(gi);
|
|
1767
|
+
const subLig = tryLig(subSeq);
|
|
1768
|
+
if (subLig) {
|
|
1769
|
+
emitGlyph(subLig.resultGid, false);
|
|
1770
|
+
gi += subLig.consumed;
|
|
1771
|
+
} else {
|
|
1772
|
+
const origCi = clusterEndIdx[gi];
|
|
1773
|
+
const ct = tamilCharType(codepoints[origCi]);
|
|
1774
|
+
if (ct === 7) {
|
|
1775
|
+
emitGlyph(clusterGids[gi], true, baseGid);
|
|
1776
|
+
} else {
|
|
1777
|
+
emitGlyph(clusterGids[gi], false);
|
|
1778
|
+
}
|
|
1779
|
+
gi++;
|
|
1780
|
+
}
|
|
1781
|
+
}
|
|
1782
|
+
} else {
|
|
1783
|
+
for (let ci = 0; ci < matraStart; ci++) {
|
|
1784
|
+
const cp = codepoints[ci];
|
|
1785
|
+
const ct = tamilCharType(cp);
|
|
1786
|
+
if (ct === 0) {
|
|
1787
|
+
emitGlyph(resolveGid(cp), false);
|
|
1788
|
+
} else if (ct === 7) {
|
|
1789
|
+
emitGlyph(resolveGid(cp), true, baseGid);
|
|
1790
|
+
}
|
|
1791
|
+
}
|
|
1792
|
+
}
|
|
1793
|
+
for (let ci = matraStart; ci < codepoints.length; ci++) {
|
|
1794
|
+
const cp = codepoints[ci];
|
|
1795
|
+
const ct = tamilCharType(cp);
|
|
1796
|
+
if (ct === 4) continue;
|
|
1797
|
+
if (ct === 2 || ct === 3) {
|
|
1798
|
+
emitGlyph(resolveGid(cp), true, baseGid);
|
|
1799
|
+
} else if (ct === 5) {
|
|
1800
|
+
emitGlyph(resolveGid(cp), false);
|
|
1801
|
+
} else if (ct === 6) {
|
|
1802
|
+
emitGlyph(resolveGid(cp), true, baseGid);
|
|
1803
|
+
} else if (ct === 9) {
|
|
1804
|
+
emitGlyph(resolveGid(cp), false);
|
|
1805
|
+
} else {
|
|
1806
|
+
emitGlyph(resolveGid(cp), false);
|
|
1807
|
+
}
|
|
1808
|
+
}
|
|
1809
|
+
for (const postCp of splitPostComponents) {
|
|
1810
|
+
emitGlyph(resolveGid(postCp), false);
|
|
1811
|
+
}
|
|
1812
|
+
}
|
|
1813
|
+
return shaped;
|
|
1814
|
+
}
|
|
1815
|
+
|
|
1816
|
+
// src/shaping/telugu-shaper.ts
|
|
1817
|
+
var VIRAMA = 3149;
|
|
1818
|
+
function teluguCharType(cp) {
|
|
1819
|
+
if (cp === VIRAMA) return 7;
|
|
1820
|
+
if (cp >= 3072 && cp <= 3076) return 6;
|
|
1821
|
+
if (cp >= 3077 && cp <= 3092) return 1;
|
|
1822
|
+
if (cp === 3133) return 1;
|
|
1823
|
+
if (cp >= 3093 && cp <= 3129) return 0;
|
|
1824
|
+
if (cp >= 3160 && cp <= 3162) return 0;
|
|
1825
|
+
if (cp === 3134) return 5;
|
|
1826
|
+
if (cp === 3135) return 2;
|
|
1827
|
+
if (cp === 3136) return 2;
|
|
1828
|
+
if (cp === 3137) return 5;
|
|
1829
|
+
if (cp === 3138) return 5;
|
|
1830
|
+
if (cp === 3139) return 2;
|
|
1831
|
+
if (cp === 3140) return 2;
|
|
1832
|
+
if (cp === 3142) return 2;
|
|
1833
|
+
if (cp === 3143) return 2;
|
|
1834
|
+
if (cp === 3144) return 2;
|
|
1835
|
+
if (cp === 3146) return 5;
|
|
1836
|
+
if (cp === 3147) return 5;
|
|
1837
|
+
if (cp === 3148) return 5;
|
|
1838
|
+
if (cp === 3170 || cp === 3171) return 3;
|
|
1839
|
+
if (cp === 3157 || cp === 3158) return 6;
|
|
1840
|
+
if (cp === 3168 || cp === 3169) return 1;
|
|
1841
|
+
if (cp >= 3174 && cp <= 3183) return 9;
|
|
1842
|
+
if (cp >= 3192 && cp <= 3199) return 9;
|
|
1843
|
+
return -1;
|
|
1844
|
+
}
|
|
1845
|
+
function isConsonant3(cp) {
|
|
1846
|
+
return teluguCharType(cp) === 0;
|
|
480
1847
|
}
|
|
481
|
-
function
|
|
1848
|
+
function buildTeluguClusters(str) {
|
|
482
1849
|
const clusters = [];
|
|
483
1850
|
const cps = [];
|
|
484
1851
|
for (let i2 = 0; i2 < str.length; ) {
|
|
@@ -489,41 +1856,285 @@ function buildBengaliClusters(str) {
|
|
|
489
1856
|
let i = 0;
|
|
490
1857
|
while (i < cps.length) {
|
|
491
1858
|
const cp = cps[i];
|
|
492
|
-
const type =
|
|
493
|
-
if (
|
|
494
|
-
|
|
1859
|
+
const type = teluguCharType(cp);
|
|
1860
|
+
if (classifyUseCategory(cp) === "ZWJ" || classifyUseCategory(cp) === "ZWNJ") {
|
|
1861
|
+
i++;
|
|
1862
|
+
continue;
|
|
1863
|
+
}
|
|
1864
|
+
if (type < 0 || cp < TELUGU_START || cp > TELUGU_END) {
|
|
1865
|
+
clusters.push({ codepoints: [cp], baseIndex: 0 });
|
|
1866
|
+
i++;
|
|
1867
|
+
continue;
|
|
1868
|
+
}
|
|
1869
|
+
const syllable = [];
|
|
1870
|
+
let lastConsonantIdx = -1;
|
|
1871
|
+
while (i < cps.length) {
|
|
1872
|
+
const cc = cps[i];
|
|
1873
|
+
const ct = teluguCharType(cc);
|
|
1874
|
+
if (ct === 0) {
|
|
1875
|
+
lastConsonantIdx = syllable.length;
|
|
1876
|
+
syllable.push(cc);
|
|
1877
|
+
i++;
|
|
1878
|
+
if (i < cps.length && cps[i] === VIRAMA) {
|
|
1879
|
+
let j = i + 1;
|
|
1880
|
+
let zwnj = false;
|
|
1881
|
+
if (j < cps.length) {
|
|
1882
|
+
const jc = classifyUseCategory(cps[j]);
|
|
1883
|
+
if (jc === "ZWJ") {
|
|
1884
|
+
j++;
|
|
1885
|
+
} else if (jc === "ZWNJ") {
|
|
1886
|
+
j++;
|
|
1887
|
+
zwnj = true;
|
|
1888
|
+
}
|
|
1889
|
+
}
|
|
1890
|
+
if (!zwnj && j < cps.length && isConsonant3(cps[j])) {
|
|
1891
|
+
syllable.push(cps[i]);
|
|
1892
|
+
i = j;
|
|
1893
|
+
continue;
|
|
1894
|
+
} else {
|
|
1895
|
+
syllable.push(cps[i]);
|
|
1896
|
+
i = j;
|
|
1897
|
+
break;
|
|
1898
|
+
}
|
|
1899
|
+
}
|
|
1900
|
+
break;
|
|
1901
|
+
} else {
|
|
1902
|
+
break;
|
|
1903
|
+
}
|
|
1904
|
+
}
|
|
1905
|
+
const baseIdx = lastConsonantIdx >= 0 ? lastConsonantIdx : 0;
|
|
1906
|
+
while (i < cps.length) {
|
|
1907
|
+
const ct = teluguCharType(cps[i]);
|
|
1908
|
+
if (ct >= 2 && ct <= 5) {
|
|
1909
|
+
syllable.push(cps[i]);
|
|
1910
|
+
i++;
|
|
1911
|
+
} else {
|
|
1912
|
+
break;
|
|
1913
|
+
}
|
|
1914
|
+
}
|
|
1915
|
+
while (i < cps.length && teluguCharType(cps[i]) === 6) {
|
|
1916
|
+
syllable.push(cps[i]);
|
|
1917
|
+
i++;
|
|
1918
|
+
}
|
|
1919
|
+
if (syllable.length === 0) {
|
|
1920
|
+
syllable.push(cps[i] ?? 32);
|
|
1921
|
+
i++;
|
|
1922
|
+
}
|
|
1923
|
+
clusters.push({ codepoints: syllable, baseIndex: baseIdx });
|
|
1924
|
+
}
|
|
1925
|
+
return clusters;
|
|
1926
|
+
}
|
|
1927
|
+
function shapeTeluguText(str, fontData) {
|
|
1928
|
+
const { cmap, ligatures, markAnchors, widths, defaultWidth } = fontData;
|
|
1929
|
+
const shaped = [];
|
|
1930
|
+
function resolveGid(cp) {
|
|
1931
|
+
const normCp = cp === 8239 || cp === 160 ? 32 : cp;
|
|
1932
|
+
return cmap[normCp] || 0;
|
|
1933
|
+
}
|
|
1934
|
+
function tryLig(gids) {
|
|
1935
|
+
return tryLigature(gids, ligatures);
|
|
1936
|
+
}
|
|
1937
|
+
function getAdv(gid) {
|
|
1938
|
+
return widths[gid] !== void 0 ? widths[gid] : defaultWidth;
|
|
1939
|
+
}
|
|
1940
|
+
function getBaseAnchor2(baseGid, markClass) {
|
|
1941
|
+
const base = markAnchors && markAnchors.bases && markAnchors.bases[baseGid];
|
|
1942
|
+
if (!base) return null;
|
|
1943
|
+
return base[markClass] ?? null;
|
|
1944
|
+
}
|
|
1945
|
+
function getMarkAnchor2(markGid) {
|
|
1946
|
+
const mark = markAnchors && markAnchors.marks && markAnchors.marks[markGid];
|
|
1947
|
+
if (!mark) return null;
|
|
1948
|
+
return { classIdx: mark[0], x: mark[1], y: mark[2] };
|
|
1949
|
+
}
|
|
1950
|
+
function emitGlyph(gid, isZero, baseGid) {
|
|
1951
|
+
if (isZero && baseGid !== void 0) {
|
|
1952
|
+
const markAnchor = getMarkAnchor2(gid);
|
|
1953
|
+
if (markAnchor) {
|
|
1954
|
+
const baseAnchorPt = getBaseAnchor2(baseGid, markAnchor.classIdx);
|
|
1955
|
+
if (baseAnchorPt) {
|
|
1956
|
+
const baseAdv = getAdv(baseGid);
|
|
1957
|
+
shaped.push({
|
|
1958
|
+
gid,
|
|
1959
|
+
dx: baseAnchorPt[0] - markAnchor.x - baseAdv,
|
|
1960
|
+
dy: baseAnchorPt[1] - markAnchor.y,
|
|
1961
|
+
isZeroAdvance: true
|
|
1962
|
+
});
|
|
1963
|
+
return;
|
|
1964
|
+
}
|
|
1965
|
+
}
|
|
1966
|
+
shaped.push({ gid, dx: 0, dy: 0, isZeroAdvance: true });
|
|
1967
|
+
} else {
|
|
1968
|
+
shaped.push({ gid, dx: 0, dy: 0, isZeroAdvance: false });
|
|
1969
|
+
}
|
|
1970
|
+
}
|
|
1971
|
+
const clusters = buildTeluguClusters(str);
|
|
1972
|
+
for (const cluster of clusters) {
|
|
1973
|
+
const { codepoints } = cluster;
|
|
1974
|
+
let baseGid = 0;
|
|
1975
|
+
for (let ci = 0; ci < codepoints.length; ci++) {
|
|
1976
|
+
const ct = teluguCharType(codepoints[ci]);
|
|
1977
|
+
if (ct === 0) {
|
|
1978
|
+
baseGid = resolveGid(codepoints[ci]);
|
|
1979
|
+
} else if (ct >= 2) {
|
|
1980
|
+
break;
|
|
1981
|
+
}
|
|
1982
|
+
}
|
|
1983
|
+
const clusterGids = [];
|
|
1984
|
+
const clusterEndIdx = [];
|
|
1985
|
+
let matraStart = codepoints.length;
|
|
1986
|
+
for (let ci = 0; ci < codepoints.length; ci++) {
|
|
1987
|
+
const ct = teluguCharType(codepoints[ci]);
|
|
1988
|
+
if (ct === 0 || ct === 7) {
|
|
1989
|
+
clusterGids.push(resolveGid(codepoints[ci]));
|
|
1990
|
+
clusterEndIdx.push(ci);
|
|
1991
|
+
} else if (ct >= 2) {
|
|
1992
|
+
matraStart = ci;
|
|
1993
|
+
break;
|
|
1994
|
+
} else if (ct < 0 || ct === 1 || ct === 9) {
|
|
1995
|
+
emitGlyph(resolveGid(codepoints[ci]), false);
|
|
1996
|
+
}
|
|
1997
|
+
}
|
|
1998
|
+
const ligResult = tryLig(clusterGids);
|
|
1999
|
+
if (ligResult) {
|
|
2000
|
+
emitGlyph(ligResult.resultGid, false);
|
|
2001
|
+
baseGid = ligResult.resultGid;
|
|
2002
|
+
let gi = ligResult.consumed;
|
|
2003
|
+
while (gi < clusterGids.length) {
|
|
2004
|
+
const subSeq = clusterGids.slice(gi);
|
|
2005
|
+
const subLig = tryLig(subSeq);
|
|
2006
|
+
if (subLig) {
|
|
2007
|
+
emitGlyph(subLig.resultGid, false);
|
|
2008
|
+
gi += subLig.consumed;
|
|
2009
|
+
} else {
|
|
2010
|
+
const origCi = clusterEndIdx[gi];
|
|
2011
|
+
const ct = teluguCharType(codepoints[origCi]);
|
|
2012
|
+
if (ct === 7) {
|
|
2013
|
+
emitGlyph(clusterGids[gi], true, baseGid);
|
|
2014
|
+
} else {
|
|
2015
|
+
emitGlyph(clusterGids[gi], false);
|
|
2016
|
+
}
|
|
2017
|
+
gi++;
|
|
2018
|
+
}
|
|
2019
|
+
}
|
|
2020
|
+
} else {
|
|
2021
|
+
for (let ci = 0; ci < matraStart; ci++) {
|
|
2022
|
+
const cp = codepoints[ci];
|
|
2023
|
+
const ct = teluguCharType(cp);
|
|
2024
|
+
if (ct === 0) {
|
|
2025
|
+
emitGlyph(resolveGid(cp), false);
|
|
2026
|
+
} else if (ct === 7) {
|
|
2027
|
+
emitGlyph(resolveGid(cp), true, baseGid);
|
|
2028
|
+
}
|
|
2029
|
+
}
|
|
2030
|
+
}
|
|
2031
|
+
for (let ci = matraStart; ci < codepoints.length; ci++) {
|
|
2032
|
+
const cp = codepoints[ci];
|
|
2033
|
+
const ct = teluguCharType(cp);
|
|
2034
|
+
if (ct === 2 || ct === 3) {
|
|
2035
|
+
emitGlyph(resolveGid(cp), true, baseGid);
|
|
2036
|
+
} else if (ct === 5) {
|
|
2037
|
+
emitGlyph(resolveGid(cp), false);
|
|
2038
|
+
} else if (ct === 6) {
|
|
2039
|
+
emitGlyph(resolveGid(cp), true, baseGid);
|
|
2040
|
+
} else if (ct === 9) {
|
|
2041
|
+
emitGlyph(resolveGid(cp), false);
|
|
2042
|
+
} else {
|
|
2043
|
+
emitGlyph(resolveGid(cp), false);
|
|
2044
|
+
}
|
|
2045
|
+
}
|
|
2046
|
+
}
|
|
2047
|
+
return shaped;
|
|
2048
|
+
}
|
|
2049
|
+
|
|
2050
|
+
// src/shaping/sinhala-shaper.ts
|
|
2051
|
+
var VIRAMA2 = SINHALA_VIRAMA;
|
|
2052
|
+
var TWO_PART_VOWELS = {
|
|
2053
|
+
3546: [3545, 3530],
|
|
2054
|
+
// ේ (ee)
|
|
2055
|
+
3548: [3545, 3535],
|
|
2056
|
+
// ො (o)
|
|
2057
|
+
3549: [3545, 3535, 3530],
|
|
2058
|
+
// ෝ (oo)
|
|
2059
|
+
3550: [3545, 3551]
|
|
2060
|
+
// ෞ (au)
|
|
2061
|
+
};
|
|
2062
|
+
function sinhalaCharType(cp) {
|
|
2063
|
+
if (cp === VIRAMA2) return 7;
|
|
2064
|
+
if (cp >= 3458 && cp <= 3460) return 6;
|
|
2065
|
+
if (cp >= 3461 && cp <= 3478) return 1;
|
|
2066
|
+
if (cp >= 3482 && cp <= 3526) return 0;
|
|
2067
|
+
if (cp === 3535) return 5;
|
|
2068
|
+
if (cp === 3536 || cp === 3537) return 5;
|
|
2069
|
+
if (cp === 3538 || cp === 3539) return 2;
|
|
2070
|
+
if (cp === 3540 || cp === 3542) return 3;
|
|
2071
|
+
if (cp === 3544) return 5;
|
|
2072
|
+
if (cp === 3545 || cp === 3547) return 4;
|
|
2073
|
+
if (cp === 3551) return 5;
|
|
2074
|
+
if (cp === 3570 || cp === 3571) return 5;
|
|
2075
|
+
if (cp >= 3558 && cp <= 3567) return 9;
|
|
2076
|
+
return -1;
|
|
2077
|
+
}
|
|
2078
|
+
function isConsonant4(cp) {
|
|
2079
|
+
return sinhalaCharType(cp) === 0;
|
|
2080
|
+
}
|
|
2081
|
+
function buildSinhalaClusters(str) {
|
|
2082
|
+
const clusters = [];
|
|
2083
|
+
const cps = [];
|
|
2084
|
+
for (let i2 = 0; i2 < str.length; ) {
|
|
2085
|
+
const cp = str.codePointAt(i2) ?? 0;
|
|
2086
|
+
const decomp = TWO_PART_VOWELS[cp];
|
|
2087
|
+
if (decomp) {
|
|
2088
|
+
for (const d of decomp) cps.push(d);
|
|
2089
|
+
} else {
|
|
2090
|
+
cps.push(cp);
|
|
2091
|
+
}
|
|
2092
|
+
i2 += cp > 65535 ? 2 : 1;
|
|
2093
|
+
}
|
|
2094
|
+
let i = 0;
|
|
2095
|
+
while (i < cps.length) {
|
|
2096
|
+
const cp = cps[i];
|
|
2097
|
+
const type = sinhalaCharType(cp);
|
|
2098
|
+
if (classifyUseCategory(cp) === "ZWNJ") {
|
|
2099
|
+
i++;
|
|
2100
|
+
continue;
|
|
2101
|
+
}
|
|
2102
|
+
if (type < 0 || cp < SINHALA_START || cp > SINHALA_END) {
|
|
2103
|
+
clusters.push({ codepoints: [cp], baseIndex: 0 });
|
|
495
2104
|
i++;
|
|
496
2105
|
continue;
|
|
497
2106
|
}
|
|
498
2107
|
const syllable = [];
|
|
499
|
-
let baseIdx = 0;
|
|
500
|
-
let hasReph = false;
|
|
501
|
-
const preMatras = [];
|
|
502
|
-
if (isConsonant(cp) && cp === RA && i + 2 < cps.length && cps[i + 1] === HALANT && isConsonant(cps[i + 2])) {
|
|
503
|
-
hasReph = true;
|
|
504
|
-
syllable.push(cp, cps[i + 1]);
|
|
505
|
-
i += 2;
|
|
506
|
-
}
|
|
507
2108
|
let lastConsonantIdx = -1;
|
|
508
2109
|
while (i < cps.length) {
|
|
509
2110
|
const cc = cps[i];
|
|
510
|
-
const ct =
|
|
2111
|
+
const ct = sinhalaCharType(cc);
|
|
511
2112
|
if (ct === 0) {
|
|
512
2113
|
lastConsonantIdx = syllable.length;
|
|
513
2114
|
syllable.push(cc);
|
|
514
2115
|
i++;
|
|
515
|
-
if (i < cps.length && cps[i] ===
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
2116
|
+
if (i < cps.length && cps[i] === VIRAMA2) {
|
|
2117
|
+
let j = i + 1;
|
|
2118
|
+
let zwnj = false;
|
|
2119
|
+
let zwj = false;
|
|
2120
|
+
if (j < cps.length) {
|
|
2121
|
+
const jc = classifyUseCategory(cps[j]);
|
|
2122
|
+
if (jc === "ZWJ") {
|
|
2123
|
+
j++;
|
|
2124
|
+
zwj = true;
|
|
2125
|
+
} else if (jc === "ZWNJ") {
|
|
2126
|
+
j++;
|
|
2127
|
+
zwnj = true;
|
|
2128
|
+
}
|
|
2129
|
+
}
|
|
2130
|
+
if (!zwnj && j < cps.length && isConsonant4(cps[j])) {
|
|
521
2131
|
syllable.push(cps[i]);
|
|
522
|
-
|
|
2132
|
+
if (zwj) syllable.push(8205);
|
|
2133
|
+
i = j;
|
|
523
2134
|
continue;
|
|
524
2135
|
} else {
|
|
525
2136
|
syllable.push(cps[i]);
|
|
526
|
-
i
|
|
2137
|
+
i = j;
|
|
527
2138
|
break;
|
|
528
2139
|
}
|
|
529
2140
|
}
|
|
@@ -532,20 +2143,17 @@ function buildBengaliClusters(str) {
|
|
|
532
2143
|
break;
|
|
533
2144
|
}
|
|
534
2145
|
}
|
|
535
|
-
baseIdx = lastConsonantIdx >= 0 ? lastConsonantIdx : 0;
|
|
2146
|
+
const baseIdx = lastConsonantIdx >= 0 ? lastConsonantIdx : 0;
|
|
536
2147
|
while (i < cps.length) {
|
|
537
|
-
const ct =
|
|
2148
|
+
const ct = sinhalaCharType(cps[i]);
|
|
538
2149
|
if (ct >= 2 && ct <= 5) {
|
|
539
|
-
if (ct === 4) {
|
|
540
|
-
preMatras.push(syllable.length);
|
|
541
|
-
}
|
|
542
2150
|
syllable.push(cps[i]);
|
|
543
2151
|
i++;
|
|
544
2152
|
} else {
|
|
545
2153
|
break;
|
|
546
2154
|
}
|
|
547
2155
|
}
|
|
548
|
-
while (i < cps.length &&
|
|
2156
|
+
while (i < cps.length && sinhalaCharType(cps[i]) === 6) {
|
|
549
2157
|
syllable.push(cps[i]);
|
|
550
2158
|
i++;
|
|
551
2159
|
}
|
|
@@ -553,28 +2161,375 @@ function buildBengaliClusters(str) {
|
|
|
553
2161
|
syllable.push(cps[i] ?? 32);
|
|
554
2162
|
i++;
|
|
555
2163
|
}
|
|
556
|
-
clusters.push({
|
|
557
|
-
codepoints: syllable,
|
|
558
|
-
baseIndex: hasReph ? baseIdx + 2 : baseIdx,
|
|
559
|
-
// Adjust for reph prefix
|
|
560
|
-
hasReph,
|
|
561
|
-
preBaseMatras: preMatras
|
|
562
|
-
});
|
|
2164
|
+
clusters.push({ codepoints: syllable, baseIndex: baseIdx });
|
|
563
2165
|
}
|
|
564
2166
|
return clusters;
|
|
565
2167
|
}
|
|
566
|
-
function
|
|
567
|
-
const { cmap,
|
|
2168
|
+
function shapeSinhalaText(str, fontData) {
|
|
2169
|
+
const { cmap, ligatures, markAnchors, widths, defaultWidth } = fontData;
|
|
2170
|
+
const shaped = [];
|
|
2171
|
+
function resolveGid(cp) {
|
|
2172
|
+
const normCp = cp === 8239 || cp === 160 ? 32 : cp;
|
|
2173
|
+
return cmap[normCp] || 0;
|
|
2174
|
+
}
|
|
2175
|
+
function tryLig(gids) {
|
|
2176
|
+
return tryLigature(gids, ligatures);
|
|
2177
|
+
}
|
|
2178
|
+
function getAdv(gid) {
|
|
2179
|
+
return widths[gid] !== void 0 ? widths[gid] : defaultWidth;
|
|
2180
|
+
}
|
|
2181
|
+
function getBaseAnchor2(baseGid, markClass) {
|
|
2182
|
+
const base = markAnchors && markAnchors.bases && markAnchors.bases[baseGid];
|
|
2183
|
+
if (!base) return null;
|
|
2184
|
+
return base[markClass] ?? null;
|
|
2185
|
+
}
|
|
2186
|
+
function getMarkAnchor2(markGid) {
|
|
2187
|
+
const mark = markAnchors && markAnchors.marks && markAnchors.marks[markGid];
|
|
2188
|
+
if (!mark) return null;
|
|
2189
|
+
return { classIdx: mark[0], x: mark[1], y: mark[2] };
|
|
2190
|
+
}
|
|
2191
|
+
function emitGlyph(gid, isZero, baseGid) {
|
|
2192
|
+
if (isZero && baseGid !== void 0) {
|
|
2193
|
+
const markAnchor = getMarkAnchor2(gid);
|
|
2194
|
+
if (markAnchor) {
|
|
2195
|
+
const baseAnchorPt = getBaseAnchor2(baseGid, markAnchor.classIdx);
|
|
2196
|
+
if (baseAnchorPt) {
|
|
2197
|
+
const baseAdv = getAdv(baseGid);
|
|
2198
|
+
shaped.push({
|
|
2199
|
+
gid,
|
|
2200
|
+
dx: baseAnchorPt[0] - markAnchor.x - baseAdv,
|
|
2201
|
+
dy: baseAnchorPt[1] - markAnchor.y,
|
|
2202
|
+
isZeroAdvance: true
|
|
2203
|
+
});
|
|
2204
|
+
return;
|
|
2205
|
+
}
|
|
2206
|
+
}
|
|
2207
|
+
shaped.push({ gid, dx: 0, dy: 0, isZeroAdvance: true });
|
|
2208
|
+
} else {
|
|
2209
|
+
shaped.push({ gid, dx: 0, dy: 0, isZeroAdvance: false });
|
|
2210
|
+
}
|
|
2211
|
+
}
|
|
2212
|
+
const clusters = buildSinhalaClusters(str);
|
|
2213
|
+
for (const cluster of clusters) {
|
|
2214
|
+
const { codepoints } = cluster;
|
|
2215
|
+
let baseGid = 0;
|
|
2216
|
+
for (let ci = 0; ci < codepoints.length; ci++) {
|
|
2217
|
+
const ct = sinhalaCharType(codepoints[ci]);
|
|
2218
|
+
if (ct === 0) {
|
|
2219
|
+
baseGid = resolveGid(codepoints[ci]);
|
|
2220
|
+
} else if (ct >= 2 && ct !== 7) {
|
|
2221
|
+
break;
|
|
2222
|
+
}
|
|
2223
|
+
}
|
|
2224
|
+
for (let ci = 0; ci < codepoints.length; ci++) {
|
|
2225
|
+
if (sinhalaCharType(codepoints[ci]) === 4) {
|
|
2226
|
+
emitGlyph(resolveGid(codepoints[ci]), false);
|
|
2227
|
+
}
|
|
2228
|
+
}
|
|
2229
|
+
const clusterGids = [];
|
|
2230
|
+
const clusterEndIdx = [];
|
|
2231
|
+
let matraStart = codepoints.length;
|
|
2232
|
+
for (let ci = 0; ci < codepoints.length; ci++) {
|
|
2233
|
+
const cp = codepoints[ci];
|
|
2234
|
+
const ct = sinhalaCharType(cp);
|
|
2235
|
+
if (ct === 0 || ct === 7 || cp === 8205) {
|
|
2236
|
+
clusterGids.push(resolveGid(cp));
|
|
2237
|
+
clusterEndIdx.push(ci);
|
|
2238
|
+
} else if (ct >= 2 && ct <= 6) {
|
|
2239
|
+
matraStart = ci;
|
|
2240
|
+
break;
|
|
2241
|
+
} else if (ct < 0 || ct === 1 || ct === 9) {
|
|
2242
|
+
emitGlyph(resolveGid(cp), false);
|
|
2243
|
+
}
|
|
2244
|
+
}
|
|
2245
|
+
const ligResult = tryLig(clusterGids);
|
|
2246
|
+
if (ligResult) {
|
|
2247
|
+
emitGlyph(ligResult.resultGid, false);
|
|
2248
|
+
baseGid = ligResult.resultGid;
|
|
2249
|
+
let gi = ligResult.consumed;
|
|
2250
|
+
while (gi < clusterGids.length) {
|
|
2251
|
+
const subSeq = clusterGids.slice(gi);
|
|
2252
|
+
const subLig = tryLig(subSeq);
|
|
2253
|
+
if (subLig) {
|
|
2254
|
+
emitGlyph(subLig.resultGid, false);
|
|
2255
|
+
gi += subLig.consumed;
|
|
2256
|
+
} else {
|
|
2257
|
+
const origCi = clusterEndIdx[gi];
|
|
2258
|
+
const ct = sinhalaCharType(codepoints[origCi]);
|
|
2259
|
+
if (ct === 7) emitGlyph(clusterGids[gi], true, baseGid);
|
|
2260
|
+
else emitGlyph(clusterGids[gi], false);
|
|
2261
|
+
gi++;
|
|
2262
|
+
}
|
|
2263
|
+
}
|
|
2264
|
+
} else {
|
|
2265
|
+
for (let ci = 0; ci < matraStart; ci++) {
|
|
2266
|
+
const cp = codepoints[ci];
|
|
2267
|
+
const ct = sinhalaCharType(cp);
|
|
2268
|
+
if (ct === 0) emitGlyph(resolveGid(cp), false);
|
|
2269
|
+
else if (ct === 7) emitGlyph(resolveGid(cp), true, baseGid);
|
|
2270
|
+
else ;
|
|
2271
|
+
}
|
|
2272
|
+
}
|
|
2273
|
+
for (let ci = matraStart; ci < codepoints.length; ci++) {
|
|
2274
|
+
const cp = codepoints[ci];
|
|
2275
|
+
const ct = sinhalaCharType(cp);
|
|
2276
|
+
if (ct === 2 || ct === 3 || ct === 6) emitGlyph(resolveGid(cp), true, baseGid);
|
|
2277
|
+
else if (ct === 5) emitGlyph(resolveGid(cp), false);
|
|
2278
|
+
else if (ct === 4) ; else emitGlyph(resolveGid(cp), false);
|
|
2279
|
+
}
|
|
2280
|
+
}
|
|
2281
|
+
return shaped;
|
|
2282
|
+
}
|
|
2283
|
+
|
|
2284
|
+
// src/shaping/tibetan-shaper.ts
|
|
2285
|
+
function tibetanCharType(cp) {
|
|
2286
|
+
if (cp >= 3984 && cp <= 4028) return 8;
|
|
2287
|
+
if (cp === 3972) return 6;
|
|
2288
|
+
if (cp >= 3904 && cp <= 3948) return 0;
|
|
2289
|
+
if (cp === 3954 || cp === 3962 || cp === 3963 || cp === 3964 || cp === 3965 || cp === 3968 || cp === 3969 || cp === 3971 || cp === 3970 || cp === 3966) return 2;
|
|
2290
|
+
if (cp === 3953 || cp === 3956 || cp === 3955 || cp === 3957 || cp === 3864 || cp === 3865 || cp === 3893 || cp === 3895 || cp === 3897) return 3;
|
|
2291
|
+
if (cp === 3967) return 5;
|
|
2292
|
+
if (cp === 3840 || cp >= 3976 && cp <= 3980) return 1;
|
|
2293
|
+
if (cp >= 3872 && cp <= 3891) return 9;
|
|
2294
|
+
if (cp >= 3841 && cp <= 3863) return 9;
|
|
2295
|
+
if (cp >= 3898 && cp <= 3903) return 9;
|
|
2296
|
+
if (cp >= 4030 && cp <= 4047) return 9;
|
|
2297
|
+
if (cp === 3892 || cp === 3894 || cp === 3896) return 9;
|
|
2298
|
+
if (cp === 3973) return 9;
|
|
2299
|
+
return -1;
|
|
2300
|
+
}
|
|
2301
|
+
function buildTibetanStacks(str) {
|
|
2302
|
+
const stacks = [];
|
|
2303
|
+
const cps = [];
|
|
2304
|
+
for (let i2 = 0; i2 < str.length; ) {
|
|
2305
|
+
const cp = str.codePointAt(i2) ?? 0;
|
|
2306
|
+
cps.push(cp);
|
|
2307
|
+
i2 += cp > 65535 ? 2 : 1;
|
|
2308
|
+
}
|
|
2309
|
+
let i = 0;
|
|
2310
|
+
while (i < cps.length) {
|
|
2311
|
+
const cp = cps[i];
|
|
2312
|
+
const type = tibetanCharType(cp);
|
|
2313
|
+
if (type < 0 || cp < TIBETAN_START || cp > TIBETAN_END) {
|
|
2314
|
+
stacks.push({ codepoints: [cp] });
|
|
2315
|
+
i++;
|
|
2316
|
+
continue;
|
|
2317
|
+
}
|
|
2318
|
+
if (type !== 0 && type !== 1) {
|
|
2319
|
+
stacks.push({ codepoints: [cp] });
|
|
2320
|
+
i++;
|
|
2321
|
+
continue;
|
|
2322
|
+
}
|
|
2323
|
+
const stack = [cp];
|
|
2324
|
+
i++;
|
|
2325
|
+
while (i < cps.length && tibetanCharType(cps[i]) === 8) {
|
|
2326
|
+
stack.push(cps[i]);
|
|
2327
|
+
i++;
|
|
2328
|
+
}
|
|
2329
|
+
while (i < cps.length) {
|
|
2330
|
+
const ct = tibetanCharType(cps[i]);
|
|
2331
|
+
if (ct === 2 || ct === 3 || ct === 5 || ct === 6) {
|
|
2332
|
+
stack.push(cps[i]);
|
|
2333
|
+
i++;
|
|
2334
|
+
} else {
|
|
2335
|
+
break;
|
|
2336
|
+
}
|
|
2337
|
+
}
|
|
2338
|
+
stacks.push({ codepoints: stack });
|
|
2339
|
+
}
|
|
2340
|
+
return stacks;
|
|
2341
|
+
}
|
|
2342
|
+
function shapeTibetanText(str, fontData) {
|
|
2343
|
+
const { cmap, ligatures, markAnchors, widths, defaultWidth } = fontData;
|
|
2344
|
+
const shaped = [];
|
|
2345
|
+
function resolveGid(cp) {
|
|
2346
|
+
const normCp = cp === 8239 || cp === 160 ? 32 : cp;
|
|
2347
|
+
return cmap[normCp] || 0;
|
|
2348
|
+
}
|
|
2349
|
+
function tryLig(gids) {
|
|
2350
|
+
return tryLigature(gids, ligatures);
|
|
2351
|
+
}
|
|
2352
|
+
function getAdv(gid) {
|
|
2353
|
+
return widths[gid] !== void 0 ? widths[gid] : defaultWidth;
|
|
2354
|
+
}
|
|
2355
|
+
function getBaseAnchor2(baseGid, markClass) {
|
|
2356
|
+
const base = markAnchors && markAnchors.bases && markAnchors.bases[baseGid];
|
|
2357
|
+
if (!base) return null;
|
|
2358
|
+
return base[markClass] ?? null;
|
|
2359
|
+
}
|
|
2360
|
+
function getMarkAnchor2(markGid) {
|
|
2361
|
+
const mark = markAnchors && markAnchors.marks && markAnchors.marks[markGid];
|
|
2362
|
+
if (!mark) return null;
|
|
2363
|
+
return { classIdx: mark[0], x: mark[1], y: mark[2] };
|
|
2364
|
+
}
|
|
2365
|
+
function emitGlyph(gid, isZero, baseGid) {
|
|
2366
|
+
if (isZero && baseGid !== void 0) {
|
|
2367
|
+
const markAnchor = getMarkAnchor2(gid);
|
|
2368
|
+
if (markAnchor) {
|
|
2369
|
+
const baseAnchorPt = getBaseAnchor2(baseGid, markAnchor.classIdx);
|
|
2370
|
+
if (baseAnchorPt) {
|
|
2371
|
+
const baseAdv = getAdv(baseGid);
|
|
2372
|
+
shaped.push({
|
|
2373
|
+
gid,
|
|
2374
|
+
dx: baseAnchorPt[0] - markAnchor.x - baseAdv,
|
|
2375
|
+
dy: baseAnchorPt[1] - markAnchor.y,
|
|
2376
|
+
isZeroAdvance: true
|
|
2377
|
+
});
|
|
2378
|
+
return;
|
|
2379
|
+
}
|
|
2380
|
+
}
|
|
2381
|
+
shaped.push({ gid, dx: 0, dy: 0, isZeroAdvance: true });
|
|
2382
|
+
} else {
|
|
2383
|
+
shaped.push({ gid, dx: 0, dy: 0, isZeroAdvance: false });
|
|
2384
|
+
}
|
|
2385
|
+
}
|
|
2386
|
+
const stacks = buildTibetanStacks(str);
|
|
2387
|
+
for (const stack of stacks) {
|
|
2388
|
+
const { codepoints } = stack;
|
|
2389
|
+
const stackGids = [];
|
|
2390
|
+
const stackEndIdx = [];
|
|
2391
|
+
let markStart = codepoints.length;
|
|
2392
|
+
for (let ci = 0; ci < codepoints.length; ci++) {
|
|
2393
|
+
const ct = tibetanCharType(codepoints[ci]);
|
|
2394
|
+
if (ct === 0 || ct === 8 || ct === 1 || ct === 6) {
|
|
2395
|
+
stackGids.push(resolveGid(codepoints[ci]));
|
|
2396
|
+
stackEndIdx.push(ci);
|
|
2397
|
+
} else if (ct === 2 || ct === 3 || ct === 5) {
|
|
2398
|
+
markStart = ci;
|
|
2399
|
+
break;
|
|
2400
|
+
} else if (ct < 0 || ct === 9) {
|
|
2401
|
+
emitGlyph(resolveGid(codepoints[ci]), false);
|
|
2402
|
+
}
|
|
2403
|
+
}
|
|
2404
|
+
let baseGid = stackGids.length > 0 ? stackGids[0] : 0;
|
|
2405
|
+
const ligResult = tryLig(stackGids);
|
|
2406
|
+
if (ligResult) {
|
|
2407
|
+
emitGlyph(ligResult.resultGid, false);
|
|
2408
|
+
baseGid = ligResult.resultGid;
|
|
2409
|
+
let gi = ligResult.consumed;
|
|
2410
|
+
while (gi < stackGids.length) {
|
|
2411
|
+
const subSeq = stackGids.slice(gi);
|
|
2412
|
+
const subLig = tryLig(subSeq);
|
|
2413
|
+
if (subLig) {
|
|
2414
|
+
emitGlyph(subLig.resultGid, false);
|
|
2415
|
+
gi += subLig.consumed;
|
|
2416
|
+
} else {
|
|
2417
|
+
const origCi = stackEndIdx[gi];
|
|
2418
|
+
const ct = tibetanCharType(codepoints[origCi]);
|
|
2419
|
+
if (ct === 8 || ct === 6) emitGlyph(stackGids[gi], true, baseGid);
|
|
2420
|
+
else emitGlyph(stackGids[gi], false);
|
|
2421
|
+
gi++;
|
|
2422
|
+
}
|
|
2423
|
+
}
|
|
2424
|
+
} else {
|
|
2425
|
+
for (let gi = 0; gi < stackGids.length; gi++) {
|
|
2426
|
+
const origCi = stackEndIdx[gi];
|
|
2427
|
+
const ct = tibetanCharType(codepoints[origCi]);
|
|
2428
|
+
if (gi === 0) emitGlyph(stackGids[gi], false);
|
|
2429
|
+
else if (ct === 8 || ct === 6) emitGlyph(stackGids[gi], true, baseGid);
|
|
2430
|
+
else emitGlyph(stackGids[gi], false);
|
|
2431
|
+
}
|
|
2432
|
+
}
|
|
2433
|
+
for (let ci = markStart; ci < codepoints.length; ci++) {
|
|
2434
|
+
const cp = codepoints[ci];
|
|
2435
|
+
const ct = tibetanCharType(cp);
|
|
2436
|
+
if (ct === 2 || ct === 3) emitGlyph(resolveGid(cp), true, baseGid);
|
|
2437
|
+
else if (ct === 5) emitGlyph(resolveGid(cp), false);
|
|
2438
|
+
else emitGlyph(resolveGid(cp), false);
|
|
2439
|
+
}
|
|
2440
|
+
}
|
|
2441
|
+
return shaped;
|
|
2442
|
+
}
|
|
2443
|
+
|
|
2444
|
+
// src/shaping/khmer-shaper.ts
|
|
2445
|
+
var COENG = KHMER_COENG;
|
|
2446
|
+
var TWO_PART_VOWELS2 = {
|
|
2447
|
+
6078: [6081, 6070],
|
|
2448
|
+
// ើ
|
|
2449
|
+
6079: [6081, 6072],
|
|
2450
|
+
// ឿ (approx: e + y-like)
|
|
2451
|
+
6080: [6081, 6073],
|
|
2452
|
+
// ៀ (approx)
|
|
2453
|
+
6084: [6081, 6070],
|
|
2454
|
+
// ោ (e + aa)
|
|
2455
|
+
6085: [6081, 6070]
|
|
2456
|
+
// ៅ (e + aa-like)
|
|
2457
|
+
};
|
|
2458
|
+
function khmerCharType(cp) {
|
|
2459
|
+
if (cp === COENG) return 7;
|
|
2460
|
+
if (cp >= 6016 && cp <= 6050) return 0;
|
|
2461
|
+
if (cp >= 6051 && cp <= 6069) return 1;
|
|
2462
|
+
if (cp === 6081 || cp === 6082 || cp === 6083) return 4;
|
|
2463
|
+
if (cp === 6071 || cp === 6072 || cp === 6073 || cp === 6074 || cp === 6075 || cp === 6077) return 2;
|
|
2464
|
+
if (cp === 6076) return 3;
|
|
2465
|
+
if (cp === 6070 || cp === 6084 || cp === 6085) return 5;
|
|
2466
|
+
if (cp >= 6086 && cp <= 6097) return 6;
|
|
2467
|
+
if (cp === 6091 || cp === 6093 || cp === 6094 || cp === 6095 || cp === 6096) return 6;
|
|
2468
|
+
if (cp === 6109) return 6;
|
|
2469
|
+
if (cp >= 6112 && cp <= 6121) return 9;
|
|
2470
|
+
if (cp >= 6100 && cp <= 6108) return 9;
|
|
2471
|
+
if (cp >= 6128 && cp <= 6137) return 9;
|
|
2472
|
+
return -1;
|
|
2473
|
+
}
|
|
2474
|
+
function isConsonant5(cp) {
|
|
2475
|
+
return khmerCharType(cp) === 0;
|
|
2476
|
+
}
|
|
2477
|
+
function buildKhmerClusters(str) {
|
|
2478
|
+
const clusters = [];
|
|
2479
|
+
const cps = [];
|
|
2480
|
+
for (let i2 = 0; i2 < str.length; ) {
|
|
2481
|
+
const cp = str.codePointAt(i2) ?? 0;
|
|
2482
|
+
const decomp = TWO_PART_VOWELS2[cp];
|
|
2483
|
+
if (decomp) {
|
|
2484
|
+
for (const d of decomp) cps.push(d);
|
|
2485
|
+
} else cps.push(cp);
|
|
2486
|
+
i2 += cp > 65535 ? 2 : 1;
|
|
2487
|
+
}
|
|
2488
|
+
let i = 0;
|
|
2489
|
+
while (i < cps.length) {
|
|
2490
|
+
const cp = cps[i];
|
|
2491
|
+
const type = khmerCharType(cp);
|
|
2492
|
+
if (type < 0 || cp < KHMER_START || cp > KHMER_END) {
|
|
2493
|
+
clusters.push({ codepoints: [cp] });
|
|
2494
|
+
i++;
|
|
2495
|
+
continue;
|
|
2496
|
+
}
|
|
2497
|
+
if (type !== 0 && type !== 1) {
|
|
2498
|
+
clusters.push({ codepoints: [cp] });
|
|
2499
|
+
i++;
|
|
2500
|
+
continue;
|
|
2501
|
+
}
|
|
2502
|
+
const cluster = [cp];
|
|
2503
|
+
i++;
|
|
2504
|
+
while (i < cps.length && cps[i] === COENG) {
|
|
2505
|
+
if (i + 1 < cps.length && isConsonant5(cps[i + 1])) {
|
|
2506
|
+
cluster.push(cps[i]);
|
|
2507
|
+
cluster.push(cps[i + 1]);
|
|
2508
|
+
i += 2;
|
|
2509
|
+
} else {
|
|
2510
|
+
cluster.push(cps[i]);
|
|
2511
|
+
i++;
|
|
2512
|
+
break;
|
|
2513
|
+
}
|
|
2514
|
+
}
|
|
2515
|
+
while (i < cps.length) {
|
|
2516
|
+
const ct = khmerCharType(cps[i]);
|
|
2517
|
+
if (ct >= 2 && ct <= 6) {
|
|
2518
|
+
cluster.push(cps[i]);
|
|
2519
|
+
i++;
|
|
2520
|
+
} else break;
|
|
2521
|
+
}
|
|
2522
|
+
clusters.push({ codepoints: cluster });
|
|
2523
|
+
}
|
|
2524
|
+
return clusters;
|
|
2525
|
+
}
|
|
2526
|
+
function shapeKhmerText(str, fontData) {
|
|
2527
|
+
const { cmap, ligatures, markAnchors, widths, defaultWidth } = fontData;
|
|
568
2528
|
const shaped = [];
|
|
569
2529
|
function resolveGid(cp) {
|
|
570
2530
|
const normCp = cp === 8239 || cp === 160 ? 32 : cp;
|
|
571
2531
|
return cmap[normCp] || 0;
|
|
572
2532
|
}
|
|
573
|
-
function resolveGidGsub(cp) {
|
|
574
|
-
const gid = resolveGid(cp);
|
|
575
|
-
if (gsub[gid] !== void 0) return gsub[gid];
|
|
576
|
-
return gid;
|
|
577
|
-
}
|
|
578
2533
|
function tryLig(gids) {
|
|
579
2534
|
return tryLigature(gids, ligatures);
|
|
580
2535
|
}
|
|
@@ -612,143 +2567,100 @@ function shapeBengaliText(str, fontData) {
|
|
|
612
2567
|
shaped.push({ gid, dx: 0, dy: 0, isZeroAdvance: false });
|
|
613
2568
|
}
|
|
614
2569
|
}
|
|
615
|
-
const clusters =
|
|
2570
|
+
const clusters = buildKhmerClusters(str);
|
|
616
2571
|
for (const cluster of clusters) {
|
|
617
|
-
const { codepoints
|
|
618
|
-
const baseStart = hasReph ? 2 : 0;
|
|
2572
|
+
const { codepoints } = cluster;
|
|
619
2573
|
let baseGid = 0;
|
|
620
|
-
for (let ci =
|
|
621
|
-
const ct =
|
|
622
|
-
if (ct === 0) {
|
|
2574
|
+
for (let ci = 0; ci < codepoints.length; ci++) {
|
|
2575
|
+
const ct = khmerCharType(codepoints[ci]);
|
|
2576
|
+
if (ct === 0 || ct === 1) {
|
|
623
2577
|
baseGid = resolveGid(codepoints[ci]);
|
|
624
|
-
} else if (ct >= 2) {
|
|
625
2578
|
break;
|
|
626
2579
|
}
|
|
627
2580
|
}
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
if (mIdx < codepoints.length) {
|
|
631
|
-
const mCp = codepoints[mIdx];
|
|
632
|
-
if (mCp === 2507) {
|
|
633
|
-
emitGlyph(resolveGid(2503), false);
|
|
634
|
-
splitPostComponents.push(2494);
|
|
635
|
-
} else if (mCp === 2508) {
|
|
636
|
-
emitGlyph(resolveGid(2503), false);
|
|
637
|
-
splitPostComponents.push(2519);
|
|
638
|
-
} else {
|
|
639
|
-
emitGlyph(resolveGid(mCp), false);
|
|
640
|
-
}
|
|
641
|
-
}
|
|
2581
|
+
for (let ci = 0; ci < codepoints.length; ci++) {
|
|
2582
|
+
if (khmerCharType(codepoints[ci]) === 4) emitGlyph(resolveGid(codepoints[ci]), false);
|
|
642
2583
|
}
|
|
643
|
-
const
|
|
644
|
-
const
|
|
645
|
-
let
|
|
646
|
-
for (let ci =
|
|
647
|
-
const
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
matraStart = ci;
|
|
2584
|
+
const stackGids = [];
|
|
2585
|
+
const stackEndIdx = [];
|
|
2586
|
+
let markStart = codepoints.length;
|
|
2587
|
+
for (let ci = 0; ci < codepoints.length; ci++) {
|
|
2588
|
+
const cp = codepoints[ci];
|
|
2589
|
+
const ct = khmerCharType(cp);
|
|
2590
|
+
if (ct === 0 || ct === 1 || ct === 7) {
|
|
2591
|
+
stackGids.push(resolveGid(cp));
|
|
2592
|
+
stackEndIdx.push(ci);
|
|
2593
|
+
} else if (ct >= 2 && ct <= 6) {
|
|
2594
|
+
markStart = ci;
|
|
655
2595
|
break;
|
|
2596
|
+
} else {
|
|
2597
|
+
emitGlyph(resolveGid(cp), false);
|
|
656
2598
|
}
|
|
657
2599
|
}
|
|
658
|
-
|
|
659
|
-
const ligResult = tryLig(clusterGids);
|
|
2600
|
+
const ligResult = tryLig(stackGids);
|
|
660
2601
|
if (ligResult) {
|
|
661
2602
|
emitGlyph(ligResult.resultGid, false);
|
|
662
2603
|
baseGid = ligResult.resultGid;
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
const subSeq = clusterGids.slice(gi);
|
|
667
|
-
const subLig = tryLig(subSeq);
|
|
2604
|
+
let gi = ligResult.consumed;
|
|
2605
|
+
while (gi < stackGids.length) {
|
|
2606
|
+
const subLig = tryLig(stackGids.slice(gi));
|
|
668
2607
|
if (subLig) {
|
|
669
2608
|
emitGlyph(subLig.resultGid, false);
|
|
670
2609
|
gi += subLig.consumed;
|
|
671
2610
|
} else {
|
|
672
|
-
const origCi =
|
|
673
|
-
const ct =
|
|
674
|
-
if (ct === 7)
|
|
675
|
-
|
|
676
|
-
} else {
|
|
677
|
-
emitGlyph(clusterGids[gi], false);
|
|
678
|
-
}
|
|
2611
|
+
const origCi = stackEndIdx[gi];
|
|
2612
|
+
const ct = khmerCharType(codepoints[origCi]);
|
|
2613
|
+
if (ct === 7) emitGlyph(stackGids[gi], true, baseGid);
|
|
2614
|
+
else emitGlyph(stackGids[gi], false);
|
|
679
2615
|
gi++;
|
|
680
2616
|
}
|
|
681
2617
|
}
|
|
682
2618
|
} else {
|
|
683
|
-
for (let
|
|
684
|
-
const
|
|
685
|
-
const ct =
|
|
686
|
-
if (
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
emitGlyph(resolveGid(cp), true, baseGid);
|
|
690
|
-
} else if (ct === 8) {
|
|
691
|
-
emitGlyph(resolveGid(cp), true, baseGid);
|
|
692
|
-
}
|
|
2619
|
+
for (let gi = 0; gi < stackGids.length; gi++) {
|
|
2620
|
+
const origCi = stackEndIdx[gi];
|
|
2621
|
+
const ct = khmerCharType(codepoints[origCi]);
|
|
2622
|
+
if (gi === 0) emitGlyph(stackGids[gi], false);
|
|
2623
|
+
else if (ct === 7) emitGlyph(stackGids[gi], true, baseGid);
|
|
2624
|
+
else emitGlyph(stackGids[gi], true, baseGid);
|
|
693
2625
|
}
|
|
694
2626
|
}
|
|
695
|
-
for (let ci =
|
|
2627
|
+
for (let ci = markStart; ci < codepoints.length; ci++) {
|
|
696
2628
|
const cp = codepoints[ci];
|
|
697
|
-
const ct =
|
|
698
|
-
if (ct ===
|
|
699
|
-
if (ct ===
|
|
700
|
-
|
|
701
|
-
} else if (ct === 5) {
|
|
702
|
-
emitGlyph(resolveGid(cp), false);
|
|
703
|
-
} else if (ct === 6) {
|
|
704
|
-
emitGlyph(resolveGid(cp), true, baseGid);
|
|
705
|
-
} else if (ct === 9) {
|
|
706
|
-
emitGlyph(resolveGid(cp), false);
|
|
707
|
-
} else {
|
|
708
|
-
emitGlyph(resolveGid(cp), false);
|
|
709
|
-
}
|
|
710
|
-
}
|
|
711
|
-
for (const postCp of splitPostComponents) {
|
|
712
|
-
emitGlyph(resolveGid(postCp), false);
|
|
713
|
-
}
|
|
714
|
-
if (hasReph) {
|
|
715
|
-
const raGid = resolveGidGsub(RA);
|
|
716
|
-
const halantGid = resolveGid(HALANT);
|
|
717
|
-
emitGlyph(raGid, true, baseGid);
|
|
718
|
-
emitGlyph(halantGid, true, baseGid);
|
|
2629
|
+
const ct = khmerCharType(cp);
|
|
2630
|
+
if (ct === 2 || ct === 3 || ct === 6) emitGlyph(resolveGid(cp), true, baseGid);
|
|
2631
|
+
else if (ct === 5) emitGlyph(resolveGid(cp), false);
|
|
2632
|
+
else if (ct === 4) ; else emitGlyph(resolveGid(cp), false);
|
|
719
2633
|
}
|
|
720
2634
|
}
|
|
721
2635
|
return shaped;
|
|
722
2636
|
}
|
|
723
2637
|
|
|
724
|
-
// src/shaping/
|
|
725
|
-
var
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
if (cp ===
|
|
729
|
-
if (cp
|
|
730
|
-
if (cp
|
|
731
|
-
if (cp
|
|
732
|
-
if (cp ===
|
|
733
|
-
if (cp
|
|
734
|
-
if (cp ===
|
|
735
|
-
if (cp ===
|
|
736
|
-
if (cp ===
|
|
737
|
-
if (cp ===
|
|
738
|
-
if (cp ===
|
|
739
|
-
if (cp ===
|
|
740
|
-
if (cp
|
|
741
|
-
if (cp
|
|
742
|
-
if (cp
|
|
743
|
-
if (cp >=
|
|
744
|
-
if (cp >= 3056 && cp <= 3066) return 9;
|
|
745
|
-
if (cp === 3024) return 6;
|
|
2638
|
+
// src/shaping/myanmar-shaper.ts
|
|
2639
|
+
var VIRAMA3 = MYANMAR_VIRAMA;
|
|
2640
|
+
var MEDIAL_RA = 4156;
|
|
2641
|
+
function myanmarCharType(cp) {
|
|
2642
|
+
if (cp === VIRAMA3) return 7;
|
|
2643
|
+
if (cp === MEDIAL_RA) return 4;
|
|
2644
|
+
if (cp === 4155 || cp === 4157 || cp === 4158) return 8;
|
|
2645
|
+
if (cp >= 4096 && cp <= 4138) return 0;
|
|
2646
|
+
if (cp === 4159) return 0;
|
|
2647
|
+
if (cp >= 4176 && cp <= 4181) return 0;
|
|
2648
|
+
if (cp === 4141 || cp === 4142 || cp === 4146 || cp === 4150 || cp === 4147 || cp === 4148 || cp === 4149) return 2;
|
|
2649
|
+
if (cp === 4143 || cp === 4144 || cp === 4151) return 3;
|
|
2650
|
+
if (cp === 4139 || cp === 4140) return 5;
|
|
2651
|
+
if (cp === 4145) return 4;
|
|
2652
|
+
if (cp === 4154) return 6;
|
|
2653
|
+
if (cp === 4152) return 5;
|
|
2654
|
+
if (cp >= 4160 && cp <= 4169) return 9;
|
|
2655
|
+
if (cp >= 4240 && cp <= 4249) return 9;
|
|
2656
|
+
if (cp === 4170 || cp === 4171) return 9;
|
|
2657
|
+
if (cp >= 4172 && cp <= 4175) return 9;
|
|
746
2658
|
return -1;
|
|
747
2659
|
}
|
|
748
|
-
function
|
|
749
|
-
return
|
|
2660
|
+
function isConsonant6(cp) {
|
|
2661
|
+
return myanmarCharType(cp) === 0;
|
|
750
2662
|
}
|
|
751
|
-
function
|
|
2663
|
+
function buildMyanmarClusters(str) {
|
|
752
2664
|
const clusters = [];
|
|
753
2665
|
const cps = [];
|
|
754
2666
|
for (let i2 = 0; i2 < str.length; ) {
|
|
@@ -759,68 +2671,46 @@ function buildTamilClusters(str) {
|
|
|
759
2671
|
let i = 0;
|
|
760
2672
|
while (i < cps.length) {
|
|
761
2673
|
const cp = cps[i];
|
|
762
|
-
const type =
|
|
763
|
-
if (type < 0 || cp <
|
|
764
|
-
clusters.push({ codepoints: [cp]
|
|
2674
|
+
const type = myanmarCharType(cp);
|
|
2675
|
+
if (type < 0 || cp < MYANMAR_START || cp > MYANMAR_END) {
|
|
2676
|
+
clusters.push({ codepoints: [cp] });
|
|
765
2677
|
i++;
|
|
766
2678
|
continue;
|
|
767
2679
|
}
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
const cc = cps[i];
|
|
773
|
-
const ct = tamilCharType(cc);
|
|
774
|
-
if (ct === 0) {
|
|
775
|
-
lastConsonantIdx = syllable.length;
|
|
776
|
-
syllable.push(cc);
|
|
777
|
-
i++;
|
|
778
|
-
if (i < cps.length && cps[i] === PULLI) {
|
|
779
|
-
if (i + 1 < cps.length && isConsonant2(cps[i + 1])) {
|
|
780
|
-
syllable.push(cps[i]);
|
|
781
|
-
i++;
|
|
782
|
-
continue;
|
|
783
|
-
} else {
|
|
784
|
-
syllable.push(cps[i]);
|
|
785
|
-
i++;
|
|
786
|
-
break;
|
|
787
|
-
}
|
|
788
|
-
}
|
|
789
|
-
break;
|
|
790
|
-
} else {
|
|
791
|
-
break;
|
|
792
|
-
}
|
|
2680
|
+
if (type !== 0) {
|
|
2681
|
+
clusters.push({ codepoints: [cp] });
|
|
2682
|
+
i++;
|
|
2683
|
+
continue;
|
|
793
2684
|
}
|
|
794
|
-
const
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
if (
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
syllable.push(cps[i]);
|
|
802
|
-
i++;
|
|
2685
|
+
const cluster = [cp];
|
|
2686
|
+
i++;
|
|
2687
|
+
while (i < cps.length && cps[i] === VIRAMA3) {
|
|
2688
|
+
if (i + 1 < cps.length && isConsonant6(cps[i + 1])) {
|
|
2689
|
+
cluster.push(cps[i]);
|
|
2690
|
+
cluster.push(cps[i + 1]);
|
|
2691
|
+
i += 2;
|
|
803
2692
|
} else {
|
|
2693
|
+
cluster.push(cps[i]);
|
|
2694
|
+
i++;
|
|
804
2695
|
break;
|
|
805
2696
|
}
|
|
806
2697
|
}
|
|
807
|
-
while (i < cps.length &&
|
|
808
|
-
|
|
2698
|
+
while (i < cps.length && myanmarCharType(cps[i]) === 8) {
|
|
2699
|
+
cluster.push(cps[i]);
|
|
809
2700
|
i++;
|
|
810
2701
|
}
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
2702
|
+
while (i < cps.length) {
|
|
2703
|
+
const ct = myanmarCharType(cps[i]);
|
|
2704
|
+
if (ct >= 2 && ct <= 6) {
|
|
2705
|
+
cluster.push(cps[i]);
|
|
2706
|
+
i++;
|
|
2707
|
+
} else break;
|
|
814
2708
|
}
|
|
815
|
-
clusters.push({
|
|
816
|
-
codepoints: syllable,
|
|
817
|
-
baseIndex: baseIdx,
|
|
818
|
-
preBaseMatras: preMatras
|
|
819
|
-
});
|
|
2709
|
+
clusters.push({ codepoints: cluster });
|
|
820
2710
|
}
|
|
821
2711
|
return clusters;
|
|
822
2712
|
}
|
|
823
|
-
function
|
|
2713
|
+
function shapeMyanmarText(str, fontData) {
|
|
824
2714
|
const { cmap, ligatures, markAnchors, widths, defaultWidth } = fontData;
|
|
825
2715
|
const shaped = [];
|
|
826
2716
|
function resolveGid(cp) {
|
|
@@ -864,106 +2754,68 @@ function shapeTamilText(str, fontData) {
|
|
|
864
2754
|
shaped.push({ gid, dx: 0, dy: 0, isZeroAdvance: false });
|
|
865
2755
|
}
|
|
866
2756
|
}
|
|
867
|
-
const clusters =
|
|
2757
|
+
const clusters = buildMyanmarClusters(str);
|
|
868
2758
|
for (const cluster of clusters) {
|
|
869
|
-
const { codepoints
|
|
2759
|
+
const { codepoints } = cluster;
|
|
870
2760
|
let baseGid = 0;
|
|
871
2761
|
for (let ci = 0; ci < codepoints.length; ci++) {
|
|
872
|
-
|
|
873
|
-
if (ct === 0) {
|
|
2762
|
+
if (myanmarCharType(codepoints[ci]) === 0) {
|
|
874
2763
|
baseGid = resolveGid(codepoints[ci]);
|
|
875
|
-
} else if (ct >= 2) {
|
|
876
2764
|
break;
|
|
877
2765
|
}
|
|
878
2766
|
}
|
|
879
|
-
const splitPostComponents = [];
|
|
880
|
-
for (const mIdx of preBaseMatras) {
|
|
881
|
-
if (mIdx < codepoints.length) {
|
|
882
|
-
const mCp = codepoints[mIdx];
|
|
883
|
-
if (mCp === 3018) {
|
|
884
|
-
emitGlyph(resolveGid(3014), false);
|
|
885
|
-
splitPostComponents.push(3006);
|
|
886
|
-
} else if (mCp === 3019) {
|
|
887
|
-
emitGlyph(resolveGid(3015), false);
|
|
888
|
-
splitPostComponents.push(3006);
|
|
889
|
-
} else if (mCp === 3020) {
|
|
890
|
-
emitGlyph(resolveGid(3014), false);
|
|
891
|
-
splitPostComponents.push(3031);
|
|
892
|
-
} else {
|
|
893
|
-
emitGlyph(resolveGid(mCp), false);
|
|
894
|
-
}
|
|
895
|
-
}
|
|
896
|
-
}
|
|
897
|
-
const clusterGids = [];
|
|
898
|
-
const clusterEndIdx = [];
|
|
899
|
-
let matraStart = codepoints.length;
|
|
900
2767
|
for (let ci = 0; ci < codepoints.length; ci++) {
|
|
901
|
-
|
|
902
|
-
if (ct === 0 || ct === 7) {
|
|
903
|
-
clusterGids.push(resolveGid(codepoints[ci]));
|
|
904
|
-
clusterEndIdx.push(ci);
|
|
905
|
-
} else if (ct >= 2) {
|
|
906
|
-
matraStart = ci;
|
|
907
|
-
break;
|
|
908
|
-
} else if (ct < 0) {
|
|
909
|
-
emitGlyph(resolveGid(codepoints[ci]), false);
|
|
910
|
-
} else if (ct === 1) {
|
|
911
|
-
emitGlyph(resolveGid(codepoints[ci]), false);
|
|
912
|
-
}
|
|
913
|
-
}
|
|
914
|
-
let ligConsumed = 0;
|
|
915
|
-
const ligResult = tryLig(clusterGids);
|
|
916
|
-
if (ligResult) {
|
|
917
|
-
emitGlyph(ligResult.resultGid, false);
|
|
918
|
-
baseGid = ligResult.resultGid;
|
|
919
|
-
ligConsumed = ligResult.consumed;
|
|
920
|
-
let gi = ligConsumed;
|
|
921
|
-
while (gi < clusterGids.length) {
|
|
922
|
-
const subSeq = clusterGids.slice(gi);
|
|
923
|
-
const subLig = tryLig(subSeq);
|
|
924
|
-
if (subLig) {
|
|
925
|
-
emitGlyph(subLig.resultGid, false);
|
|
926
|
-
gi += subLig.consumed;
|
|
927
|
-
} else {
|
|
928
|
-
const origCi = clusterEndIdx[gi];
|
|
929
|
-
const ct = tamilCharType(codepoints[origCi]);
|
|
930
|
-
if (ct === 7) {
|
|
931
|
-
emitGlyph(clusterGids[gi], true, baseGid);
|
|
932
|
-
} else {
|
|
933
|
-
emitGlyph(clusterGids[gi], false);
|
|
934
|
-
}
|
|
935
|
-
gi++;
|
|
936
|
-
}
|
|
937
|
-
}
|
|
938
|
-
} else {
|
|
939
|
-
for (let ci = 0; ci < matraStart; ci++) {
|
|
940
|
-
const cp = codepoints[ci];
|
|
941
|
-
const ct = tamilCharType(cp);
|
|
942
|
-
if (ct === 0) {
|
|
943
|
-
emitGlyph(resolveGid(cp), false);
|
|
944
|
-
} else if (ct === 7) {
|
|
945
|
-
emitGlyph(resolveGid(cp), true, baseGid);
|
|
946
|
-
}
|
|
947
|
-
}
|
|
2768
|
+
if (myanmarCharType(codepoints[ci]) === 4) emitGlyph(resolveGid(codepoints[ci]), false);
|
|
948
2769
|
}
|
|
949
|
-
|
|
2770
|
+
const stackGids = [];
|
|
2771
|
+
const stackEndIdx = [];
|
|
2772
|
+
let markStart = codepoints.length;
|
|
2773
|
+
for (let ci = 0; ci < codepoints.length; ci++) {
|
|
950
2774
|
const cp = codepoints[ci];
|
|
951
|
-
const ct =
|
|
952
|
-
if (ct ===
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
} else if (ct === 5) {
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
} else if (ct === 9) {
|
|
960
|
-
emitGlyph(resolveGid(cp), false);
|
|
961
|
-
} else {
|
|
2775
|
+
const ct = myanmarCharType(cp);
|
|
2776
|
+
if (ct === 0 || ct === 7 || ct === 8) {
|
|
2777
|
+
stackGids.push(resolveGid(cp));
|
|
2778
|
+
stackEndIdx.push(ci);
|
|
2779
|
+
} else if (ct === 2 || ct === 3 || ct === 5 || ct === 6) {
|
|
2780
|
+
markStart = ci;
|
|
2781
|
+
break;
|
|
2782
|
+
} else if (ct === 4) ; else {
|
|
962
2783
|
emitGlyph(resolveGid(cp), false);
|
|
963
2784
|
}
|
|
964
2785
|
}
|
|
965
|
-
|
|
966
|
-
|
|
2786
|
+
const ligResult = tryLig(stackGids);
|
|
2787
|
+
if (ligResult) {
|
|
2788
|
+
emitGlyph(ligResult.resultGid, false);
|
|
2789
|
+
baseGid = ligResult.resultGid;
|
|
2790
|
+
let gi = ligResult.consumed;
|
|
2791
|
+
while (gi < stackGids.length) {
|
|
2792
|
+
const subLig = tryLig(stackGids.slice(gi));
|
|
2793
|
+
if (subLig) {
|
|
2794
|
+
emitGlyph(subLig.resultGid, false);
|
|
2795
|
+
gi += subLig.consumed;
|
|
2796
|
+
} else {
|
|
2797
|
+
const origCi = stackEndIdx[gi];
|
|
2798
|
+
const ct = myanmarCharType(codepoints[origCi]);
|
|
2799
|
+
if (ct === 7 || ct === 8) emitGlyph(stackGids[gi], true, baseGid);
|
|
2800
|
+
else emitGlyph(stackGids[gi], false);
|
|
2801
|
+
gi++;
|
|
2802
|
+
}
|
|
2803
|
+
}
|
|
2804
|
+
} else {
|
|
2805
|
+
for (let gi = 0; gi < stackGids.length; gi++) {
|
|
2806
|
+
const origCi = stackEndIdx[gi];
|
|
2807
|
+
const ct = myanmarCharType(codepoints[origCi]);
|
|
2808
|
+
if (gi === 0) emitGlyph(stackGids[gi], false);
|
|
2809
|
+
else if (ct === 7 || ct === 8) emitGlyph(stackGids[gi], true, baseGid);
|
|
2810
|
+
else emitGlyph(stackGids[gi], false);
|
|
2811
|
+
}
|
|
2812
|
+
}
|
|
2813
|
+
for (let ci = markStart; ci < codepoints.length; ci++) {
|
|
2814
|
+
const cp = codepoints[ci];
|
|
2815
|
+
const ct = myanmarCharType(cp);
|
|
2816
|
+
if (ct === 2 || ct === 3 || ct === 6) emitGlyph(resolveGid(cp), true, baseGid);
|
|
2817
|
+
else if (ct === 5) emitGlyph(resolveGid(cp), false);
|
|
2818
|
+
else if (ct === 4) ; else emitGlyph(resolveGid(cp), false);
|
|
967
2819
|
}
|
|
968
2820
|
}
|
|
969
2821
|
return shaped;
|
|
@@ -1015,7 +2867,7 @@ function devanagariCharType(cp) {
|
|
|
1015
2867
|
if (cp === 2384) return 1;
|
|
1016
2868
|
return -1;
|
|
1017
2869
|
}
|
|
1018
|
-
function
|
|
2870
|
+
function isConsonant7(cp) {
|
|
1019
2871
|
return devanagariCharType(cp) === 0;
|
|
1020
2872
|
}
|
|
1021
2873
|
function buildDevanagariClusters(str) {
|
|
@@ -1030,6 +2882,10 @@ function buildDevanagariClusters(str) {
|
|
|
1030
2882
|
while (i < cps.length) {
|
|
1031
2883
|
const cp = cps[i];
|
|
1032
2884
|
const type = devanagariCharType(cp);
|
|
2885
|
+
if (classifyUseCategory(cp) === "ZWJ" || classifyUseCategory(cp) === "ZWNJ") {
|
|
2886
|
+
i++;
|
|
2887
|
+
continue;
|
|
2888
|
+
}
|
|
1033
2889
|
if (type < 0 || cp < DEVANAGARI_START || cp > DEVANAGARI_END) {
|
|
1034
2890
|
clusters.push({ codepoints: [cp], baseIndex: 0, hasReph: false, preBaseMatras: [] });
|
|
1035
2891
|
i++;
|
|
@@ -1038,7 +2894,7 @@ function buildDevanagariClusters(str) {
|
|
|
1038
2894
|
const syllable = [];
|
|
1039
2895
|
let hasReph = false;
|
|
1040
2896
|
const preMatras = [];
|
|
1041
|
-
if (
|
|
2897
|
+
if (isConsonant7(cp) && cp === RA2 && i + 2 < cps.length && cps[i + 1] === HALANT2 && isConsonant7(cps[i + 2])) {
|
|
1042
2898
|
hasReph = true;
|
|
1043
2899
|
syllable.push(cp, cps[i + 1]);
|
|
1044
2900
|
i += 2;
|
|
@@ -1056,13 +2912,24 @@ function buildDevanagariClusters(str) {
|
|
|
1056
2912
|
i++;
|
|
1057
2913
|
}
|
|
1058
2914
|
if (i < cps.length && cps[i] === HALANT2) {
|
|
1059
|
-
|
|
2915
|
+
let j = i + 1;
|
|
2916
|
+
let zwnj = false;
|
|
2917
|
+
if (j < cps.length) {
|
|
2918
|
+
const jc = classifyUseCategory(cps[j]);
|
|
2919
|
+
if (jc === "ZWJ") {
|
|
2920
|
+
j++;
|
|
2921
|
+
} else if (jc === "ZWNJ") {
|
|
2922
|
+
j++;
|
|
2923
|
+
zwnj = true;
|
|
2924
|
+
}
|
|
2925
|
+
}
|
|
2926
|
+
if (!zwnj && j < cps.length && isConsonant7(cps[j])) {
|
|
1060
2927
|
syllable.push(cps[i]);
|
|
1061
|
-
i
|
|
2928
|
+
i = j;
|
|
1062
2929
|
continue;
|
|
1063
2930
|
} else {
|
|
1064
2931
|
syllable.push(cps[i]);
|
|
1065
|
-
i
|
|
2932
|
+
i = j;
|
|
1066
2933
|
break;
|
|
1067
2934
|
}
|
|
1068
2935
|
}
|
|
@@ -1310,533 +3177,514 @@ var ARABIC_PRES_FORMS = /* @__PURE__ */ new Map([
|
|
|
1310
3177
|
[1609, { isol: 65263, fina: 65264 }],
|
|
1311
3178
|
[1610, { isol: 65265, fina: 65266, init: 65267, medi: 65268 }]
|
|
1312
3179
|
]);
|
|
1313
|
-
var LAM_ALEF_PRES = /* @__PURE__ */ new Map([
|
|
1314
|
-
[1570, [65269, 65270]],
|
|
1315
|
-
// LAM + ALEF WITH MADDA ABOVE
|
|
1316
|
-
[1571, [65271, 65272]],
|
|
1317
|
-
// LAM + ALEF WITH HAMZA ABOVE
|
|
1318
|
-
[1573, [65273, 65274]],
|
|
1319
|
-
// LAM + ALEF WITH HAMZA BELOW
|
|
1320
|
-
[1575, [65275, 65276]]
|
|
1321
|
-
// LAM + ALEF
|
|
1322
|
-
]);
|
|
1323
|
-
function resolvePositionalForms(codePoints) {
|
|
1324
|
-
const len = codePoints.length;
|
|
1325
|
-
const forms = new Array(len).fill("isol");
|
|
1326
|
-
const joining = codePoints.map(getJoiningType);
|
|
1327
|
-
for (let i = 0; i < len; i++) {
|
|
1328
|
-
if (joining[i] === "T" || joining[i] === "U") continue;
|
|
1329
|
-
let prevJoin = "U";
|
|
1330
|
-
for (let j = i - 1; j >= 0; j--) {
|
|
1331
|
-
if (joining[j] !== "T") {
|
|
1332
|
-
prevJoin = joining[j];
|
|
1333
|
-
break;
|
|
1334
|
-
}
|
|
1335
|
-
}
|
|
1336
|
-
let nextJoin = "U";
|
|
1337
|
-
for (let j = i + 1; j < len; j++) {
|
|
1338
|
-
if (joining[j] !== "T") {
|
|
1339
|
-
nextJoin = joining[j];
|
|
1340
|
-
break;
|
|
1341
|
-
}
|
|
1342
|
-
}
|
|
1343
|
-
const joinsToPrev = prevJoin === "D" || prevJoin === "C";
|
|
1344
|
-
const joinsToNext = (nextJoin === "D" || nextJoin === "R" || nextJoin === "C") && (joining[i] === "D" || joining[i] === "C");
|
|
1345
|
-
if (joinsToPrev && joinsToNext) {
|
|
1346
|
-
forms[i] = "medi";
|
|
1347
|
-
} else if (joinsToPrev) {
|
|
1348
|
-
forms[i] = "fina";
|
|
1349
|
-
} else if (joinsToNext) {
|
|
1350
|
-
forms[i] = "init";
|
|
1351
|
-
} else {
|
|
1352
|
-
forms[i] = "isol";
|
|
1353
|
-
}
|
|
1354
|
-
}
|
|
1355
|
-
return forms;
|
|
1356
|
-
}
|
|
1357
|
-
var LAM = 1604;
|
|
1358
|
-
var ALEF_VARIANTS = /* @__PURE__ */ new Set([1570, 1571, 1573, 1575]);
|
|
1359
|
-
function isLamAlef(cp1, cp2) {
|
|
1360
|
-
return cp1 === LAM && ALEF_VARIANTS.has(cp2);
|
|
1361
|
-
}
|
|
1362
|
-
function shapeArabicText(str, fontData) {
|
|
1363
|
-
if (!str) return [];
|
|
1364
|
-
const codePoints = [];
|
|
1365
|
-
for (let i = 0; i < str.length; ) {
|
|
1366
|
-
const cp = str.codePointAt(i) ?? 0;
|
|
1367
|
-
codePoints.push(cp);
|
|
1368
|
-
i += cp > 65535 ? 2 : 1;
|
|
1369
|
-
}
|
|
1370
|
-
const forms = resolvePositionalForms(codePoints);
|
|
1371
|
-
const glyphs = [];
|
|
1372
|
-
const cmap = fontData.cmap;
|
|
1373
|
-
const widths = fontData.widths;
|
|
1374
|
-
const defaultWidth = fontData.defaultWidth;
|
|
1375
|
-
const markAnchors = fontData.markAnchors;
|
|
1376
|
-
let lastBaseGid = 0;
|
|
1377
|
-
for (let i = 0; i < codePoints.length; i++) {
|
|
1378
|
-
const cp = codePoints[i];
|
|
1379
|
-
if (i < codePoints.length - 1 && isLamAlef(cp, codePoints[i + 1])) {
|
|
1380
|
-
const ligForms = LAM_ALEF_PRES.get(codePoints[i + 1]);
|
|
1381
|
-
if (ligForms) {
|
|
1382
|
-
const isFinal = forms[i] === "medi" || forms[i] === "fina";
|
|
1383
|
-
const ligCP = isFinal ? ligForms[1] : ligForms[0];
|
|
1384
|
-
const ligGid = cmap[ligCP];
|
|
1385
|
-
if (ligGid) {
|
|
1386
|
-
glyphs.push({ gid: ligGid, dx: 0, dy: 0, isZeroAdvance: false });
|
|
1387
|
-
lastBaseGid = ligGid;
|
|
1388
|
-
i++;
|
|
1389
|
-
continue;
|
|
1390
|
-
}
|
|
1391
|
-
}
|
|
1392
|
-
}
|
|
1393
|
-
let gid = cmap[cp] ?? 0;
|
|
1394
|
-
const presForm = ARABIC_PRES_FORMS.get(cp);
|
|
1395
|
-
if (presForm) {
|
|
1396
|
-
const form = forms[i];
|
|
1397
|
-
let presCP;
|
|
1398
|
-
if (form === "init") presCP = presForm.init;
|
|
1399
|
-
else if (form === "medi") presCP = presForm.medi;
|
|
1400
|
-
else if (form === "fina") presCP = presForm.fina;
|
|
1401
|
-
else presCP = presForm.isol;
|
|
1402
|
-
if (presCP) {
|
|
1403
|
-
const presGid = cmap[presCP];
|
|
1404
|
-
if (presGid) gid = presGid;
|
|
1405
|
-
}
|
|
1406
|
-
}
|
|
1407
|
-
const joining = getJoiningType(cp);
|
|
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
|
-
}
|
|
1417
|
-
glyphs.push({ gid, dx: 0, dy: 0, isZeroAdvance });
|
|
1418
|
-
if (!isZeroAdvance) lastBaseGid = gid;
|
|
1419
|
-
}
|
|
1420
|
-
return glyphs;
|
|
1421
|
-
}
|
|
1422
|
-
|
|
1423
|
-
// src/shaping/bidi.ts
|
|
1424
|
-
function classifyBidiType(cp) {
|
|
1425
|
-
if (cp >= 768 && cp <= 879 || // Combining Diacritical Marks
|
|
1426
|
-
cp >= 1425 && cp <= 1469 || // Hebrew marks
|
|
1427
|
-
cp >= 1471 && cp <= 1471 || cp >= 1473 && cp <= 1474 || cp >= 1476 && cp <= 1477 || cp >= 1479 && cp <= 1479 || cp >= 1552 && cp <= 1562 || // Arabic marks
|
|
1428
|
-
cp >= 1611 && cp <= 1631 || // Arabic harakat
|
|
1429
|
-
cp >= 1648 && cp <= 1648 || // Arabic superscript alef
|
|
1430
|
-
cp >= 1750 && cp <= 1756 || cp >= 1759 && cp <= 1764 || cp >= 1767 && cp <= 1768 || cp >= 1770 && cp <= 1773 || cp >= 65056 && cp <= 65071) return "NSM";
|
|
1431
|
-
if (cp === 8203 || cp === 8204 || cp === 8205 || cp === 8206 || cp === 8207 || cp === 65279 || cp === 8294 || cp === 8295 || cp === 8296 || cp === 8297) return "BN";
|
|
1432
|
-
if (cp >= 1632 && cp <= 1641) return "AN";
|
|
1433
|
-
if (cp >= 1776 && cp <= 1785) return "AN";
|
|
1434
|
-
if (cp >= 48 && cp <= 57) return "EN";
|
|
1435
|
-
if (cp === 43 || cp === 45) return "ES";
|
|
1436
|
-
if (cp === 35 || cp === 36 || cp === 37 || cp === 162 || cp === 163 || cp === 164 || cp === 165 || cp === 8364 || cp === 8377 || cp === 8378) return "ET";
|
|
1437
|
-
if (cp === 44 || cp === 46 || cp === 47 || cp === 58 || cp === 160) return "CS";
|
|
1438
|
-
if (cp === 32 || cp === 9 || cp === 10 || cp === 13 || cp === 12 || cp === 8192 || cp === 8202 || cp === 8232 || cp === 8233 || cp === 8239 || cp === 8287 || cp === 12288) return "WS";
|
|
1439
|
-
if (cp >= 1536 && cp <= 1791) return "AL";
|
|
1440
|
-
if (cp >= 1872 && cp <= 1919) return "AL";
|
|
1441
|
-
if (cp >= 2208 && cp <= 2303) return "AL";
|
|
1442
|
-
if (cp >= 64336 && cp <= 65023) return "AL";
|
|
1443
|
-
if (cp >= 65136 && cp <= 65278) return "AL";
|
|
1444
|
-
if (cp >= 1488 && cp <= 1514) return "R";
|
|
1445
|
-
if (cp >= 1520 && cp <= 1524) return "R";
|
|
1446
|
-
if (cp >= 1792 && cp <= 1871) return "R";
|
|
1447
|
-
if (cp >= 1920 && cp <= 1983) return "R";
|
|
1448
|
-
if (cp >= 64285 && cp <= 64335) return "R";
|
|
1449
|
-
if (cp >= 65 && cp <= 90) return "L";
|
|
1450
|
-
if (cp >= 97 && cp <= 122) return "L";
|
|
1451
|
-
if (cp >= 192 && cp <= 591) return "L";
|
|
1452
|
-
if (cp >= 880 && cp <= 1023) return "L";
|
|
1453
|
-
if (cp >= 1024 && cp <= 1279) return "L";
|
|
1454
|
-
if (cp >= 3584 && cp <= 3711) return "L";
|
|
1455
|
-
if (cp >= 2304 && cp <= 2431) return "L";
|
|
1456
|
-
if (cp >= 12352 && cp <= 12543) return "L";
|
|
1457
|
-
if (cp >= 19968 && cp <= 40959) return "L";
|
|
1458
|
-
if (cp >= 44032 && cp <= 55215) return "L";
|
|
1459
|
-
if (cp >= 33 && cp <= 47) return "ON";
|
|
1460
|
-
if (cp >= 58 && cp <= 64) return "ON";
|
|
1461
|
-
if (cp >= 91 && cp <= 96) return "ON";
|
|
1462
|
-
if (cp >= 123 && cp <= 126) return "ON";
|
|
1463
|
-
if (cp >= 161 && cp <= 191) return "ON";
|
|
1464
|
-
if (cp >= 8208 && cp <= 8231) return "ON";
|
|
1465
|
-
if (cp >= 8240 && cp <= 8286) return "ON";
|
|
1466
|
-
return "L";
|
|
1467
|
-
}
|
|
1468
|
-
function detectParagraphLevel(types) {
|
|
1469
|
-
for (const t of types) {
|
|
1470
|
-
if (t === "L") return 0;
|
|
1471
|
-
if (t === "R" || t === "AL") return 1;
|
|
1472
|
-
}
|
|
1473
|
-
return 0;
|
|
1474
|
-
}
|
|
1475
|
-
function resolveWeakTypes(types, paraLevel) {
|
|
1476
|
-
const len = types.length;
|
|
1477
|
-
let prevType = paraLevel === 0 ? "L" : "R";
|
|
1478
|
-
for (let i = 0; i < len; i++) {
|
|
1479
|
-
if (types[i] === "BN") continue;
|
|
1480
|
-
if (types[i] === "NSM") {
|
|
1481
|
-
types[i] = prevType;
|
|
1482
|
-
}
|
|
1483
|
-
prevType = types[i];
|
|
1484
|
-
}
|
|
1485
|
-
let lastStrong = paraLevel === 0 ? "L" : "R";
|
|
1486
|
-
for (let i = 0; i < len; i++) {
|
|
1487
|
-
if (types[i] === "BN") continue;
|
|
1488
|
-
if (types[i] === "R" || types[i] === "L" || types[i] === "AL") {
|
|
1489
|
-
lastStrong = types[i];
|
|
1490
|
-
} else if (types[i] === "EN" && lastStrong === "AL") {
|
|
1491
|
-
types[i] = "AN";
|
|
1492
|
-
}
|
|
1493
|
-
}
|
|
1494
|
-
for (let i = 0; i < len; i++) {
|
|
1495
|
-
if (types[i] === "AL") types[i] = "R";
|
|
1496
|
-
}
|
|
1497
|
-
for (let i = 1; i < len - 1; i++) {
|
|
1498
|
-
if (types[i] === "BN") continue;
|
|
1499
|
-
if (types[i] === "ES" && types[i - 1] === "EN" && types[i + 1] === "EN") {
|
|
1500
|
-
types[i] = "EN";
|
|
1501
|
-
} else if (types[i] === "CS") {
|
|
1502
|
-
if (types[i - 1] === "EN" && types[i + 1] === "EN") types[i] = "EN";
|
|
1503
|
-
else if (types[i - 1] === "AN" && types[i + 1] === "AN") types[i] = "AN";
|
|
1504
|
-
}
|
|
1505
|
-
}
|
|
1506
|
-
for (let i = 0; i < len; i++) {
|
|
1507
|
-
if (types[i] === "ET") {
|
|
1508
|
-
let found = false;
|
|
1509
|
-
for (let j = i - 1; j >= 0; j--) {
|
|
1510
|
-
if (types[j] === "EN") {
|
|
1511
|
-
found = true;
|
|
1512
|
-
break;
|
|
1513
|
-
}
|
|
1514
|
-
if (types[j] !== "ET" && types[j] !== "BN") break;
|
|
1515
|
-
}
|
|
1516
|
-
if (!found) {
|
|
1517
|
-
for (let j = i + 1; j < len; j++) {
|
|
1518
|
-
if (types[j] === "EN") {
|
|
1519
|
-
found = true;
|
|
1520
|
-
break;
|
|
1521
|
-
}
|
|
1522
|
-
if (types[j] !== "ET" && types[j] !== "BN") break;
|
|
1523
|
-
}
|
|
1524
|
-
}
|
|
1525
|
-
if (found) types[i] = "EN";
|
|
1526
|
-
}
|
|
1527
|
-
}
|
|
1528
|
-
for (let i = 0; i < len; i++) {
|
|
1529
|
-
if (types[i] === "ES" || types[i] === "ET" || types[i] === "CS") {
|
|
1530
|
-
types[i] = "ON";
|
|
1531
|
-
}
|
|
1532
|
-
}
|
|
1533
|
-
lastStrong = paraLevel === 0 ? "L" : "R";
|
|
1534
|
-
for (let i = 0; i < len; i++) {
|
|
1535
|
-
if (types[i] === "BN") continue;
|
|
1536
|
-
if (types[i] === "L" || types[i] === "R") {
|
|
1537
|
-
lastStrong = types[i];
|
|
1538
|
-
} else if (types[i] === "EN" && lastStrong === "L") {
|
|
1539
|
-
types[i] = "L";
|
|
1540
|
-
}
|
|
1541
|
-
}
|
|
1542
|
-
}
|
|
1543
|
-
function resolveNeutralTypes(types, paraLevel) {
|
|
1544
|
-
const len = types.length;
|
|
1545
|
-
const paraDir = paraLevel === 0 ? "L" : "R";
|
|
3180
|
+
var LAM_ALEF_PRES = /* @__PURE__ */ new Map([
|
|
3181
|
+
[1570, [65269, 65270]],
|
|
3182
|
+
// LAM + ALEF WITH MADDA ABOVE
|
|
3183
|
+
[1571, [65271, 65272]],
|
|
3184
|
+
// LAM + ALEF WITH HAMZA ABOVE
|
|
3185
|
+
[1573, [65273, 65274]],
|
|
3186
|
+
// LAM + ALEF WITH HAMZA BELOW
|
|
3187
|
+
[1575, [65275, 65276]]
|
|
3188
|
+
// LAM + ALEF
|
|
3189
|
+
]);
|
|
3190
|
+
function resolvePositionalForms(codePoints) {
|
|
3191
|
+
const len = codePoints.length;
|
|
3192
|
+
const forms = new Array(len).fill("isol");
|
|
3193
|
+
const joining = codePoints.map(getJoiningType);
|
|
1546
3194
|
for (let i = 0; i < len; i++) {
|
|
1547
|
-
if (
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
for (let j = start - 1; j >= 0; j--) {
|
|
1553
|
-
if (types[j] === "L" || types[j] === "R" || types[j] === "EN" || types[j] === "AN") {
|
|
1554
|
-
prevStrong = types[j] === "EN" || types[j] === "AN" ? "R" : types[j];
|
|
3195
|
+
if (joining[i] === "T" || joining[i] === "U") continue;
|
|
3196
|
+
let prevJoin = "U";
|
|
3197
|
+
for (let j = i - 1; j >= 0; j--) {
|
|
3198
|
+
if (joining[j] !== "T") {
|
|
3199
|
+
prevJoin = joining[j];
|
|
1555
3200
|
break;
|
|
1556
3201
|
}
|
|
1557
3202
|
}
|
|
1558
|
-
let
|
|
1559
|
-
for (let j =
|
|
1560
|
-
if (
|
|
1561
|
-
|
|
3203
|
+
let nextJoin = "U";
|
|
3204
|
+
for (let j = i + 1; j < len; j++) {
|
|
3205
|
+
if (joining[j] !== "T") {
|
|
3206
|
+
nextJoin = joining[j];
|
|
1562
3207
|
break;
|
|
1563
3208
|
}
|
|
1564
3209
|
}
|
|
1565
|
-
const
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
for (const t of types) {
|
|
1574
|
-
if (paraLevel === 0) {
|
|
1575
|
-
levels.push(t === "R" || t === "AN" ? 1 : 0);
|
|
3210
|
+
const joinsToPrev = prevJoin === "D" || prevJoin === "C";
|
|
3211
|
+
const joinsToNext = (nextJoin === "D" || nextJoin === "R" || nextJoin === "C") && (joining[i] === "D" || joining[i] === "C");
|
|
3212
|
+
if (joinsToPrev && joinsToNext) {
|
|
3213
|
+
forms[i] = "medi";
|
|
3214
|
+
} else if (joinsToPrev) {
|
|
3215
|
+
forms[i] = "fina";
|
|
3216
|
+
} else if (joinsToNext) {
|
|
3217
|
+
forms[i] = "init";
|
|
1576
3218
|
} else {
|
|
1577
|
-
|
|
3219
|
+
forms[i] = "isol";
|
|
1578
3220
|
}
|
|
1579
3221
|
}
|
|
1580
|
-
return
|
|
3222
|
+
return forms;
|
|
1581
3223
|
}
|
|
1582
|
-
var
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
// ,
|
|
1587
|
-
59,
|
|
1588
|
-
// ;
|
|
1589
|
-
58,
|
|
1590
|
-
// :
|
|
1591
|
-
33,
|
|
1592
|
-
// !
|
|
1593
|
-
63
|
|
1594
|
-
// ?
|
|
1595
|
-
]);
|
|
1596
|
-
function fixPunctuationAffinity(types, codePoints, len) {
|
|
1597
|
-
for (let i = 1; i < len; i++) {
|
|
1598
|
-
if (types[i] === "R" && SENTENCE_PUNCT.has(codePoints[i])) {
|
|
1599
|
-
let prevIdx = i - 1;
|
|
1600
|
-
while (prevIdx >= 0 && (types[prevIdx] === "WS" || types[prevIdx] === "BN")) prevIdx--;
|
|
1601
|
-
if (prevIdx >= 0 && types[prevIdx] === "L") {
|
|
1602
|
-
types[i] = "L";
|
|
1603
|
-
}
|
|
1604
|
-
}
|
|
1605
|
-
}
|
|
3224
|
+
var LAM = 1604;
|
|
3225
|
+
var ALEF_VARIANTS = /* @__PURE__ */ new Set([1570, 1571, 1573, 1575]);
|
|
3226
|
+
function isLamAlef(cp1, cp2) {
|
|
3227
|
+
return cp1 === LAM && ALEF_VARIANTS.has(cp2);
|
|
1606
3228
|
}
|
|
1607
|
-
function
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
3229
|
+
function shapeArabicText(str, fontData) {
|
|
3230
|
+
if (!str) return [];
|
|
3231
|
+
const codePoints = [];
|
|
3232
|
+
for (let i = 0; i < str.length; ) {
|
|
3233
|
+
const cp = str.codePointAt(i) ?? 0;
|
|
3234
|
+
codePoints.push(cp);
|
|
3235
|
+
i += cp > 65535 ? 2 : 1;
|
|
3236
|
+
}
|
|
3237
|
+
const forms = resolvePositionalForms(codePoints);
|
|
3238
|
+
const glyphs = [];
|
|
3239
|
+
const cmap = fontData.cmap;
|
|
3240
|
+
const widths = fontData.widths;
|
|
3241
|
+
const defaultWidth = fontData.defaultWidth;
|
|
3242
|
+
const markAnchors = fontData.markAnchors;
|
|
3243
|
+
let lastBaseGid = 0;
|
|
3244
|
+
for (let i = 0; i < codePoints.length; i++) {
|
|
3245
|
+
const cp = codePoints[i];
|
|
3246
|
+
if (i < codePoints.length - 1 && isLamAlef(cp, codePoints[i + 1])) {
|
|
3247
|
+
const ligForms = LAM_ALEF_PRES.get(codePoints[i + 1]);
|
|
3248
|
+
if (ligForms) {
|
|
3249
|
+
const isFinal = forms[i] === "medi" || forms[i] === "fina";
|
|
3250
|
+
const ligCP = isFinal ? ligForms[1] : ligForms[0];
|
|
3251
|
+
const ligGid = cmap[ligCP];
|
|
3252
|
+
if (ligGid) {
|
|
3253
|
+
glyphs.push({ gid: ligGid, dx: 0, dy: 0, isZeroAdvance: false });
|
|
3254
|
+
lastBaseGid = ligGid;
|
|
3255
|
+
i++;
|
|
3256
|
+
continue;
|
|
1628
3257
|
}
|
|
1629
3258
|
}
|
|
1630
3259
|
}
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
3260
|
+
let gid = cmap[cp] ?? 0;
|
|
3261
|
+
const presForm = ARABIC_PRES_FORMS.get(cp);
|
|
3262
|
+
if (presForm) {
|
|
3263
|
+
const form = forms[i];
|
|
3264
|
+
let presCP;
|
|
3265
|
+
if (form === "init") presCP = presForm.init;
|
|
3266
|
+
else if (form === "medi") presCP = presForm.medi;
|
|
3267
|
+
else if (form === "fina") presCP = presForm.fina;
|
|
3268
|
+
else presCP = presForm.isol;
|
|
3269
|
+
if (presCP) {
|
|
3270
|
+
const presGid = cmap[presCP];
|
|
3271
|
+
if (presGid) gid = presGid;
|
|
1637
3272
|
}
|
|
1638
3273
|
}
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
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++;
|
|
3274
|
+
const joining = getJoiningType(cp);
|
|
3275
|
+
const isZeroAdvance = joining === "T";
|
|
3276
|
+
if (isZeroAdvance && lastBaseGid !== 0) {
|
|
3277
|
+
const baseAdv = widths[lastBaseGid] !== void 0 ? widths[lastBaseGid] : defaultWidth;
|
|
3278
|
+
const offset = positionMarkOnBase(markAnchors, gid, lastBaseGid, baseAdv);
|
|
3279
|
+
if (offset) {
|
|
3280
|
+
glyphs.push({ gid, dx: offset.dx, dy: offset.dy, isZeroAdvance: true });
|
|
1666
3281
|
continue;
|
|
1667
3282
|
}
|
|
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
3283
|
}
|
|
3284
|
+
glyphs.push({ gid, dx: 0, dy: 0, isZeroAdvance });
|
|
3285
|
+
if (!isZeroAdvance) lastBaseGid = gid;
|
|
1674
3286
|
}
|
|
1675
|
-
return
|
|
3287
|
+
return glyphs;
|
|
1676
3288
|
}
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
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;
|
|
3289
|
+
|
|
3290
|
+
// src/fonts/font-loader.ts
|
|
3291
|
+
var _fontBinaryCache = /* @__PURE__ */ new WeakMap();
|
|
3292
|
+
function getDecodedFontBytes(fontData) {
|
|
3293
|
+
const cached = _fontBinaryCache.get(fontData);
|
|
3294
|
+
if (cached) return cached;
|
|
3295
|
+
let bytes;
|
|
3296
|
+
if (typeof atob === "function") {
|
|
3297
|
+
const binaryStr = atob(fontData.ttfBase64);
|
|
3298
|
+
bytes = new Uint8Array(binaryStr.length);
|
|
3299
|
+
for (let i = 0; i < binaryStr.length; i++) bytes[i] = binaryStr.charCodeAt(i);
|
|
3300
|
+
} else {
|
|
3301
|
+
const buf = globalThis["Buffer"];
|
|
3302
|
+
bytes = buf.from(fontData.ttfBase64, "base64");
|
|
1731
3303
|
}
|
|
1732
|
-
|
|
1733
|
-
return
|
|
3304
|
+
_fontBinaryCache.set(fontData, bytes);
|
|
3305
|
+
return bytes;
|
|
1734
3306
|
}
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
const
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
const
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
3307
|
+
|
|
3308
|
+
// src/fonts/glyf-outline.ts
|
|
3309
|
+
function readTableDirectory(view) {
|
|
3310
|
+
const numTables = view.getUint16(4);
|
|
3311
|
+
const tables = {};
|
|
3312
|
+
for (let i = 0; i < numTables; i++) {
|
|
3313
|
+
const rec = 12 + i * 16;
|
|
3314
|
+
const tag = String.fromCharCode(
|
|
3315
|
+
view.getUint8(rec),
|
|
3316
|
+
view.getUint8(rec + 1),
|
|
3317
|
+
view.getUint8(rec + 2),
|
|
3318
|
+
view.getUint8(rec + 3)
|
|
3319
|
+
);
|
|
3320
|
+
tables[tag] = { offset: view.getUint32(rec + 8), length: view.getUint32(rec + 12) };
|
|
1749
3321
|
}
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
3322
|
+
return tables;
|
|
3323
|
+
}
|
|
3324
|
+
function parseGlyfFont(bytes) {
|
|
3325
|
+
if (bytes.length < 12) return null;
|
|
3326
|
+
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
3327
|
+
const tables = readTableDirectory(view);
|
|
3328
|
+
const head = tables["head"];
|
|
3329
|
+
const maxp = tables["maxp"];
|
|
3330
|
+
const loca = tables["loca"];
|
|
3331
|
+
const glyf = tables["glyf"];
|
|
3332
|
+
if (!head || !maxp || !loca || !glyf) return null;
|
|
3333
|
+
const unitsPerEm = view.getUint16(head.offset + 18) || 1e3;
|
|
3334
|
+
const locaFormat = view.getInt16(head.offset + 50);
|
|
3335
|
+
const numGlyphs = view.getUint16(maxp.offset + 4);
|
|
3336
|
+
const locaOffsets = new Array(numGlyphs + 1);
|
|
3337
|
+
for (let i = 0; i <= numGlyphs; i++) {
|
|
3338
|
+
locaOffsets[i] = locaFormat === 0 ? view.getUint16(loca.offset + i * 2) * 2 : view.getUint32(loca.offset + i * 4);
|
|
3339
|
+
}
|
|
3340
|
+
return { view, glyfOffset: glyf.offset, locaOffsets, unitsPerEm };
|
|
3341
|
+
}
|
|
3342
|
+
function transformPoint(p, a, b, c, d, e, f) {
|
|
3343
|
+
return {
|
|
3344
|
+
x: a * p.x + c * p.y + e,
|
|
3345
|
+
y: b * p.x + d * p.y + f,
|
|
3346
|
+
onCurve: p.onCurve
|
|
1761
3347
|
};
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
3348
|
+
}
|
|
3349
|
+
function readF2Dot14(view, pos) {
|
|
3350
|
+
return view.getInt16(pos) / 16384;
|
|
3351
|
+
}
|
|
3352
|
+
function extractGlyphContours(font, gid, depth = 0) {
|
|
3353
|
+
if (depth > 8) return [];
|
|
3354
|
+
const { view, glyfOffset, locaOffsets } = font;
|
|
3355
|
+
if (gid < 0 || gid + 1 >= locaOffsets.length) return [];
|
|
3356
|
+
const start = locaOffsets[gid];
|
|
3357
|
+
const end = locaOffsets[gid + 1];
|
|
3358
|
+
if (end <= start) return [];
|
|
3359
|
+
const base = glyfOffset + start;
|
|
3360
|
+
const numberOfContours = view.getInt16(base);
|
|
3361
|
+
if (numberOfContours < 0) {
|
|
3362
|
+
return extractCompositeContours(font, base, depth);
|
|
3363
|
+
}
|
|
3364
|
+
return extractSimpleContours(view, base, numberOfContours);
|
|
3365
|
+
}
|
|
3366
|
+
function extractSimpleContours(view, base, numberOfContours) {
|
|
3367
|
+
let pos = base + 10;
|
|
3368
|
+
const endPts = new Array(numberOfContours);
|
|
3369
|
+
for (let i = 0; i < numberOfContours; i++) {
|
|
3370
|
+
endPts[i] = view.getUint16(pos);
|
|
3371
|
+
pos += 2;
|
|
3372
|
+
}
|
|
3373
|
+
const numPoints = numberOfContours > 0 ? endPts[numberOfContours - 1] + 1 : 0;
|
|
3374
|
+
const instrLen = view.getUint16(pos);
|
|
3375
|
+
pos += 2 + instrLen;
|
|
3376
|
+
const flags = new Array(numPoints);
|
|
3377
|
+
for (let i = 0; i < numPoints; ) {
|
|
3378
|
+
const flag = view.getUint8(pos++);
|
|
3379
|
+
flags[i++] = flag;
|
|
3380
|
+
if (flag & 8) {
|
|
3381
|
+
let repeat = view.getUint8(pos++);
|
|
3382
|
+
while (repeat-- > 0 && i < numPoints) flags[i++] = flag;
|
|
3383
|
+
}
|
|
3384
|
+
}
|
|
3385
|
+
const xs = new Array(numPoints);
|
|
3386
|
+
let x = 0;
|
|
3387
|
+
for (let i = 0; i < numPoints; i++) {
|
|
3388
|
+
const flag = flags[i];
|
|
3389
|
+
if (flag & 2) {
|
|
3390
|
+
const dx = view.getUint8(pos++);
|
|
3391
|
+
x += flag & 16 ? dx : -dx;
|
|
3392
|
+
} else if (!(flag & 16)) {
|
|
3393
|
+
x += view.getInt16(pos);
|
|
3394
|
+
pos += 2;
|
|
3395
|
+
}
|
|
3396
|
+
xs[i] = x;
|
|
3397
|
+
}
|
|
3398
|
+
const ys = new Array(numPoints);
|
|
3399
|
+
let y = 0;
|
|
3400
|
+
for (let i = 0; i < numPoints; i++) {
|
|
3401
|
+
const flag = flags[i];
|
|
3402
|
+
if (flag & 4) {
|
|
3403
|
+
const dy = view.getUint8(pos++);
|
|
3404
|
+
y += flag & 32 ? dy : -dy;
|
|
3405
|
+
} else if (!(flag & 32)) {
|
|
3406
|
+
y += view.getInt16(pos);
|
|
3407
|
+
pos += 2;
|
|
3408
|
+
}
|
|
3409
|
+
ys[i] = y;
|
|
3410
|
+
}
|
|
3411
|
+
const contours = [];
|
|
3412
|
+
let startPt = 0;
|
|
3413
|
+
for (let c = 0; c < numberOfContours; c++) {
|
|
3414
|
+
const endPt = endPts[c];
|
|
3415
|
+
const contour = [];
|
|
3416
|
+
for (let i = startPt; i <= endPt; i++) {
|
|
3417
|
+
contour.push({ x: xs[i], y: ys[i], onCurve: (flags[i] & 1) !== 0 });
|
|
3418
|
+
}
|
|
3419
|
+
if (contour.length > 0) contours.push(contour);
|
|
3420
|
+
startPt = endPt + 1;
|
|
3421
|
+
}
|
|
3422
|
+
return contours;
|
|
3423
|
+
}
|
|
3424
|
+
function extractCompositeContours(font, base, depth) {
|
|
3425
|
+
const { view } = font;
|
|
3426
|
+
let pos = base + 10;
|
|
3427
|
+
const out = [];
|
|
3428
|
+
while (true) {
|
|
3429
|
+
const flags = view.getUint16(pos);
|
|
3430
|
+
pos += 2;
|
|
3431
|
+
const componentGid = view.getUint16(pos);
|
|
3432
|
+
pos += 2;
|
|
3433
|
+
let arg1;
|
|
3434
|
+
let arg2;
|
|
3435
|
+
if (flags & 1) {
|
|
3436
|
+
arg1 = view.getInt16(pos);
|
|
3437
|
+
pos += 2;
|
|
3438
|
+
arg2 = view.getInt16(pos);
|
|
3439
|
+
pos += 2;
|
|
3440
|
+
} else {
|
|
3441
|
+
arg1 = view.getInt8(pos);
|
|
3442
|
+
pos += 1;
|
|
3443
|
+
arg2 = view.getInt8(pos);
|
|
3444
|
+
pos += 1;
|
|
3445
|
+
}
|
|
3446
|
+
let a = 1, b = 0, c = 0, d = 1;
|
|
3447
|
+
if (flags & 8) {
|
|
3448
|
+
a = d = readF2Dot14(view, pos);
|
|
3449
|
+
pos += 2;
|
|
3450
|
+
} else if (flags & 64) {
|
|
3451
|
+
a = readF2Dot14(view, pos);
|
|
3452
|
+
pos += 2;
|
|
3453
|
+
d = readF2Dot14(view, pos);
|
|
3454
|
+
pos += 2;
|
|
3455
|
+
} else if (flags & 128) {
|
|
3456
|
+
a = readF2Dot14(view, pos);
|
|
3457
|
+
pos += 2;
|
|
3458
|
+
b = readF2Dot14(view, pos);
|
|
3459
|
+
pos += 2;
|
|
3460
|
+
c = readF2Dot14(view, pos);
|
|
3461
|
+
pos += 2;
|
|
3462
|
+
d = readF2Dot14(view, pos);
|
|
3463
|
+
pos += 2;
|
|
3464
|
+
}
|
|
3465
|
+
const e = flags & 2 ? arg1 : 0;
|
|
3466
|
+
const f = flags & 2 ? arg2 : 0;
|
|
3467
|
+
const sub = extractGlyphContours(font, componentGid, depth + 1);
|
|
3468
|
+
for (const contour of sub) {
|
|
3469
|
+
out.push(contour.map((p) => transformPoint(p, a, b, c, d, e, f)));
|
|
3470
|
+
}
|
|
3471
|
+
if (!(flags & 32)) break;
|
|
1783
3472
|
}
|
|
1784
|
-
emit(cursor, codePoints.length, forcedLevel);
|
|
1785
3473
|
return out;
|
|
1786
3474
|
}
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
3475
|
+
|
|
3476
|
+
// src/core/pdf-color-glyph.ts
|
|
3477
|
+
var ID = [1, 0, 0, 1, 0, 0];
|
|
3478
|
+
function n(v) {
|
|
3479
|
+
if (!Number.isFinite(v)) return "0";
|
|
3480
|
+
if (Number.isInteger(v)) return String(v);
|
|
3481
|
+
let s = v.toFixed(3);
|
|
3482
|
+
s = s.replace(/0+$/, "").replace(/\.$/, "");
|
|
3483
|
+
return s === "-0" ? "0" : s;
|
|
3484
|
+
}
|
|
3485
|
+
function tx(m, x, y) {
|
|
3486
|
+
return [m[0] * x + m[2] * y + m[4], m[1] * x + m[3] * y + m[5]];
|
|
3487
|
+
}
|
|
3488
|
+
function ch(v) {
|
|
3489
|
+
return n(Math.max(0, Math.min(1, v / 255)));
|
|
3490
|
+
}
|
|
3491
|
+
function contoursToPath(contours, m = ID) {
|
|
3492
|
+
const ops = [];
|
|
3493
|
+
for (const contour of contours) {
|
|
3494
|
+
if (contour.length === 0) continue;
|
|
3495
|
+
const pts = contour.slice();
|
|
3496
|
+
let startIdx = pts.findIndex((p) => p.onCurve);
|
|
3497
|
+
let start;
|
|
3498
|
+
if (startIdx < 0) {
|
|
3499
|
+
const a = pts[0], b = pts[pts.length - 1];
|
|
3500
|
+
start = [(a.x + b.x) / 2, (a.y + b.y) / 2];
|
|
3501
|
+
startIdx = 0;
|
|
3502
|
+
} else {
|
|
3503
|
+
start = [pts[startIdx].x, pts[startIdx].y];
|
|
3504
|
+
}
|
|
3505
|
+
const [sx, sy] = tx(m, start[0], start[1]);
|
|
3506
|
+
ops.push(`${n(sx)} ${n(sy)} m`);
|
|
3507
|
+
const len = pts.length;
|
|
3508
|
+
let curX = start[0], curY = start[1];
|
|
3509
|
+
let i = 1;
|
|
3510
|
+
while (i <= len) {
|
|
3511
|
+
const p = pts[(startIdx + i) % len];
|
|
3512
|
+
if (p.onCurve) {
|
|
3513
|
+
const [px, py] = tx(m, p.x, p.y);
|
|
3514
|
+
ops.push(`${n(px)} ${n(py)} l`);
|
|
3515
|
+
curX = p.x;
|
|
3516
|
+
curY = p.y;
|
|
3517
|
+
i++;
|
|
3518
|
+
} else {
|
|
3519
|
+
const next = pts[(startIdx + i + 1) % len];
|
|
3520
|
+
let endX, endY;
|
|
3521
|
+
let consumed;
|
|
3522
|
+
if (next.onCurve) {
|
|
3523
|
+
endX = next.x;
|
|
3524
|
+
endY = next.y;
|
|
3525
|
+
consumed = 2;
|
|
3526
|
+
} else {
|
|
3527
|
+
endX = (p.x + next.x) / 2;
|
|
3528
|
+
endY = (p.y + next.y) / 2;
|
|
3529
|
+
consumed = 1;
|
|
3530
|
+
}
|
|
3531
|
+
const c1x = curX + 2 / 3 * (p.x - curX);
|
|
3532
|
+
const c1y = curY + 2 / 3 * (p.y - curY);
|
|
3533
|
+
const c2x = endX + 2 / 3 * (p.x - endX);
|
|
3534
|
+
const c2y = endY + 2 / 3 * (p.y - endY);
|
|
3535
|
+
const [a1, b1] = tx(m, c1x, c1y);
|
|
3536
|
+
const [a2, b2] = tx(m, c2x, c2y);
|
|
3537
|
+
const [ex, ey] = tx(m, endX, endY);
|
|
3538
|
+
ops.push(`${n(a1)} ${n(b1)} ${n(a2)} ${n(b2)} ${n(ex)} ${n(ey)} c`);
|
|
3539
|
+
curX = endX;
|
|
3540
|
+
curY = endY;
|
|
3541
|
+
i += consumed;
|
|
1814
3542
|
}
|
|
1815
3543
|
}
|
|
3544
|
+
ops.push("h");
|
|
1816
3545
|
}
|
|
1817
|
-
|
|
1818
|
-
runs.reverse();
|
|
1819
|
-
}
|
|
1820
|
-
return runs;
|
|
3546
|
+
return ops.join("\n");
|
|
1821
3547
|
}
|
|
1822
|
-
function
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
3548
|
+
function buildGradientFunction(stops) {
|
|
3549
|
+
const sorted = stops.slice().sort((a, b) => a.offset - b.offset);
|
|
3550
|
+
if (sorted.length === 0) return "<< /FunctionType 2 /Domain [0 1] /C0 [0 0 0] /C1 [0 0 0] /N 1 >>";
|
|
3551
|
+
if (sorted.length === 1) {
|
|
3552
|
+
const c = sorted[0].color;
|
|
3553
|
+
return `<< /FunctionType 2 /Domain [0 1] /C0 [${ch(c[0])} ${ch(c[1])} ${ch(c[2])}] /C1 [${ch(c[0])} ${ch(c[1])} ${ch(c[2])}] /N 1 >>`;
|
|
3554
|
+
}
|
|
3555
|
+
if (sorted.length === 2) {
|
|
3556
|
+
const a = sorted[0].color, b = sorted[1].color;
|
|
3557
|
+
return `<< /FunctionType 2 /Domain [0 1] /C0 [${ch(a[0])} ${ch(a[1])} ${ch(a[2])}] /C1 [${ch(b[0])} ${ch(b[1])} ${ch(b[2])}] /N 1 >>`;
|
|
3558
|
+
}
|
|
3559
|
+
const subFns = [];
|
|
3560
|
+
const bounds = [];
|
|
3561
|
+
const encode = [];
|
|
3562
|
+
for (let i = 0; i < sorted.length - 1; i++) {
|
|
3563
|
+
const a = sorted[i].color, b = sorted[i + 1].color;
|
|
3564
|
+
subFns.push(`<< /FunctionType 2 /Domain [0 1] /C0 [${ch(a[0])} ${ch(a[1])} ${ch(a[2])}] /C1 [${ch(b[0])} ${ch(b[1])} ${ch(b[2])}] /N 1 >>`);
|
|
3565
|
+
encode.push("0 1");
|
|
3566
|
+
if (i > 0) bounds.push(n(Math.max(0, Math.min(1, sorted[i].offset))));
|
|
3567
|
+
}
|
|
3568
|
+
return `<< /FunctionType 3 /Domain [0 1] /Functions [${subFns.join(" ")}] /Bounds [${bounds.join(" ")}] /Encode [${encode.join(" ")}] >>`;
|
|
3569
|
+
}
|
|
3570
|
+
function extendFlags(extend) {
|
|
3571
|
+
return extend === "pad" ? "[true true]" : "[true true]";
|
|
3572
|
+
}
|
|
3573
|
+
function linearShadingDict(p, m) {
|
|
3574
|
+
const [x0, y0] = tx(m, p.p0[0], p.p0[1]);
|
|
3575
|
+
const [x1, y1] = tx(m, p.p1[0], p.p1[1]);
|
|
3576
|
+
return `<< /ShadingType 2 /ColorSpace /DeviceRGB /Coords [${n(x0)} ${n(y0)} ${n(x1)} ${n(y1)}] /Function ${buildGradientFunction(p.stops)} /Extend ${extendFlags(p.extend)} >>`;
|
|
3577
|
+
}
|
|
3578
|
+
function radialShadingDict(p, m) {
|
|
3579
|
+
const [x0, y0] = tx(m, p.c0[0], p.c0[1]);
|
|
3580
|
+
const [x1, y1] = tx(m, p.c1[0], p.c1[1]);
|
|
3581
|
+
const sx = Math.hypot(m[0], m[1]);
|
|
3582
|
+
const sy = Math.hypot(m[2], m[3]);
|
|
3583
|
+
const s = (sx + sy) / 2 || 1;
|
|
3584
|
+
return `<< /ShadingType 3 /ColorSpace /DeviceRGB /Coords [${n(x0)} ${n(y0)} ${n(p.r0 * s)} ${n(x1)} ${n(y1)} ${n(p.r1 * s)}] /Function ${buildGradientFunction(p.stops)} /Extend ${extendFlags(p.extend)} >>`;
|
|
3585
|
+
}
|
|
3586
|
+
function renderColorGlyph(glyph, outlines, unitsPerEm) {
|
|
3587
|
+
const body = [];
|
|
3588
|
+
const shadings = [];
|
|
3589
|
+
const extGStates = [];
|
|
3590
|
+
const alphaMap = /* @__PURE__ */ new Map();
|
|
3591
|
+
let shadingIdx = 0;
|
|
3592
|
+
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
|
|
3593
|
+
for (const layer of glyph.layers) {
|
|
3594
|
+
const m = layer.transform ?? ID;
|
|
3595
|
+
const contours = outlines(layer.glyphId);
|
|
3596
|
+
if (contours.length === 0) continue;
|
|
3597
|
+
const path = contoursToPath(contours, m);
|
|
3598
|
+
for (const contour of contours) {
|
|
3599
|
+
for (const pt of contour) {
|
|
3600
|
+
const [px, py] = tx(m, pt.x, pt.y);
|
|
3601
|
+
if (px < minX) minX = px;
|
|
3602
|
+
if (py < minY) minY = py;
|
|
3603
|
+
if (px > maxX) maxX = px;
|
|
3604
|
+
if (py > maxY) maxY = py;
|
|
3605
|
+
}
|
|
3606
|
+
}
|
|
3607
|
+
if (layer.paint.kind === "solid") {
|
|
3608
|
+
const c = layer.paint.color;
|
|
3609
|
+
const alpha = c[3] / 255;
|
|
3610
|
+
body.push("q");
|
|
3611
|
+
if (alpha < 0.999) {
|
|
3612
|
+
let gs = alphaMap.get(c[3]);
|
|
3613
|
+
if (!gs) {
|
|
3614
|
+
gs = `GsA${alphaMap.size}`;
|
|
3615
|
+
alphaMap.set(c[3], gs);
|
|
3616
|
+
extGStates.push({ name: gs, dict: `<< /ca ${n(alpha)} /CA ${n(alpha)} >>` });
|
|
3617
|
+
}
|
|
3618
|
+
body.push(`/${gs} gs`);
|
|
3619
|
+
}
|
|
3620
|
+
body.push(`${ch(c[0])} ${ch(c[1])} ${ch(c[2])} rg`);
|
|
3621
|
+
body.push(path);
|
|
3622
|
+
body.push("f");
|
|
3623
|
+
body.push("Q");
|
|
3624
|
+
} else {
|
|
3625
|
+
const name = `Sh${shadingIdx++}`;
|
|
3626
|
+
const dict = layer.paint.kind === "linear" ? linearShadingDict(layer.paint, m) : radialShadingDict(layer.paint, m);
|
|
3627
|
+
shadings.push({ name, dict });
|
|
3628
|
+
body.push("q");
|
|
3629
|
+
body.push(path);
|
|
3630
|
+
body.push("W n");
|
|
3631
|
+
body.push(`/${name} sh`);
|
|
3632
|
+
body.push("Q");
|
|
3633
|
+
}
|
|
1828
3634
|
}
|
|
1829
|
-
|
|
3635
|
+
const bbox = Number.isFinite(minX) ? [Math.floor(minX) - 1, Math.floor(minY) - 1, Math.ceil(maxX) + 1, Math.ceil(maxY) + 1] : [0, 0, unitsPerEm, unitsPerEm];
|
|
3636
|
+
return {
|
|
3637
|
+
content: body.join("\n"),
|
|
3638
|
+
bbox,
|
|
3639
|
+
shadings,
|
|
3640
|
+
extGStates
|
|
3641
|
+
};
|
|
1830
3642
|
}
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
3643
|
+
|
|
3644
|
+
// src/core/color-emoji.ts
|
|
3645
|
+
function createColorEmojiCollector() {
|
|
3646
|
+
const forms = [];
|
|
3647
|
+
const nameByGlyph = /* @__PURE__ */ new WeakMap();
|
|
3648
|
+
const glyfByFont = /* @__PURE__ */ new WeakMap();
|
|
3649
|
+
function glyfFor(fontData) {
|
|
3650
|
+
let g = glyfByFont.get(fontData);
|
|
3651
|
+
if (g === void 0) {
|
|
3652
|
+
g = parseGlyfFont(getDecodedFontBytes(fontData));
|
|
3653
|
+
glyfByFont.set(fontData, g);
|
|
3654
|
+
}
|
|
3655
|
+
return g;
|
|
3656
|
+
}
|
|
3657
|
+
function useGlyph(fontData, gid) {
|
|
3658
|
+
const colorGlyph = fontData.colorGlyphs?.[gid];
|
|
3659
|
+
if (!colorGlyph) return null;
|
|
3660
|
+
let perFont = nameByGlyph.get(fontData);
|
|
3661
|
+
if (!perFont) {
|
|
3662
|
+
perFont = /* @__PURE__ */ new Map();
|
|
3663
|
+
nameByGlyph.set(fontData, perFont);
|
|
3664
|
+
}
|
|
3665
|
+
const cached = perFont.get(gid);
|
|
3666
|
+
if (cached) return cached;
|
|
3667
|
+
const glyf = glyfFor(fontData);
|
|
3668
|
+
if (!glyf) return null;
|
|
3669
|
+
const rendered = renderColorGlyph(
|
|
3670
|
+
colorGlyph,
|
|
3671
|
+
(baseGid) => extractGlyphContours(glyf, baseGid),
|
|
3672
|
+
fontData.metrics.unitsPerEm
|
|
3673
|
+
);
|
|
3674
|
+
if (rendered.content.trim() === "") return null;
|
|
3675
|
+
const name = `CEm${forms.length}`;
|
|
3676
|
+
const resParts = [];
|
|
3677
|
+
if (rendered.shadings.length > 0) {
|
|
3678
|
+
resParts.push(`/Shading << ${rendered.shadings.map((s) => `/${s.name} ${s.dict}`).join(" ")} >>`);
|
|
3679
|
+
}
|
|
3680
|
+
if (rendered.extGStates.length > 0) {
|
|
3681
|
+
resParts.push(`/ExtGState << ${rendered.extGStates.map((g) => `/${g.name} ${g.dict}`).join(" ")} >>`);
|
|
3682
|
+
}
|
|
3683
|
+
forms.push({ name, content: rendered.content, resources: resParts.join(" "), bbox: rendered.bbox });
|
|
3684
|
+
perFont.set(gid, name);
|
|
3685
|
+
return name;
|
|
1837
3686
|
}
|
|
1838
|
-
|
|
1839
|
-
return String.fromCodePoint(...cps);
|
|
3687
|
+
return { useGlyph, forms };
|
|
1840
3688
|
}
|
|
1841
3689
|
|
|
1842
3690
|
// src/core/encoding-context.ts
|
|
@@ -1943,13 +3791,14 @@ function buildTextRunsWithFallback(text, fontRef, fd, sz, trackGid, pdfA = false
|
|
|
1943
3791
|
if (mode === "hel") flushHel();
|
|
1944
3792
|
return result;
|
|
1945
3793
|
}
|
|
1946
|
-
function createEncodingContext(fontEntries, pdfA = false) {
|
|
3794
|
+
function createEncodingContext(fontEntries, pdfA = false, normalize = false) {
|
|
3795
|
+
const _norm = normalize ? (s) => s.normalize(normalize) : (s) => s;
|
|
1947
3796
|
if (!fontEntries || fontEntries.length === 0) {
|
|
1948
3797
|
return {
|
|
1949
3798
|
isUnicode: false,
|
|
1950
3799
|
fontEntries: [],
|
|
1951
|
-
ps: pdfString,
|
|
1952
|
-
tw: helveticaWidth,
|
|
3800
|
+
ps: normalize ? (s) => pdfString(_norm(s)) : pdfString,
|
|
3801
|
+
tw: normalize ? (s, sz) => helveticaWidth(_norm(s), sz) : helveticaWidth,
|
|
1953
3802
|
textRuns: () => [],
|
|
1954
3803
|
f1: "/F1",
|
|
1955
3804
|
f2: "/F2"
|
|
@@ -1962,6 +3811,7 @@ function createEncodingContext(fontEntries, pdfA = false) {
|
|
|
1962
3811
|
const s = _usedGids.get(fontRef);
|
|
1963
3812
|
if (s) s.add(gid);
|
|
1964
3813
|
}
|
|
3814
|
+
const _colorEmoji = fontEntries.some((fe) => fe.fontData.colorGlyphs) ? createColorEmojiCollector() : void 0;
|
|
1965
3815
|
return {
|
|
1966
3816
|
isUnicode: true,
|
|
1967
3817
|
fontEntries,
|
|
@@ -1971,7 +3821,11 @@ function createEncodingContext(fontEntries, pdfA = false) {
|
|
|
1971
3821
|
getUsedGids() {
|
|
1972
3822
|
return _usedGids;
|
|
1973
3823
|
},
|
|
3824
|
+
colorEmoji: _colorEmoji,
|
|
1974
3825
|
textRuns(str, sz) {
|
|
3826
|
+
if (!str) return [];
|
|
3827
|
+
str = _norm(str);
|
|
3828
|
+
str = stripBidiControls(str);
|
|
1975
3829
|
if (!str) return [];
|
|
1976
3830
|
if (containsRTL(str)) {
|
|
1977
3831
|
const bidiRuns = resolveBidiRuns(str);
|
|
@@ -2037,6 +3891,56 @@ function createEncodingContext(fontEntries, pdfA = false) {
|
|
|
2037
3891
|
}
|
|
2038
3892
|
}
|
|
2039
3893
|
result.push({ text: fRun.text, fontRef, fontData: fd, shaped, hexStr: null, widthPt: designW * sz / upm });
|
|
3894
|
+
} else if (containsTelugu(fRun.text)) {
|
|
3895
|
+
const shaped = shapeTeluguText(fRun.text, fd);
|
|
3896
|
+
let designW = 0;
|
|
3897
|
+
for (const g of shaped) {
|
|
3898
|
+
_trackGid(fontRef, g.gid);
|
|
3899
|
+
if (!g.isZeroAdvance) {
|
|
3900
|
+
designW += fd.widths[g.gid] !== void 0 ? fd.widths[g.gid] : fd.defaultWidth;
|
|
3901
|
+
}
|
|
3902
|
+
}
|
|
3903
|
+
result.push({ text: fRun.text, fontRef, fontData: fd, shaped, hexStr: null, widthPt: designW * sz / upm });
|
|
3904
|
+
} else if (containsSinhala(fRun.text)) {
|
|
3905
|
+
const shaped = shapeSinhalaText(fRun.text, fd);
|
|
3906
|
+
let designW = 0;
|
|
3907
|
+
for (const g of shaped) {
|
|
3908
|
+
_trackGid(fontRef, g.gid);
|
|
3909
|
+
if (!g.isZeroAdvance) {
|
|
3910
|
+
designW += fd.widths[g.gid] !== void 0 ? fd.widths[g.gid] : fd.defaultWidth;
|
|
3911
|
+
}
|
|
3912
|
+
}
|
|
3913
|
+
result.push({ text: fRun.text, fontRef, fontData: fd, shaped, hexStr: null, widthPt: designW * sz / upm });
|
|
3914
|
+
} else if (containsTibetan(fRun.text)) {
|
|
3915
|
+
const shaped = shapeTibetanText(fRun.text, fd);
|
|
3916
|
+
let designW = 0;
|
|
3917
|
+
for (const g of shaped) {
|
|
3918
|
+
_trackGid(fontRef, g.gid);
|
|
3919
|
+
if (!g.isZeroAdvance) {
|
|
3920
|
+
designW += fd.widths[g.gid] !== void 0 ? fd.widths[g.gid] : fd.defaultWidth;
|
|
3921
|
+
}
|
|
3922
|
+
}
|
|
3923
|
+
result.push({ text: fRun.text, fontRef, fontData: fd, shaped, hexStr: null, widthPt: designW * sz / upm });
|
|
3924
|
+
} else if (containsKhmer(fRun.text)) {
|
|
3925
|
+
const shaped = shapeKhmerText(fRun.text, fd);
|
|
3926
|
+
let designW = 0;
|
|
3927
|
+
for (const g of shaped) {
|
|
3928
|
+
_trackGid(fontRef, g.gid);
|
|
3929
|
+
if (!g.isZeroAdvance) {
|
|
3930
|
+
designW += fd.widths[g.gid] !== void 0 ? fd.widths[g.gid] : fd.defaultWidth;
|
|
3931
|
+
}
|
|
3932
|
+
}
|
|
3933
|
+
result.push({ text: fRun.text, fontRef, fontData: fd, shaped, hexStr: null, widthPt: designW * sz / upm });
|
|
3934
|
+
} else if (containsMyanmar(fRun.text)) {
|
|
3935
|
+
const shaped = shapeMyanmarText(fRun.text, fd);
|
|
3936
|
+
let designW = 0;
|
|
3937
|
+
for (const g of shaped) {
|
|
3938
|
+
_trackGid(fontRef, g.gid);
|
|
3939
|
+
if (!g.isZeroAdvance) {
|
|
3940
|
+
designW += fd.widths[g.gid] !== void 0 ? fd.widths[g.gid] : fd.defaultWidth;
|
|
3941
|
+
}
|
|
3942
|
+
}
|
|
3943
|
+
result.push({ text: fRun.text, fontRef, fontData: fd, shaped, hexStr: null, widthPt: designW * sz / upm });
|
|
2040
3944
|
} else if (containsDevanagari(fRun.text)) {
|
|
2041
3945
|
const shaped = shapeDevanagariText(fRun.text, fd);
|
|
2042
3946
|
let designW = 0;
|
|
@@ -2094,6 +3998,61 @@ function createEncodingContext(fontEntries, pdfA = false) {
|
|
|
2094
3998
|
}
|
|
2095
3999
|
return [{ text: run.text, fontRef, fontData: fd, shaped, hexStr: null, widthPt: designW * sz / upm }];
|
|
2096
4000
|
}
|
|
4001
|
+
if (containsTelugu(run.text)) {
|
|
4002
|
+
const shaped = shapeTeluguText(run.text, fd);
|
|
4003
|
+
let designW = 0;
|
|
4004
|
+
for (const g of shaped) {
|
|
4005
|
+
_trackGid(fontRef, g.gid);
|
|
4006
|
+
if (!g.isZeroAdvance) {
|
|
4007
|
+
designW += fd.widths[g.gid] !== void 0 ? fd.widths[g.gid] : fd.defaultWidth;
|
|
4008
|
+
}
|
|
4009
|
+
}
|
|
4010
|
+
return [{ text: run.text, fontRef, fontData: fd, shaped, hexStr: null, widthPt: designW * sz / upm }];
|
|
4011
|
+
}
|
|
4012
|
+
if (containsSinhala(run.text)) {
|
|
4013
|
+
const shaped = shapeSinhalaText(run.text, fd);
|
|
4014
|
+
let designW = 0;
|
|
4015
|
+
for (const g of shaped) {
|
|
4016
|
+
_trackGid(fontRef, g.gid);
|
|
4017
|
+
if (!g.isZeroAdvance) {
|
|
4018
|
+
designW += fd.widths[g.gid] !== void 0 ? fd.widths[g.gid] : fd.defaultWidth;
|
|
4019
|
+
}
|
|
4020
|
+
}
|
|
4021
|
+
return [{ text: run.text, fontRef, fontData: fd, shaped, hexStr: null, widthPt: designW * sz / upm }];
|
|
4022
|
+
}
|
|
4023
|
+
if (containsTibetan(run.text)) {
|
|
4024
|
+
const shaped = shapeTibetanText(run.text, fd);
|
|
4025
|
+
let designW = 0;
|
|
4026
|
+
for (const g of shaped) {
|
|
4027
|
+
_trackGid(fontRef, g.gid);
|
|
4028
|
+
if (!g.isZeroAdvance) {
|
|
4029
|
+
designW += fd.widths[g.gid] !== void 0 ? fd.widths[g.gid] : fd.defaultWidth;
|
|
4030
|
+
}
|
|
4031
|
+
}
|
|
4032
|
+
return [{ text: run.text, fontRef, fontData: fd, shaped, hexStr: null, widthPt: designW * sz / upm }];
|
|
4033
|
+
}
|
|
4034
|
+
if (containsKhmer(run.text)) {
|
|
4035
|
+
const shaped = shapeKhmerText(run.text, fd);
|
|
4036
|
+
let designW = 0;
|
|
4037
|
+
for (const g of shaped) {
|
|
4038
|
+
_trackGid(fontRef, g.gid);
|
|
4039
|
+
if (!g.isZeroAdvance) {
|
|
4040
|
+
designW += fd.widths[g.gid] !== void 0 ? fd.widths[g.gid] : fd.defaultWidth;
|
|
4041
|
+
}
|
|
4042
|
+
}
|
|
4043
|
+
return [{ text: run.text, fontRef, fontData: fd, shaped, hexStr: null, widthPt: designW * sz / upm }];
|
|
4044
|
+
}
|
|
4045
|
+
if (containsMyanmar(run.text)) {
|
|
4046
|
+
const shaped = shapeMyanmarText(run.text, fd);
|
|
4047
|
+
let designW = 0;
|
|
4048
|
+
for (const g of shaped) {
|
|
4049
|
+
_trackGid(fontRef, g.gid);
|
|
4050
|
+
if (!g.isZeroAdvance) {
|
|
4051
|
+
designW += fd.widths[g.gid] !== void 0 ? fd.widths[g.gid] : fd.defaultWidth;
|
|
4052
|
+
}
|
|
4053
|
+
}
|
|
4054
|
+
return [{ text: run.text, fontRef, fontData: fd, shaped, hexStr: null, widthPt: designW * sz / upm }];
|
|
4055
|
+
}
|
|
2097
4056
|
if (containsDevanagari(run.text)) {
|
|
2098
4057
|
const shaped = shapeDevanagariText(run.text, fd);
|
|
2099
4058
|
let designW = 0;
|
|
@@ -2109,6 +4068,9 @@ function createEncodingContext(fontEntries, pdfA = false) {
|
|
|
2109
4068
|
});
|
|
2110
4069
|
},
|
|
2111
4070
|
ps(str) {
|
|
4071
|
+
if (!str) return "<>";
|
|
4072
|
+
str = _norm(str);
|
|
4073
|
+
str = stripBidiControls(str);
|
|
2112
4074
|
if (!str) return "<>";
|
|
2113
4075
|
const { cmap } = primary.fontData;
|
|
2114
4076
|
if (containsRTL(str)) {
|
|
@@ -2136,7 +4098,7 @@ function createEncodingContext(fontEntries, pdfA = false) {
|
|
|
2136
4098
|
}
|
|
2137
4099
|
return `<${hex2.toUpperCase()}>`;
|
|
2138
4100
|
}
|
|
2139
|
-
if (!containsThai(str) && !containsBengali(str) && !containsTamil(str) && !containsDevanagari(str)) {
|
|
4101
|
+
if (!containsThai(str) && !containsBengali(str) && !containsTamil(str) && !containsTelugu(str) && !containsSinhala(str) && !containsTibetan(str) && !containsKhmer(str) && !containsMyanmar(str) && !containsDevanagari(str)) {
|
|
2140
4102
|
let hex2 = "";
|
|
2141
4103
|
for (let i = 0; i < str.length; i++) {
|
|
2142
4104
|
const rawCp = str.codePointAt(i) ?? 0;
|
|
@@ -2148,7 +4110,7 @@ function createEncodingContext(fontEntries, pdfA = false) {
|
|
|
2148
4110
|
}
|
|
2149
4111
|
return `<${hex2.toUpperCase()}>`;
|
|
2150
4112
|
}
|
|
2151
|
-
const shapeFn = containsThai(str) ? shapeThaiText : containsBengali(str) ? shapeBengaliText : containsTamil(str) ? shapeTamilText : shapeDevanagariText;
|
|
4113
|
+
const shapeFn = containsThai(str) ? shapeThaiText : containsBengali(str) ? shapeBengaliText : containsTamil(str) ? shapeTamilText : containsTelugu(str) ? shapeTeluguText : containsSinhala(str) ? shapeSinhalaText : containsTibetan(str) ? shapeTibetanText : containsKhmer(str) ? shapeKhmerText : containsMyanmar(str) ? shapeMyanmarText : shapeDevanagariText;
|
|
2152
4114
|
const shaped = shapeFn(str, primary.fontData);
|
|
2153
4115
|
let hex = "";
|
|
2154
4116
|
for (const g of shaped) {
|
|
@@ -2230,24 +4192,6 @@ function buildSubsetWidthArray(widths, usedGids) {
|
|
|
2230
4192
|
return parts.join(" ");
|
|
2231
4193
|
}
|
|
2232
4194
|
|
|
2233
|
-
// src/fonts/font-loader.ts
|
|
2234
|
-
var _fontBinaryCache = /* @__PURE__ */ new WeakMap();
|
|
2235
|
-
function getDecodedFontBytes(fontData) {
|
|
2236
|
-
const cached = _fontBinaryCache.get(fontData);
|
|
2237
|
-
if (cached) return cached;
|
|
2238
|
-
let bytes;
|
|
2239
|
-
if (typeof atob === "function") {
|
|
2240
|
-
const binaryStr = atob(fontData.ttfBase64);
|
|
2241
|
-
bytes = new Uint8Array(binaryStr.length);
|
|
2242
|
-
for (let i = 0; i < binaryStr.length; i++) bytes[i] = binaryStr.charCodeAt(i);
|
|
2243
|
-
} else {
|
|
2244
|
-
const buf = globalThis["Buffer"];
|
|
2245
|
-
bytes = buf.from(fontData.ttfBase64, "base64");
|
|
2246
|
-
}
|
|
2247
|
-
_fontBinaryCache.set(fontData, bytes);
|
|
2248
|
-
return bytes;
|
|
2249
|
-
}
|
|
2250
|
-
|
|
2251
4195
|
// src/fonts/font-subsetter.ts
|
|
2252
4196
|
function subsetTTF(ttfInput, usedGids) {
|
|
2253
4197
|
try {
|
|
@@ -2561,7 +4505,7 @@ function buildStructureTree(root, startObjNum, pageObjToStructParents) {
|
|
|
2561
4505
|
};
|
|
2562
4506
|
}
|
|
2563
4507
|
function buildPdfMetadata(now = /* @__PURE__ */ new Date()) {
|
|
2564
|
-
const pad2 = (
|
|
4508
|
+
const pad2 = (n2) => String(n2).padStart(2, "0");
|
|
2565
4509
|
const yyyy = now.getFullYear();
|
|
2566
4510
|
const mm = pad2(now.getMonth() + 1);
|
|
2567
4511
|
const dd = pad2(now.getDate());
|
|
@@ -2769,45 +4713,64 @@ function txtShaped(shaped, x, y, font, sz, fontData) {
|
|
|
2769
4713
|
}
|
|
2770
4714
|
return parts.join("\n");
|
|
2771
4715
|
}
|
|
4716
|
+
var fmtScale = (v) => v.toFixed(5).replace(/0+$/, "").replace(/\.$/, "") || "0";
|
|
4717
|
+
function emitColorEmojiRun(parts, run, penX, y, sz, enc) {
|
|
4718
|
+
const fd = run.fontData;
|
|
4719
|
+
const upm = fd.metrics.unitsPerEm;
|
|
4720
|
+
const scale = sz / upm;
|
|
4721
|
+
const s = fmtScale(scale);
|
|
4722
|
+
const hex = (run.hexStr ?? "").replace(/[<>]/g, "");
|
|
4723
|
+
for (let i = 0; i + 4 <= hex.length; i += 4) {
|
|
4724
|
+
const tag = hex.substr(i, 4);
|
|
4725
|
+
const gid = parseInt(tag, 16);
|
|
4726
|
+
const adv = (fd.widths[gid] !== void 0 ? fd.widths[gid] : fd.defaultWidth) * scale;
|
|
4727
|
+
const name = enc.colorEmoji?.useGlyph(fd, gid) ?? null;
|
|
4728
|
+
if (name) {
|
|
4729
|
+
parts.push(`q ${s} 0 0 ${s} ${fmtNum(penX)} ${fmtNum(y)} cm /${name} Do Q`);
|
|
4730
|
+
} else {
|
|
4731
|
+
parts.push(`BT ${run.fontRef} ${sz} Tf ${fmtNum(penX)} ${fmtNum(y)} Td <${tag}> Tj ET`);
|
|
4732
|
+
}
|
|
4733
|
+
penX += adv;
|
|
4734
|
+
}
|
|
4735
|
+
return penX;
|
|
4736
|
+
}
|
|
2772
4737
|
function txt(str, x, y, font, sz, enc) {
|
|
2773
4738
|
if (!enc.isUnicode) {
|
|
2774
4739
|
return `BT ${font} ${sz} Tf ${fmtNum(x)} ${fmtNum(y)} Td ${enc.ps(str)} Tj ET`;
|
|
2775
4740
|
}
|
|
2776
4741
|
const runs = enc.textRuns(str, sz);
|
|
2777
4742
|
if (runs.length === 0) return "";
|
|
2778
|
-
if (runs.length === 1) {
|
|
2779
|
-
const run = runs[0];
|
|
2780
|
-
if (run.shaped) return txtShaped(run.shaped, x, y, run.fontRef, sz, run.fontData);
|
|
2781
|
-
return `BT ${run.fontRef} ${sz} Tf ${fmtNum(x)} ${fmtNum(y)} Td ${run.hexStr} Tj ET`;
|
|
2782
|
-
}
|
|
2783
4743
|
const parts = [];
|
|
2784
4744
|
let penX = x;
|
|
2785
4745
|
for (const run of runs) {
|
|
2786
|
-
if (run.
|
|
4746
|
+
if (enc.colorEmoji && run.fontData.colorGlyphs && run.hexStr) {
|
|
4747
|
+
penX = emitColorEmojiRun(parts, run, penX, y, sz, enc);
|
|
4748
|
+
} else if (run.shaped) {
|
|
2787
4749
|
parts.push(txtShaped(run.shaped, penX, y, run.fontRef, sz, run.fontData));
|
|
4750
|
+
penX += run.widthPt;
|
|
2788
4751
|
} else {
|
|
2789
4752
|
parts.push(`BT ${run.fontRef} ${sz} Tf ${fmtNum(penX)} ${fmtNum(y)} Td ${run.hexStr} Tj ET`);
|
|
4753
|
+
penX += run.widthPt;
|
|
2790
4754
|
}
|
|
2791
|
-
penX += run.widthPt;
|
|
2792
4755
|
}
|
|
2793
4756
|
return parts.join("\n");
|
|
2794
4757
|
}
|
|
2795
|
-
function txtR(str, rightX, y, font, sz, enc) {
|
|
2796
|
-
const width = enc.isUnicode ? enc.tw(str, sz) : helveticaWidth(toWinAnsi(str), sz);
|
|
4758
|
+
function txtR(str, rightX, y, font, sz, enc, bold = false) {
|
|
4759
|
+
const width = enc.isUnicode ? enc.tw(str, sz) : bold ? helveticaBoldWidth(str, sz) : helveticaWidth(toWinAnsi(str), sz);
|
|
2797
4760
|
return txt(str, rightX - width, y, font, sz, enc);
|
|
2798
4761
|
}
|
|
2799
|
-
function txtC(str, leftX, y, font, sz, colW, enc) {
|
|
2800
|
-
const width = enc.isUnicode ? enc.tw(str, sz) : helveticaWidth(toWinAnsi(str), sz);
|
|
4762
|
+
function txtC(str, leftX, y, font, sz, colW, enc, bold = false) {
|
|
4763
|
+
const width = enc.isUnicode ? enc.tw(str, sz) : bold ? helveticaBoldWidth(str, sz) : helveticaWidth(toWinAnsi(str), sz);
|
|
2801
4764
|
return txt(str, leftX + (colW - width) / 2, y, font, sz, enc);
|
|
2802
4765
|
}
|
|
2803
4766
|
function txtTagged(str, x, y, font, sz, enc, mcid) {
|
|
2804
4767
|
return wrapSpan(txt(str, x, y, font, sz, enc), str, mcid);
|
|
2805
4768
|
}
|
|
2806
|
-
function txtRTagged(str, rightX, y, font, sz, enc, mcid) {
|
|
2807
|
-
return wrapSpan(txtR(str, rightX, y, font, sz, enc), str, mcid);
|
|
4769
|
+
function txtRTagged(str, rightX, y, font, sz, enc, mcid, bold = false) {
|
|
4770
|
+
return wrapSpan(txtR(str, rightX, y, font, sz, enc, bold), str, mcid);
|
|
2808
4771
|
}
|
|
2809
|
-
function txtCTagged(str, leftX, y, font, sz, colW, enc, mcid) {
|
|
2810
|
-
return wrapSpan(txtC(str, leftX, y, font, sz, colW, enc), str, mcid);
|
|
4772
|
+
function txtCTagged(str, leftX, y, font, sz, colW, enc, mcid, bold = false) {
|
|
4773
|
+
return wrapSpan(txtC(str, leftX, y, font, sz, colW, enc, bold), str, mcid);
|
|
2811
4774
|
}
|
|
2812
4775
|
function encodePdfTextString(str) {
|
|
2813
4776
|
let ascii = true;
|
|
@@ -2881,12 +4844,12 @@ var DEFAULT_COLUMNS = [
|
|
|
2881
4844
|
{ f: 0.18, a: "c", mx: 20, mxH: 20 }
|
|
2882
4845
|
];
|
|
2883
4846
|
function computeColumnPositions(columns, marginLeft, contentWidth) {
|
|
2884
|
-
const
|
|
2885
|
-
const cwi = new Array(
|
|
2886
|
-
const fixed = new Array(
|
|
4847
|
+
const n2 = columns.length;
|
|
4848
|
+
const cwi = new Array(n2).fill(0);
|
|
4849
|
+
const fixed = new Array(n2).fill(false);
|
|
2887
4850
|
let totalFixed = 0;
|
|
2888
4851
|
let freeWeight = 0;
|
|
2889
|
-
for (let i = 0; i <
|
|
4852
|
+
for (let i = 0; i < n2; i++) {
|
|
2890
4853
|
const col = columns[i];
|
|
2891
4854
|
let w = col.f * contentWidth;
|
|
2892
4855
|
let clamped = false;
|
|
@@ -2908,13 +4871,13 @@ function computeColumnPositions(columns, marginLeft, contentWidth) {
|
|
|
2908
4871
|
}
|
|
2909
4872
|
const remaining = contentWidth - totalFixed;
|
|
2910
4873
|
if (freeWeight > 0) {
|
|
2911
|
-
for (let i = 0; i <
|
|
4874
|
+
for (let i = 0; i < n2; i++) {
|
|
2912
4875
|
if (!fixed[i]) cwi[i] = columns[i].f / freeWeight * remaining;
|
|
2913
4876
|
}
|
|
2914
4877
|
}
|
|
2915
|
-
const cx = new Array(
|
|
4878
|
+
const cx = new Array(n2);
|
|
2916
4879
|
let x = marginLeft;
|
|
2917
|
-
for (let i = 0; i <
|
|
4880
|
+
for (let i = 0; i < n2; i++) {
|
|
2918
4881
|
cx[i] = x;
|
|
2919
4882
|
x += cwi[i];
|
|
2920
4883
|
}
|
|
@@ -2940,8 +4903,8 @@ function parseColor(input) {
|
|
|
2940
4903
|
`Invalid color format: ${JSON.stringify(input)}. Expected "#RRGGBB", "#RGB", [r, g, b] (0\u2013255), or "R G B" (0.0\u20131.0).`
|
|
2941
4904
|
);
|
|
2942
4905
|
}
|
|
2943
|
-
function fmtChannel(
|
|
2944
|
-
const clamped = Math.max(0, Math.min(1,
|
|
4906
|
+
function fmtChannel(n2) {
|
|
4907
|
+
const clamped = Math.max(0, Math.min(1, n2));
|
|
2945
4908
|
const rounded = Math.round(clamped * 1e3) / 1e3;
|
|
2946
4909
|
return String(rounded);
|
|
2947
4910
|
}
|
|
@@ -2983,8 +4946,8 @@ function parseTupleColor(tuple) {
|
|
|
2983
4946
|
function parsePdfRgbString(str) {
|
|
2984
4947
|
const parts = str.split(" ");
|
|
2985
4948
|
for (const p of parts) {
|
|
2986
|
-
const
|
|
2987
|
-
if (
|
|
4949
|
+
const n2 = Number(p);
|
|
4950
|
+
if (n2 < 0 || n2 > 1) {
|
|
2988
4951
|
throw new Error(
|
|
2989
4952
|
`Invalid PDF RGB value: ${p} in ${JSON.stringify(str)}. Each value must be 0.0\u20131.0.`
|
|
2990
4953
|
);
|
|
@@ -3124,8 +5087,8 @@ var MD5_K = new Uint32Array([
|
|
|
3124
5087
|
718787259,
|
|
3125
5088
|
3951481745
|
|
3126
5089
|
]);
|
|
3127
|
-
function rotl32(x,
|
|
3128
|
-
return (x <<
|
|
5090
|
+
function rotl32(x, n2) {
|
|
5091
|
+
return (x << n2 | x >>> 32 - n2) >>> 0;
|
|
3129
5092
|
}
|
|
3130
5093
|
function md5(input) {
|
|
3131
5094
|
const len = input.length;
|
|
@@ -3265,17 +5228,17 @@ function _buildTableHeader(y, headers, enc, cx, cwi, columns, cw, mgL, mgR, pgW,
|
|
|
3265
5228
|
const thEl = { type: "TH", children: [mcref] };
|
|
3266
5229
|
thChildren.push(thEl);
|
|
3267
5230
|
if (columns[i].a === "r") {
|
|
3268
|
-
ops.push(txtRTagged(t, cx[i] + cwi[i] - 3, y - TH_H + 4, enc.f2, fs.th, enc, mcid));
|
|
5231
|
+
ops.push(txtRTagged(t, cx[i] + cwi[i] - 3, y - TH_H + 4, enc.f2, fs.th, enc, mcid, true));
|
|
3269
5232
|
} else if (columns[i].a === "c") {
|
|
3270
|
-
ops.push(txtCTagged(t, cx[i], y - TH_H + 4, enc.f2, fs.th, cwi[i], enc, mcid));
|
|
5233
|
+
ops.push(txtCTagged(t, cx[i], y - TH_H + 4, enc.f2, fs.th, cwi[i], enc, mcid, true));
|
|
3271
5234
|
} else {
|
|
3272
5235
|
ops.push(txtTagged(t, cx[i] + 3, y - TH_H + 4, enc.f2, fs.th, enc, mcid));
|
|
3273
5236
|
}
|
|
3274
5237
|
} else {
|
|
3275
5238
|
if (columns[i].a === "r") {
|
|
3276
|
-
ops.push(txtR(t, cx[i] + cwi[i] - 3, y - TH_H + 4, enc.f2, fs.th, enc));
|
|
5239
|
+
ops.push(txtR(t, cx[i] + cwi[i] - 3, y - TH_H + 4, enc.f2, fs.th, enc, true));
|
|
3277
5240
|
} else if (columns[i].a === "c") {
|
|
3278
|
-
ops.push(txtC(t, cx[i], y - TH_H + 4, enc.f2, fs.th, cwi[i], enc));
|
|
5241
|
+
ops.push(txtC(t, cx[i], y - TH_H + 4, enc.f2, fs.th, cwi[i], enc, true));
|
|
3279
5242
|
} else {
|
|
3280
5243
|
ops.push(txt(t, cx[i] + 3, y - TH_H + 4, enc.f2, fs.th, enc));
|
|
3281
5244
|
}
|
|
@@ -3363,6 +5326,9 @@ function _buildPageTemplate(template, page, pages, title, date, y, enc, mgL, mgR
|
|
|
3363
5326
|
return { ops, structEls };
|
|
3364
5327
|
}
|
|
3365
5328
|
function buildPDF(params, layoutOptions) {
|
|
5329
|
+
return assembleTableParts(params, layoutOptions).join("");
|
|
5330
|
+
}
|
|
5331
|
+
function assembleTableParts(params, layoutOptions) {
|
|
3366
5332
|
if (!params || typeof params !== "object") {
|
|
3367
5333
|
throw new Error("buildPDF: params is required and must be an object");
|
|
3368
5334
|
}
|
|
@@ -3388,14 +5354,14 @@ function buildPDF(params, layoutOptions) {
|
|
|
3388
5354
|
const fontEntries = params.fontEntries || (fontData ? [{ fontData, fontRef: "/F3", lang: "unknown" }] : []);
|
|
3389
5355
|
const pdfaConfig = resolvePdfAConfig();
|
|
3390
5356
|
const tagged = pdfaConfig.enabled;
|
|
3391
|
-
const enc = createEncodingContext(fontEntries, tagged);
|
|
5357
|
+
const enc = createEncodingContext(fontEntries, tagged, false);
|
|
3392
5358
|
const footerTpl = {
|
|
3393
5359
|
left: footerText || void 0,
|
|
3394
5360
|
right: "{page}/{pages}"
|
|
3395
5361
|
};
|
|
3396
5362
|
const headerH = 0;
|
|
3397
5363
|
const dateNow = /* @__PURE__ */ new Date();
|
|
3398
|
-
const pad2d = (
|
|
5364
|
+
const pad2d = (n2) => String(n2).padStart(2, "0");
|
|
3399
5365
|
const dateStr = `${dateNow.getFullYear()}-${pad2d(dateNow.getMonth() + 1)}-${pad2d(dateNow.getDate())}`;
|
|
3400
5366
|
const infoCount = infoItems.length;
|
|
3401
5367
|
const page1Header = TITLE_LN + 16 + infoCount * INFO_LN + 8 + BAL_H + 10;
|
|
@@ -3417,6 +5383,9 @@ function buildPDF(params, layoutOptions) {
|
|
|
3417
5383
|
const pageStreams = [];
|
|
3418
5384
|
let rowIdx = 0;
|
|
3419
5385
|
const prePageObjStart = enc.isUnicode && fontEntries.length > 0 ? 5 + fontEntries.length * 5 + wmExtraObjs : 5 + wmExtraObjs;
|
|
5386
|
+
const preBaseObjCount = enc.isUnicode && fontEntries.length > 0 ? 4 + fontEntries.length * 5 + wmExtraObjs + totalPages * 2 : 4 + wmExtraObjs + totalPages * 2;
|
|
5387
|
+
const latinToUniObjNum = tagged ? 0 : preBaseObjCount + 2;
|
|
5388
|
+
const baseFontToUniRef = latinToUniObjNum ? ` /ToUnicode ${latinToUniObjNum} 0 R` : "";
|
|
3420
5389
|
const pageObjToStructParents = /* @__PURE__ */ new Map();
|
|
3421
5390
|
for (let p = 0; p < totalPages; p++) {
|
|
3422
5391
|
const pageObjNum = prePageObjStart + p * 2;
|
|
@@ -3546,8 +5515,8 @@ function buildPDF(params, layoutOptions) {
|
|
|
3546
5515
|
emitObj(3, refDict);
|
|
3547
5516
|
emitObj(4, refDict);
|
|
3548
5517
|
} else {
|
|
3549
|
-
emitObj(3,
|
|
3550
|
-
emitObj(4,
|
|
5518
|
+
emitObj(3, `<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica /Encoding /WinAnsiEncoding${baseFontToUniRef} >>`);
|
|
5519
|
+
emitObj(4, `<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding${baseFontToUniRef} >>`);
|
|
3551
5520
|
}
|
|
3552
5521
|
for (let fi = 0; fi < fontEntries.length; fi++) {
|
|
3553
5522
|
const fe = fontEntries[fi];
|
|
@@ -3605,8 +5574,8 @@ function buildPDF(params, layoutOptions) {
|
|
|
3605
5574
|
kids.push(`${pageObjStart + p * 2} 0 R`);
|
|
3606
5575
|
}
|
|
3607
5576
|
emitObj(2, `<< /Type /Pages /Kids [${kids.join(" ")}] /Count ${totalPages} >>`);
|
|
3608
|
-
emitObj(3,
|
|
3609
|
-
emitObj(4,
|
|
5577
|
+
emitObj(3, `<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica /Encoding /WinAnsiEncoding${baseFontToUniRef} >>`);
|
|
5578
|
+
emitObj(4, `<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding${baseFontToUniRef} >>`);
|
|
3610
5579
|
let wmGsResLatin = "";
|
|
3611
5580
|
let wmImgResLatin = "";
|
|
3612
5581
|
for (let p = 0; p < totalPages; p++) {
|
|
@@ -3623,13 +5592,18 @@ function buildPDF(params, layoutOptions) {
|
|
|
3623
5592
|
}
|
|
3624
5593
|
const baseObjCount = enc.isUnicode ? 4 + fontEntries.length * 5 + wmExtraObjs + totalPages * 2 : 4 + wmExtraObjs + totalPages * 2;
|
|
3625
5594
|
const infoObjNum = baseObjCount + 1;
|
|
3626
|
-
const { pdfDate, xmpDate: isoDate } = buildPdfMetadata();
|
|
5595
|
+
const { pdfDate, xmpDate: isoDate } = buildPdfMetadata(layoutOptions?.creationDate);
|
|
3627
5596
|
const infoTitle = params.docTitle || title || "";
|
|
3628
5597
|
emitObj(
|
|
3629
5598
|
infoObjNum,
|
|
3630
5599
|
`<< /Title ${encodePdfTextString(infoTitle)} /Producer (pdfnative) /CreationDate (${pdfDate}) >>`
|
|
3631
5600
|
);
|
|
3632
5601
|
let totalObjs = infoObjNum;
|
|
5602
|
+
if (latinToUniObjNum) {
|
|
5603
|
+
const cmap = buildWinAnsiToUnicodeCMap();
|
|
5604
|
+
emitStreamObj(latinToUniObjNum, `<< /Length ${cmap.length}`, cmap);
|
|
5605
|
+
totalObjs = latinToUniObjNum;
|
|
5606
|
+
}
|
|
3633
5607
|
let xmpObjNum = 0;
|
|
3634
5608
|
let outputIntentObjNum = 0;
|
|
3635
5609
|
if (tagged) {
|
|
@@ -3685,7 +5659,7 @@ endobj
|
|
|
3685
5659
|
}
|
|
3686
5660
|
const writer = { emit, emitObj, emitStreamObj, offset: getOffset, adjustOffset, objOffsets, parts };
|
|
3687
5661
|
writeXrefTrailer(writer, totalObjs, infoObjNum, encState, `${infoTitle}|${pdfDate}`);
|
|
3688
|
-
return parts
|
|
5662
|
+
return parts;
|
|
3689
5663
|
}
|
|
3690
5664
|
|
|
3691
5665
|
// src/worker/pdf-worker.ts
|