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