@vectojs/core 1.7.0 → 1.8.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.
@@ -1,6 +1,8 @@
1
1
  import {
2
+ ArabicShaper,
3
+ BidiResolver,
2
4
  LayoutWorkerManager
3
- } from "./chunk-STWPTWO4.mjs";
5
+ } from "./chunk-IESDTEJ4.mjs";
4
6
 
5
7
  // src/math/SpringPhysics.ts
6
8
  var MAX_FRAME_DT = 0.25;
@@ -1459,6 +1461,205 @@ var SVGEntity = class extends Entity {
1459
1461
  }
1460
1462
  };
1461
1463
 
1464
+ // src/text/PreparedContentGrid.ts
1465
+ var nextRevision = 1;
1466
+ var graphemeSegmenter = typeof Intl !== "undefined" && "Segmenter" in Intl ? new Intl.Segmenter(void 0, { granularity: "grapheme" }) : null;
1467
+ var MARK = /\p{Mark}/u;
1468
+ var EXTENDED_PICTOGRAPHIC = /\p{Extended_Pictographic}/u;
1469
+ var REGIONAL_INDICATOR = /\p{Regional_Indicator}/u;
1470
+ var BIDI_CONTROL = /\p{Bidi_Control}/u;
1471
+ var EAST_ASIAN_WIDE = /[ᄀ-ᅟ⌚-⌛⏩-⏬⏰⏳◽-◾⺀-〾ぁ-㏿㐀-䶿一-鿿ꀀ-꓏가-힣豈-﫿︰-﹏＀-⦆¢-₩]/u;
1472
+ function codePointAt(text, index) {
1473
+ const point = text.codePointAt(index);
1474
+ if (point === void 0) return { value: "", next: index };
1475
+ const value = String.fromCodePoint(point);
1476
+ return { value, next: index + value.length };
1477
+ }
1478
+ function fallbackGraphemes(text) {
1479
+ const parts = [];
1480
+ let index = 0;
1481
+ while (index < text.length) {
1482
+ const start = index;
1483
+ let current = codePointAt(text, index);
1484
+ let segment = current.value;
1485
+ index = current.next;
1486
+ let regionalCount = REGIONAL_INDICATOR.test(segment) ? 1 : 0;
1487
+ while (index < text.length) {
1488
+ current = codePointAt(text, index);
1489
+ const point = current.value.codePointAt(0) ?? 0;
1490
+ const isVariation = point >= 65024 && point <= 65039;
1491
+ const isEmojiModifier = point >= 127995 && point <= 127999;
1492
+ const isKeycap = point === 8419;
1493
+ if (MARK.test(current.value) || isVariation || isEmojiModifier || isKeycap) {
1494
+ segment += current.value;
1495
+ index = current.next;
1496
+ continue;
1497
+ }
1498
+ if (REGIONAL_INDICATOR.test(current.value) && regionalCount === 1) {
1499
+ segment += current.value;
1500
+ index = current.next;
1501
+ regionalCount++;
1502
+ continue;
1503
+ }
1504
+ if (point === 8205) {
1505
+ segment += current.value;
1506
+ index = current.next;
1507
+ if (index < text.length) {
1508
+ current = codePointAt(text, index);
1509
+ segment += current.value;
1510
+ index = current.next;
1511
+ }
1512
+ continue;
1513
+ }
1514
+ break;
1515
+ }
1516
+ parts.push({ segment, index: start });
1517
+ }
1518
+ return parts;
1519
+ }
1520
+ function graphemes(text) {
1521
+ if (!graphemeSegmenter) return fallbackGraphemes(text);
1522
+ return Array.from(graphemeSegmenter.segment(text), (part) => ({
1523
+ segment: part.segment,
1524
+ index: part.index
1525
+ }));
1526
+ }
1527
+ function lowerBound(values, target) {
1528
+ let low = 0;
1529
+ let high = values.length;
1530
+ while (low < high) {
1531
+ const middle = low + high >>> 1;
1532
+ if (values[middle] < target) low = middle + 1;
1533
+ else high = middle;
1534
+ }
1535
+ return low;
1536
+ }
1537
+ function isWideCluster(cluster) {
1538
+ if (EXTENDED_PICTOGRAPHIC.test(cluster) || REGIONAL_INDICATOR.test(cluster)) return true;
1539
+ if (cluster.includes("\u20E3")) return true;
1540
+ if (EAST_ASIAN_WIDE.test(cluster)) return true;
1541
+ const point = cluster.codePointAt(0) ?? 0;
1542
+ return point >= 131072 && point <= 262141;
1543
+ }
1544
+ function sourceLines(source) {
1545
+ const lines = [];
1546
+ let start = 0;
1547
+ while (true) {
1548
+ let end = start;
1549
+ while (end < source.length && source[end] !== "\r" && source[end] !== "\n") end++;
1550
+ if (end === source.length) {
1551
+ lines.push({
1552
+ sourceStart: start,
1553
+ sourceEnd: end,
1554
+ nextSourceStart: end,
1555
+ text: source.slice(start)
1556
+ });
1557
+ break;
1558
+ }
1559
+ const next = source[end] === "\r" && source[end + 1] === "\n" ? end + 2 : end + 1;
1560
+ lines.push({
1561
+ sourceStart: start,
1562
+ sourceEnd: end,
1563
+ nextSourceStart: next,
1564
+ text: source.slice(start, end)
1565
+ });
1566
+ start = next;
1567
+ if (start === source.length) {
1568
+ lines.push({ sourceStart: start, sourceEnd: start, nextSourceStart: start, text: "" });
1569
+ break;
1570
+ }
1571
+ }
1572
+ return lines;
1573
+ }
1574
+ function assertPositiveFinite(value, name) {
1575
+ if (!Number.isFinite(value) || value <= 0) {
1576
+ throw new RangeError(`${name} must be a positive finite number`);
1577
+ }
1578
+ }
1579
+ function prepareContentGrid(source, options) {
1580
+ assertPositiveFinite(options.cellWidth, "cellWidth");
1581
+ assertPositiveFinite(options.lineHeight, "lineHeight");
1582
+ if (!Number.isFinite(options.baseline)) throw new RangeError("baseline must be finite");
1583
+ const tabSize = options.tabSize ?? 4;
1584
+ if (!Number.isInteger(tabSize) || tabSize <= 0) {
1585
+ throw new RangeError("tabSize must be a positive integer");
1586
+ }
1587
+ const rawLines = sourceLines(source);
1588
+ const lines = [];
1589
+ for (let lineIndex = 0; lineIndex < rawLines.length; lineIndex++) {
1590
+ const sourceLine = rawLines[lineIndex];
1591
+ const rawLine = sourceLine.text;
1592
+ const { sourceStart: lineStart, sourceEnd, nextSourceStart } = sourceLine;
1593
+ const rawCaretBoundaries = [
1594
+ 0,
1595
+ ...graphemes(rawLine).map((grapheme) => grapheme.index + grapheme.segment.length)
1596
+ ];
1597
+ const shaped = ArabicShaper.shapeArabic(rawLine);
1598
+ const shapedParts = graphemes(shaped.shapedText);
1599
+ const levels = BidiResolver.resolveLevels(shaped.shapedText);
1600
+ const cells = [];
1601
+ let column = 0;
1602
+ for (let index = 0; index < shapedParts.length; index++) {
1603
+ const part = shapedParts[index];
1604
+ const sourceOffset = shaped.indexMap[part.index] ?? part.index;
1605
+ const nextPart = shapedParts[index + 1];
1606
+ const sourceOffsetEnd = nextPart ? shaped.indexMap[nextPart.index] ?? nextPart.index : rawLine.length;
1607
+ const raw = rawLine.slice(sourceOffset, sourceOffsetEnd);
1608
+ const sourceCaretOffsets = [0];
1609
+ for (let caretIndex = lowerBound(rawCaretBoundaries, sourceOffset + 1); caretIndex < rawCaretBoundaries.length && rawCaretBoundaries[caretIndex] < sourceOffsetEnd; caretIndex++) {
1610
+ sourceCaretOffsets.push(rawCaretBoundaries[caretIndex] - sourceOffset);
1611
+ }
1612
+ if (sourceCaretOffsets.at(-1) !== sourceOffsetEnd - sourceOffset) {
1613
+ sourceCaretOffsets.push(sourceOffsetEnd - sourceOffset);
1614
+ }
1615
+ let columns;
1616
+ if (BIDI_CONTROL.test(raw)) columns = 0;
1617
+ else if (raw === " ") columns = tabSize - column % tabSize;
1618
+ else columns = isWideCluster(raw) ? 2 : 1;
1619
+ const advance = columns * options.cellWidth;
1620
+ cells.push({
1621
+ sourceStart: lineStart + sourceOffset,
1622
+ sourceEnd: lineStart + sourceOffsetEnd,
1623
+ sourceCaretOffsets: Object.freeze(sourceCaretOffsets),
1624
+ glyph: part.segment,
1625
+ x: 0,
1626
+ advance,
1627
+ level: levels[part.index] ?? 0,
1628
+ char: part.segment
1629
+ });
1630
+ column += columns;
1631
+ }
1632
+ const visualCells = [...cells];
1633
+ BidiResolver.reorderVisual(visualCells, BidiResolver.getBaseLevel(shaped.shapedText));
1634
+ let visualX = 0;
1635
+ for (const cell of visualCells) {
1636
+ cell.x = visualX;
1637
+ visualX += cell.advance;
1638
+ }
1639
+ const frozenCells = cells.map(({ char: _char, ...cell }) => Object.freeze(cell));
1640
+ lines.push(
1641
+ Object.freeze({
1642
+ sourceStart: lineStart,
1643
+ sourceEnd,
1644
+ nextSourceStart,
1645
+ width: visualX,
1646
+ cells: Object.freeze(frozenCells)
1647
+ })
1648
+ );
1649
+ }
1650
+ return Object.freeze({
1651
+ kind: "content-grid",
1652
+ revision: nextRevision++,
1653
+ source,
1654
+ font: options.font,
1655
+ cellWidth: options.cellWidth,
1656
+ lineHeight: options.lineHeight,
1657
+ baseline: options.baseline,
1658
+ tabSize,
1659
+ lines: Object.freeze(lines)
1660
+ });
1661
+ }
1662
+
1462
1663
  export {
1463
1664
  SpringPhysics,
1464
1665
  Easing,
@@ -1471,5 +1672,6 @@ export {
1471
1672
  clearCssLineBoxMetrics,
1472
1673
  MSDFFont,
1473
1674
  MSDFTextEntity,
1474
- SVGEntity
1675
+ SVGEntity,
1676
+ prepareContentGrid
1475
1677
  };
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  ArabicShaper,
3
3
  BidiResolver
4
- } from "./chunk-STWPTWO4.mjs";
4
+ } from "./chunk-IESDTEJ4.mjs";
5
5
 
6
6
  // src/layout/LayoutEngine.ts
7
7
  function computeLineSegments(top, bottom, maxWidth, exclusions) {
package/dist/index.d.ts CHANGED
@@ -25,4 +25,5 @@ export { SVGEntity } from './text/SVGEntity';
25
25
  export * from './tree/ComputeParticleEntity';
26
26
  export * from './text/ArabicShaper';
27
27
  export * from './text/BidiResolver';
28
+ export * from './text/PreparedContentGrid';
28
29
  export * from './text/Typography';