pdfnative 1.1.0 → 1.2.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 +35 -10
- package/dist/index.cjs +2831 -2123
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +418 -19
- package/dist/index.d.ts +418 -19
- package/dist/index.js +2823 -2124
- package/dist/index.js.map +1 -1
- package/dist/worker/index.cjs +1137 -1045
- package/dist/worker/index.cjs.map +1 -1
- package/dist/worker/index.js +1137 -1045
- package/dist/worker/index.js.map +1 -1
- package/package.json +1 -1
package/dist/worker/index.cjs
CHANGED
|
@@ -354,401 +354,638 @@ function splitTextByFont(str, fontEntries) {
|
|
|
354
354
|
return runs;
|
|
355
355
|
}
|
|
356
356
|
|
|
357
|
-
// src/
|
|
358
|
-
function
|
|
359
|
-
if (
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
return
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
return "
|
|
401
|
-
}
|
|
402
|
-
function truncate(str, max) {
|
|
403
|
-
if (!str || str.length <= max) return str || "";
|
|
404
|
-
if (max <= 1) return "\u2026";
|
|
405
|
-
return str.slice(0, max - 1) + "\u2026";
|
|
357
|
+
// src/shaping/bidi.ts
|
|
358
|
+
function classifyBidiType(cp) {
|
|
359
|
+
if (cp >= 768 && cp <= 879 || // Combining Diacritical Marks
|
|
360
|
+
cp >= 1425 && cp <= 1469 || // Hebrew marks
|
|
361
|
+
cp >= 1471 && cp <= 1471 || cp >= 1473 && cp <= 1474 || cp >= 1476 && cp <= 1477 || cp >= 1479 && cp <= 1479 || cp >= 1552 && cp <= 1562 || // Arabic marks
|
|
362
|
+
cp >= 1611 && cp <= 1631 || // Arabic harakat
|
|
363
|
+
cp >= 1648 && cp <= 1648 || // Arabic superscript alef
|
|
364
|
+
cp >= 1750 && cp <= 1756 || cp >= 1759 && cp <= 1764 || cp >= 1767 && cp <= 1768 || cp >= 1770 && cp <= 1773 || cp >= 65056 && cp <= 65071) return "NSM";
|
|
365
|
+
if (cp === 8203 || cp === 8204 || cp === 8205 || cp === 8206 || cp === 8207 || cp === 65279 || cp === 8294 || cp === 8295 || cp === 8296 || cp === 8297) return "BN";
|
|
366
|
+
if (cp >= 1632 && cp <= 1641) return "AN";
|
|
367
|
+
if (cp >= 1776 && cp <= 1785) return "AN";
|
|
368
|
+
if (cp >= 48 && cp <= 57) return "EN";
|
|
369
|
+
if (cp === 43 || cp === 45) return "ES";
|
|
370
|
+
if (cp === 35 || cp === 36 || cp === 37 || cp === 162 || cp === 163 || cp === 164 || cp === 165 || cp === 8364 || cp === 8377 || cp === 8378) return "ET";
|
|
371
|
+
if (cp === 44 || cp === 46 || cp === 47 || cp === 58 || cp === 160) return "CS";
|
|
372
|
+
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";
|
|
373
|
+
if (cp >= 1536 && cp <= 1791) return "AL";
|
|
374
|
+
if (cp >= 1872 && cp <= 1919) return "AL";
|
|
375
|
+
if (cp >= 2208 && cp <= 2303) return "AL";
|
|
376
|
+
if (cp >= 64336 && cp <= 65023) return "AL";
|
|
377
|
+
if (cp >= 65136 && cp <= 65278) return "AL";
|
|
378
|
+
if (cp >= 1488 && cp <= 1514) return "R";
|
|
379
|
+
if (cp >= 1520 && cp <= 1524) return "R";
|
|
380
|
+
if (cp >= 1792 && cp <= 1871) return "R";
|
|
381
|
+
if (cp >= 1920 && cp <= 1983) return "R";
|
|
382
|
+
if (cp >= 64285 && cp <= 64335) return "R";
|
|
383
|
+
if (cp >= 65 && cp <= 90) return "L";
|
|
384
|
+
if (cp >= 97 && cp <= 122) return "L";
|
|
385
|
+
if (cp >= 192 && cp <= 591) return "L";
|
|
386
|
+
if (cp >= 880 && cp <= 1023) return "L";
|
|
387
|
+
if (cp >= 1024 && cp <= 1279) return "L";
|
|
388
|
+
if (cp >= 3584 && cp <= 3711) return "L";
|
|
389
|
+
if (cp >= 2304 && cp <= 2431) return "L";
|
|
390
|
+
if (cp >= 12352 && cp <= 12543) return "L";
|
|
391
|
+
if (cp >= 19968 && cp <= 40959) return "L";
|
|
392
|
+
if (cp >= 44032 && cp <= 55215) return "L";
|
|
393
|
+
if (cp >= 33 && cp <= 47) return "ON";
|
|
394
|
+
if (cp >= 58 && cp <= 64) return "ON";
|
|
395
|
+
if (cp >= 91 && cp <= 96) return "ON";
|
|
396
|
+
if (cp >= 123 && cp <= 126) return "ON";
|
|
397
|
+
if (cp >= 161 && cp <= 191) return "ON";
|
|
398
|
+
if (cp >= 8208 && cp <= 8231) return "ON";
|
|
399
|
+
if (cp >= 8240 && cp <= 8286) return "ON";
|
|
400
|
+
return "L";
|
|
406
401
|
}
|
|
407
|
-
function
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
if (cp > 65535) i++;
|
|
412
|
-
if (cp >= 48 && cp <= 57) w += 556;
|
|
413
|
-
else if (cp >= 65 && cp <= 90) w += 680;
|
|
414
|
-
else if (cp >= 97 && cp <= 122) w += 500;
|
|
415
|
-
else if (cp === 32) w += 278;
|
|
416
|
-
else if (cp === 46 || cp === 44) w += 278;
|
|
417
|
-
else if (cp === 43) w += 584;
|
|
418
|
-
else if (cp === 45) w += 333;
|
|
419
|
-
else if (cp === 47 || cp === 58) w += 278;
|
|
420
|
-
else if (cp === 8212) w += 1e3;
|
|
421
|
-
else if (cp === 8211) w += 556;
|
|
422
|
-
else if (cp === 8230) w += 1e3;
|
|
423
|
-
else if (cp === 8216 || cp === 8217) w += 222;
|
|
424
|
-
else if (cp === 8220 || cp === 8221) w += 333;
|
|
425
|
-
else if (cp === 8364) w += 556;
|
|
426
|
-
else w += 556;
|
|
402
|
+
function detectParagraphLevel(types) {
|
|
403
|
+
for (const t of types) {
|
|
404
|
+
if (t === "L") return 0;
|
|
405
|
+
if (t === "R" || t === "AL") return 1;
|
|
427
406
|
}
|
|
428
|
-
return
|
|
407
|
+
return 0;
|
|
429
408
|
}
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
for (const entry of entries) {
|
|
438
|
-
const compCount = entry.length - 1;
|
|
439
|
-
if (compCount > gids.length - 1) continue;
|
|
440
|
-
let match = true;
|
|
441
|
-
for (let ci = 0; ci < compCount; ci++) {
|
|
442
|
-
if (gids[1 + ci] !== entry[1 + ci]) {
|
|
443
|
-
match = false;
|
|
444
|
-
break;
|
|
445
|
-
}
|
|
409
|
+
function resolveWeakTypes(types, paraLevel) {
|
|
410
|
+
const len = types.length;
|
|
411
|
+
let prevType = paraLevel === 0 ? "L" : "R";
|
|
412
|
+
for (let i = 0; i < len; i++) {
|
|
413
|
+
if (types[i] === "BN") continue;
|
|
414
|
+
if (types[i] === "NSM") {
|
|
415
|
+
types[i] = prevType;
|
|
446
416
|
}
|
|
447
|
-
|
|
448
|
-
}
|
|
449
|
-
return null;
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
// src/shaping/bengali-shaper.ts
|
|
453
|
-
var HALANT = 2509;
|
|
454
|
-
var NUKTA = 2492;
|
|
455
|
-
var RA = 2480;
|
|
456
|
-
function bengaliCharType(cp) {
|
|
457
|
-
if (cp === HALANT) return 7;
|
|
458
|
-
if (cp === NUKTA) return 8;
|
|
459
|
-
if (cp >= 2433 && cp <= 2435) return 6;
|
|
460
|
-
if (cp >= 2437 && cp <= 2452) return 1;
|
|
461
|
-
if (cp >= 2453 && cp <= 2489) return 0;
|
|
462
|
-
if (cp === 2495) return 4;
|
|
463
|
-
if (cp === 2503) return 4;
|
|
464
|
-
if (cp === 2504) return 4;
|
|
465
|
-
if (cp === 2497 || cp === 2498 || cp === 2499 || cp === 2500) return 3;
|
|
466
|
-
if (cp === 2494) return 5;
|
|
467
|
-
if (cp === 2496) return 5;
|
|
468
|
-
if (cp === 2507) return 4;
|
|
469
|
-
if (cp === 2508) return 4;
|
|
470
|
-
if (cp >= 2494 && cp <= 2508) return 2;
|
|
471
|
-
if (cp === 2519) return 5;
|
|
472
|
-
if (cp >= 2534 && cp <= 2543) return 9;
|
|
473
|
-
if (cp === 2527) return 0;
|
|
474
|
-
if (cp === 2493) return 1;
|
|
475
|
-
if (cp === 2510) return 0;
|
|
476
|
-
return -1;
|
|
477
|
-
}
|
|
478
|
-
function isConsonant(cp) {
|
|
479
|
-
return bengaliCharType(cp) === 0;
|
|
480
|
-
}
|
|
481
|
-
function buildBengaliClusters(str) {
|
|
482
|
-
const clusters = [];
|
|
483
|
-
const cps = [];
|
|
484
|
-
for (let i2 = 0; i2 < str.length; ) {
|
|
485
|
-
const cp = str.codePointAt(i2) ?? 0;
|
|
486
|
-
cps.push(cp);
|
|
487
|
-
i2 += cp > 65535 ? 2 : 1;
|
|
417
|
+
prevType = types[i];
|
|
488
418
|
}
|
|
489
|
-
let
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
i
|
|
496
|
-
continue;
|
|
419
|
+
let lastStrong = paraLevel === 0 ? "L" : "R";
|
|
420
|
+
for (let i = 0; i < len; i++) {
|
|
421
|
+
if (types[i] === "BN") continue;
|
|
422
|
+
if (types[i] === "R" || types[i] === "L" || types[i] === "AL") {
|
|
423
|
+
lastStrong = types[i];
|
|
424
|
+
} else if (types[i] === "EN" && lastStrong === "AL") {
|
|
425
|
+
types[i] = "AN";
|
|
497
426
|
}
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
i
|
|
427
|
+
}
|
|
428
|
+
for (let i = 0; i < len; i++) {
|
|
429
|
+
if (types[i] === "AL") types[i] = "R";
|
|
430
|
+
}
|
|
431
|
+
for (let i = 1; i < len - 1; i++) {
|
|
432
|
+
if (types[i] === "BN") continue;
|
|
433
|
+
if (types[i] === "ES" && types[i - 1] === "EN" && types[i + 1] === "EN") {
|
|
434
|
+
types[i] = "EN";
|
|
435
|
+
} else if (types[i] === "CS") {
|
|
436
|
+
if (types[i - 1] === "EN" && types[i + 1] === "EN") types[i] = "EN";
|
|
437
|
+
else if (types[i - 1] === "AN" && types[i + 1] === "AN") types[i] = "AN";
|
|
506
438
|
}
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
if (i < cps.length && cps[i] === NUKTA) {
|
|
516
|
-
syllable.push(cps[i]);
|
|
517
|
-
i++;
|
|
439
|
+
}
|
|
440
|
+
for (let i = 0; i < len; i++) {
|
|
441
|
+
if (types[i] === "ET") {
|
|
442
|
+
let found = false;
|
|
443
|
+
for (let j = i - 1; j >= 0; j--) {
|
|
444
|
+
if (types[j] === "EN") {
|
|
445
|
+
found = true;
|
|
446
|
+
break;
|
|
518
447
|
}
|
|
519
|
-
if (
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
syllable.push(cps[i]);
|
|
526
|
-
i++;
|
|
448
|
+
if (types[j] !== "ET" && types[j] !== "BN") break;
|
|
449
|
+
}
|
|
450
|
+
if (!found) {
|
|
451
|
+
for (let j = i + 1; j < len; j++) {
|
|
452
|
+
if (types[j] === "EN") {
|
|
453
|
+
found = true;
|
|
527
454
|
break;
|
|
528
455
|
}
|
|
456
|
+
if (types[j] !== "ET" && types[j] !== "BN") break;
|
|
529
457
|
}
|
|
530
|
-
break;
|
|
531
|
-
} else {
|
|
532
|
-
break;
|
|
533
458
|
}
|
|
459
|
+
if (found) types[i] = "EN";
|
|
534
460
|
}
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
461
|
+
}
|
|
462
|
+
for (let i = 0; i < len; i++) {
|
|
463
|
+
if (types[i] === "ES" || types[i] === "ET" || types[i] === "CS") {
|
|
464
|
+
types[i] = "ON";
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
lastStrong = paraLevel === 0 ? "L" : "R";
|
|
468
|
+
for (let i = 0; i < len; i++) {
|
|
469
|
+
if (types[i] === "BN") continue;
|
|
470
|
+
if (types[i] === "L" || types[i] === "R") {
|
|
471
|
+
lastStrong = types[i];
|
|
472
|
+
} else if (types[i] === "EN" && lastStrong === "L") {
|
|
473
|
+
types[i] = "L";
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
function resolveNeutralTypes(types, paraLevel) {
|
|
478
|
+
const len = types.length;
|
|
479
|
+
const paraDir = paraLevel === 0 ? "L" : "R";
|
|
480
|
+
for (let i = 0; i < len; i++) {
|
|
481
|
+
if (types[i] !== "ON" && types[i] !== "WS" && types[i] !== "BN") continue;
|
|
482
|
+
const start = i;
|
|
483
|
+
while (i < len && (types[i] === "ON" || types[i] === "WS" || types[i] === "BN")) i++;
|
|
484
|
+
const end = i;
|
|
485
|
+
let prevStrong = paraDir;
|
|
486
|
+
for (let j = start - 1; j >= 0; j--) {
|
|
487
|
+
if (types[j] === "L" || types[j] === "R" || types[j] === "EN" || types[j] === "AN") {
|
|
488
|
+
prevStrong = types[j] === "EN" || types[j] === "AN" ? "R" : types[j];
|
|
545
489
|
break;
|
|
546
490
|
}
|
|
547
491
|
}
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
492
|
+
let nextStrong = paraDir;
|
|
493
|
+
for (let j = end; j < len; j++) {
|
|
494
|
+
if (types[j] === "L" || types[j] === "R" || types[j] === "EN" || types[j] === "AN") {
|
|
495
|
+
nextStrong = types[j] === "EN" || types[j] === "AN" ? "R" : types[j];
|
|
496
|
+
break;
|
|
497
|
+
}
|
|
551
498
|
}
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
499
|
+
const resolved = prevStrong === nextStrong ? prevStrong : paraDir;
|
|
500
|
+
for (let j = start; j < end; j++) {
|
|
501
|
+
types[j] = resolved;
|
|
555
502
|
}
|
|
556
|
-
clusters.push({
|
|
557
|
-
codepoints: syllable,
|
|
558
|
-
baseIndex: hasReph ? baseIdx + 2 : baseIdx,
|
|
559
|
-
// Adjust for reph prefix
|
|
560
|
-
hasReph,
|
|
561
|
-
preBaseMatras: preMatras
|
|
562
|
-
});
|
|
563
503
|
}
|
|
564
|
-
return clusters;
|
|
565
504
|
}
|
|
566
|
-
function
|
|
567
|
-
const
|
|
568
|
-
const
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
return cmap[normCp] || 0;
|
|
572
|
-
}
|
|
573
|
-
function resolveGidGsub(cp) {
|
|
574
|
-
const gid = resolveGid(cp);
|
|
575
|
-
if (gsub[gid] !== void 0) return gsub[gid];
|
|
576
|
-
return gid;
|
|
577
|
-
}
|
|
578
|
-
function tryLig(gids) {
|
|
579
|
-
return tryLigature(gids, ligatures);
|
|
580
|
-
}
|
|
581
|
-
function getAdv(gid) {
|
|
582
|
-
return widths[gid] !== void 0 ? widths[gid] : defaultWidth;
|
|
583
|
-
}
|
|
584
|
-
function getBaseAnchor2(baseGid, markClass) {
|
|
585
|
-
const base = markAnchors && markAnchors.bases && markAnchors.bases[baseGid];
|
|
586
|
-
if (!base) return null;
|
|
587
|
-
return base[markClass] ?? null;
|
|
588
|
-
}
|
|
589
|
-
function getMarkAnchor2(markGid) {
|
|
590
|
-
const mark = markAnchors && markAnchors.marks && markAnchors.marks[markGid];
|
|
591
|
-
if (!mark) return null;
|
|
592
|
-
return { classIdx: mark[0], x: mark[1], y: mark[2] };
|
|
593
|
-
}
|
|
594
|
-
function emitGlyph(gid, isZero, baseGid) {
|
|
595
|
-
if (isZero && baseGid !== void 0) {
|
|
596
|
-
const markAnchor = getMarkAnchor2(gid);
|
|
597
|
-
if (markAnchor) {
|
|
598
|
-
const baseAnchorPt = getBaseAnchor2(baseGid, markAnchor.classIdx);
|
|
599
|
-
if (baseAnchorPt) {
|
|
600
|
-
const baseAdv = getAdv(baseGid);
|
|
601
|
-
shaped.push({
|
|
602
|
-
gid,
|
|
603
|
-
dx: baseAnchorPt[0] - markAnchor.x - baseAdv,
|
|
604
|
-
dy: baseAnchorPt[1] - markAnchor.y,
|
|
605
|
-
isZeroAdvance: true
|
|
606
|
-
});
|
|
607
|
-
return;
|
|
608
|
-
}
|
|
609
|
-
}
|
|
610
|
-
shaped.push({ gid, dx: 0, dy: 0, isZeroAdvance: true });
|
|
505
|
+
function assignLevels(types, paraLevel) {
|
|
506
|
+
const levels = [];
|
|
507
|
+
for (const t of types) {
|
|
508
|
+
if (paraLevel === 0) {
|
|
509
|
+
levels.push(t === "R" || t === "AN" ? 1 : 0);
|
|
611
510
|
} else {
|
|
612
|
-
|
|
511
|
+
levels.push(t === "L" ? 2 : 1);
|
|
613
512
|
}
|
|
614
513
|
}
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
514
|
+
return levels;
|
|
515
|
+
}
|
|
516
|
+
var SENTENCE_PUNCT = /* @__PURE__ */ new Set([
|
|
517
|
+
46,
|
|
518
|
+
// .
|
|
519
|
+
44,
|
|
520
|
+
// ,
|
|
521
|
+
59,
|
|
522
|
+
// ;
|
|
523
|
+
58,
|
|
524
|
+
// :
|
|
525
|
+
33,
|
|
526
|
+
// !
|
|
527
|
+
63
|
|
528
|
+
// ?
|
|
529
|
+
]);
|
|
530
|
+
function fixPunctuationAffinity(types, codePoints, len) {
|
|
531
|
+
for (let i = 1; i < len; i++) {
|
|
532
|
+
if (types[i] === "R" && SENTENCE_PUNCT.has(codePoints[i])) {
|
|
533
|
+
let prevIdx = i - 1;
|
|
534
|
+
while (prevIdx >= 0 && (types[prevIdx] === "WS" || types[prevIdx] === "BN")) prevIdx--;
|
|
535
|
+
if (prevIdx >= 0 && types[prevIdx] === "L") {
|
|
536
|
+
types[i] = "L";
|
|
626
537
|
}
|
|
627
538
|
}
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
function fixBracketPairing(types, codePoints, len) {
|
|
542
|
+
const OPEN_BRACKETS = {
|
|
543
|
+
40: 41,
|
|
544
|
+
// ( → )
|
|
545
|
+
91: 93,
|
|
546
|
+
// [ → ]
|
|
547
|
+
123: 125
|
|
548
|
+
// { → }
|
|
549
|
+
};
|
|
550
|
+
for (let i = 0; i < len; i++) {
|
|
551
|
+
const closer = OPEN_BRACKETS[codePoints[i]];
|
|
552
|
+
if (closer === void 0) continue;
|
|
553
|
+
let depth = 1;
|
|
554
|
+
let closeIdx = -1;
|
|
555
|
+
for (let j = i + 1; j < len; j++) {
|
|
556
|
+
if (codePoints[j] === codePoints[i]) depth++;
|
|
557
|
+
else if (codePoints[j] === closer) {
|
|
558
|
+
depth--;
|
|
559
|
+
if (depth === 0) {
|
|
560
|
+
closeIdx = j;
|
|
561
|
+
break;
|
|
640
562
|
}
|
|
641
563
|
}
|
|
642
564
|
}
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
let
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
if (ct === 0 || ct === 7 || ct === 8) {
|
|
649
|
-
clusterGids.push(resolveGid(codepoints[ci]));
|
|
650
|
-
clusterEndIdx.push(ci);
|
|
651
|
-
} else if (ct < 0 || ct === 1 || ct === 9) {
|
|
652
|
-
emitGlyph(resolveGid(codepoints[ci]), false);
|
|
653
|
-
} else {
|
|
654
|
-
matraStart = ci;
|
|
565
|
+
if (closeIdx === -1) continue;
|
|
566
|
+
let hasL = false;
|
|
567
|
+
for (let j = i + 1; j < closeIdx; j++) {
|
|
568
|
+
if (types[j] === "L") {
|
|
569
|
+
hasL = true;
|
|
655
570
|
break;
|
|
656
571
|
}
|
|
657
572
|
}
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
573
|
+
if (hasL) {
|
|
574
|
+
types[i] = "L";
|
|
575
|
+
types[closeIdx] = "L";
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
function findOutermostIsolatePairs(codePoints) {
|
|
580
|
+
const pairs = [];
|
|
581
|
+
let i = 0;
|
|
582
|
+
while (i < codePoints.length) {
|
|
583
|
+
const cp = codePoints[i];
|
|
584
|
+
if (cp === 8294 || cp === 8295 || cp === 8296) {
|
|
585
|
+
let depth = 1;
|
|
586
|
+
let close = -1;
|
|
587
|
+
for (let j = i + 1; j < codePoints.length; j++) {
|
|
588
|
+
const cj = codePoints[j];
|
|
589
|
+
if (cj === 8294 || cj === 8295 || cj === 8296) depth++;
|
|
590
|
+
else if (cj === 8297) {
|
|
591
|
+
depth--;
|
|
592
|
+
if (depth === 0) {
|
|
593
|
+
close = j;
|
|
594
|
+
break;
|
|
678
595
|
}
|
|
679
|
-
gi++;
|
|
680
596
|
}
|
|
681
597
|
}
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
const ct = bengaliCharType(cp);
|
|
686
|
-
if (ct === 0) {
|
|
687
|
-
emitGlyph(resolveGid(cp), false);
|
|
688
|
-
} else if (ct === 7) {
|
|
689
|
-
emitGlyph(resolveGid(cp), true, baseGid);
|
|
690
|
-
} else if (ct === 8) {
|
|
691
|
-
emitGlyph(resolveGid(cp), true, baseGid);
|
|
692
|
-
}
|
|
598
|
+
if (close === -1) {
|
|
599
|
+
i++;
|
|
600
|
+
continue;
|
|
693
601
|
}
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
602
|
+
const kind = cp === 8294 ? "LRI" : cp === 8295 ? "RLI" : "FSI";
|
|
603
|
+
pairs.push({ open: i, close, kind });
|
|
604
|
+
i = close + 1;
|
|
605
|
+
} else {
|
|
606
|
+
i++;
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
return pairs;
|
|
610
|
+
}
|
|
611
|
+
function normalizeBidiEmbeddings(text) {
|
|
612
|
+
const LRE = 8234, RLE = 8235, PDF_CP = 8236, LRO = 8237, RLO = 8238;
|
|
613
|
+
const LRI = 8294, RLI = 8295, PDI = 8297;
|
|
614
|
+
let hasEmbed = false;
|
|
615
|
+
for (let i = 0; i < text.length; i++) {
|
|
616
|
+
const c = text.charCodeAt(i);
|
|
617
|
+
if (c === LRE || c === RLE || c === PDF_CP || c === LRO || c === RLO) {
|
|
618
|
+
hasEmbed = true;
|
|
619
|
+
break;
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
if (!hasEmbed) return text;
|
|
623
|
+
const stack = [];
|
|
624
|
+
const out = [];
|
|
625
|
+
const MAX_DEPTH = 125;
|
|
626
|
+
for (let i = 0; i < text.length; ) {
|
|
627
|
+
const cp = text.codePointAt(i) ?? 0;
|
|
628
|
+
const cpLen = cp > 65535 ? 2 : 1;
|
|
629
|
+
if (cp === LRE || cp === RLO || cp === LRO || cp === RLE) {
|
|
630
|
+
if (stack.length >= MAX_DEPTH) {
|
|
631
|
+
i += cpLen;
|
|
632
|
+
continue;
|
|
709
633
|
}
|
|
634
|
+
stack.push(1);
|
|
635
|
+
out.push(cp === LRE || cp === LRO ? LRI : RLI);
|
|
636
|
+
i += cpLen;
|
|
637
|
+
} else if (cp === PDF_CP) {
|
|
638
|
+
if (stack.pop()) {
|
|
639
|
+
out.push(PDI);
|
|
640
|
+
}
|
|
641
|
+
i += cpLen;
|
|
642
|
+
} else {
|
|
643
|
+
out.push(cp);
|
|
644
|
+
i += cpLen;
|
|
710
645
|
}
|
|
711
|
-
|
|
712
|
-
|
|
646
|
+
}
|
|
647
|
+
let result = "";
|
|
648
|
+
for (let i = 0; i < out.length; i++) result += String.fromCodePoint(out[i]);
|
|
649
|
+
return result;
|
|
650
|
+
}
|
|
651
|
+
function resolveBidiRuns(text) {
|
|
652
|
+
if (!text) return [];
|
|
653
|
+
const normalized = normalizeBidiEmbeddings(text);
|
|
654
|
+
if (normalized !== text) {
|
|
655
|
+
return resolveBidiRuns(normalized);
|
|
656
|
+
}
|
|
657
|
+
const codePoints = [];
|
|
658
|
+
const cpToStr = [];
|
|
659
|
+
for (let i = 0; i < text.length; ) {
|
|
660
|
+
cpToStr.push(i);
|
|
661
|
+
const cp = text.codePointAt(i) ?? 0;
|
|
662
|
+
codePoints.push(cp);
|
|
663
|
+
i += cp > 65535 ? 2 : 1;
|
|
664
|
+
}
|
|
665
|
+
cpToStr.push(text.length);
|
|
666
|
+
const isolates = findOutermostIsolatePairs(codePoints);
|
|
667
|
+
if (isolates.length === 0) {
|
|
668
|
+
return resolveBidiCore(text, codePoints, cpToStr);
|
|
669
|
+
}
|
|
670
|
+
const insideIsolate = new Array(codePoints.length).fill(false);
|
|
671
|
+
for (const p of isolates) {
|
|
672
|
+
for (let k = p.open; k <= p.close; k++) insideIsolate[k] = true;
|
|
673
|
+
}
|
|
674
|
+
const outerTypes = codePoints.map((cp, idx) => insideIsolate[idx] ? "BN" : classifyBidiType(cp));
|
|
675
|
+
const parentLevel = detectParagraphLevel(outerTypes);
|
|
676
|
+
const out = [];
|
|
677
|
+
const emitSegment = (cpStart, cpEnd, forced) => {
|
|
678
|
+
if (cpStart >= cpEnd) return;
|
|
679
|
+
const segText = text.substring(cpToStr[cpStart], cpToStr[cpEnd]);
|
|
680
|
+
const segCps = codePoints.slice(cpStart, cpEnd);
|
|
681
|
+
const baseStrIdx = cpToStr[cpStart];
|
|
682
|
+
const segCpToStr = cpToStr.slice(cpStart, cpEnd + 1).map((x) => x - baseStrIdx);
|
|
683
|
+
const segRuns = forced === void 0 ? resolveBidiRuns(segText) : resolveBidiCore(segText, segCps, segCpToStr, forced);
|
|
684
|
+
for (const r of segRuns) {
|
|
685
|
+
out.push({ text: r.text, level: r.level, start: r.start + baseStrIdx });
|
|
713
686
|
}
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
687
|
+
};
|
|
688
|
+
let cursor = 0;
|
|
689
|
+
for (const pair of isolates) {
|
|
690
|
+
emitSegment(cursor, pair.open, parentLevel);
|
|
691
|
+
const innerStart = pair.open + 1;
|
|
692
|
+
const innerEnd = pair.close;
|
|
693
|
+
let innerLevel;
|
|
694
|
+
if (pair.kind === "LRI") innerLevel = 0;
|
|
695
|
+
else if (pair.kind === "RLI") innerLevel = 1;
|
|
696
|
+
else {
|
|
697
|
+
const innerTypes = codePoints.slice(innerStart, innerEnd).map(classifyBidiType);
|
|
698
|
+
innerLevel = detectParagraphLevel(innerTypes);
|
|
699
|
+
}
|
|
700
|
+
if (innerStart < innerEnd) {
|
|
701
|
+
const innerText = text.substring(cpToStr[innerStart], cpToStr[innerEnd]);
|
|
702
|
+
const innerRuns = resolveBidiRunsForced(innerText, innerLevel);
|
|
703
|
+
const baseStrIdx = cpToStr[innerStart];
|
|
704
|
+
for (const r of innerRuns) {
|
|
705
|
+
out.push({ text: r.text, level: r.level, start: r.start + baseStrIdx });
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
cursor = pair.close + 1;
|
|
709
|
+
}
|
|
710
|
+
emitSegment(cursor, codePoints.length, parentLevel);
|
|
711
|
+
return out;
|
|
712
|
+
}
|
|
713
|
+
function resolveBidiRunsForced(text, forcedLevel) {
|
|
714
|
+
if (!text) return [];
|
|
715
|
+
const codePoints = [];
|
|
716
|
+
const cpToStr = [];
|
|
717
|
+
for (let i = 0; i < text.length; ) {
|
|
718
|
+
cpToStr.push(i);
|
|
719
|
+
const cp = text.codePointAt(i) ?? 0;
|
|
720
|
+
codePoints.push(cp);
|
|
721
|
+
i += cp > 65535 ? 2 : 1;
|
|
722
|
+
}
|
|
723
|
+
cpToStr.push(text.length);
|
|
724
|
+
const isolates = findOutermostIsolatePairs(codePoints);
|
|
725
|
+
if (isolates.length === 0) {
|
|
726
|
+
return resolveBidiCore(text, codePoints, cpToStr, forcedLevel);
|
|
727
|
+
}
|
|
728
|
+
const out = [];
|
|
729
|
+
const emit = (cpStart, cpEnd, forced) => {
|
|
730
|
+
if (cpStart >= cpEnd) return;
|
|
731
|
+
const segText = text.substring(cpToStr[cpStart], cpToStr[cpEnd]);
|
|
732
|
+
const segCps = codePoints.slice(cpStart, cpEnd);
|
|
733
|
+
const baseStrIdx = cpToStr[cpStart];
|
|
734
|
+
const segCpToStr = cpToStr.slice(cpStart, cpEnd + 1).map((x) => x - baseStrIdx);
|
|
735
|
+
const segRuns = resolveBidiCore(segText, segCps, segCpToStr, forced);
|
|
736
|
+
for (const r of segRuns) {
|
|
737
|
+
out.push({ text: r.text, level: r.level, start: r.start + baseStrIdx });
|
|
738
|
+
}
|
|
739
|
+
};
|
|
740
|
+
let cursor = 0;
|
|
741
|
+
for (const pair of isolates) {
|
|
742
|
+
emit(cursor, pair.open, forcedLevel);
|
|
743
|
+
const innerStart = pair.open + 1;
|
|
744
|
+
const innerEnd = pair.close;
|
|
745
|
+
let innerLevel;
|
|
746
|
+
if (pair.kind === "LRI") innerLevel = 0;
|
|
747
|
+
else if (pair.kind === "RLI") innerLevel = 1;
|
|
748
|
+
else {
|
|
749
|
+
const innerTypes = codePoints.slice(innerStart, innerEnd).map(classifyBidiType);
|
|
750
|
+
innerLevel = detectParagraphLevel(innerTypes);
|
|
751
|
+
}
|
|
752
|
+
if (innerStart < innerEnd) {
|
|
753
|
+
const innerText = text.substring(cpToStr[innerStart], cpToStr[innerEnd]);
|
|
754
|
+
const innerRuns = resolveBidiRunsForced(innerText, innerLevel);
|
|
755
|
+
const baseStrIdx = cpToStr[innerStart];
|
|
756
|
+
for (const r of innerRuns) {
|
|
757
|
+
out.push({ text: r.text, level: r.level, start: r.start + baseStrIdx });
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
cursor = pair.close + 1;
|
|
761
|
+
}
|
|
762
|
+
emit(cursor, codePoints.length, forcedLevel);
|
|
763
|
+
return out;
|
|
764
|
+
}
|
|
765
|
+
function resolveBidiCore(text, codePoints, cpToStr, forcedLevel) {
|
|
766
|
+
const len = codePoints.length;
|
|
767
|
+
if (len === 0) return [];
|
|
768
|
+
const types = codePoints.map(classifyBidiType);
|
|
769
|
+
const paraLevel = forcedLevel !== void 0 ? forcedLevel : detectParagraphLevel(types);
|
|
770
|
+
resolveWeakTypes(types, paraLevel);
|
|
771
|
+
resolveNeutralTypes(types, paraLevel);
|
|
772
|
+
if (paraLevel === 1) {
|
|
773
|
+
fixPunctuationAffinity(types, codePoints, len);
|
|
774
|
+
fixBracketPairing(types, codePoints, len);
|
|
775
|
+
}
|
|
776
|
+
const levels = assignLevels(types, paraLevel);
|
|
777
|
+
const runs = [];
|
|
778
|
+
let runStart = 0;
|
|
779
|
+
let runLevel = levels[0];
|
|
780
|
+
for (let i = 1; i <= len; i++) {
|
|
781
|
+
if (i === len || levels[i] !== runLevel) {
|
|
782
|
+
const start = cpToStr[runStart];
|
|
783
|
+
const end = cpToStr[i];
|
|
784
|
+
let runText = text.substring(start, end);
|
|
785
|
+
if (runLevel % 2 === 1) {
|
|
786
|
+
runText = reverseString(runText);
|
|
787
|
+
}
|
|
788
|
+
runs.push({ text: runText, level: runLevel, start });
|
|
789
|
+
if (i < len) {
|
|
790
|
+
runStart = i;
|
|
791
|
+
runLevel = levels[i];
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
if (paraLevel === 1 && runs.length > 1) {
|
|
796
|
+
runs.reverse();
|
|
797
|
+
}
|
|
798
|
+
return runs;
|
|
799
|
+
}
|
|
800
|
+
function containsRTL(text) {
|
|
801
|
+
for (let i = 0; i < text.length; ) {
|
|
802
|
+
const cp = text.codePointAt(i) ?? 0;
|
|
803
|
+
const t = classifyBidiType(cp);
|
|
804
|
+
if (t === "R" || t === "AL") return true;
|
|
805
|
+
i += cp > 65535 ? 2 : 1;
|
|
806
|
+
}
|
|
807
|
+
return false;
|
|
808
|
+
}
|
|
809
|
+
function stripBidiControls(text) {
|
|
810
|
+
if (!text) return text;
|
|
811
|
+
let needs = false;
|
|
812
|
+
for (let i = 0; i < text.length; i++) {
|
|
813
|
+
const c = text.charCodeAt(i);
|
|
814
|
+
if (c === 8206 || c === 8207 || c >= 8234 && c <= 8238 || c >= 8294 && c <= 8297) {
|
|
815
|
+
needs = true;
|
|
816
|
+
break;
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
if (!needs) return text;
|
|
820
|
+
let out = "";
|
|
821
|
+
for (let i = 0; i < text.length; i++) {
|
|
822
|
+
const c = text.charCodeAt(i);
|
|
823
|
+
if (c === 8206 || c === 8207 || c >= 8234 && c <= 8238 || c >= 8294 && c <= 8297) continue;
|
|
824
|
+
out += text[i];
|
|
825
|
+
}
|
|
826
|
+
return out;
|
|
827
|
+
}
|
|
828
|
+
function reverseString(str) {
|
|
829
|
+
const cps = [];
|
|
830
|
+
for (let i = 0; i < str.length; ) {
|
|
831
|
+
const cp = str.codePointAt(i) ?? 0;
|
|
832
|
+
cps.push(cp);
|
|
833
|
+
i += cp > 65535 ? 2 : 1;
|
|
834
|
+
}
|
|
835
|
+
cps.reverse();
|
|
836
|
+
return String.fromCodePoint(...cps);
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
// src/fonts/encoding.ts
|
|
840
|
+
function toWinAnsi(str) {
|
|
841
|
+
if (!str) return "";
|
|
842
|
+
let r = "";
|
|
843
|
+
for (let i = 0; i < str.length; i++) {
|
|
844
|
+
const c = str.charCodeAt(i);
|
|
845
|
+
if (c >= 32 && c <= 126) r += str[i];
|
|
846
|
+
else if (c >= 160 && c <= 255) r += str[i];
|
|
847
|
+
else if (c === 8364) r += "\x80";
|
|
848
|
+
else if (c === 8218) r += "\x82";
|
|
849
|
+
else if (c === 402) r += "\x83";
|
|
850
|
+
else if (c === 8222) r += "\x84";
|
|
851
|
+
else if (c === 8230) r += "\x85";
|
|
852
|
+
else if (c === 8224) r += "\x86";
|
|
853
|
+
else if (c === 8225) r += "\x87";
|
|
854
|
+
else if (c === 710) r += "\x88";
|
|
855
|
+
else if (c === 8240) r += "\x89";
|
|
856
|
+
else if (c === 352) r += "\x8A";
|
|
857
|
+
else if (c === 8249) r += "\x8B";
|
|
858
|
+
else if (c === 338) r += "\x8C";
|
|
859
|
+
else if (c === 381) r += "\x8E";
|
|
860
|
+
else if (c === 8216) r += "\x91";
|
|
861
|
+
else if (c === 8217) r += "\x92";
|
|
862
|
+
else if (c === 8220) r += "\x93";
|
|
863
|
+
else if (c === 8221) r += "\x94";
|
|
864
|
+
else if (c === 8226) r += "\x95";
|
|
865
|
+
else if (c === 8211) r += "\x96";
|
|
866
|
+
else if (c === 8212) r += "\x97";
|
|
867
|
+
else if (c === 732) r += "\x98";
|
|
868
|
+
else if (c === 8482) r += "\x99";
|
|
869
|
+
else if (c === 353) r += "\x9A";
|
|
870
|
+
else if (c === 8250) r += "\x9B";
|
|
871
|
+
else if (c === 339) r += "\x9C";
|
|
872
|
+
else if (c === 382) r += "\x9E";
|
|
873
|
+
else if (c === 376) r += "\x9F";
|
|
874
|
+
else if (c === 160 || c === 8239) r += " ";
|
|
875
|
+
else if (c === 9 || c === 10 || c === 13) r += " ";
|
|
876
|
+
else if (c < 32) ; else r += "?";
|
|
877
|
+
}
|
|
878
|
+
return r;
|
|
879
|
+
}
|
|
880
|
+
function pdfString(str) {
|
|
881
|
+
const s = toWinAnsi(stripBidiControls(str));
|
|
882
|
+
return "(" + s.replace(/\\/g, "\\\\").replace(/\(/g, "\\(").replace(/\)/g, "\\)") + ")";
|
|
883
|
+
}
|
|
884
|
+
function truncate(str, max) {
|
|
885
|
+
if (!str || str.length <= max) return str || "";
|
|
886
|
+
if (max <= 1) return "\u2026";
|
|
887
|
+
return str.slice(0, max - 1) + "\u2026";
|
|
888
|
+
}
|
|
889
|
+
function helveticaWidth(str, sz) {
|
|
890
|
+
str = stripBidiControls(str);
|
|
891
|
+
let w = 0;
|
|
892
|
+
for (let i = 0; i < str.length; i++) {
|
|
893
|
+
const cp = str.codePointAt(i) ?? 0;
|
|
894
|
+
if (cp > 65535) i++;
|
|
895
|
+
if (cp >= 48 && cp <= 57) w += 556;
|
|
896
|
+
else if (cp >= 65 && cp <= 90) w += 680;
|
|
897
|
+
else if (cp >= 97 && cp <= 122) w += 500;
|
|
898
|
+
else if (cp === 32) w += 278;
|
|
899
|
+
else if (cp === 46 || cp === 44) w += 278;
|
|
900
|
+
else if (cp === 43) w += 584;
|
|
901
|
+
else if (cp === 45) w += 333;
|
|
902
|
+
else if (cp === 47 || cp === 58) w += 278;
|
|
903
|
+
else if (cp === 8212) w += 1e3;
|
|
904
|
+
else if (cp === 8211) w += 556;
|
|
905
|
+
else if (cp === 8230) w += 1e3;
|
|
906
|
+
else if (cp === 8216 || cp === 8217) w += 222;
|
|
907
|
+
else if (cp === 8220 || cp === 8221) w += 333;
|
|
908
|
+
else if (cp === 8364) w += 556;
|
|
909
|
+
else w += 556;
|
|
910
|
+
}
|
|
911
|
+
return w * sz / 1e3;
|
|
912
|
+
}
|
|
913
|
+
function helveticaBoldWidth(str, sz) {
|
|
914
|
+
str = stripBidiControls(str);
|
|
915
|
+
let w = 0;
|
|
916
|
+
for (let i = 0; i < str.length; i++) {
|
|
917
|
+
const cp = str.codePointAt(i) ?? 0;
|
|
918
|
+
if (cp > 65535) i++;
|
|
919
|
+
if (cp >= 48 && cp <= 57) w += 556;
|
|
920
|
+
else if (cp >= 65 && cp <= 90) w += 722;
|
|
921
|
+
else if (cp >= 97 && cp <= 122) w += 611;
|
|
922
|
+
else if (cp === 32) w += 278;
|
|
923
|
+
else if (cp === 46 || cp === 44) w += 278;
|
|
924
|
+
else if (cp === 43) w += 584;
|
|
925
|
+
else if (cp === 45) w += 333;
|
|
926
|
+
else if (cp === 47 || cp === 58) w += 278;
|
|
927
|
+
else if (cp === 8212) w += 1e3;
|
|
928
|
+
else if (cp === 8211) w += 556;
|
|
929
|
+
else if (cp === 8230) w += 1e3;
|
|
930
|
+
else if (cp === 8216 || cp === 8217) w += 278;
|
|
931
|
+
else if (cp === 8220 || cp === 8221) w += 500;
|
|
932
|
+
else if (cp === 8364) w += 556;
|
|
933
|
+
else w += 611;
|
|
934
|
+
}
|
|
935
|
+
return w * sz / 1e3;
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
// src/shaping/gsub-driver.ts
|
|
939
|
+
function tryLigature(gids, ligatures) {
|
|
940
|
+
if (!ligatures || gids.length < 2) return null;
|
|
941
|
+
const firstGid = gids[0];
|
|
942
|
+
const entries = ligatures[firstGid];
|
|
943
|
+
if (!entries) return null;
|
|
944
|
+
for (const entry of entries) {
|
|
945
|
+
const compCount = entry.length - 1;
|
|
946
|
+
if (compCount > gids.length - 1) continue;
|
|
947
|
+
let match = true;
|
|
948
|
+
for (let ci = 0; ci < compCount; ci++) {
|
|
949
|
+
if (gids[1 + ci] !== entry[1 + ci]) {
|
|
950
|
+
match = false;
|
|
951
|
+
break;
|
|
952
|
+
}
|
|
719
953
|
}
|
|
954
|
+
if (match) return { resultGid: entry[0], consumed: compCount + 1 };
|
|
720
955
|
}
|
|
721
|
-
return
|
|
956
|
+
return null;
|
|
722
957
|
}
|
|
723
958
|
|
|
724
|
-
// src/shaping/
|
|
725
|
-
var
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
if (cp
|
|
730
|
-
if (cp
|
|
731
|
-
if (cp
|
|
732
|
-
if (cp
|
|
733
|
-
if (cp
|
|
734
|
-
if (cp ===
|
|
735
|
-
if (cp ===
|
|
736
|
-
if (cp ===
|
|
737
|
-
if (cp ===
|
|
738
|
-
if (cp ===
|
|
739
|
-
if (cp ===
|
|
740
|
-
if (cp ===
|
|
741
|
-
if (cp ===
|
|
742
|
-
if (cp >=
|
|
743
|
-
if (cp
|
|
744
|
-
if (cp >=
|
|
745
|
-
if (cp ===
|
|
959
|
+
// src/shaping/bengali-shaper.ts
|
|
960
|
+
var HALANT = 2509;
|
|
961
|
+
var NUKTA = 2492;
|
|
962
|
+
var RA = 2480;
|
|
963
|
+
function bengaliCharType(cp) {
|
|
964
|
+
if (cp === HALANT) return 7;
|
|
965
|
+
if (cp === NUKTA) return 8;
|
|
966
|
+
if (cp >= 2433 && cp <= 2435) return 6;
|
|
967
|
+
if (cp >= 2437 && cp <= 2452) return 1;
|
|
968
|
+
if (cp >= 2453 && cp <= 2489) return 0;
|
|
969
|
+
if (cp === 2495) return 4;
|
|
970
|
+
if (cp === 2503) return 4;
|
|
971
|
+
if (cp === 2504) return 4;
|
|
972
|
+
if (cp === 2497 || cp === 2498 || cp === 2499 || cp === 2500) return 3;
|
|
973
|
+
if (cp === 2494) return 5;
|
|
974
|
+
if (cp === 2496) return 5;
|
|
975
|
+
if (cp === 2507) return 4;
|
|
976
|
+
if (cp === 2508) return 4;
|
|
977
|
+
if (cp >= 2494 && cp <= 2508) return 2;
|
|
978
|
+
if (cp === 2519) return 5;
|
|
979
|
+
if (cp >= 2534 && cp <= 2543) return 9;
|
|
980
|
+
if (cp === 2527) return 0;
|
|
981
|
+
if (cp === 2493) return 1;
|
|
982
|
+
if (cp === 2510) return 0;
|
|
746
983
|
return -1;
|
|
747
984
|
}
|
|
748
|
-
function
|
|
749
|
-
return
|
|
985
|
+
function isConsonant(cp) {
|
|
986
|
+
return bengaliCharType(cp) === 0;
|
|
750
987
|
}
|
|
751
|
-
function
|
|
988
|
+
function buildBengaliClusters(str) {
|
|
752
989
|
const clusters = [];
|
|
753
990
|
const cps = [];
|
|
754
991
|
for (let i2 = 0; i2 < str.length; ) {
|
|
@@ -759,24 +996,35 @@ function buildTamilClusters(str) {
|
|
|
759
996
|
let i = 0;
|
|
760
997
|
while (i < cps.length) {
|
|
761
998
|
const cp = cps[i];
|
|
762
|
-
const type =
|
|
763
|
-
if (type < 0 || cp <
|
|
764
|
-
clusters.push({ codepoints: [cp], baseIndex: 0, preBaseMatras: [] });
|
|
999
|
+
const type = bengaliCharType(cp);
|
|
1000
|
+
if (type < 0 || cp < BENGALI_START || cp > BENGALI_END) {
|
|
1001
|
+
clusters.push({ codepoints: [cp], baseIndex: 0, hasReph: false, preBaseMatras: [] });
|
|
765
1002
|
i++;
|
|
766
1003
|
continue;
|
|
767
1004
|
}
|
|
768
1005
|
const syllable = [];
|
|
769
|
-
let
|
|
1006
|
+
let baseIdx = 0;
|
|
1007
|
+
let hasReph = false;
|
|
770
1008
|
const preMatras = [];
|
|
1009
|
+
if (isConsonant(cp) && cp === RA && i + 2 < cps.length && cps[i + 1] === HALANT && isConsonant(cps[i + 2])) {
|
|
1010
|
+
hasReph = true;
|
|
1011
|
+
syllable.push(cp, cps[i + 1]);
|
|
1012
|
+
i += 2;
|
|
1013
|
+
}
|
|
1014
|
+
let lastConsonantIdx = -1;
|
|
771
1015
|
while (i < cps.length) {
|
|
772
1016
|
const cc = cps[i];
|
|
773
|
-
const ct =
|
|
1017
|
+
const ct = bengaliCharType(cc);
|
|
774
1018
|
if (ct === 0) {
|
|
775
1019
|
lastConsonantIdx = syllable.length;
|
|
776
1020
|
syllable.push(cc);
|
|
777
1021
|
i++;
|
|
778
|
-
if (i < cps.length && cps[i] ===
|
|
779
|
-
|
|
1022
|
+
if (i < cps.length && cps[i] === NUKTA) {
|
|
1023
|
+
syllable.push(cps[i]);
|
|
1024
|
+
i++;
|
|
1025
|
+
}
|
|
1026
|
+
if (i < cps.length && cps[i] === HALANT) {
|
|
1027
|
+
if (i + 1 < cps.length && isConsonant(cps[i + 1])) {
|
|
780
1028
|
syllable.push(cps[i]);
|
|
781
1029
|
i++;
|
|
782
1030
|
continue;
|
|
@@ -791,9 +1039,9 @@ function buildTamilClusters(str) {
|
|
|
791
1039
|
break;
|
|
792
1040
|
}
|
|
793
1041
|
}
|
|
794
|
-
|
|
1042
|
+
baseIdx = lastConsonantIdx >= 0 ? lastConsonantIdx : 0;
|
|
795
1043
|
while (i < cps.length) {
|
|
796
|
-
const ct =
|
|
1044
|
+
const ct = bengaliCharType(cps[i]);
|
|
797
1045
|
if (ct >= 2 && ct <= 5) {
|
|
798
1046
|
if (ct === 4) {
|
|
799
1047
|
preMatras.push(syllable.length);
|
|
@@ -804,7 +1052,7 @@ function buildTamilClusters(str) {
|
|
|
804
1052
|
break;
|
|
805
1053
|
}
|
|
806
1054
|
}
|
|
807
|
-
while (i < cps.length &&
|
|
1055
|
+
while (i < cps.length && bengaliCharType(cps[i]) === 6) {
|
|
808
1056
|
syllable.push(cps[i]);
|
|
809
1057
|
i++;
|
|
810
1058
|
}
|
|
@@ -814,19 +1062,26 @@ function buildTamilClusters(str) {
|
|
|
814
1062
|
}
|
|
815
1063
|
clusters.push({
|
|
816
1064
|
codepoints: syllable,
|
|
817
|
-
baseIndex: baseIdx,
|
|
1065
|
+
baseIndex: hasReph ? baseIdx + 2 : baseIdx,
|
|
1066
|
+
// Adjust for reph prefix
|
|
1067
|
+
hasReph,
|
|
818
1068
|
preBaseMatras: preMatras
|
|
819
1069
|
});
|
|
820
1070
|
}
|
|
821
1071
|
return clusters;
|
|
822
1072
|
}
|
|
823
|
-
function
|
|
824
|
-
const { cmap, ligatures, markAnchors, widths, defaultWidth } = fontData;
|
|
1073
|
+
function shapeBengaliText(str, fontData) {
|
|
1074
|
+
const { cmap, gsub, ligatures, markAnchors, widths, defaultWidth } = fontData;
|
|
825
1075
|
const shaped = [];
|
|
826
1076
|
function resolveGid(cp) {
|
|
827
1077
|
const normCp = cp === 8239 || cp === 160 ? 32 : cp;
|
|
828
1078
|
return cmap[normCp] || 0;
|
|
829
1079
|
}
|
|
1080
|
+
function resolveGidGsub(cp) {
|
|
1081
|
+
const gid = resolveGid(cp);
|
|
1082
|
+
if (gsub[gid] !== void 0) return gsub[gid];
|
|
1083
|
+
return gid;
|
|
1084
|
+
}
|
|
830
1085
|
function tryLig(gids) {
|
|
831
1086
|
return tryLigature(gids, ligatures);
|
|
832
1087
|
}
|
|
@@ -864,12 +1119,13 @@ function shapeTamilText(str, fontData) {
|
|
|
864
1119
|
shaped.push({ gid, dx: 0, dy: 0, isZeroAdvance: false });
|
|
865
1120
|
}
|
|
866
1121
|
}
|
|
867
|
-
const clusters =
|
|
1122
|
+
const clusters = buildBengaliClusters(str);
|
|
868
1123
|
for (const cluster of clusters) {
|
|
869
|
-
const { codepoints, preBaseMatras } = cluster;
|
|
1124
|
+
const { codepoints, hasReph, preBaseMatras } = cluster;
|
|
1125
|
+
const baseStart = hasReph ? 2 : 0;
|
|
870
1126
|
let baseGid = 0;
|
|
871
|
-
for (let ci =
|
|
872
|
-
const ct =
|
|
1127
|
+
for (let ci = baseStart; ci < codepoints.length; ci++) {
|
|
1128
|
+
const ct = bengaliCharType(codepoints[ci]);
|
|
873
1129
|
if (ct === 0) {
|
|
874
1130
|
baseGid = resolveGid(codepoints[ci]);
|
|
875
1131
|
} else if (ct >= 2) {
|
|
@@ -880,15 +1136,12 @@ function shapeTamilText(str, fontData) {
|
|
|
880
1136
|
for (const mIdx of preBaseMatras) {
|
|
881
1137
|
if (mIdx < codepoints.length) {
|
|
882
1138
|
const mCp = codepoints[mIdx];
|
|
883
|
-
if (mCp ===
|
|
884
|
-
emitGlyph(resolveGid(
|
|
885
|
-
splitPostComponents.push(
|
|
886
|
-
} else if (mCp ===
|
|
887
|
-
emitGlyph(resolveGid(
|
|
888
|
-
splitPostComponents.push(
|
|
889
|
-
} else if (mCp === 3020) {
|
|
890
|
-
emitGlyph(resolveGid(3014), false);
|
|
891
|
-
splitPostComponents.push(3031);
|
|
1139
|
+
if (mCp === 2507) {
|
|
1140
|
+
emitGlyph(resolveGid(2503), false);
|
|
1141
|
+
splitPostComponents.push(2494);
|
|
1142
|
+
} else if (mCp === 2508) {
|
|
1143
|
+
emitGlyph(resolveGid(2503), false);
|
|
1144
|
+
splitPostComponents.push(2519);
|
|
892
1145
|
} else {
|
|
893
1146
|
emitGlyph(resolveGid(mCp), false);
|
|
894
1147
|
}
|
|
@@ -897,18 +1150,16 @@ function shapeTamilText(str, fontData) {
|
|
|
897
1150
|
const clusterGids = [];
|
|
898
1151
|
const clusterEndIdx = [];
|
|
899
1152
|
let matraStart = codepoints.length;
|
|
900
|
-
for (let ci =
|
|
901
|
-
const ct =
|
|
902
|
-
if (ct === 0 || ct === 7) {
|
|
1153
|
+
for (let ci = baseStart; ci < codepoints.length; ci++) {
|
|
1154
|
+
const ct = bengaliCharType(codepoints[ci]);
|
|
1155
|
+
if (ct === 0 || ct === 7 || ct === 8) {
|
|
903
1156
|
clusterGids.push(resolveGid(codepoints[ci]));
|
|
904
1157
|
clusterEndIdx.push(ci);
|
|
905
|
-
} else if (ct
|
|
1158
|
+
} else if (ct < 0 || ct === 1 || ct === 9) {
|
|
1159
|
+
emitGlyph(resolveGid(codepoints[ci]), false);
|
|
1160
|
+
} else {
|
|
906
1161
|
matraStart = ci;
|
|
907
1162
|
break;
|
|
908
|
-
} else if (ct < 0) {
|
|
909
|
-
emitGlyph(resolveGid(codepoints[ci]), false);
|
|
910
|
-
} else if (ct === 1) {
|
|
911
|
-
emitGlyph(resolveGid(codepoints[ci]), false);
|
|
912
1163
|
}
|
|
913
1164
|
}
|
|
914
1165
|
let ligConsumed = 0;
|
|
@@ -926,7 +1177,7 @@ function shapeTamilText(str, fontData) {
|
|
|
926
1177
|
gi += subLig.consumed;
|
|
927
1178
|
} else {
|
|
928
1179
|
const origCi = clusterEndIdx[gi];
|
|
929
|
-
const ct =
|
|
1180
|
+
const ct = bengaliCharType(codepoints[origCi]);
|
|
930
1181
|
if (ct === 7) {
|
|
931
1182
|
emitGlyph(clusterGids[gi], true, baseGid);
|
|
932
1183
|
} else {
|
|
@@ -936,19 +1187,21 @@ function shapeTamilText(str, fontData) {
|
|
|
936
1187
|
}
|
|
937
1188
|
}
|
|
938
1189
|
} else {
|
|
939
|
-
for (let ci =
|
|
1190
|
+
for (let ci = baseStart; ci < matraStart; ci++) {
|
|
940
1191
|
const cp = codepoints[ci];
|
|
941
|
-
const ct =
|
|
1192
|
+
const ct = bengaliCharType(cp);
|
|
942
1193
|
if (ct === 0) {
|
|
943
1194
|
emitGlyph(resolveGid(cp), false);
|
|
944
1195
|
} else if (ct === 7) {
|
|
945
1196
|
emitGlyph(resolveGid(cp), true, baseGid);
|
|
1197
|
+
} else if (ct === 8) {
|
|
1198
|
+
emitGlyph(resolveGid(cp), true, baseGid);
|
|
946
1199
|
}
|
|
947
1200
|
}
|
|
948
1201
|
}
|
|
949
1202
|
for (let ci = matraStart; ci < codepoints.length; ci++) {
|
|
950
1203
|
const cp = codepoints[ci];
|
|
951
|
-
const ct =
|
|
1204
|
+
const ct = bengaliCharType(cp);
|
|
952
1205
|
if (ct === 4) continue;
|
|
953
1206
|
if (ct === 2 || ct === 3) {
|
|
954
1207
|
emitGlyph(resolveGid(cp), true, baseGid);
|
|
@@ -965,60 +1218,44 @@ function shapeTamilText(str, fontData) {
|
|
|
965
1218
|
for (const postCp of splitPostComponents) {
|
|
966
1219
|
emitGlyph(resolveGid(postCp), false);
|
|
967
1220
|
}
|
|
1221
|
+
if (hasReph) {
|
|
1222
|
+
const raGid = resolveGidGsub(RA);
|
|
1223
|
+
const halantGid = resolveGid(HALANT);
|
|
1224
|
+
emitGlyph(raGid, true, baseGid);
|
|
1225
|
+
emitGlyph(halantGid, true, baseGid);
|
|
1226
|
+
}
|
|
968
1227
|
}
|
|
969
1228
|
return shaped;
|
|
970
1229
|
}
|
|
971
1230
|
|
|
972
|
-
// src/shaping/
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
if (
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
if (
|
|
981
|
-
|
|
982
|
-
if (
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
if (
|
|
988
|
-
|
|
989
|
-
if (
|
|
990
|
-
return
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
}
|
|
995
|
-
|
|
996
|
-
// src/shaping/devanagari-shaper.ts
|
|
997
|
-
var HALANT2 = 2381;
|
|
998
|
-
var NUKTA2 = 2364;
|
|
999
|
-
var RA2 = 2352;
|
|
1000
|
-
function devanagariCharType(cp) {
|
|
1001
|
-
if (cp === HALANT2) return 7;
|
|
1002
|
-
if (cp === NUKTA2) return 8;
|
|
1003
|
-
if (cp >= 2305 && cp <= 2307) return 6;
|
|
1004
|
-
if (cp >= 2308 && cp <= 2324) return 1;
|
|
1005
|
-
if (cp >= 2325 && cp <= 2361) return 0;
|
|
1006
|
-
if (cp === 2367) return 4;
|
|
1007
|
-
if (cp >= 2369 && cp <= 2372) return 3;
|
|
1008
|
-
if (cp === 2366 || cp === 2368) return 5;
|
|
1009
|
-
if (cp === 2375 || cp === 2376) return 2;
|
|
1010
|
-
if (cp === 2379 || cp === 2380) return 4;
|
|
1011
|
-
if (cp >= 2373 && cp <= 2380) return 2;
|
|
1012
|
-
if (cp >= 2406 && cp <= 2415) return 9;
|
|
1013
|
-
if (cp >= 2392 && cp <= 2399) return 0;
|
|
1014
|
-
if (cp === 2365) return 1;
|
|
1015
|
-
if (cp === 2384) return 1;
|
|
1231
|
+
// src/shaping/tamil-shaper.ts
|
|
1232
|
+
var PULLI = 3021;
|
|
1233
|
+
function tamilCharType(cp) {
|
|
1234
|
+
if (cp === PULLI) return 7;
|
|
1235
|
+
if (cp === 2946 || cp === 2947) return 6;
|
|
1236
|
+
if (cp >= 2949 && cp <= 2964) return 1;
|
|
1237
|
+
if (cp >= 2965 && cp <= 3001) return 0;
|
|
1238
|
+
if (cp === 3007) return 4;
|
|
1239
|
+
if (cp === 3014) return 4;
|
|
1240
|
+
if (cp === 3015) return 4;
|
|
1241
|
+
if (cp === 3016) return 4;
|
|
1242
|
+
if (cp === 3018) return 4;
|
|
1243
|
+
if (cp === 3019) return 4;
|
|
1244
|
+
if (cp === 3020) return 4;
|
|
1245
|
+
if (cp === 3006) return 5;
|
|
1246
|
+
if (cp === 3008) return 5;
|
|
1247
|
+
if (cp === 3009 || cp === 3010) return 2;
|
|
1248
|
+
if (cp === 3031) return 5;
|
|
1249
|
+
if (cp >= 3006 && cp <= 3020) return 2;
|
|
1250
|
+
if (cp >= 3046 && cp <= 3055) return 9;
|
|
1251
|
+
if (cp >= 3056 && cp <= 3066) return 9;
|
|
1252
|
+
if (cp === 3024) return 6;
|
|
1016
1253
|
return -1;
|
|
1017
1254
|
}
|
|
1018
|
-
function
|
|
1019
|
-
return
|
|
1255
|
+
function isConsonant2(cp) {
|
|
1256
|
+
return tamilCharType(cp) === 0;
|
|
1020
1257
|
}
|
|
1021
|
-
function
|
|
1258
|
+
function buildTamilClusters(str) {
|
|
1022
1259
|
const clusters = [];
|
|
1023
1260
|
const cps = [];
|
|
1024
1261
|
for (let i2 = 0; i2 < str.length; ) {
|
|
@@ -1029,34 +1266,24 @@ function buildDevanagariClusters(str) {
|
|
|
1029
1266
|
let i = 0;
|
|
1030
1267
|
while (i < cps.length) {
|
|
1031
1268
|
const cp = cps[i];
|
|
1032
|
-
const type =
|
|
1033
|
-
if (type < 0 || cp <
|
|
1034
|
-
clusters.push({ codepoints: [cp], baseIndex: 0,
|
|
1269
|
+
const type = tamilCharType(cp);
|
|
1270
|
+
if (type < 0 || cp < TAMIL_START || cp > TAMIL_END) {
|
|
1271
|
+
clusters.push({ codepoints: [cp], baseIndex: 0, preBaseMatras: [] });
|
|
1035
1272
|
i++;
|
|
1036
1273
|
continue;
|
|
1037
1274
|
}
|
|
1038
1275
|
const syllable = [];
|
|
1039
|
-
let hasReph = false;
|
|
1040
|
-
const preMatras = [];
|
|
1041
|
-
if (isConsonant3(cp) && cp === RA2 && i + 2 < cps.length && cps[i + 1] === HALANT2 && isConsonant3(cps[i + 2])) {
|
|
1042
|
-
hasReph = true;
|
|
1043
|
-
syllable.push(cp, cps[i + 1]);
|
|
1044
|
-
i += 2;
|
|
1045
|
-
}
|
|
1046
1276
|
let lastConsonantIdx = -1;
|
|
1277
|
+
const preMatras = [];
|
|
1047
1278
|
while (i < cps.length) {
|
|
1048
1279
|
const cc = cps[i];
|
|
1049
|
-
const ct =
|
|
1280
|
+
const ct = tamilCharType(cc);
|
|
1050
1281
|
if (ct === 0) {
|
|
1051
1282
|
lastConsonantIdx = syllable.length;
|
|
1052
1283
|
syllable.push(cc);
|
|
1053
1284
|
i++;
|
|
1054
|
-
if (i < cps.length && cps[i] ===
|
|
1055
|
-
|
|
1056
|
-
i++;
|
|
1057
|
-
}
|
|
1058
|
-
if (i < cps.length && cps[i] === HALANT2) {
|
|
1059
|
-
if (i + 1 < cps.length && isConsonant3(cps[i + 1])) {
|
|
1285
|
+
if (i < cps.length && cps[i] === PULLI) {
|
|
1286
|
+
if (i + 1 < cps.length && isConsonant2(cps[i + 1])) {
|
|
1060
1287
|
syllable.push(cps[i]);
|
|
1061
1288
|
i++;
|
|
1062
1289
|
continue;
|
|
@@ -1073,7 +1300,7 @@ function buildDevanagariClusters(str) {
|
|
|
1073
1300
|
}
|
|
1074
1301
|
const baseIdx = lastConsonantIdx >= 0 ? lastConsonantIdx : 0;
|
|
1075
1302
|
while (i < cps.length) {
|
|
1076
|
-
const ct =
|
|
1303
|
+
const ct = tamilCharType(cps[i]);
|
|
1077
1304
|
if (ct >= 2 && ct <= 5) {
|
|
1078
1305
|
if (ct === 4) {
|
|
1079
1306
|
preMatras.push(syllable.length);
|
|
@@ -1084,7 +1311,7 @@ function buildDevanagariClusters(str) {
|
|
|
1084
1311
|
break;
|
|
1085
1312
|
}
|
|
1086
1313
|
}
|
|
1087
|
-
while (i < cps.length &&
|
|
1314
|
+
while (i < cps.length && tamilCharType(cps[i]) === 6) {
|
|
1088
1315
|
syllable.push(cps[i]);
|
|
1089
1316
|
i++;
|
|
1090
1317
|
}
|
|
@@ -1094,15 +1321,14 @@ function buildDevanagariClusters(str) {
|
|
|
1094
1321
|
}
|
|
1095
1322
|
clusters.push({
|
|
1096
1323
|
codepoints: syllable,
|
|
1097
|
-
baseIndex:
|
|
1098
|
-
hasReph,
|
|
1324
|
+
baseIndex: baseIdx,
|
|
1099
1325
|
preBaseMatras: preMatras
|
|
1100
1326
|
});
|
|
1101
1327
|
}
|
|
1102
1328
|
return clusters;
|
|
1103
1329
|
}
|
|
1104
|
-
function
|
|
1105
|
-
const { cmap,
|
|
1330
|
+
function shapeTamilText(str, fontData) {
|
|
1331
|
+
const { cmap, ligatures, markAnchors, widths, defaultWidth } = fontData;
|
|
1106
1332
|
const shaped = [];
|
|
1107
1333
|
function resolveGid(cp) {
|
|
1108
1334
|
const normCp = cp === 8239 || cp === 160 ? 32 : cp;
|
|
@@ -1114,11 +1340,21 @@ function shapeDevanagariText(str, fontData) {
|
|
|
1114
1340
|
function getAdv(gid) {
|
|
1115
1341
|
return widths[gid] !== void 0 ? widths[gid] : defaultWidth;
|
|
1116
1342
|
}
|
|
1343
|
+
function getBaseAnchor2(baseGid, markClass) {
|
|
1344
|
+
const base = markAnchors && markAnchors.bases && markAnchors.bases[baseGid];
|
|
1345
|
+
if (!base) return null;
|
|
1346
|
+
return base[markClass] ?? null;
|
|
1347
|
+
}
|
|
1348
|
+
function getMarkAnchor2(markGid) {
|
|
1349
|
+
const mark = markAnchors && markAnchors.marks && markAnchors.marks[markGid];
|
|
1350
|
+
if (!mark) return null;
|
|
1351
|
+
return { classIdx: mark[0], x: mark[1], y: mark[2] };
|
|
1352
|
+
}
|
|
1117
1353
|
function emitGlyph(gid, isZero, baseGid) {
|
|
1118
1354
|
if (isZero && baseGid !== void 0) {
|
|
1119
|
-
const markAnchor =
|
|
1355
|
+
const markAnchor = getMarkAnchor2(gid);
|
|
1120
1356
|
if (markAnchor) {
|
|
1121
|
-
const baseAnchorPt =
|
|
1357
|
+
const baseAnchorPt = getBaseAnchor2(baseGid, markAnchor.classIdx);
|
|
1122
1358
|
if (baseAnchorPt) {
|
|
1123
1359
|
const baseAdv = getAdv(baseGid);
|
|
1124
1360
|
shaped.push({
|
|
@@ -1135,13 +1371,12 @@ function shapeDevanagariText(str, fontData) {
|
|
|
1135
1371
|
shaped.push({ gid, dx: 0, dy: 0, isZeroAdvance: false });
|
|
1136
1372
|
}
|
|
1137
1373
|
}
|
|
1138
|
-
const clusters =
|
|
1374
|
+
const clusters = buildTamilClusters(str);
|
|
1139
1375
|
for (const cluster of clusters) {
|
|
1140
|
-
const { codepoints,
|
|
1141
|
-
const baseStart = hasReph ? 2 : 0;
|
|
1376
|
+
const { codepoints, preBaseMatras } = cluster;
|
|
1142
1377
|
let baseGid = 0;
|
|
1143
|
-
for (let ci =
|
|
1144
|
-
const ct =
|
|
1378
|
+
for (let ci = 0; ci < codepoints.length; ci++) {
|
|
1379
|
+
const ct = tamilCharType(codepoints[ci]);
|
|
1145
1380
|
if (ct === 0) {
|
|
1146
1381
|
baseGid = resolveGid(codepoints[ci]);
|
|
1147
1382
|
} else if (ct >= 2) {
|
|
@@ -1152,48 +1387,44 @@ function shapeDevanagariText(str, fontData) {
|
|
|
1152
1387
|
for (const mIdx of preBaseMatras) {
|
|
1153
1388
|
if (mIdx < codepoints.length) {
|
|
1154
1389
|
const mCp = codepoints[mIdx];
|
|
1155
|
-
if (mCp ===
|
|
1156
|
-
emitGlyph(resolveGid(
|
|
1157
|
-
splitPostComponents.push(
|
|
1158
|
-
} else if (mCp ===
|
|
1159
|
-
emitGlyph(resolveGid(
|
|
1160
|
-
splitPostComponents.push(
|
|
1390
|
+
if (mCp === 3018) {
|
|
1391
|
+
emitGlyph(resolveGid(3014), false);
|
|
1392
|
+
splitPostComponents.push(3006);
|
|
1393
|
+
} else if (mCp === 3019) {
|
|
1394
|
+
emitGlyph(resolveGid(3015), false);
|
|
1395
|
+
splitPostComponents.push(3006);
|
|
1396
|
+
} else if (mCp === 3020) {
|
|
1397
|
+
emitGlyph(resolveGid(3014), false);
|
|
1398
|
+
splitPostComponents.push(3031);
|
|
1161
1399
|
} else {
|
|
1162
1400
|
emitGlyph(resolveGid(mCp), false);
|
|
1163
1401
|
}
|
|
1164
1402
|
}
|
|
1165
1403
|
}
|
|
1166
|
-
if (hasReph) {
|
|
1167
|
-
const raGid = resolveGid(RA2);
|
|
1168
|
-
const halantGid = resolveGid(HALANT2);
|
|
1169
|
-
const rephLig = tryLig([raGid, halantGid]);
|
|
1170
|
-
if (rephLig) {
|
|
1171
|
-
emitGlyph(rephLig.resultGid, true, baseGid);
|
|
1172
|
-
} else {
|
|
1173
|
-
const raGsubbed = gsub[raGid] !== void 0 ? gsub[raGid] : raGid;
|
|
1174
|
-
emitGlyph(raGsubbed, true, baseGid);
|
|
1175
|
-
}
|
|
1176
|
-
}
|
|
1177
1404
|
const clusterGids = [];
|
|
1178
1405
|
const clusterEndIdx = [];
|
|
1179
1406
|
let matraStart = codepoints.length;
|
|
1180
|
-
for (let ci =
|
|
1181
|
-
const ct =
|
|
1182
|
-
if (ct === 0 || ct === 7
|
|
1407
|
+
for (let ci = 0; ci < codepoints.length; ci++) {
|
|
1408
|
+
const ct = tamilCharType(codepoints[ci]);
|
|
1409
|
+
if (ct === 0 || ct === 7) {
|
|
1183
1410
|
clusterGids.push(resolveGid(codepoints[ci]));
|
|
1184
1411
|
clusterEndIdx.push(ci);
|
|
1185
|
-
} else if (ct
|
|
1186
|
-
emitGlyph(resolveGid(codepoints[ci]), false);
|
|
1187
|
-
} else {
|
|
1412
|
+
} else if (ct >= 2) {
|
|
1188
1413
|
matraStart = ci;
|
|
1189
1414
|
break;
|
|
1415
|
+
} else if (ct < 0) {
|
|
1416
|
+
emitGlyph(resolveGid(codepoints[ci]), false);
|
|
1417
|
+
} else if (ct === 1) {
|
|
1418
|
+
emitGlyph(resolveGid(codepoints[ci]), false);
|
|
1190
1419
|
}
|
|
1191
1420
|
}
|
|
1421
|
+
let ligConsumed = 0;
|
|
1192
1422
|
const ligResult = tryLig(clusterGids);
|
|
1193
1423
|
if (ligResult) {
|
|
1194
1424
|
emitGlyph(ligResult.resultGid, false);
|
|
1195
1425
|
baseGid = ligResult.resultGid;
|
|
1196
|
-
|
|
1426
|
+
ligConsumed = ligResult.consumed;
|
|
1427
|
+
let gi = ligConsumed;
|
|
1197
1428
|
while (gi < clusterGids.length) {
|
|
1198
1429
|
const subSeq = clusterGids.slice(gi);
|
|
1199
1430
|
const subLig = tryLig(subSeq);
|
|
@@ -1202,7 +1433,7 @@ function shapeDevanagariText(str, fontData) {
|
|
|
1202
1433
|
gi += subLig.consumed;
|
|
1203
1434
|
} else {
|
|
1204
1435
|
const origCi = clusterEndIdx[gi];
|
|
1205
|
-
const ct =
|
|
1436
|
+
const ct = tamilCharType(codepoints[origCi]);
|
|
1206
1437
|
if (ct === 7) {
|
|
1207
1438
|
emitGlyph(clusterGids[gi], true, baseGid);
|
|
1208
1439
|
} else {
|
|
@@ -1212,21 +1443,19 @@ function shapeDevanagariText(str, fontData) {
|
|
|
1212
1443
|
}
|
|
1213
1444
|
}
|
|
1214
1445
|
} else {
|
|
1215
|
-
for (let ci =
|
|
1446
|
+
for (let ci = 0; ci < matraStart; ci++) {
|
|
1216
1447
|
const cp = codepoints[ci];
|
|
1217
|
-
const ct =
|
|
1448
|
+
const ct = tamilCharType(cp);
|
|
1218
1449
|
if (ct === 0) {
|
|
1219
1450
|
emitGlyph(resolveGid(cp), false);
|
|
1220
1451
|
} else if (ct === 7) {
|
|
1221
1452
|
emitGlyph(resolveGid(cp), true, baseGid);
|
|
1222
|
-
} else if (ct === 8) {
|
|
1223
|
-
emitGlyph(resolveGid(cp), true, baseGid);
|
|
1224
1453
|
}
|
|
1225
1454
|
}
|
|
1226
1455
|
}
|
|
1227
1456
|
for (let ci = matraStart; ci < codepoints.length; ci++) {
|
|
1228
1457
|
const cp = codepoints[ci];
|
|
1229
|
-
const ct =
|
|
1458
|
+
const ct = tamilCharType(cp);
|
|
1230
1459
|
if (ct === 4) continue;
|
|
1231
1460
|
if (ct === 2 || ct === 3) {
|
|
1232
1461
|
emitGlyph(resolveGid(cp), true, baseGid);
|
|
@@ -1247,596 +1476,455 @@ function shapeDevanagariText(str, fontData) {
|
|
|
1247
1476
|
return shaped;
|
|
1248
1477
|
}
|
|
1249
1478
|
|
|
1250
|
-
// src/shaping/
|
|
1251
|
-
function
|
|
1252
|
-
if (
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
if (cp >= 1574 && cp <= 1576 || // YEH HAMZA, BA series
|
|
1257
|
-
cp >= 1578 && cp <= 1582 || // TA through KHA
|
|
1258
|
-
cp >= 1587 && cp <= 1594 || // SEEN through GHAIN
|
|
1259
|
-
cp >= 1601 && cp <= 1607 || // FA through HA
|
|
1260
|
-
cp === 1609 || // ALEF MAKSURA
|
|
1261
|
-
cp === 1610 || // YA
|
|
1262
|
-
cp === 1656 || // HIGH HAMZA YEH
|
|
1263
|
-
cp >= 1690 && cp <= 1727 || // Extended Arabic
|
|
1264
|
-
cp >= 1729 && cp <= 1731 || cp >= 1740 && cp <= 1742 || cp >= 1744 && cp <= 1747 || cp === 1749 || cp === 1786 || cp === 1787 || cp === 1788) return "D";
|
|
1265
|
-
if (cp === 1570 || cp === 1571 || cp === 1572 || cp === 1573 || cp === 1575 || // ALEF
|
|
1266
|
-
cp === 1577 || // TEH MARBUTA
|
|
1267
|
-
cp === 1583 || cp === 1584 || // DAL, THAL
|
|
1268
|
-
cp === 1585 || cp === 1586 || // RA, ZAIN
|
|
1269
|
-
cp === 1608 || // WAW
|
|
1270
|
-
cp >= 1649 && cp <= 1651 || cp === 1653 || cp === 1654 || cp === 1655 || cp >= 1672 && cp <= 1689 || // Extended DAL/RA series
|
|
1271
|
-
cp === 1728 || cp >= 1732 && cp <= 1739 || cp === 1743 || cp === 1774 || cp === 1775) return "R";
|
|
1272
|
-
if (cp >= ARABIC_START && cp <= ARABIC_END) return "U";
|
|
1273
|
-
return "U";
|
|
1479
|
+
// src/shaping/gpos-positioner.ts
|
|
1480
|
+
function getBaseAnchor(markAnchors, baseGid, markClass) {
|
|
1481
|
+
if (!markAnchors) return null;
|
|
1482
|
+
const base = markAnchors.bases[baseGid];
|
|
1483
|
+
if (!base) return null;
|
|
1484
|
+
return base[markClass] ?? null;
|
|
1274
1485
|
}
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1486
|
+
function getMarkAnchor(markAnchors, markGid) {
|
|
1487
|
+
if (!markAnchors) return null;
|
|
1488
|
+
const mark = markAnchors.marks[markGid];
|
|
1489
|
+
if (!mark) return null;
|
|
1490
|
+
return { classIdx: mark[0], x: mark[1], y: mark[2] };
|
|
1491
|
+
}
|
|
1492
|
+
function positionMarkOnBase(markAnchors, markGid, baseGid, baseAdv) {
|
|
1493
|
+
const mark = getMarkAnchor(markAnchors, markGid);
|
|
1494
|
+
if (!mark) return null;
|
|
1495
|
+
const base = getBaseAnchor(markAnchors, baseGid, mark.classIdx);
|
|
1496
|
+
if (!base) return null;
|
|
1497
|
+
return {
|
|
1498
|
+
dx: base[0] - mark.x - baseAdv,
|
|
1499
|
+
dy: base[1] - mark.y
|
|
1500
|
+
};
|
|
1501
|
+
}
|
|
1502
|
+
|
|
1503
|
+
// src/shaping/devanagari-shaper.ts
|
|
1504
|
+
var HALANT2 = 2381;
|
|
1505
|
+
var NUKTA2 = 2364;
|
|
1506
|
+
var RA2 = 2352;
|
|
1507
|
+
function devanagariCharType(cp) {
|
|
1508
|
+
if (cp === HALANT2) return 7;
|
|
1509
|
+
if (cp === NUKTA2) return 8;
|
|
1510
|
+
if (cp >= 2305 && cp <= 2307) return 6;
|
|
1511
|
+
if (cp >= 2308 && cp <= 2324) return 1;
|
|
1512
|
+
if (cp >= 2325 && cp <= 2361) return 0;
|
|
1513
|
+
if (cp === 2367) return 4;
|
|
1514
|
+
if (cp >= 2369 && cp <= 2372) return 3;
|
|
1515
|
+
if (cp === 2366 || cp === 2368) return 5;
|
|
1516
|
+
if (cp === 2375 || cp === 2376) return 2;
|
|
1517
|
+
if (cp === 2379 || cp === 2380) return 4;
|
|
1518
|
+
if (cp >= 2373 && cp <= 2380) return 2;
|
|
1519
|
+
if (cp >= 2406 && cp <= 2415) return 9;
|
|
1520
|
+
if (cp >= 2392 && cp <= 2399) return 0;
|
|
1521
|
+
if (cp === 2365) return 1;
|
|
1522
|
+
if (cp === 2384) return 1;
|
|
1523
|
+
return -1;
|
|
1524
|
+
}
|
|
1525
|
+
function isConsonant3(cp) {
|
|
1526
|
+
return devanagariCharType(cp) === 0;
|
|
1527
|
+
}
|
|
1528
|
+
function buildDevanagariClusters(str) {
|
|
1529
|
+
const clusters = [];
|
|
1530
|
+
const cps = [];
|
|
1531
|
+
for (let i2 = 0; i2 < str.length; ) {
|
|
1532
|
+
const cp = str.codePointAt(i2) ?? 0;
|
|
1533
|
+
cps.push(cp);
|
|
1534
|
+
i2 += cp > 65535 ? 2 : 1;
|
|
1535
|
+
}
|
|
1536
|
+
let i = 0;
|
|
1537
|
+
while (i < cps.length) {
|
|
1538
|
+
const cp = cps[i];
|
|
1539
|
+
const type = devanagariCharType(cp);
|
|
1540
|
+
if (type < 0 || cp < DEVANAGARI_START || cp > DEVANAGARI_END) {
|
|
1541
|
+
clusters.push({ codepoints: [cp], baseIndex: 0, hasReph: false, preBaseMatras: [] });
|
|
1542
|
+
i++;
|
|
1543
|
+
continue;
|
|
1544
|
+
}
|
|
1545
|
+
const syllable = [];
|
|
1546
|
+
let hasReph = false;
|
|
1547
|
+
const preMatras = [];
|
|
1548
|
+
if (isConsonant3(cp) && cp === RA2 && i + 2 < cps.length && cps[i + 1] === HALANT2 && isConsonant3(cps[i + 2])) {
|
|
1549
|
+
hasReph = true;
|
|
1550
|
+
syllable.push(cp, cps[i + 1]);
|
|
1551
|
+
i += 2;
|
|
1552
|
+
}
|
|
1553
|
+
let lastConsonantIdx = -1;
|
|
1554
|
+
while (i < cps.length) {
|
|
1555
|
+
const cc = cps[i];
|
|
1556
|
+
const ct = devanagariCharType(cc);
|
|
1557
|
+
if (ct === 0) {
|
|
1558
|
+
lastConsonantIdx = syllable.length;
|
|
1559
|
+
syllable.push(cc);
|
|
1560
|
+
i++;
|
|
1561
|
+
if (i < cps.length && cps[i] === NUKTA2) {
|
|
1562
|
+
syllable.push(cps[i]);
|
|
1563
|
+
i++;
|
|
1564
|
+
}
|
|
1565
|
+
if (i < cps.length && cps[i] === HALANT2) {
|
|
1566
|
+
if (i + 1 < cps.length && isConsonant3(cps[i + 1])) {
|
|
1567
|
+
syllable.push(cps[i]);
|
|
1568
|
+
i++;
|
|
1569
|
+
continue;
|
|
1570
|
+
} else {
|
|
1571
|
+
syllable.push(cps[i]);
|
|
1572
|
+
i++;
|
|
1573
|
+
break;
|
|
1574
|
+
}
|
|
1575
|
+
}
|
|
1333
1576
|
break;
|
|
1334
|
-
}
|
|
1335
|
-
}
|
|
1336
|
-
let nextJoin = "U";
|
|
1337
|
-
for (let j = i + 1; j < len; j++) {
|
|
1338
|
-
if (joining[j] !== "T") {
|
|
1339
|
-
nextJoin = joining[j];
|
|
1577
|
+
} else {
|
|
1340
1578
|
break;
|
|
1341
1579
|
}
|
|
1342
1580
|
}
|
|
1343
|
-
const
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
} else if (joinsToNext) {
|
|
1350
|
-
forms[i] = "init";
|
|
1351
|
-
} else {
|
|
1352
|
-
forms[i] = "isol";
|
|
1353
|
-
}
|
|
1354
|
-
}
|
|
1355
|
-
return forms;
|
|
1356
|
-
}
|
|
1357
|
-
var LAM = 1604;
|
|
1358
|
-
var ALEF_VARIANTS = /* @__PURE__ */ new Set([1570, 1571, 1573, 1575]);
|
|
1359
|
-
function isLamAlef(cp1, cp2) {
|
|
1360
|
-
return cp1 === LAM && ALEF_VARIANTS.has(cp2);
|
|
1361
|
-
}
|
|
1362
|
-
function shapeArabicText(str, fontData) {
|
|
1363
|
-
if (!str) return [];
|
|
1364
|
-
const codePoints = [];
|
|
1365
|
-
for (let i = 0; i < str.length; ) {
|
|
1366
|
-
const cp = str.codePointAt(i) ?? 0;
|
|
1367
|
-
codePoints.push(cp);
|
|
1368
|
-
i += cp > 65535 ? 2 : 1;
|
|
1369
|
-
}
|
|
1370
|
-
const forms = resolvePositionalForms(codePoints);
|
|
1371
|
-
const glyphs = [];
|
|
1372
|
-
const cmap = fontData.cmap;
|
|
1373
|
-
const widths = fontData.widths;
|
|
1374
|
-
const defaultWidth = fontData.defaultWidth;
|
|
1375
|
-
const markAnchors = fontData.markAnchors;
|
|
1376
|
-
let lastBaseGid = 0;
|
|
1377
|
-
for (let i = 0; i < codePoints.length; i++) {
|
|
1378
|
-
const cp = codePoints[i];
|
|
1379
|
-
if (i < codePoints.length - 1 && isLamAlef(cp, codePoints[i + 1])) {
|
|
1380
|
-
const ligForms = LAM_ALEF_PRES.get(codePoints[i + 1]);
|
|
1381
|
-
if (ligForms) {
|
|
1382
|
-
const isFinal = forms[i] === "medi" || forms[i] === "fina";
|
|
1383
|
-
const ligCP = isFinal ? ligForms[1] : ligForms[0];
|
|
1384
|
-
const ligGid = cmap[ligCP];
|
|
1385
|
-
if (ligGid) {
|
|
1386
|
-
glyphs.push({ gid: ligGid, dx: 0, dy: 0, isZeroAdvance: false });
|
|
1387
|
-
lastBaseGid = ligGid;
|
|
1388
|
-
i++;
|
|
1389
|
-
continue;
|
|
1581
|
+
const baseIdx = lastConsonantIdx >= 0 ? lastConsonantIdx : 0;
|
|
1582
|
+
while (i < cps.length) {
|
|
1583
|
+
const ct = devanagariCharType(cps[i]);
|
|
1584
|
+
if (ct >= 2 && ct <= 5) {
|
|
1585
|
+
if (ct === 4) {
|
|
1586
|
+
preMatras.push(syllable.length);
|
|
1390
1587
|
}
|
|
1588
|
+
syllable.push(cps[i]);
|
|
1589
|
+
i++;
|
|
1590
|
+
} else {
|
|
1591
|
+
break;
|
|
1391
1592
|
}
|
|
1392
1593
|
}
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
const form = forms[i];
|
|
1397
|
-
let presCP;
|
|
1398
|
-
if (form === "init") presCP = presForm.init;
|
|
1399
|
-
else if (form === "medi") presCP = presForm.medi;
|
|
1400
|
-
else if (form === "fina") presCP = presForm.fina;
|
|
1401
|
-
else presCP = presForm.isol;
|
|
1402
|
-
if (presCP) {
|
|
1403
|
-
const presGid = cmap[presCP];
|
|
1404
|
-
if (presGid) gid = presGid;
|
|
1405
|
-
}
|
|
1594
|
+
while (i < cps.length && devanagariCharType(cps[i]) === 6) {
|
|
1595
|
+
syllable.push(cps[i]);
|
|
1596
|
+
i++;
|
|
1406
1597
|
}
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
const baseAdv = widths[lastBaseGid] !== void 0 ? widths[lastBaseGid] : defaultWidth;
|
|
1411
|
-
const offset = positionMarkOnBase(markAnchors, gid, lastBaseGid, baseAdv);
|
|
1412
|
-
if (offset) {
|
|
1413
|
-
glyphs.push({ gid, dx: offset.dx, dy: offset.dy, isZeroAdvance: true });
|
|
1414
|
-
continue;
|
|
1415
|
-
}
|
|
1598
|
+
if (syllable.length === 0) {
|
|
1599
|
+
syllable.push(cps[i] ?? 32);
|
|
1600
|
+
i++;
|
|
1416
1601
|
}
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
// src/shaping/bidi.ts
|
|
1424
|
-
function classifyBidiType(cp) {
|
|
1425
|
-
if (cp >= 768 && cp <= 879 || // Combining Diacritical Marks
|
|
1426
|
-
cp >= 1425 && cp <= 1469 || // Hebrew marks
|
|
1427
|
-
cp >= 1471 && cp <= 1471 || cp >= 1473 && cp <= 1474 || cp >= 1476 && cp <= 1477 || cp >= 1479 && cp <= 1479 || cp >= 1552 && cp <= 1562 || // Arabic marks
|
|
1428
|
-
cp >= 1611 && cp <= 1631 || // Arabic harakat
|
|
1429
|
-
cp >= 1648 && cp <= 1648 || // Arabic superscript alef
|
|
1430
|
-
cp >= 1750 && cp <= 1756 || cp >= 1759 && cp <= 1764 || cp >= 1767 && cp <= 1768 || cp >= 1770 && cp <= 1773 || cp >= 65056 && cp <= 65071) return "NSM";
|
|
1431
|
-
if (cp === 8203 || cp === 8204 || cp === 8205 || cp === 8206 || cp === 8207 || cp === 65279 || cp === 8294 || cp === 8295 || cp === 8296 || cp === 8297) return "BN";
|
|
1432
|
-
if (cp >= 1632 && cp <= 1641) return "AN";
|
|
1433
|
-
if (cp >= 1776 && cp <= 1785) return "AN";
|
|
1434
|
-
if (cp >= 48 && cp <= 57) return "EN";
|
|
1435
|
-
if (cp === 43 || cp === 45) return "ES";
|
|
1436
|
-
if (cp === 35 || cp === 36 || cp === 37 || cp === 162 || cp === 163 || cp === 164 || cp === 165 || cp === 8364 || cp === 8377 || cp === 8378) return "ET";
|
|
1437
|
-
if (cp === 44 || cp === 46 || cp === 47 || cp === 58 || cp === 160) return "CS";
|
|
1438
|
-
if (cp === 32 || cp === 9 || cp === 10 || cp === 13 || cp === 12 || cp === 8192 || cp === 8202 || cp === 8232 || cp === 8233 || cp === 8239 || cp === 8287 || cp === 12288) return "WS";
|
|
1439
|
-
if (cp >= 1536 && cp <= 1791) return "AL";
|
|
1440
|
-
if (cp >= 1872 && cp <= 1919) return "AL";
|
|
1441
|
-
if (cp >= 2208 && cp <= 2303) return "AL";
|
|
1442
|
-
if (cp >= 64336 && cp <= 65023) return "AL";
|
|
1443
|
-
if (cp >= 65136 && cp <= 65278) return "AL";
|
|
1444
|
-
if (cp >= 1488 && cp <= 1514) return "R";
|
|
1445
|
-
if (cp >= 1520 && cp <= 1524) return "R";
|
|
1446
|
-
if (cp >= 1792 && cp <= 1871) return "R";
|
|
1447
|
-
if (cp >= 1920 && cp <= 1983) return "R";
|
|
1448
|
-
if (cp >= 64285 && cp <= 64335) return "R";
|
|
1449
|
-
if (cp >= 65 && cp <= 90) return "L";
|
|
1450
|
-
if (cp >= 97 && cp <= 122) return "L";
|
|
1451
|
-
if (cp >= 192 && cp <= 591) return "L";
|
|
1452
|
-
if (cp >= 880 && cp <= 1023) return "L";
|
|
1453
|
-
if (cp >= 1024 && cp <= 1279) return "L";
|
|
1454
|
-
if (cp >= 3584 && cp <= 3711) return "L";
|
|
1455
|
-
if (cp >= 2304 && cp <= 2431) return "L";
|
|
1456
|
-
if (cp >= 12352 && cp <= 12543) return "L";
|
|
1457
|
-
if (cp >= 19968 && cp <= 40959) return "L";
|
|
1458
|
-
if (cp >= 44032 && cp <= 55215) return "L";
|
|
1459
|
-
if (cp >= 33 && cp <= 47) return "ON";
|
|
1460
|
-
if (cp >= 58 && cp <= 64) return "ON";
|
|
1461
|
-
if (cp >= 91 && cp <= 96) return "ON";
|
|
1462
|
-
if (cp >= 123 && cp <= 126) return "ON";
|
|
1463
|
-
if (cp >= 161 && cp <= 191) return "ON";
|
|
1464
|
-
if (cp >= 8208 && cp <= 8231) return "ON";
|
|
1465
|
-
if (cp >= 8240 && cp <= 8286) return "ON";
|
|
1466
|
-
return "L";
|
|
1467
|
-
}
|
|
1468
|
-
function detectParagraphLevel(types) {
|
|
1469
|
-
for (const t of types) {
|
|
1470
|
-
if (t === "L") return 0;
|
|
1471
|
-
if (t === "R" || t === "AL") return 1;
|
|
1602
|
+
clusters.push({
|
|
1603
|
+
codepoints: syllable,
|
|
1604
|
+
baseIndex: hasReph ? baseIdx + 2 : baseIdx,
|
|
1605
|
+
hasReph,
|
|
1606
|
+
preBaseMatras: preMatras
|
|
1607
|
+
});
|
|
1472
1608
|
}
|
|
1473
|
-
return
|
|
1609
|
+
return clusters;
|
|
1474
1610
|
}
|
|
1475
|
-
function
|
|
1476
|
-
const
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
types[i] = prevType;
|
|
1482
|
-
}
|
|
1483
|
-
prevType = types[i];
|
|
1611
|
+
function shapeDevanagariText(str, fontData) {
|
|
1612
|
+
const { cmap, gsub, ligatures, markAnchors, widths, defaultWidth } = fontData;
|
|
1613
|
+
const shaped = [];
|
|
1614
|
+
function resolveGid(cp) {
|
|
1615
|
+
const normCp = cp === 8239 || cp === 160 ? 32 : cp;
|
|
1616
|
+
return cmap[normCp] || 0;
|
|
1484
1617
|
}
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
if (types[i] === "BN") continue;
|
|
1488
|
-
if (types[i] === "R" || types[i] === "L" || types[i] === "AL") {
|
|
1489
|
-
lastStrong = types[i];
|
|
1490
|
-
} else if (types[i] === "EN" && lastStrong === "AL") {
|
|
1491
|
-
types[i] = "AN";
|
|
1492
|
-
}
|
|
1618
|
+
function tryLig(gids) {
|
|
1619
|
+
return tryLigature(gids, ligatures);
|
|
1493
1620
|
}
|
|
1494
|
-
|
|
1495
|
-
|
|
1621
|
+
function getAdv(gid) {
|
|
1622
|
+
return widths[gid] !== void 0 ? widths[gid] : defaultWidth;
|
|
1496
1623
|
}
|
|
1497
|
-
|
|
1498
|
-
if (
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1624
|
+
function emitGlyph(gid, isZero, baseGid) {
|
|
1625
|
+
if (isZero && baseGid !== void 0) {
|
|
1626
|
+
const markAnchor = getMarkAnchor(markAnchors, gid);
|
|
1627
|
+
if (markAnchor) {
|
|
1628
|
+
const baseAnchorPt = getBaseAnchor(markAnchors, baseGid, markAnchor.classIdx);
|
|
1629
|
+
if (baseAnchorPt) {
|
|
1630
|
+
const baseAdv = getAdv(baseGid);
|
|
1631
|
+
shaped.push({
|
|
1632
|
+
gid,
|
|
1633
|
+
dx: baseAnchorPt[0] - markAnchor.x - baseAdv,
|
|
1634
|
+
dy: baseAnchorPt[1] - markAnchor.y,
|
|
1635
|
+
isZeroAdvance: true
|
|
1636
|
+
});
|
|
1637
|
+
return;
|
|
1638
|
+
}
|
|
1639
|
+
}
|
|
1640
|
+
shaped.push({ gid, dx: 0, dy: 0, isZeroAdvance: true });
|
|
1641
|
+
} else {
|
|
1642
|
+
shaped.push({ gid, dx: 0, dy: 0, isZeroAdvance: false });
|
|
1504
1643
|
}
|
|
1505
1644
|
}
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1645
|
+
const clusters = buildDevanagariClusters(str);
|
|
1646
|
+
for (const cluster of clusters) {
|
|
1647
|
+
const { codepoints, hasReph, preBaseMatras } = cluster;
|
|
1648
|
+
const baseStart = hasReph ? 2 : 0;
|
|
1649
|
+
let baseGid = 0;
|
|
1650
|
+
for (let ci = baseStart; ci < codepoints.length; ci++) {
|
|
1651
|
+
const ct = devanagariCharType(codepoints[ci]);
|
|
1652
|
+
if (ct === 0) {
|
|
1653
|
+
baseGid = resolveGid(codepoints[ci]);
|
|
1654
|
+
} else if (ct >= 2) {
|
|
1655
|
+
break;
|
|
1515
1656
|
}
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1657
|
+
}
|
|
1658
|
+
const splitPostComponents = [];
|
|
1659
|
+
for (const mIdx of preBaseMatras) {
|
|
1660
|
+
if (mIdx < codepoints.length) {
|
|
1661
|
+
const mCp = codepoints[mIdx];
|
|
1662
|
+
if (mCp === 2379) {
|
|
1663
|
+
emitGlyph(resolveGid(2375), false);
|
|
1664
|
+
splitPostComponents.push(2366);
|
|
1665
|
+
} else if (mCp === 2380) {
|
|
1666
|
+
emitGlyph(resolveGid(2375), false);
|
|
1667
|
+
splitPostComponents.push(2380);
|
|
1668
|
+
} else {
|
|
1669
|
+
emitGlyph(resolveGid(mCp), false);
|
|
1523
1670
|
}
|
|
1524
1671
|
}
|
|
1525
|
-
if (found) types[i] = "EN";
|
|
1526
|
-
}
|
|
1527
|
-
}
|
|
1528
|
-
for (let i = 0; i < len; i++) {
|
|
1529
|
-
if (types[i] === "ES" || types[i] === "ET" || types[i] === "CS") {
|
|
1530
|
-
types[i] = "ON";
|
|
1531
|
-
}
|
|
1532
|
-
}
|
|
1533
|
-
lastStrong = paraLevel === 0 ? "L" : "R";
|
|
1534
|
-
for (let i = 0; i < len; i++) {
|
|
1535
|
-
if (types[i] === "BN") continue;
|
|
1536
|
-
if (types[i] === "L" || types[i] === "R") {
|
|
1537
|
-
lastStrong = types[i];
|
|
1538
|
-
} else if (types[i] === "EN" && lastStrong === "L") {
|
|
1539
|
-
types[i] = "L";
|
|
1540
1672
|
}
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
const end = i;
|
|
1551
|
-
let prevStrong = paraDir;
|
|
1552
|
-
for (let j = start - 1; j >= 0; j--) {
|
|
1553
|
-
if (types[j] === "L" || types[j] === "R" || types[j] === "EN" || types[j] === "AN") {
|
|
1554
|
-
prevStrong = types[j] === "EN" || types[j] === "AN" ? "R" : types[j];
|
|
1555
|
-
break;
|
|
1673
|
+
if (hasReph) {
|
|
1674
|
+
const raGid = resolveGid(RA2);
|
|
1675
|
+
const halantGid = resolveGid(HALANT2);
|
|
1676
|
+
const rephLig = tryLig([raGid, halantGid]);
|
|
1677
|
+
if (rephLig) {
|
|
1678
|
+
emitGlyph(rephLig.resultGid, true, baseGid);
|
|
1679
|
+
} else {
|
|
1680
|
+
const raGsubbed = gsub[raGid] !== void 0 ? gsub[raGid] : raGid;
|
|
1681
|
+
emitGlyph(raGsubbed, true, baseGid);
|
|
1556
1682
|
}
|
|
1557
1683
|
}
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1684
|
+
const clusterGids = [];
|
|
1685
|
+
const clusterEndIdx = [];
|
|
1686
|
+
let matraStart = codepoints.length;
|
|
1687
|
+
for (let ci = baseStart; ci < codepoints.length; ci++) {
|
|
1688
|
+
const ct = devanagariCharType(codepoints[ci]);
|
|
1689
|
+
if (ct === 0 || ct === 7 || ct === 8) {
|
|
1690
|
+
clusterGids.push(resolveGid(codepoints[ci]));
|
|
1691
|
+
clusterEndIdx.push(ci);
|
|
1692
|
+
} else if (ct < 0 || ct === 1 || ct === 9) {
|
|
1693
|
+
emitGlyph(resolveGid(codepoints[ci]), false);
|
|
1694
|
+
} else {
|
|
1695
|
+
matraStart = ci;
|
|
1562
1696
|
break;
|
|
1563
1697
|
}
|
|
1564
1698
|
}
|
|
1565
|
-
const
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1699
|
+
const ligResult = tryLig(clusterGids);
|
|
1700
|
+
if (ligResult) {
|
|
1701
|
+
emitGlyph(ligResult.resultGid, false);
|
|
1702
|
+
baseGid = ligResult.resultGid;
|
|
1703
|
+
let gi = ligResult.consumed;
|
|
1704
|
+
while (gi < clusterGids.length) {
|
|
1705
|
+
const subSeq = clusterGids.slice(gi);
|
|
1706
|
+
const subLig = tryLig(subSeq);
|
|
1707
|
+
if (subLig) {
|
|
1708
|
+
emitGlyph(subLig.resultGid, false);
|
|
1709
|
+
gi += subLig.consumed;
|
|
1710
|
+
} else {
|
|
1711
|
+
const origCi = clusterEndIdx[gi];
|
|
1712
|
+
const ct = devanagariCharType(codepoints[origCi]);
|
|
1713
|
+
if (ct === 7) {
|
|
1714
|
+
emitGlyph(clusterGids[gi], true, baseGid);
|
|
1715
|
+
} else {
|
|
1716
|
+
emitGlyph(clusterGids[gi], false);
|
|
1717
|
+
}
|
|
1718
|
+
gi++;
|
|
1719
|
+
}
|
|
1720
|
+
}
|
|
1576
1721
|
} else {
|
|
1577
|
-
|
|
1722
|
+
for (let ci = baseStart; ci < matraStart; ci++) {
|
|
1723
|
+
const cp = codepoints[ci];
|
|
1724
|
+
const ct = devanagariCharType(cp);
|
|
1725
|
+
if (ct === 0) {
|
|
1726
|
+
emitGlyph(resolveGid(cp), false);
|
|
1727
|
+
} else if (ct === 7) {
|
|
1728
|
+
emitGlyph(resolveGid(cp), true, baseGid);
|
|
1729
|
+
} else if (ct === 8) {
|
|
1730
|
+
emitGlyph(resolveGid(cp), true, baseGid);
|
|
1731
|
+
}
|
|
1732
|
+
}
|
|
1578
1733
|
}
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
63
|
|
1594
|
-
// ?
|
|
1595
|
-
]);
|
|
1596
|
-
function fixPunctuationAffinity(types, codePoints, len) {
|
|
1597
|
-
for (let i = 1; i < len; i++) {
|
|
1598
|
-
if (types[i] === "R" && SENTENCE_PUNCT.has(codePoints[i])) {
|
|
1599
|
-
let prevIdx = i - 1;
|
|
1600
|
-
while (prevIdx >= 0 && (types[prevIdx] === "WS" || types[prevIdx] === "BN")) prevIdx--;
|
|
1601
|
-
if (prevIdx >= 0 && types[prevIdx] === "L") {
|
|
1602
|
-
types[i] = "L";
|
|
1734
|
+
for (let ci = matraStart; ci < codepoints.length; ci++) {
|
|
1735
|
+
const cp = codepoints[ci];
|
|
1736
|
+
const ct = devanagariCharType(cp);
|
|
1737
|
+
if (ct === 4) continue;
|
|
1738
|
+
if (ct === 2 || ct === 3) {
|
|
1739
|
+
emitGlyph(resolveGid(cp), true, baseGid);
|
|
1740
|
+
} else if (ct === 5) {
|
|
1741
|
+
emitGlyph(resolveGid(cp), false);
|
|
1742
|
+
} else if (ct === 6) {
|
|
1743
|
+
emitGlyph(resolveGid(cp), true, baseGid);
|
|
1744
|
+
} else if (ct === 9) {
|
|
1745
|
+
emitGlyph(resolveGid(cp), false);
|
|
1746
|
+
} else {
|
|
1747
|
+
emitGlyph(resolveGid(cp), false);
|
|
1603
1748
|
}
|
|
1604
1749
|
}
|
|
1750
|
+
for (const postCp of splitPostComponents) {
|
|
1751
|
+
emitGlyph(resolveGid(postCp), false);
|
|
1752
|
+
}
|
|
1605
1753
|
}
|
|
1754
|
+
return shaped;
|
|
1606
1755
|
}
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1756
|
+
|
|
1757
|
+
// src/shaping/arabic-shaper.ts
|
|
1758
|
+
function getJoiningType(cp) {
|
|
1759
|
+
if (cp >= 1611 && cp <= 1631 || // Harakat (vowel marks)
|
|
1760
|
+
cp === 1648 || // Superscript alef
|
|
1761
|
+
cp >= 1750 && cp <= 1756 || cp >= 1759 && cp <= 1764 || cp >= 1767 && cp <= 1768 || cp >= 1770 && cp <= 1773 || cp >= 1552 && cp <= 1562) return "T";
|
|
1762
|
+
if (cp === 1600) return "C";
|
|
1763
|
+
if (cp >= 1574 && cp <= 1576 || // YEH HAMZA, BA series
|
|
1764
|
+
cp >= 1578 && cp <= 1582 || // TA through KHA
|
|
1765
|
+
cp >= 1587 && cp <= 1594 || // SEEN through GHAIN
|
|
1766
|
+
cp >= 1601 && cp <= 1607 || // FA through HA
|
|
1767
|
+
cp === 1609 || // ALEF MAKSURA
|
|
1768
|
+
cp === 1610 || // YA
|
|
1769
|
+
cp === 1656 || // HIGH HAMZA YEH
|
|
1770
|
+
cp >= 1690 && cp <= 1727 || // Extended Arabic
|
|
1771
|
+
cp >= 1729 && cp <= 1731 || cp >= 1740 && cp <= 1742 || cp >= 1744 && cp <= 1747 || cp === 1749 || cp === 1786 || cp === 1787 || cp === 1788) return "D";
|
|
1772
|
+
if (cp === 1570 || cp === 1571 || cp === 1572 || cp === 1573 || cp === 1575 || // ALEF
|
|
1773
|
+
cp === 1577 || // TEH MARBUTA
|
|
1774
|
+
cp === 1583 || cp === 1584 || // DAL, THAL
|
|
1775
|
+
cp === 1585 || cp === 1586 || // RA, ZAIN
|
|
1776
|
+
cp === 1608 || // WAW
|
|
1777
|
+
cp >= 1649 && cp <= 1651 || cp === 1653 || cp === 1654 || cp === 1655 || cp >= 1672 && cp <= 1689 || // Extended DAL/RA series
|
|
1778
|
+
cp === 1728 || cp >= 1732 && cp <= 1739 || cp === 1743 || cp === 1774 || cp === 1775) return "R";
|
|
1779
|
+
if (cp >= ARABIC_START && cp <= ARABIC_END) return "U";
|
|
1780
|
+
return "U";
|
|
1781
|
+
}
|
|
1782
|
+
var ARABIC_PRES_FORMS = /* @__PURE__ */ new Map([
|
|
1783
|
+
[1569, { isol: 65152 }],
|
|
1784
|
+
[1570, { isol: 65153, fina: 65154 }],
|
|
1785
|
+
[1571, { isol: 65155, fina: 65156 }],
|
|
1786
|
+
[1572, { isol: 65157, fina: 65158 }],
|
|
1787
|
+
[1573, { isol: 65159, fina: 65160 }],
|
|
1788
|
+
[1574, { isol: 65161, fina: 65162, init: 65163, medi: 65164 }],
|
|
1789
|
+
[1575, { isol: 65165, fina: 65166 }],
|
|
1790
|
+
[1576, { isol: 65167, fina: 65168, init: 65169, medi: 65170 }],
|
|
1791
|
+
[1577, { isol: 65171, fina: 65172 }],
|
|
1792
|
+
[1578, { isol: 65173, fina: 65174, init: 65175, medi: 65176 }],
|
|
1793
|
+
[1579, { isol: 65177, fina: 65178, init: 65179, medi: 65180 }],
|
|
1794
|
+
[1580, { isol: 65181, fina: 65182, init: 65183, medi: 65184 }],
|
|
1795
|
+
[1581, { isol: 65185, fina: 65186, init: 65187, medi: 65188 }],
|
|
1796
|
+
[1582, { isol: 65189, fina: 65190, init: 65191, medi: 65192 }],
|
|
1797
|
+
[1583, { isol: 65193, fina: 65194 }],
|
|
1798
|
+
[1584, { isol: 65195, fina: 65196 }],
|
|
1799
|
+
[1585, { isol: 65197, fina: 65198 }],
|
|
1800
|
+
[1586, { isol: 65199, fina: 65200 }],
|
|
1801
|
+
[1587, { isol: 65201, fina: 65202, init: 65203, medi: 65204 }],
|
|
1802
|
+
[1588, { isol: 65205, fina: 65206, init: 65207, medi: 65208 }],
|
|
1803
|
+
[1589, { isol: 65209, fina: 65210, init: 65211, medi: 65212 }],
|
|
1804
|
+
[1590, { isol: 65213, fina: 65214, init: 65215, medi: 65216 }],
|
|
1805
|
+
[1591, { isol: 65217, fina: 65218, init: 65219, medi: 65220 }],
|
|
1806
|
+
[1592, { isol: 65221, fina: 65222, init: 65223, medi: 65224 }],
|
|
1807
|
+
[1593, { isol: 65225, fina: 65226, init: 65227, medi: 65228 }],
|
|
1808
|
+
[1594, { isol: 65229, fina: 65230, init: 65231, medi: 65232 }],
|
|
1809
|
+
[1601, { isol: 65233, fina: 65234, init: 65235, medi: 65236 }],
|
|
1810
|
+
[1602, { isol: 65237, fina: 65238, init: 65239, medi: 65240 }],
|
|
1811
|
+
[1603, { isol: 65241, fina: 65242, init: 65243, medi: 65244 }],
|
|
1812
|
+
[1604, { isol: 65245, fina: 65246, init: 65247, medi: 65248 }],
|
|
1813
|
+
[1605, { isol: 65249, fina: 65250, init: 65251, medi: 65252 }],
|
|
1814
|
+
[1606, { isol: 65253, fina: 65254, init: 65255, medi: 65256 }],
|
|
1815
|
+
[1607, { isol: 65257, fina: 65258, init: 65259, medi: 65260 }],
|
|
1816
|
+
[1608, { isol: 65261, fina: 65262 }],
|
|
1817
|
+
[1609, { isol: 65263, fina: 65264 }],
|
|
1818
|
+
[1610, { isol: 65265, fina: 65266, init: 65267, medi: 65268 }]
|
|
1819
|
+
]);
|
|
1820
|
+
var LAM_ALEF_PRES = /* @__PURE__ */ new Map([
|
|
1821
|
+
[1570, [65269, 65270]],
|
|
1822
|
+
// LAM + ALEF WITH MADDA ABOVE
|
|
1823
|
+
[1571, [65271, 65272]],
|
|
1824
|
+
// LAM + ALEF WITH HAMZA ABOVE
|
|
1825
|
+
[1573, [65273, 65274]],
|
|
1826
|
+
// LAM + ALEF WITH HAMZA BELOW
|
|
1827
|
+
[1575, [65275, 65276]]
|
|
1828
|
+
// LAM + ALEF
|
|
1829
|
+
]);
|
|
1830
|
+
function resolvePositionalForms(codePoints) {
|
|
1831
|
+
const len = codePoints.length;
|
|
1832
|
+
const forms = new Array(len).fill("isol");
|
|
1833
|
+
const joining = codePoints.map(getJoiningType);
|
|
1616
1834
|
for (let i = 0; i < len; i++) {
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
let
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
if (codePoints[j] === codePoints[i]) depth++;
|
|
1623
|
-
else if (codePoints[j] === closer) {
|
|
1624
|
-
depth--;
|
|
1625
|
-
if (depth === 0) {
|
|
1626
|
-
closeIdx = j;
|
|
1627
|
-
break;
|
|
1628
|
-
}
|
|
1629
|
-
}
|
|
1630
|
-
}
|
|
1631
|
-
if (closeIdx === -1) continue;
|
|
1632
|
-
let hasL = false;
|
|
1633
|
-
for (let j = i + 1; j < closeIdx; j++) {
|
|
1634
|
-
if (types[j] === "L") {
|
|
1635
|
-
hasL = true;
|
|
1835
|
+
if (joining[i] === "T" || joining[i] === "U") continue;
|
|
1836
|
+
let prevJoin = "U";
|
|
1837
|
+
for (let j = i - 1; j >= 0; j--) {
|
|
1838
|
+
if (joining[j] !== "T") {
|
|
1839
|
+
prevJoin = joining[j];
|
|
1636
1840
|
break;
|
|
1637
1841
|
}
|
|
1638
1842
|
}
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
}
|
|
1645
|
-
function findOutermostIsolatePairs(codePoints) {
|
|
1646
|
-
const pairs = [];
|
|
1647
|
-
let i = 0;
|
|
1648
|
-
while (i < codePoints.length) {
|
|
1649
|
-
const cp = codePoints[i];
|
|
1650
|
-
if (cp === 8294 || cp === 8295 || cp === 8296) {
|
|
1651
|
-
let depth = 1;
|
|
1652
|
-
let close = -1;
|
|
1653
|
-
for (let j = i + 1; j < codePoints.length; j++) {
|
|
1654
|
-
const cj = codePoints[j];
|
|
1655
|
-
if (cj === 8294 || cj === 8295 || cj === 8296) depth++;
|
|
1656
|
-
else if (cj === 8297) {
|
|
1657
|
-
depth--;
|
|
1658
|
-
if (depth === 0) {
|
|
1659
|
-
close = j;
|
|
1660
|
-
break;
|
|
1661
|
-
}
|
|
1662
|
-
}
|
|
1663
|
-
}
|
|
1664
|
-
if (close === -1) {
|
|
1665
|
-
i++;
|
|
1666
|
-
continue;
|
|
1843
|
+
let nextJoin = "U";
|
|
1844
|
+
for (let j = i + 1; j < len; j++) {
|
|
1845
|
+
if (joining[j] !== "T") {
|
|
1846
|
+
nextJoin = joining[j];
|
|
1847
|
+
break;
|
|
1667
1848
|
}
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1849
|
+
}
|
|
1850
|
+
const joinsToPrev = prevJoin === "D" || prevJoin === "C";
|
|
1851
|
+
const joinsToNext = (nextJoin === "D" || nextJoin === "R" || nextJoin === "C") && (joining[i] === "D" || joining[i] === "C");
|
|
1852
|
+
if (joinsToPrev && joinsToNext) {
|
|
1853
|
+
forms[i] = "medi";
|
|
1854
|
+
} else if (joinsToPrev) {
|
|
1855
|
+
forms[i] = "fina";
|
|
1856
|
+
} else if (joinsToNext) {
|
|
1857
|
+
forms[i] = "init";
|
|
1671
1858
|
} else {
|
|
1672
|
-
i
|
|
1859
|
+
forms[i] = "isol";
|
|
1673
1860
|
}
|
|
1674
1861
|
}
|
|
1675
|
-
return
|
|
1862
|
+
return forms;
|
|
1676
1863
|
}
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
for (let i = 0; i < text.length; ) {
|
|
1682
|
-
cpToStr.push(i);
|
|
1683
|
-
const cp = text.codePointAt(i) ?? 0;
|
|
1684
|
-
codePoints.push(cp);
|
|
1685
|
-
i += cp > 65535 ? 2 : 1;
|
|
1686
|
-
}
|
|
1687
|
-
cpToStr.push(text.length);
|
|
1688
|
-
const isolates = findOutermostIsolatePairs(codePoints);
|
|
1689
|
-
if (isolates.length === 0) {
|
|
1690
|
-
return resolveBidiCore(text, codePoints, cpToStr);
|
|
1691
|
-
}
|
|
1692
|
-
const insideIsolate = new Array(codePoints.length).fill(false);
|
|
1693
|
-
for (const p of isolates) {
|
|
1694
|
-
for (let k = p.open; k <= p.close; k++) insideIsolate[k] = true;
|
|
1695
|
-
}
|
|
1696
|
-
const outerTypes = codePoints.map((cp, idx) => insideIsolate[idx] ? "BN" : classifyBidiType(cp));
|
|
1697
|
-
const parentLevel = detectParagraphLevel(outerTypes);
|
|
1698
|
-
const out = [];
|
|
1699
|
-
const emitSegment = (cpStart, cpEnd, forced) => {
|
|
1700
|
-
if (cpStart >= cpEnd) return;
|
|
1701
|
-
const segText = text.substring(cpToStr[cpStart], cpToStr[cpEnd]);
|
|
1702
|
-
const segCps = codePoints.slice(cpStart, cpEnd);
|
|
1703
|
-
const baseStrIdx = cpToStr[cpStart];
|
|
1704
|
-
const segCpToStr = cpToStr.slice(cpStart, cpEnd + 1).map((x) => x - baseStrIdx);
|
|
1705
|
-
const segRuns = forced === void 0 ? resolveBidiRuns(segText) : resolveBidiCore(segText, segCps, segCpToStr, forced);
|
|
1706
|
-
for (const r of segRuns) {
|
|
1707
|
-
out.push({ text: r.text, level: r.level, start: r.start + baseStrIdx });
|
|
1708
|
-
}
|
|
1709
|
-
};
|
|
1710
|
-
let cursor = 0;
|
|
1711
|
-
for (const pair of isolates) {
|
|
1712
|
-
emitSegment(cursor, pair.open, parentLevel);
|
|
1713
|
-
const innerStart = pair.open + 1;
|
|
1714
|
-
const innerEnd = pair.close;
|
|
1715
|
-
let innerLevel;
|
|
1716
|
-
if (pair.kind === "LRI") innerLevel = 0;
|
|
1717
|
-
else if (pair.kind === "RLI") innerLevel = 1;
|
|
1718
|
-
else {
|
|
1719
|
-
const innerTypes = codePoints.slice(innerStart, innerEnd).map(classifyBidiType);
|
|
1720
|
-
innerLevel = detectParagraphLevel(innerTypes);
|
|
1721
|
-
}
|
|
1722
|
-
if (innerStart < innerEnd) {
|
|
1723
|
-
const innerText = text.substring(cpToStr[innerStart], cpToStr[innerEnd]);
|
|
1724
|
-
const innerRuns = resolveBidiRunsForced(innerText, innerLevel);
|
|
1725
|
-
const baseStrIdx = cpToStr[innerStart];
|
|
1726
|
-
for (const r of innerRuns) {
|
|
1727
|
-
out.push({ text: r.text, level: r.level, start: r.start + baseStrIdx });
|
|
1728
|
-
}
|
|
1729
|
-
}
|
|
1730
|
-
cursor = pair.close + 1;
|
|
1731
|
-
}
|
|
1732
|
-
emitSegment(cursor, codePoints.length, parentLevel);
|
|
1733
|
-
return out;
|
|
1864
|
+
var LAM = 1604;
|
|
1865
|
+
var ALEF_VARIANTS = /* @__PURE__ */ new Set([1570, 1571, 1573, 1575]);
|
|
1866
|
+
function isLamAlef(cp1, cp2) {
|
|
1867
|
+
return cp1 === LAM && ALEF_VARIANTS.has(cp2);
|
|
1734
1868
|
}
|
|
1735
|
-
function
|
|
1736
|
-
if (!
|
|
1869
|
+
function shapeArabicText(str, fontData) {
|
|
1870
|
+
if (!str) return [];
|
|
1737
1871
|
const codePoints = [];
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
cpToStr.push(i);
|
|
1741
|
-
const cp = text.codePointAt(i) ?? 0;
|
|
1872
|
+
for (let i = 0; i < str.length; ) {
|
|
1873
|
+
const cp = str.codePointAt(i) ?? 0;
|
|
1742
1874
|
codePoints.push(cp);
|
|
1743
1875
|
i += cp > 65535 ? 2 : 1;
|
|
1744
1876
|
}
|
|
1745
|
-
|
|
1746
|
-
const
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
const
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
const
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
const innerEnd = pair.close;
|
|
1767
|
-
let innerLevel;
|
|
1768
|
-
if (pair.kind === "LRI") innerLevel = 0;
|
|
1769
|
-
else if (pair.kind === "RLI") innerLevel = 1;
|
|
1770
|
-
else {
|
|
1771
|
-
const innerTypes = codePoints.slice(innerStart, innerEnd).map(classifyBidiType);
|
|
1772
|
-
innerLevel = detectParagraphLevel(innerTypes);
|
|
1773
|
-
}
|
|
1774
|
-
if (innerStart < innerEnd) {
|
|
1775
|
-
const innerText = text.substring(cpToStr[innerStart], cpToStr[innerEnd]);
|
|
1776
|
-
const innerRuns = resolveBidiRunsForced(innerText, innerLevel);
|
|
1777
|
-
const baseStrIdx = cpToStr[innerStart];
|
|
1778
|
-
for (const r of innerRuns) {
|
|
1779
|
-
out.push({ text: r.text, level: r.level, start: r.start + baseStrIdx });
|
|
1877
|
+
const forms = resolvePositionalForms(codePoints);
|
|
1878
|
+
const glyphs = [];
|
|
1879
|
+
const cmap = fontData.cmap;
|
|
1880
|
+
const widths = fontData.widths;
|
|
1881
|
+
const defaultWidth = fontData.defaultWidth;
|
|
1882
|
+
const markAnchors = fontData.markAnchors;
|
|
1883
|
+
let lastBaseGid = 0;
|
|
1884
|
+
for (let i = 0; i < codePoints.length; i++) {
|
|
1885
|
+
const cp = codePoints[i];
|
|
1886
|
+
if (i < codePoints.length - 1 && isLamAlef(cp, codePoints[i + 1])) {
|
|
1887
|
+
const ligForms = LAM_ALEF_PRES.get(codePoints[i + 1]);
|
|
1888
|
+
if (ligForms) {
|
|
1889
|
+
const isFinal = forms[i] === "medi" || forms[i] === "fina";
|
|
1890
|
+
const ligCP = isFinal ? ligForms[1] : ligForms[0];
|
|
1891
|
+
const ligGid = cmap[ligCP];
|
|
1892
|
+
if (ligGid) {
|
|
1893
|
+
glyphs.push({ gid: ligGid, dx: 0, dy: 0, isZeroAdvance: false });
|
|
1894
|
+
lastBaseGid = ligGid;
|
|
1895
|
+
i++;
|
|
1896
|
+
continue;
|
|
1897
|
+
}
|
|
1780
1898
|
}
|
|
1781
1899
|
}
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
if (paraLevel === 1) {
|
|
1795
|
-
fixPunctuationAffinity(types, codePoints, len);
|
|
1796
|
-
fixBracketPairing(types, codePoints, len);
|
|
1797
|
-
}
|
|
1798
|
-
const levels = assignLevels(types, paraLevel);
|
|
1799
|
-
const runs = [];
|
|
1800
|
-
let runStart = 0;
|
|
1801
|
-
let runLevel = levels[0];
|
|
1802
|
-
for (let i = 1; i <= len; i++) {
|
|
1803
|
-
if (i === len || levels[i] !== runLevel) {
|
|
1804
|
-
const start = cpToStr[runStart];
|
|
1805
|
-
const end = cpToStr[i];
|
|
1806
|
-
let runText = text.substring(start, end);
|
|
1807
|
-
if (runLevel % 2 === 1) {
|
|
1808
|
-
runText = reverseString(runText);
|
|
1900
|
+
let gid = cmap[cp] ?? 0;
|
|
1901
|
+
const presForm = ARABIC_PRES_FORMS.get(cp);
|
|
1902
|
+
if (presForm) {
|
|
1903
|
+
const form = forms[i];
|
|
1904
|
+
let presCP;
|
|
1905
|
+
if (form === "init") presCP = presForm.init;
|
|
1906
|
+
else if (form === "medi") presCP = presForm.medi;
|
|
1907
|
+
else if (form === "fina") presCP = presForm.fina;
|
|
1908
|
+
else presCP = presForm.isol;
|
|
1909
|
+
if (presCP) {
|
|
1910
|
+
const presGid = cmap[presCP];
|
|
1911
|
+
if (presGid) gid = presGid;
|
|
1809
1912
|
}
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1913
|
+
}
|
|
1914
|
+
const joining = getJoiningType(cp);
|
|
1915
|
+
const isZeroAdvance = joining === "T";
|
|
1916
|
+
if (isZeroAdvance && lastBaseGid !== 0) {
|
|
1917
|
+
const baseAdv = widths[lastBaseGid] !== void 0 ? widths[lastBaseGid] : defaultWidth;
|
|
1918
|
+
const offset = positionMarkOnBase(markAnchors, gid, lastBaseGid, baseAdv);
|
|
1919
|
+
if (offset) {
|
|
1920
|
+
glyphs.push({ gid, dx: offset.dx, dy: offset.dy, isZeroAdvance: true });
|
|
1921
|
+
continue;
|
|
1814
1922
|
}
|
|
1815
1923
|
}
|
|
1924
|
+
glyphs.push({ gid, dx: 0, dy: 0, isZeroAdvance });
|
|
1925
|
+
if (!isZeroAdvance) lastBaseGid = gid;
|
|
1816
1926
|
}
|
|
1817
|
-
|
|
1818
|
-
runs.reverse();
|
|
1819
|
-
}
|
|
1820
|
-
return runs;
|
|
1821
|
-
}
|
|
1822
|
-
function containsRTL(text) {
|
|
1823
|
-
for (let i = 0; i < text.length; ) {
|
|
1824
|
-
const cp = text.codePointAt(i) ?? 0;
|
|
1825
|
-
const t = classifyBidiType(cp);
|
|
1826
|
-
if (t === "R" || t === "AL") return true;
|
|
1827
|
-
i += cp > 65535 ? 2 : 1;
|
|
1828
|
-
}
|
|
1829
|
-
return false;
|
|
1830
|
-
}
|
|
1831
|
-
function reverseString(str) {
|
|
1832
|
-
const cps = [];
|
|
1833
|
-
for (let i = 0; i < str.length; ) {
|
|
1834
|
-
const cp = str.codePointAt(i) ?? 0;
|
|
1835
|
-
cps.push(cp);
|
|
1836
|
-
i += cp > 65535 ? 2 : 1;
|
|
1837
|
-
}
|
|
1838
|
-
cps.reverse();
|
|
1839
|
-
return String.fromCodePoint(...cps);
|
|
1927
|
+
return glyphs;
|
|
1840
1928
|
}
|
|
1841
1929
|
|
|
1842
1930
|
// src/core/encoding-context.ts
|
|
@@ -1972,6 +2060,8 @@ function createEncodingContext(fontEntries, pdfA = false) {
|
|
|
1972
2060
|
return _usedGids;
|
|
1973
2061
|
},
|
|
1974
2062
|
textRuns(str, sz) {
|
|
2063
|
+
if (!str) return [];
|
|
2064
|
+
str = stripBidiControls(str);
|
|
1975
2065
|
if (!str) return [];
|
|
1976
2066
|
if (containsRTL(str)) {
|
|
1977
2067
|
const bidiRuns = resolveBidiRuns(str);
|
|
@@ -2109,6 +2199,8 @@ function createEncodingContext(fontEntries, pdfA = false) {
|
|
|
2109
2199
|
});
|
|
2110
2200
|
},
|
|
2111
2201
|
ps(str) {
|
|
2202
|
+
if (!str) return "<>";
|
|
2203
|
+
str = stripBidiControls(str);
|
|
2112
2204
|
if (!str) return "<>";
|
|
2113
2205
|
const { cmap } = primary.fontData;
|
|
2114
2206
|
if (containsRTL(str)) {
|
|
@@ -2792,22 +2884,22 @@ function txt(str, x, y, font, sz, enc) {
|
|
|
2792
2884
|
}
|
|
2793
2885
|
return parts.join("\n");
|
|
2794
2886
|
}
|
|
2795
|
-
function txtR(str, rightX, y, font, sz, enc) {
|
|
2796
|
-
const width = enc.isUnicode ? enc.tw(str, sz) : helveticaWidth(toWinAnsi(str), sz);
|
|
2887
|
+
function txtR(str, rightX, y, font, sz, enc, bold = false) {
|
|
2888
|
+
const width = enc.isUnicode ? enc.tw(str, sz) : bold ? helveticaBoldWidth(str, sz) : helveticaWidth(toWinAnsi(str), sz);
|
|
2797
2889
|
return txt(str, rightX - width, y, font, sz, enc);
|
|
2798
2890
|
}
|
|
2799
|
-
function txtC(str, leftX, y, font, sz, colW, enc) {
|
|
2800
|
-
const width = enc.isUnicode ? enc.tw(str, sz) : helveticaWidth(toWinAnsi(str), sz);
|
|
2891
|
+
function txtC(str, leftX, y, font, sz, colW, enc, bold = false) {
|
|
2892
|
+
const width = enc.isUnicode ? enc.tw(str, sz) : bold ? helveticaBoldWidth(str, sz) : helveticaWidth(toWinAnsi(str), sz);
|
|
2801
2893
|
return txt(str, leftX + (colW - width) / 2, y, font, sz, enc);
|
|
2802
2894
|
}
|
|
2803
2895
|
function txtTagged(str, x, y, font, sz, enc, mcid) {
|
|
2804
2896
|
return wrapSpan(txt(str, x, y, font, sz, enc), str, mcid);
|
|
2805
2897
|
}
|
|
2806
|
-
function txtRTagged(str, rightX, y, font, sz, enc, mcid) {
|
|
2807
|
-
return wrapSpan(txtR(str, rightX, y, font, sz, enc), str, mcid);
|
|
2898
|
+
function txtRTagged(str, rightX, y, font, sz, enc, mcid, bold = false) {
|
|
2899
|
+
return wrapSpan(txtR(str, rightX, y, font, sz, enc, bold), str, mcid);
|
|
2808
2900
|
}
|
|
2809
|
-
function txtCTagged(str, leftX, y, font, sz, colW, enc, mcid) {
|
|
2810
|
-
return wrapSpan(txtC(str, leftX, y, font, sz, colW, enc), str, mcid);
|
|
2901
|
+
function txtCTagged(str, leftX, y, font, sz, colW, enc, mcid, bold = false) {
|
|
2902
|
+
return wrapSpan(txtC(str, leftX, y, font, sz, colW, enc, bold), str, mcid);
|
|
2811
2903
|
}
|
|
2812
2904
|
function encodePdfTextString(str) {
|
|
2813
2905
|
let ascii = true;
|
|
@@ -3265,17 +3357,17 @@ function _buildTableHeader(y, headers, enc, cx, cwi, columns, cw, mgL, mgR, pgW,
|
|
|
3265
3357
|
const thEl = { type: "TH", children: [mcref] };
|
|
3266
3358
|
thChildren.push(thEl);
|
|
3267
3359
|
if (columns[i].a === "r") {
|
|
3268
|
-
ops.push(txtRTagged(t, cx[i] + cwi[i] - 3, y - TH_H + 4, enc.f2, fs.th, enc, mcid));
|
|
3360
|
+
ops.push(txtRTagged(t, cx[i] + cwi[i] - 3, y - TH_H + 4, enc.f2, fs.th, enc, mcid, true));
|
|
3269
3361
|
} else if (columns[i].a === "c") {
|
|
3270
|
-
ops.push(txtCTagged(t, cx[i], y - TH_H + 4, enc.f2, fs.th, cwi[i], enc, mcid));
|
|
3362
|
+
ops.push(txtCTagged(t, cx[i], y - TH_H + 4, enc.f2, fs.th, cwi[i], enc, mcid, true));
|
|
3271
3363
|
} else {
|
|
3272
3364
|
ops.push(txtTagged(t, cx[i] + 3, y - TH_H + 4, enc.f2, fs.th, enc, mcid));
|
|
3273
3365
|
}
|
|
3274
3366
|
} else {
|
|
3275
3367
|
if (columns[i].a === "r") {
|
|
3276
|
-
ops.push(txtR(t, cx[i] + cwi[i] - 3, y - TH_H + 4, enc.f2, fs.th, enc));
|
|
3368
|
+
ops.push(txtR(t, cx[i] + cwi[i] - 3, y - TH_H + 4, enc.f2, fs.th, enc, true));
|
|
3277
3369
|
} else if (columns[i].a === "c") {
|
|
3278
|
-
ops.push(txtC(t, cx[i], y - TH_H + 4, enc.f2, fs.th, cwi[i], enc));
|
|
3370
|
+
ops.push(txtC(t, cx[i], y - TH_H + 4, enc.f2, fs.th, cwi[i], enc, true));
|
|
3279
3371
|
} else {
|
|
3280
3372
|
ops.push(txt(t, cx[i] + 3, y - TH_H + 4, enc.f2, fs.th, enc));
|
|
3281
3373
|
}
|