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