pa_font 0.1.2 → 0.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/dist/paFont.cjs CHANGED
@@ -302,7 +302,7 @@ function measureText(font, value, opts) {
302
302
  const renderOptions = toRenderOptions(opts);
303
303
  const box = font.getPath(value, opts.x, opts.y, opts.size, renderOptions).getBoundingBox();
304
304
  return {
305
- width: font.getAdvanceWidth(value, opts.size, renderOptions),
305
+ width: measureAdvanceWidth(font, value, opts),
306
306
  bbox: {
307
307
  x: box.x1,
308
308
  y: box.y1,
@@ -311,6 +311,9 @@ function measureText(font, value, opts) {
311
311
  }
312
312
  };
313
313
  }
314
+ function measureAdvanceWidth(font, value, opts) {
315
+ return font.getAdvanceWidth(value, opts.size, toRenderOptions(opts));
316
+ }
314
317
  function buildGlyphTopology(glyph, fallbackUnitsPerEm) {
315
318
  const commands = glyph.path?.commands ?? [];
316
319
  const contours = [];
@@ -1086,8 +1089,2746 @@ function resampleOpenPath(path, step) {
1086
1089
  return sampled;
1087
1090
  }
1088
1091
  //#endregion
1092
+ //#region node_modules/@chenglou/pretext/dist/bidi.js
1093
+ var baseTypes = [
1094
+ "BN",
1095
+ "BN",
1096
+ "BN",
1097
+ "BN",
1098
+ "BN",
1099
+ "BN",
1100
+ "BN",
1101
+ "BN",
1102
+ "BN",
1103
+ "S",
1104
+ "B",
1105
+ "S",
1106
+ "WS",
1107
+ "B",
1108
+ "BN",
1109
+ "BN",
1110
+ "BN",
1111
+ "BN",
1112
+ "BN",
1113
+ "BN",
1114
+ "BN",
1115
+ "BN",
1116
+ "BN",
1117
+ "BN",
1118
+ "BN",
1119
+ "BN",
1120
+ "BN",
1121
+ "BN",
1122
+ "B",
1123
+ "B",
1124
+ "B",
1125
+ "S",
1126
+ "WS",
1127
+ "ON",
1128
+ "ON",
1129
+ "ET",
1130
+ "ET",
1131
+ "ET",
1132
+ "ON",
1133
+ "ON",
1134
+ "ON",
1135
+ "ON",
1136
+ "ON",
1137
+ "ON",
1138
+ "CS",
1139
+ "ON",
1140
+ "CS",
1141
+ "ON",
1142
+ "EN",
1143
+ "EN",
1144
+ "EN",
1145
+ "EN",
1146
+ "EN",
1147
+ "EN",
1148
+ "EN",
1149
+ "EN",
1150
+ "EN",
1151
+ "EN",
1152
+ "ON",
1153
+ "ON",
1154
+ "ON",
1155
+ "ON",
1156
+ "ON",
1157
+ "ON",
1158
+ "ON",
1159
+ "L",
1160
+ "L",
1161
+ "L",
1162
+ "L",
1163
+ "L",
1164
+ "L",
1165
+ "L",
1166
+ "L",
1167
+ "L",
1168
+ "L",
1169
+ "L",
1170
+ "L",
1171
+ "L",
1172
+ "L",
1173
+ "L",
1174
+ "L",
1175
+ "L",
1176
+ "L",
1177
+ "L",
1178
+ "L",
1179
+ "L",
1180
+ "L",
1181
+ "L",
1182
+ "L",
1183
+ "L",
1184
+ "L",
1185
+ "ON",
1186
+ "ON",
1187
+ "ON",
1188
+ "ON",
1189
+ "ON",
1190
+ "ON",
1191
+ "L",
1192
+ "L",
1193
+ "L",
1194
+ "L",
1195
+ "L",
1196
+ "L",
1197
+ "L",
1198
+ "L",
1199
+ "L",
1200
+ "L",
1201
+ "L",
1202
+ "L",
1203
+ "L",
1204
+ "L",
1205
+ "L",
1206
+ "L",
1207
+ "L",
1208
+ "L",
1209
+ "L",
1210
+ "L",
1211
+ "L",
1212
+ "L",
1213
+ "L",
1214
+ "L",
1215
+ "L",
1216
+ "L",
1217
+ "ON",
1218
+ "ON",
1219
+ "ON",
1220
+ "ON",
1221
+ "BN",
1222
+ "BN",
1223
+ "BN",
1224
+ "BN",
1225
+ "BN",
1226
+ "BN",
1227
+ "B",
1228
+ "BN",
1229
+ "BN",
1230
+ "BN",
1231
+ "BN",
1232
+ "BN",
1233
+ "BN",
1234
+ "BN",
1235
+ "BN",
1236
+ "BN",
1237
+ "BN",
1238
+ "BN",
1239
+ "BN",
1240
+ "BN",
1241
+ "BN",
1242
+ "BN",
1243
+ "BN",
1244
+ "BN",
1245
+ "BN",
1246
+ "BN",
1247
+ "BN",
1248
+ "BN",
1249
+ "BN",
1250
+ "BN",
1251
+ "BN",
1252
+ "BN",
1253
+ "BN",
1254
+ "CS",
1255
+ "ON",
1256
+ "ET",
1257
+ "ET",
1258
+ "ET",
1259
+ "ET",
1260
+ "ON",
1261
+ "ON",
1262
+ "ON",
1263
+ "ON",
1264
+ "L",
1265
+ "ON",
1266
+ "ON",
1267
+ "ON",
1268
+ "ON",
1269
+ "ON",
1270
+ "ET",
1271
+ "ET",
1272
+ "EN",
1273
+ "EN",
1274
+ "ON",
1275
+ "L",
1276
+ "ON",
1277
+ "ON",
1278
+ "ON",
1279
+ "EN",
1280
+ "L",
1281
+ "ON",
1282
+ "ON",
1283
+ "ON",
1284
+ "ON",
1285
+ "ON",
1286
+ "L",
1287
+ "L",
1288
+ "L",
1289
+ "L",
1290
+ "L",
1291
+ "L",
1292
+ "L",
1293
+ "L",
1294
+ "L",
1295
+ "L",
1296
+ "L",
1297
+ "L",
1298
+ "L",
1299
+ "L",
1300
+ "L",
1301
+ "L",
1302
+ "L",
1303
+ "L",
1304
+ "L",
1305
+ "L",
1306
+ "L",
1307
+ "L",
1308
+ "L",
1309
+ "ON",
1310
+ "L",
1311
+ "L",
1312
+ "L",
1313
+ "L",
1314
+ "L",
1315
+ "L",
1316
+ "L",
1317
+ "L",
1318
+ "L",
1319
+ "L",
1320
+ "L",
1321
+ "L",
1322
+ "L",
1323
+ "L",
1324
+ "L",
1325
+ "L",
1326
+ "L",
1327
+ "L",
1328
+ "L",
1329
+ "L",
1330
+ "L",
1331
+ "L",
1332
+ "L",
1333
+ "L",
1334
+ "L",
1335
+ "L",
1336
+ "L",
1337
+ "L",
1338
+ "L",
1339
+ "L",
1340
+ "L",
1341
+ "ON",
1342
+ "L",
1343
+ "L",
1344
+ "L",
1345
+ "L",
1346
+ "L",
1347
+ "L",
1348
+ "L",
1349
+ "L"
1350
+ ];
1351
+ var arabicTypes = [
1352
+ "AL",
1353
+ "AL",
1354
+ "AL",
1355
+ "AL",
1356
+ "AL",
1357
+ "AL",
1358
+ "AL",
1359
+ "AL",
1360
+ "AL",
1361
+ "AL",
1362
+ "AL",
1363
+ "AL",
1364
+ "CS",
1365
+ "AL",
1366
+ "ON",
1367
+ "ON",
1368
+ "NSM",
1369
+ "NSM",
1370
+ "NSM",
1371
+ "NSM",
1372
+ "NSM",
1373
+ "NSM",
1374
+ "AL",
1375
+ "AL",
1376
+ "AL",
1377
+ "AL",
1378
+ "AL",
1379
+ "AL",
1380
+ "AL",
1381
+ "AL",
1382
+ "AL",
1383
+ "AL",
1384
+ "AL",
1385
+ "AL",
1386
+ "AL",
1387
+ "AL",
1388
+ "AL",
1389
+ "AL",
1390
+ "AL",
1391
+ "AL",
1392
+ "AL",
1393
+ "AL",
1394
+ "AL",
1395
+ "AL",
1396
+ "AL",
1397
+ "AL",
1398
+ "AL",
1399
+ "AL",
1400
+ "AL",
1401
+ "AL",
1402
+ "AL",
1403
+ "AL",
1404
+ "AL",
1405
+ "AL",
1406
+ "AL",
1407
+ "AL",
1408
+ "AL",
1409
+ "AL",
1410
+ "AL",
1411
+ "AL",
1412
+ "AL",
1413
+ "AL",
1414
+ "AL",
1415
+ "AL",
1416
+ "AL",
1417
+ "AL",
1418
+ "AL",
1419
+ "AL",
1420
+ "AL",
1421
+ "AL",
1422
+ "AL",
1423
+ "AL",
1424
+ "AL",
1425
+ "AL",
1426
+ "AL",
1427
+ "NSM",
1428
+ "NSM",
1429
+ "NSM",
1430
+ "NSM",
1431
+ "NSM",
1432
+ "NSM",
1433
+ "NSM",
1434
+ "NSM",
1435
+ "NSM",
1436
+ "NSM",
1437
+ "NSM",
1438
+ "NSM",
1439
+ "NSM",
1440
+ "NSM",
1441
+ "AL",
1442
+ "AL",
1443
+ "AL",
1444
+ "AL",
1445
+ "AL",
1446
+ "AL",
1447
+ "AL",
1448
+ "AN",
1449
+ "AN",
1450
+ "AN",
1451
+ "AN",
1452
+ "AN",
1453
+ "AN",
1454
+ "AN",
1455
+ "AN",
1456
+ "AN",
1457
+ "AN",
1458
+ "ET",
1459
+ "AN",
1460
+ "AN",
1461
+ "AL",
1462
+ "AL",
1463
+ "AL",
1464
+ "NSM",
1465
+ "AL",
1466
+ "AL",
1467
+ "AL",
1468
+ "AL",
1469
+ "AL",
1470
+ "AL",
1471
+ "AL",
1472
+ "AL",
1473
+ "AL",
1474
+ "AL",
1475
+ "AL",
1476
+ "AL",
1477
+ "AL",
1478
+ "AL",
1479
+ "AL",
1480
+ "AL",
1481
+ "AL",
1482
+ "AL",
1483
+ "AL",
1484
+ "AL",
1485
+ "AL",
1486
+ "AL",
1487
+ "AL",
1488
+ "AL",
1489
+ "AL",
1490
+ "AL",
1491
+ "AL",
1492
+ "AL",
1493
+ "AL",
1494
+ "AL",
1495
+ "AL",
1496
+ "AL",
1497
+ "AL",
1498
+ "AL",
1499
+ "AL",
1500
+ "AL",
1501
+ "AL",
1502
+ "AL",
1503
+ "AL",
1504
+ "AL",
1505
+ "AL",
1506
+ "AL",
1507
+ "AL",
1508
+ "AL",
1509
+ "AL",
1510
+ "AL",
1511
+ "AL",
1512
+ "AL",
1513
+ "AL",
1514
+ "AL",
1515
+ "AL",
1516
+ "AL",
1517
+ "AL",
1518
+ "AL",
1519
+ "AL",
1520
+ "AL",
1521
+ "AL",
1522
+ "AL",
1523
+ "AL",
1524
+ "AL",
1525
+ "AL",
1526
+ "AL",
1527
+ "AL",
1528
+ "AL",
1529
+ "AL",
1530
+ "AL",
1531
+ "AL",
1532
+ "AL",
1533
+ "AL",
1534
+ "AL",
1535
+ "AL",
1536
+ "AL",
1537
+ "AL",
1538
+ "AL",
1539
+ "AL",
1540
+ "AL",
1541
+ "AL",
1542
+ "AL",
1543
+ "AL",
1544
+ "AL",
1545
+ "AL",
1546
+ "AL",
1547
+ "AL",
1548
+ "AL",
1549
+ "AL",
1550
+ "AL",
1551
+ "AL",
1552
+ "AL",
1553
+ "AL",
1554
+ "AL",
1555
+ "AL",
1556
+ "AL",
1557
+ "AL",
1558
+ "AL",
1559
+ "AL",
1560
+ "AL",
1561
+ "AL",
1562
+ "AL",
1563
+ "AL",
1564
+ "AL",
1565
+ "AL",
1566
+ "NSM",
1567
+ "NSM",
1568
+ "NSM",
1569
+ "NSM",
1570
+ "NSM",
1571
+ "NSM",
1572
+ "NSM",
1573
+ "NSM",
1574
+ "NSM",
1575
+ "NSM",
1576
+ "NSM",
1577
+ "NSM",
1578
+ "NSM",
1579
+ "NSM",
1580
+ "NSM",
1581
+ "NSM",
1582
+ "NSM",
1583
+ "NSM",
1584
+ "NSM",
1585
+ "ON",
1586
+ "NSM",
1587
+ "NSM",
1588
+ "NSM",
1589
+ "NSM",
1590
+ "AL",
1591
+ "AL",
1592
+ "AL",
1593
+ "AL",
1594
+ "AL",
1595
+ "AL",
1596
+ "AL",
1597
+ "AL",
1598
+ "AL",
1599
+ "AL",
1600
+ "AL",
1601
+ "AL",
1602
+ "AL",
1603
+ "AL",
1604
+ "AL",
1605
+ "AL",
1606
+ "AL",
1607
+ "AL"
1608
+ ];
1609
+ function classifyChar(charCode) {
1610
+ if (charCode <= 255) return baseTypes[charCode];
1611
+ if (1424 <= charCode && charCode <= 1524) return "R";
1612
+ if (1536 <= charCode && charCode <= 1791) return arabicTypes[charCode & 255];
1613
+ if (1792 <= charCode && charCode <= 2220) return "AL";
1614
+ return "L";
1615
+ }
1616
+ function computeBidiLevels(str) {
1617
+ const len = str.length;
1618
+ if (len === 0) return null;
1619
+ const types = new Array(len);
1620
+ let numBidi = 0;
1621
+ for (let i = 0; i < len; i++) {
1622
+ const t = classifyChar(str.charCodeAt(i));
1623
+ if (t === "R" || t === "AL" || t === "AN") numBidi++;
1624
+ types[i] = t;
1625
+ }
1626
+ if (numBidi === 0) return null;
1627
+ const startLevel = len / numBidi < .3 ? 0 : 1;
1628
+ const levels = new Int8Array(len);
1629
+ for (let i = 0; i < len; i++) levels[i] = startLevel;
1630
+ const e = startLevel & 1 ? "R" : "L";
1631
+ const sor = e;
1632
+ let lastType = sor;
1633
+ for (let i = 0; i < len; i++) if (types[i] === "NSM") types[i] = lastType;
1634
+ else lastType = types[i];
1635
+ lastType = sor;
1636
+ for (let i = 0; i < len; i++) {
1637
+ const t = types[i];
1638
+ if (t === "EN") types[i] = lastType === "AL" ? "AN" : "EN";
1639
+ else if (t === "R" || t === "L" || t === "AL") lastType = t;
1640
+ }
1641
+ for (let i = 0; i < len; i++) if (types[i] === "AL") types[i] = "R";
1642
+ for (let i = 1; i < len - 1; i++) {
1643
+ if (types[i] === "ES" && types[i - 1] === "EN" && types[i + 1] === "EN") types[i] = "EN";
1644
+ if (types[i] === "CS" && (types[i - 1] === "EN" || types[i - 1] === "AN") && types[i + 1] === types[i - 1]) types[i] = types[i - 1];
1645
+ }
1646
+ for (let i = 0; i < len; i++) {
1647
+ if (types[i] !== "EN") continue;
1648
+ let j;
1649
+ for (j = i - 1; j >= 0 && types[j] === "ET"; j--) types[j] = "EN";
1650
+ for (j = i + 1; j < len && types[j] === "ET"; j++) types[j] = "EN";
1651
+ }
1652
+ for (let i = 0; i < len; i++) {
1653
+ const t = types[i];
1654
+ if (t === "WS" || t === "ES" || t === "ET" || t === "CS") types[i] = "ON";
1655
+ }
1656
+ lastType = sor;
1657
+ for (let i = 0; i < len; i++) {
1658
+ const t = types[i];
1659
+ if (t === "EN") types[i] = lastType === "L" ? "L" : "EN";
1660
+ else if (t === "R" || t === "L") lastType = t;
1661
+ }
1662
+ for (let i = 0; i < len; i++) {
1663
+ if (types[i] !== "ON") continue;
1664
+ let end = i + 1;
1665
+ while (end < len && types[end] === "ON") end++;
1666
+ const before = i > 0 ? types[i - 1] : sor;
1667
+ const after = end < len ? types[end] : sor;
1668
+ const bDir = before !== "L" ? "R" : "L";
1669
+ if (bDir === (after !== "L" ? "R" : "L")) for (let j = i; j < end; j++) types[j] = bDir;
1670
+ i = end - 1;
1671
+ }
1672
+ for (let i = 0; i < len; i++) if (types[i] === "ON") types[i] = e;
1673
+ for (let i = 0; i < len; i++) {
1674
+ const t = types[i];
1675
+ if ((levels[i] & 1) === 0) {
1676
+ if (t === "R") levels[i]++;
1677
+ else if (t === "AN" || t === "EN") levels[i] += 2;
1678
+ } else if (t === "L" || t === "AN" || t === "EN") levels[i]++;
1679
+ }
1680
+ return levels;
1681
+ }
1682
+ function computeSegmentLevels(normalized, segStarts) {
1683
+ const bidiLevels = computeBidiLevels(normalized);
1684
+ if (bidiLevels === null) return null;
1685
+ const segLevels = new Int8Array(segStarts.length);
1686
+ for (let i = 0; i < segStarts.length; i++) segLevels[i] = bidiLevels[segStarts[i]];
1687
+ return segLevels;
1688
+ }
1689
+ //#endregion
1690
+ //#region node_modules/@chenglou/pretext/dist/analysis.js
1691
+ var collapsibleWhitespaceRunRe = /[ \t\n\r\f]+/g;
1692
+ var needsWhitespaceNormalizationRe = /[\t\n\r\f]| {2,}|^ | $/;
1693
+ function getWhiteSpaceProfile(whiteSpace) {
1694
+ const mode = whiteSpace ?? "normal";
1695
+ return mode === "pre-wrap" ? {
1696
+ mode,
1697
+ preserveOrdinarySpaces: true,
1698
+ preserveHardBreaks: true
1699
+ } : {
1700
+ mode,
1701
+ preserveOrdinarySpaces: false,
1702
+ preserveHardBreaks: false
1703
+ };
1704
+ }
1705
+ function normalizeWhitespaceNormal(text) {
1706
+ if (!needsWhitespaceNormalizationRe.test(text)) return text;
1707
+ let normalized = text.replace(collapsibleWhitespaceRunRe, " ");
1708
+ if (normalized.charCodeAt(0) === 32) normalized = normalized.slice(1);
1709
+ if (normalized.length > 0 && normalized.charCodeAt(normalized.length - 1) === 32) normalized = normalized.slice(0, -1);
1710
+ return normalized;
1711
+ }
1712
+ function normalizeWhitespacePreWrap(text) {
1713
+ if (!/[\r\f]/.test(text)) return text.replace(/\r\n/g, "\n");
1714
+ return text.replace(/\r\n/g, "\n").replace(/[\r\f]/g, "\n");
1715
+ }
1716
+ var sharedWordSegmenter$1 = null;
1717
+ var segmenterLocale;
1718
+ function getSharedWordSegmenter() {
1719
+ if (sharedWordSegmenter$1 === null) sharedWordSegmenter$1 = new Intl.Segmenter(segmenterLocale, { granularity: "word" });
1720
+ return sharedWordSegmenter$1;
1721
+ }
1722
+ var arabicScriptRe = /\p{Script=Arabic}/u;
1723
+ var combiningMarkRe = /\p{M}/u;
1724
+ var decimalDigitRe = /\p{Nd}/u;
1725
+ function containsArabicScript(text) {
1726
+ return arabicScriptRe.test(text);
1727
+ }
1728
+ function isCJK(s) {
1729
+ for (const ch of s) {
1730
+ const c = ch.codePointAt(0);
1731
+ if (c >= 19968 && c <= 40959 || c >= 13312 && c <= 19903 || c >= 131072 && c <= 173791 || c >= 173824 && c <= 177983 || c >= 177984 && c <= 178207 || c >= 178208 && c <= 183983 || c >= 183984 && c <= 191471 || c >= 196608 && c <= 201551 || c >= 63744 && c <= 64255 || c >= 194560 && c <= 195103 || c >= 12288 && c <= 12351 || c >= 12352 && c <= 12447 || c >= 12448 && c <= 12543 || c >= 44032 && c <= 55215 || c >= 65280 && c <= 65519) return true;
1732
+ }
1733
+ return false;
1734
+ }
1735
+ var kinsokuStart = new Set([
1736
+ ",",
1737
+ ".",
1738
+ "!",
1739
+ ":",
1740
+ ";",
1741
+ "?",
1742
+ "、",
1743
+ "。",
1744
+ "・",
1745
+ ")",
1746
+ "〕",
1747
+ "〉",
1748
+ "》",
1749
+ "」",
1750
+ "』",
1751
+ "】",
1752
+ "〗",
1753
+ "〙",
1754
+ "〛",
1755
+ "ー",
1756
+ "々",
1757
+ "〻",
1758
+ "ゝ",
1759
+ "ゞ",
1760
+ "ヽ",
1761
+ "ヾ"
1762
+ ]);
1763
+ var kinsokuEnd = new Set([
1764
+ "\"",
1765
+ "(",
1766
+ "[",
1767
+ "{",
1768
+ "“",
1769
+ "‘",
1770
+ "«",
1771
+ "‹",
1772
+ "(",
1773
+ "〔",
1774
+ "〈",
1775
+ "《",
1776
+ "「",
1777
+ "『",
1778
+ "【",
1779
+ "〖",
1780
+ "〘",
1781
+ "〚"
1782
+ ]);
1783
+ var forwardStickyGlue = new Set(["'", "’"]);
1784
+ var leftStickyPunctuation = new Set([
1785
+ ".",
1786
+ ",",
1787
+ "!",
1788
+ "?",
1789
+ ":",
1790
+ ";",
1791
+ "،",
1792
+ "؛",
1793
+ "؟",
1794
+ "।",
1795
+ "॥",
1796
+ "၊",
1797
+ "။",
1798
+ "၌",
1799
+ "၍",
1800
+ "၏",
1801
+ ")",
1802
+ "]",
1803
+ "}",
1804
+ "%",
1805
+ "\"",
1806
+ "”",
1807
+ "’",
1808
+ "»",
1809
+ "›",
1810
+ "…"
1811
+ ]);
1812
+ var arabicNoSpaceTrailingPunctuation = new Set([
1813
+ ":",
1814
+ ".",
1815
+ "،",
1816
+ "؛"
1817
+ ]);
1818
+ var myanmarMedialGlue = new Set(["၏"]);
1819
+ var closingQuoteChars = new Set([
1820
+ "”",
1821
+ "’",
1822
+ "»",
1823
+ "›",
1824
+ "」",
1825
+ "』",
1826
+ "】",
1827
+ "》",
1828
+ "〉",
1829
+ "〕",
1830
+ ")"
1831
+ ]);
1832
+ function isLeftStickyPunctuationSegment(segment) {
1833
+ if (isEscapedQuoteClusterSegment(segment)) return true;
1834
+ let sawPunctuation = false;
1835
+ for (const ch of segment) {
1836
+ if (leftStickyPunctuation.has(ch)) {
1837
+ sawPunctuation = true;
1838
+ continue;
1839
+ }
1840
+ if (sawPunctuation && combiningMarkRe.test(ch)) continue;
1841
+ return false;
1842
+ }
1843
+ return sawPunctuation;
1844
+ }
1845
+ function isCJKLineStartProhibitedSegment(segment) {
1846
+ for (const ch of segment) if (!kinsokuStart.has(ch) && !leftStickyPunctuation.has(ch)) return false;
1847
+ return segment.length > 0;
1848
+ }
1849
+ function isForwardStickyClusterSegment(segment) {
1850
+ if (isEscapedQuoteClusterSegment(segment)) return true;
1851
+ for (const ch of segment) if (!kinsokuEnd.has(ch) && !forwardStickyGlue.has(ch) && !combiningMarkRe.test(ch)) return false;
1852
+ return segment.length > 0;
1853
+ }
1854
+ function isEscapedQuoteClusterSegment(segment) {
1855
+ let sawQuote = false;
1856
+ for (const ch of segment) {
1857
+ if (ch === "\\" || combiningMarkRe.test(ch)) continue;
1858
+ if (kinsokuEnd.has(ch) || leftStickyPunctuation.has(ch) || forwardStickyGlue.has(ch)) {
1859
+ sawQuote = true;
1860
+ continue;
1861
+ }
1862
+ return false;
1863
+ }
1864
+ return sawQuote;
1865
+ }
1866
+ function splitTrailingForwardStickyCluster(text) {
1867
+ const chars = Array.from(text);
1868
+ let splitIndex = chars.length;
1869
+ while (splitIndex > 0) {
1870
+ const ch = chars[splitIndex - 1];
1871
+ if (combiningMarkRe.test(ch)) {
1872
+ splitIndex--;
1873
+ continue;
1874
+ }
1875
+ if (kinsokuEnd.has(ch) || forwardStickyGlue.has(ch)) {
1876
+ splitIndex--;
1877
+ continue;
1878
+ }
1879
+ break;
1880
+ }
1881
+ if (splitIndex <= 0 || splitIndex === chars.length) return null;
1882
+ return {
1883
+ head: chars.slice(0, splitIndex).join(""),
1884
+ tail: chars.slice(splitIndex).join("")
1885
+ };
1886
+ }
1887
+ function isRepeatedSingleCharRun(segment, ch) {
1888
+ if (segment.length === 0) return false;
1889
+ for (const part of segment) if (part !== ch) return false;
1890
+ return true;
1891
+ }
1892
+ function endsWithArabicNoSpacePunctuation(segment) {
1893
+ if (!containsArabicScript(segment) || segment.length === 0) return false;
1894
+ return arabicNoSpaceTrailingPunctuation.has(segment[segment.length - 1]);
1895
+ }
1896
+ function endsWithMyanmarMedialGlue(segment) {
1897
+ if (segment.length === 0) return false;
1898
+ return myanmarMedialGlue.has(segment[segment.length - 1]);
1899
+ }
1900
+ function splitLeadingSpaceAndMarks(segment) {
1901
+ if (segment.length < 2 || segment[0] !== " ") return null;
1902
+ const marks = segment.slice(1);
1903
+ if (/^\p{M}+$/u.test(marks)) return {
1904
+ space: " ",
1905
+ marks
1906
+ };
1907
+ return null;
1908
+ }
1909
+ function endsWithClosingQuote(text) {
1910
+ for (let i = text.length - 1; i >= 0; i--) {
1911
+ const ch = text[i];
1912
+ if (closingQuoteChars.has(ch)) return true;
1913
+ if (!leftStickyPunctuation.has(ch)) return false;
1914
+ }
1915
+ return false;
1916
+ }
1917
+ function classifySegmentBreakChar(ch, whiteSpaceProfile) {
1918
+ if (whiteSpaceProfile.preserveOrdinarySpaces || whiteSpaceProfile.preserveHardBreaks) {
1919
+ if (ch === " ") return "preserved-space";
1920
+ if (ch === " ") return "tab";
1921
+ if (whiteSpaceProfile.preserveHardBreaks && ch === "\n") return "hard-break";
1922
+ }
1923
+ if (ch === " ") return "space";
1924
+ if (ch === "\xA0" || ch === " " || ch === "⁠" || ch === "") return "glue";
1925
+ if (ch === "​") return "zero-width-break";
1926
+ if (ch === "­") return "soft-hyphen";
1927
+ return "text";
1928
+ }
1929
+ function joinTextParts(parts) {
1930
+ return parts.length === 1 ? parts[0] : parts.join("");
1931
+ }
1932
+ function splitSegmentByBreakKind(segment, isWordLike, start, whiteSpaceProfile) {
1933
+ const pieces = [];
1934
+ let currentKind = null;
1935
+ let currentTextParts = [];
1936
+ let currentStart = start;
1937
+ let currentWordLike = false;
1938
+ let offset = 0;
1939
+ for (const ch of segment) {
1940
+ const kind = classifySegmentBreakChar(ch, whiteSpaceProfile);
1941
+ const wordLike = kind === "text" && isWordLike;
1942
+ if (currentKind !== null && kind === currentKind && wordLike === currentWordLike) {
1943
+ currentTextParts.push(ch);
1944
+ offset += ch.length;
1945
+ continue;
1946
+ }
1947
+ if (currentKind !== null) pieces.push({
1948
+ text: joinTextParts(currentTextParts),
1949
+ isWordLike: currentWordLike,
1950
+ kind: currentKind,
1951
+ start: currentStart
1952
+ });
1953
+ currentKind = kind;
1954
+ currentTextParts = [ch];
1955
+ currentStart = start + offset;
1956
+ currentWordLike = wordLike;
1957
+ offset += ch.length;
1958
+ }
1959
+ if (currentKind !== null) pieces.push({
1960
+ text: joinTextParts(currentTextParts),
1961
+ isWordLike: currentWordLike,
1962
+ kind: currentKind,
1963
+ start: currentStart
1964
+ });
1965
+ return pieces;
1966
+ }
1967
+ function isTextRunBoundary(kind) {
1968
+ return kind === "space" || kind === "preserved-space" || kind === "zero-width-break" || kind === "hard-break";
1969
+ }
1970
+ var urlSchemeSegmentRe = /^[A-Za-z][A-Za-z0-9+.-]*:$/;
1971
+ function isUrlLikeRunStart(segmentation, index) {
1972
+ const text = segmentation.texts[index];
1973
+ if (text.startsWith("www.")) return true;
1974
+ return urlSchemeSegmentRe.test(text) && index + 1 < segmentation.len && segmentation.kinds[index + 1] === "text" && segmentation.texts[index + 1] === "//";
1975
+ }
1976
+ function isUrlQueryBoundarySegment(text) {
1977
+ return text.includes("?") && (text.includes("://") || text.startsWith("www."));
1978
+ }
1979
+ function mergeUrlLikeRuns(segmentation) {
1980
+ const texts = segmentation.texts.slice();
1981
+ const isWordLike = segmentation.isWordLike.slice();
1982
+ const kinds = segmentation.kinds.slice();
1983
+ const starts = segmentation.starts.slice();
1984
+ for (let i = 0; i < segmentation.len; i++) {
1985
+ if (kinds[i] !== "text" || !isUrlLikeRunStart(segmentation, i)) continue;
1986
+ const mergedParts = [texts[i]];
1987
+ let j = i + 1;
1988
+ while (j < segmentation.len && !isTextRunBoundary(kinds[j])) {
1989
+ mergedParts.push(texts[j]);
1990
+ isWordLike[i] = true;
1991
+ const endsQueryPrefix = texts[j].includes("?");
1992
+ kinds[j] = "text";
1993
+ texts[j] = "";
1994
+ j++;
1995
+ if (endsQueryPrefix) break;
1996
+ }
1997
+ texts[i] = joinTextParts(mergedParts);
1998
+ }
1999
+ let compactLen = 0;
2000
+ for (let read = 0; read < texts.length; read++) {
2001
+ const text = texts[read];
2002
+ if (text.length === 0) continue;
2003
+ if (compactLen !== read) {
2004
+ texts[compactLen] = text;
2005
+ isWordLike[compactLen] = isWordLike[read];
2006
+ kinds[compactLen] = kinds[read];
2007
+ starts[compactLen] = starts[read];
2008
+ }
2009
+ compactLen++;
2010
+ }
2011
+ texts.length = compactLen;
2012
+ isWordLike.length = compactLen;
2013
+ kinds.length = compactLen;
2014
+ starts.length = compactLen;
2015
+ return {
2016
+ len: compactLen,
2017
+ texts,
2018
+ isWordLike,
2019
+ kinds,
2020
+ starts
2021
+ };
2022
+ }
2023
+ function mergeUrlQueryRuns(segmentation) {
2024
+ const texts = [];
2025
+ const isWordLike = [];
2026
+ const kinds = [];
2027
+ const starts = [];
2028
+ for (let i = 0; i < segmentation.len; i++) {
2029
+ const text = segmentation.texts[i];
2030
+ texts.push(text);
2031
+ isWordLike.push(segmentation.isWordLike[i]);
2032
+ kinds.push(segmentation.kinds[i]);
2033
+ starts.push(segmentation.starts[i]);
2034
+ if (!isUrlQueryBoundarySegment(text)) continue;
2035
+ const nextIndex = i + 1;
2036
+ if (nextIndex >= segmentation.len || isTextRunBoundary(segmentation.kinds[nextIndex])) continue;
2037
+ const queryParts = [];
2038
+ const queryStart = segmentation.starts[nextIndex];
2039
+ let j = nextIndex;
2040
+ while (j < segmentation.len && !isTextRunBoundary(segmentation.kinds[j])) {
2041
+ queryParts.push(segmentation.texts[j]);
2042
+ j++;
2043
+ }
2044
+ if (queryParts.length > 0) {
2045
+ texts.push(joinTextParts(queryParts));
2046
+ isWordLike.push(true);
2047
+ kinds.push("text");
2048
+ starts.push(queryStart);
2049
+ i = j - 1;
2050
+ }
2051
+ }
2052
+ return {
2053
+ len: texts.length,
2054
+ texts,
2055
+ isWordLike,
2056
+ kinds,
2057
+ starts
2058
+ };
2059
+ }
2060
+ var numericJoinerChars = new Set([
2061
+ ":",
2062
+ "-",
2063
+ "/",
2064
+ "×",
2065
+ ",",
2066
+ ".",
2067
+ "+",
2068
+ "–",
2069
+ "—"
2070
+ ]);
2071
+ var asciiPunctuationChainSegmentRe = /^[A-Za-z0-9_]+[,:;]*$/;
2072
+ var asciiPunctuationChainTrailingJoinersRe = /[,:;]+$/;
2073
+ function segmentContainsDecimalDigit(text) {
2074
+ for (const ch of text) if (decimalDigitRe.test(ch)) return true;
2075
+ return false;
2076
+ }
2077
+ function isNumericRunSegment(text) {
2078
+ if (text.length === 0) return false;
2079
+ for (const ch of text) {
2080
+ if (decimalDigitRe.test(ch) || numericJoinerChars.has(ch)) continue;
2081
+ return false;
2082
+ }
2083
+ return true;
2084
+ }
2085
+ function mergeNumericRuns(segmentation) {
2086
+ const texts = [];
2087
+ const isWordLike = [];
2088
+ const kinds = [];
2089
+ const starts = [];
2090
+ for (let i = 0; i < segmentation.len; i++) {
2091
+ const text = segmentation.texts[i];
2092
+ const kind = segmentation.kinds[i];
2093
+ if (kind === "text" && isNumericRunSegment(text) && segmentContainsDecimalDigit(text)) {
2094
+ const mergedParts = [text];
2095
+ let j = i + 1;
2096
+ while (j < segmentation.len && segmentation.kinds[j] === "text" && isNumericRunSegment(segmentation.texts[j])) {
2097
+ mergedParts.push(segmentation.texts[j]);
2098
+ j++;
2099
+ }
2100
+ texts.push(joinTextParts(mergedParts));
2101
+ isWordLike.push(true);
2102
+ kinds.push("text");
2103
+ starts.push(segmentation.starts[i]);
2104
+ i = j - 1;
2105
+ continue;
2106
+ }
2107
+ texts.push(text);
2108
+ isWordLike.push(segmentation.isWordLike[i]);
2109
+ kinds.push(kind);
2110
+ starts.push(segmentation.starts[i]);
2111
+ }
2112
+ return {
2113
+ len: texts.length,
2114
+ texts,
2115
+ isWordLike,
2116
+ kinds,
2117
+ starts
2118
+ };
2119
+ }
2120
+ function mergeAsciiPunctuationChains(segmentation) {
2121
+ const texts = [];
2122
+ const isWordLike = [];
2123
+ const kinds = [];
2124
+ const starts = [];
2125
+ for (let i = 0; i < segmentation.len; i++) {
2126
+ const text = segmentation.texts[i];
2127
+ const kind = segmentation.kinds[i];
2128
+ const wordLike = segmentation.isWordLike[i];
2129
+ if (kind === "text" && wordLike && asciiPunctuationChainSegmentRe.test(text)) {
2130
+ const mergedParts = [text];
2131
+ let endsWithJoiners = asciiPunctuationChainTrailingJoinersRe.test(text);
2132
+ let j = i + 1;
2133
+ while (endsWithJoiners && j < segmentation.len && segmentation.kinds[j] === "text" && segmentation.isWordLike[j] && asciiPunctuationChainSegmentRe.test(segmentation.texts[j])) {
2134
+ const nextText = segmentation.texts[j];
2135
+ mergedParts.push(nextText);
2136
+ endsWithJoiners = asciiPunctuationChainTrailingJoinersRe.test(nextText);
2137
+ j++;
2138
+ }
2139
+ texts.push(joinTextParts(mergedParts));
2140
+ isWordLike.push(true);
2141
+ kinds.push("text");
2142
+ starts.push(segmentation.starts[i]);
2143
+ i = j - 1;
2144
+ continue;
2145
+ }
2146
+ texts.push(text);
2147
+ isWordLike.push(wordLike);
2148
+ kinds.push(kind);
2149
+ starts.push(segmentation.starts[i]);
2150
+ }
2151
+ return {
2152
+ len: texts.length,
2153
+ texts,
2154
+ isWordLike,
2155
+ kinds,
2156
+ starts
2157
+ };
2158
+ }
2159
+ function splitHyphenatedNumericRuns(segmentation) {
2160
+ const texts = [];
2161
+ const isWordLike = [];
2162
+ const kinds = [];
2163
+ const starts = [];
2164
+ for (let i = 0; i < segmentation.len; i++) {
2165
+ const text = segmentation.texts[i];
2166
+ if (segmentation.kinds[i] === "text" && text.includes("-")) {
2167
+ const parts = text.split("-");
2168
+ let shouldSplit = parts.length > 1;
2169
+ for (let j = 0; j < parts.length; j++) {
2170
+ const part = parts[j];
2171
+ if (!shouldSplit) break;
2172
+ if (part.length === 0 || !segmentContainsDecimalDigit(part) || !isNumericRunSegment(part)) shouldSplit = false;
2173
+ }
2174
+ if (shouldSplit) {
2175
+ let offset = 0;
2176
+ for (let j = 0; j < parts.length; j++) {
2177
+ const part = parts[j];
2178
+ const splitText = j < parts.length - 1 ? `${part}-` : part;
2179
+ texts.push(splitText);
2180
+ isWordLike.push(true);
2181
+ kinds.push("text");
2182
+ starts.push(segmentation.starts[i] + offset);
2183
+ offset += splitText.length;
2184
+ }
2185
+ continue;
2186
+ }
2187
+ }
2188
+ texts.push(text);
2189
+ isWordLike.push(segmentation.isWordLike[i]);
2190
+ kinds.push(segmentation.kinds[i]);
2191
+ starts.push(segmentation.starts[i]);
2192
+ }
2193
+ return {
2194
+ len: texts.length,
2195
+ texts,
2196
+ isWordLike,
2197
+ kinds,
2198
+ starts
2199
+ };
2200
+ }
2201
+ function mergeGlueConnectedTextRuns(segmentation) {
2202
+ const texts = [];
2203
+ const isWordLike = [];
2204
+ const kinds = [];
2205
+ const starts = [];
2206
+ let read = 0;
2207
+ while (read < segmentation.len) {
2208
+ const textParts = [segmentation.texts[read]];
2209
+ let wordLike = segmentation.isWordLike[read];
2210
+ let kind = segmentation.kinds[read];
2211
+ let start = segmentation.starts[read];
2212
+ if (kind === "glue") {
2213
+ const glueParts = [textParts[0]];
2214
+ const glueStart = start;
2215
+ read++;
2216
+ while (read < segmentation.len && segmentation.kinds[read] === "glue") {
2217
+ glueParts.push(segmentation.texts[read]);
2218
+ read++;
2219
+ }
2220
+ const glueText = joinTextParts(glueParts);
2221
+ if (read < segmentation.len && segmentation.kinds[read] === "text") {
2222
+ textParts[0] = glueText;
2223
+ textParts.push(segmentation.texts[read]);
2224
+ wordLike = segmentation.isWordLike[read];
2225
+ kind = "text";
2226
+ start = glueStart;
2227
+ read++;
2228
+ } else {
2229
+ texts.push(glueText);
2230
+ isWordLike.push(false);
2231
+ kinds.push("glue");
2232
+ starts.push(glueStart);
2233
+ continue;
2234
+ }
2235
+ } else read++;
2236
+ if (kind === "text") while (read < segmentation.len && segmentation.kinds[read] === "glue") {
2237
+ const glueParts = [];
2238
+ while (read < segmentation.len && segmentation.kinds[read] === "glue") {
2239
+ glueParts.push(segmentation.texts[read]);
2240
+ read++;
2241
+ }
2242
+ const glueText = joinTextParts(glueParts);
2243
+ if (read < segmentation.len && segmentation.kinds[read] === "text") {
2244
+ textParts.push(glueText, segmentation.texts[read]);
2245
+ wordLike = wordLike || segmentation.isWordLike[read];
2246
+ read++;
2247
+ continue;
2248
+ }
2249
+ textParts.push(glueText);
2250
+ }
2251
+ texts.push(joinTextParts(textParts));
2252
+ isWordLike.push(wordLike);
2253
+ kinds.push(kind);
2254
+ starts.push(start);
2255
+ }
2256
+ return {
2257
+ len: texts.length,
2258
+ texts,
2259
+ isWordLike,
2260
+ kinds,
2261
+ starts
2262
+ };
2263
+ }
2264
+ function carryTrailingForwardStickyAcrossCJKBoundary(segmentation) {
2265
+ const texts = segmentation.texts.slice();
2266
+ const isWordLike = segmentation.isWordLike.slice();
2267
+ const kinds = segmentation.kinds.slice();
2268
+ const starts = segmentation.starts.slice();
2269
+ for (let i = 0; i < texts.length - 1; i++) {
2270
+ if (kinds[i] !== "text" || kinds[i + 1] !== "text") continue;
2271
+ if (!isCJK(texts[i]) || !isCJK(texts[i + 1])) continue;
2272
+ const split = splitTrailingForwardStickyCluster(texts[i]);
2273
+ if (split === null) continue;
2274
+ texts[i] = split.head;
2275
+ texts[i + 1] = split.tail + texts[i + 1];
2276
+ starts[i + 1] = starts[i] + split.head.length;
2277
+ }
2278
+ return {
2279
+ len: texts.length,
2280
+ texts,
2281
+ isWordLike,
2282
+ kinds,
2283
+ starts
2284
+ };
2285
+ }
2286
+ function buildMergedSegmentation(normalized, profile, whiteSpaceProfile) {
2287
+ const wordSegmenter = getSharedWordSegmenter();
2288
+ let mergedLen = 0;
2289
+ const mergedTexts = [];
2290
+ const mergedWordLike = [];
2291
+ const mergedKinds = [];
2292
+ const mergedStarts = [];
2293
+ for (const s of wordSegmenter.segment(normalized)) for (const piece of splitSegmentByBreakKind(s.segment, s.isWordLike ?? false, s.index, whiteSpaceProfile)) {
2294
+ const isText = piece.kind === "text";
2295
+ if (profile.carryCJKAfterClosingQuote && isText && mergedLen > 0 && mergedKinds[mergedLen - 1] === "text" && isCJK(piece.text) && isCJK(mergedTexts[mergedLen - 1]) && endsWithClosingQuote(mergedTexts[mergedLen - 1])) {
2296
+ mergedTexts[mergedLen - 1] += piece.text;
2297
+ mergedWordLike[mergedLen - 1] = mergedWordLike[mergedLen - 1] || piece.isWordLike;
2298
+ } else if (isText && mergedLen > 0 && mergedKinds[mergedLen - 1] === "text" && isCJKLineStartProhibitedSegment(piece.text) && isCJK(mergedTexts[mergedLen - 1])) {
2299
+ mergedTexts[mergedLen - 1] += piece.text;
2300
+ mergedWordLike[mergedLen - 1] = mergedWordLike[mergedLen - 1] || piece.isWordLike;
2301
+ } else if (isText && mergedLen > 0 && mergedKinds[mergedLen - 1] === "text" && endsWithMyanmarMedialGlue(mergedTexts[mergedLen - 1])) {
2302
+ mergedTexts[mergedLen - 1] += piece.text;
2303
+ mergedWordLike[mergedLen - 1] = mergedWordLike[mergedLen - 1] || piece.isWordLike;
2304
+ } else if (isText && mergedLen > 0 && mergedKinds[mergedLen - 1] === "text" && piece.isWordLike && containsArabicScript(piece.text) && endsWithArabicNoSpacePunctuation(mergedTexts[mergedLen - 1])) {
2305
+ mergedTexts[mergedLen - 1] += piece.text;
2306
+ mergedWordLike[mergedLen - 1] = true;
2307
+ } else if (isText && !piece.isWordLike && mergedLen > 0 && mergedKinds[mergedLen - 1] === "text" && piece.text.length === 1 && piece.text !== "-" && piece.text !== "—" && isRepeatedSingleCharRun(mergedTexts[mergedLen - 1], piece.text)) mergedTexts[mergedLen - 1] += piece.text;
2308
+ else if (isText && !piece.isWordLike && mergedLen > 0 && mergedKinds[mergedLen - 1] === "text" && (isLeftStickyPunctuationSegment(piece.text) || piece.text === "-" && mergedWordLike[mergedLen - 1])) mergedTexts[mergedLen - 1] += piece.text;
2309
+ else {
2310
+ mergedTexts[mergedLen] = piece.text;
2311
+ mergedWordLike[mergedLen] = piece.isWordLike;
2312
+ mergedKinds[mergedLen] = piece.kind;
2313
+ mergedStarts[mergedLen] = piece.start;
2314
+ mergedLen++;
2315
+ }
2316
+ }
2317
+ for (let i = 1; i < mergedLen; i++) if (mergedKinds[i] === "text" && !mergedWordLike[i] && isEscapedQuoteClusterSegment(mergedTexts[i]) && mergedKinds[i - 1] === "text") {
2318
+ mergedTexts[i - 1] += mergedTexts[i];
2319
+ mergedWordLike[i - 1] = mergedWordLike[i - 1] || mergedWordLike[i];
2320
+ mergedTexts[i] = "";
2321
+ }
2322
+ for (let i = mergedLen - 2; i >= 0; i--) if (mergedKinds[i] === "text" && !mergedWordLike[i] && isForwardStickyClusterSegment(mergedTexts[i])) {
2323
+ let j = i + 1;
2324
+ while (j < mergedLen && mergedTexts[j] === "") j++;
2325
+ if (j < mergedLen && mergedKinds[j] === "text") {
2326
+ mergedTexts[j] = mergedTexts[i] + mergedTexts[j];
2327
+ mergedStarts[j] = mergedStarts[i];
2328
+ mergedTexts[i] = "";
2329
+ }
2330
+ }
2331
+ let compactLen = 0;
2332
+ for (let read = 0; read < mergedLen; read++) {
2333
+ const text = mergedTexts[read];
2334
+ if (text.length === 0) continue;
2335
+ if (compactLen !== read) {
2336
+ mergedTexts[compactLen] = text;
2337
+ mergedWordLike[compactLen] = mergedWordLike[read];
2338
+ mergedKinds[compactLen] = mergedKinds[read];
2339
+ mergedStarts[compactLen] = mergedStarts[read];
2340
+ }
2341
+ compactLen++;
2342
+ }
2343
+ mergedTexts.length = compactLen;
2344
+ mergedWordLike.length = compactLen;
2345
+ mergedKinds.length = compactLen;
2346
+ mergedStarts.length = compactLen;
2347
+ const withMergedUrls = carryTrailingForwardStickyAcrossCJKBoundary(mergeAsciiPunctuationChains(splitHyphenatedNumericRuns(mergeNumericRuns(mergeUrlQueryRuns(mergeUrlLikeRuns(mergeGlueConnectedTextRuns({
2348
+ len: compactLen,
2349
+ texts: mergedTexts,
2350
+ isWordLike: mergedWordLike,
2351
+ kinds: mergedKinds,
2352
+ starts: mergedStarts
2353
+ })))))));
2354
+ for (let i = 0; i < withMergedUrls.len - 1; i++) {
2355
+ const split = splitLeadingSpaceAndMarks(withMergedUrls.texts[i]);
2356
+ if (split === null) continue;
2357
+ if (withMergedUrls.kinds[i] !== "space" && withMergedUrls.kinds[i] !== "preserved-space" || withMergedUrls.kinds[i + 1] !== "text" || !containsArabicScript(withMergedUrls.texts[i + 1])) continue;
2358
+ withMergedUrls.texts[i] = split.space;
2359
+ withMergedUrls.isWordLike[i] = false;
2360
+ withMergedUrls.kinds[i] = withMergedUrls.kinds[i] === "preserved-space" ? "preserved-space" : "space";
2361
+ withMergedUrls.texts[i + 1] = split.marks + withMergedUrls.texts[i + 1];
2362
+ withMergedUrls.starts[i + 1] = withMergedUrls.starts[i] + split.space.length;
2363
+ }
2364
+ return withMergedUrls;
2365
+ }
2366
+ function compileAnalysisChunks(segmentation, whiteSpaceProfile) {
2367
+ if (segmentation.len === 0) return [];
2368
+ if (!whiteSpaceProfile.preserveHardBreaks) return [{
2369
+ startSegmentIndex: 0,
2370
+ endSegmentIndex: segmentation.len,
2371
+ consumedEndSegmentIndex: segmentation.len
2372
+ }];
2373
+ const chunks = [];
2374
+ let startSegmentIndex = 0;
2375
+ for (let i = 0; i < segmentation.len; i++) {
2376
+ if (segmentation.kinds[i] !== "hard-break") continue;
2377
+ chunks.push({
2378
+ startSegmentIndex,
2379
+ endSegmentIndex: i,
2380
+ consumedEndSegmentIndex: i + 1
2381
+ });
2382
+ startSegmentIndex = i + 1;
2383
+ }
2384
+ if (startSegmentIndex < segmentation.len) chunks.push({
2385
+ startSegmentIndex,
2386
+ endSegmentIndex: segmentation.len,
2387
+ consumedEndSegmentIndex: segmentation.len
2388
+ });
2389
+ return chunks;
2390
+ }
2391
+ function analyzeText(text, profile, whiteSpace = "normal") {
2392
+ const whiteSpaceProfile = getWhiteSpaceProfile(whiteSpace);
2393
+ const normalized = whiteSpaceProfile.mode === "pre-wrap" ? normalizeWhitespacePreWrap(text) : normalizeWhitespaceNormal(text);
2394
+ if (normalized.length === 0) return {
2395
+ normalized,
2396
+ chunks: [],
2397
+ len: 0,
2398
+ texts: [],
2399
+ isWordLike: [],
2400
+ kinds: [],
2401
+ starts: []
2402
+ };
2403
+ const segmentation = buildMergedSegmentation(normalized, profile, whiteSpaceProfile);
2404
+ return {
2405
+ normalized,
2406
+ chunks: compileAnalysisChunks(segmentation, whiteSpaceProfile),
2407
+ ...segmentation
2408
+ };
2409
+ }
2410
+ //#endregion
2411
+ //#region node_modules/@chenglou/pretext/dist/measurement.js
2412
+ var measureContext = null;
2413
+ var segmentMetricCaches = /* @__PURE__ */ new Map();
2414
+ var cachedEngineProfile = null;
2415
+ var emojiPresentationRe = /\p{Emoji_Presentation}/u;
2416
+ var maybeEmojiRe = /[\p{Emoji_Presentation}\p{Extended_Pictographic}\p{Regional_Indicator}\uFE0F\u20E3]/u;
2417
+ var sharedGraphemeSegmenter$2 = null;
2418
+ var emojiCorrectionCache = /* @__PURE__ */ new Map();
2419
+ function getMeasureContext$1() {
2420
+ if (measureContext !== null) return measureContext;
2421
+ if (typeof OffscreenCanvas !== "undefined") {
2422
+ measureContext = new OffscreenCanvas(1, 1).getContext("2d");
2423
+ return measureContext;
2424
+ }
2425
+ if (typeof document !== "undefined") {
2426
+ measureContext = document.createElement("canvas").getContext("2d");
2427
+ return measureContext;
2428
+ }
2429
+ throw new Error("Text measurement requires OffscreenCanvas or a DOM canvas context.");
2430
+ }
2431
+ function getSegmentMetricCache(font) {
2432
+ let cache = segmentMetricCaches.get(font);
2433
+ if (!cache) {
2434
+ cache = /* @__PURE__ */ new Map();
2435
+ segmentMetricCaches.set(font, cache);
2436
+ }
2437
+ return cache;
2438
+ }
2439
+ function getSegmentMetrics(seg, cache) {
2440
+ let metrics = cache.get(seg);
2441
+ if (metrics === void 0) {
2442
+ metrics = {
2443
+ width: getMeasureContext$1().measureText(seg).width,
2444
+ containsCJK: isCJK(seg)
2445
+ };
2446
+ cache.set(seg, metrics);
2447
+ }
2448
+ return metrics;
2449
+ }
2450
+ function getEngineProfile() {
2451
+ if (cachedEngineProfile !== null) return cachedEngineProfile;
2452
+ if (typeof navigator === "undefined") {
2453
+ cachedEngineProfile = {
2454
+ lineFitEpsilon: .005,
2455
+ carryCJKAfterClosingQuote: false,
2456
+ preferPrefixWidthsForBreakableRuns: false,
2457
+ preferEarlySoftHyphenBreak: false
2458
+ };
2459
+ return cachedEngineProfile;
2460
+ }
2461
+ const ua = navigator.userAgent;
2462
+ const isSafari = navigator.vendor === "Apple Computer, Inc." && ua.includes("Safari/") && !ua.includes("Chrome/") && !ua.includes("Chromium/") && !ua.includes("CriOS/") && !ua.includes("FxiOS/") && !ua.includes("EdgiOS/");
2463
+ const isChromium = ua.includes("Chrome/") || ua.includes("Chromium/") || ua.includes("CriOS/") || ua.includes("Edg/");
2464
+ cachedEngineProfile = {
2465
+ lineFitEpsilon: isSafari ? 1 / 64 : .005,
2466
+ carryCJKAfterClosingQuote: isChromium,
2467
+ preferPrefixWidthsForBreakableRuns: isSafari,
2468
+ preferEarlySoftHyphenBreak: isSafari
2469
+ };
2470
+ return cachedEngineProfile;
2471
+ }
2472
+ function parseFontSize(font) {
2473
+ const m = font.match(/(\d+(?:\.\d+)?)\s*px/);
2474
+ return m ? parseFloat(m[1]) : 16;
2475
+ }
2476
+ function getSharedGraphemeSegmenter$1() {
2477
+ if (sharedGraphemeSegmenter$2 === null) sharedGraphemeSegmenter$2 = new Intl.Segmenter(void 0, { granularity: "grapheme" });
2478
+ return sharedGraphemeSegmenter$2;
2479
+ }
2480
+ function isEmojiGrapheme(g) {
2481
+ return emojiPresentationRe.test(g) || g.includes("️");
2482
+ }
2483
+ function textMayContainEmoji(text) {
2484
+ return maybeEmojiRe.test(text);
2485
+ }
2486
+ function getEmojiCorrection(font, fontSize) {
2487
+ let correction = emojiCorrectionCache.get(font);
2488
+ if (correction !== void 0) return correction;
2489
+ const ctx = getMeasureContext$1();
2490
+ ctx.font = font;
2491
+ const canvasW = ctx.measureText("😀").width;
2492
+ correction = 0;
2493
+ if (canvasW > fontSize + .5 && typeof document !== "undefined" && document.body !== null) {
2494
+ const span = document.createElement("span");
2495
+ span.style.font = font;
2496
+ span.style.display = "inline-block";
2497
+ span.style.visibility = "hidden";
2498
+ span.style.position = "absolute";
2499
+ span.textContent = "😀";
2500
+ document.body.appendChild(span);
2501
+ const domW = span.getBoundingClientRect().width;
2502
+ document.body.removeChild(span);
2503
+ if (canvasW - domW > .5) correction = canvasW - domW;
2504
+ }
2505
+ emojiCorrectionCache.set(font, correction);
2506
+ return correction;
2507
+ }
2508
+ function countEmojiGraphemes(text) {
2509
+ let count = 0;
2510
+ const graphemeSegmenter = getSharedGraphemeSegmenter$1();
2511
+ for (const g of graphemeSegmenter.segment(text)) if (isEmojiGrapheme(g.segment)) count++;
2512
+ return count;
2513
+ }
2514
+ function getEmojiCount(seg, metrics) {
2515
+ if (metrics.emojiCount === void 0) metrics.emojiCount = countEmojiGraphemes(seg);
2516
+ return metrics.emojiCount;
2517
+ }
2518
+ function getCorrectedSegmentWidth(seg, metrics, emojiCorrection) {
2519
+ if (emojiCorrection === 0) return metrics.width;
2520
+ return metrics.width - getEmojiCount(seg, metrics) * emojiCorrection;
2521
+ }
2522
+ function getSegmentGraphemeWidths(seg, metrics, cache, emojiCorrection) {
2523
+ if (metrics.graphemeWidths !== void 0) return metrics.graphemeWidths;
2524
+ const widths = [];
2525
+ const graphemeSegmenter = getSharedGraphemeSegmenter$1();
2526
+ for (const gs of graphemeSegmenter.segment(seg)) {
2527
+ const graphemeMetrics = getSegmentMetrics(gs.segment, cache);
2528
+ widths.push(getCorrectedSegmentWidth(gs.segment, graphemeMetrics, emojiCorrection));
2529
+ }
2530
+ metrics.graphemeWidths = widths.length > 1 ? widths : null;
2531
+ return metrics.graphemeWidths;
2532
+ }
2533
+ function getSegmentGraphemePrefixWidths(seg, metrics, cache, emojiCorrection) {
2534
+ if (metrics.graphemePrefixWidths !== void 0) return metrics.graphemePrefixWidths;
2535
+ const prefixWidths = [];
2536
+ const graphemeSegmenter = getSharedGraphemeSegmenter$1();
2537
+ let prefix = "";
2538
+ for (const gs of graphemeSegmenter.segment(seg)) {
2539
+ prefix += gs.segment;
2540
+ const prefixMetrics = getSegmentMetrics(prefix, cache);
2541
+ prefixWidths.push(getCorrectedSegmentWidth(prefix, prefixMetrics, emojiCorrection));
2542
+ }
2543
+ metrics.graphemePrefixWidths = prefixWidths.length > 1 ? prefixWidths : null;
2544
+ return metrics.graphemePrefixWidths;
2545
+ }
2546
+ function getFontMeasurementState(font, needsEmojiCorrection) {
2547
+ const ctx = getMeasureContext$1();
2548
+ ctx.font = font;
2549
+ const cache = getSegmentMetricCache(font);
2550
+ const fontSize = parseFontSize(font);
2551
+ return {
2552
+ cache,
2553
+ fontSize,
2554
+ emojiCorrection: needsEmojiCorrection ? getEmojiCorrection(font, fontSize) : 0
2555
+ };
2556
+ }
2557
+ //#endregion
2558
+ //#region node_modules/@chenglou/pretext/dist/line-break.js
2559
+ function canBreakAfter(kind) {
2560
+ return kind === "space" || kind === "preserved-space" || kind === "tab" || kind === "zero-width-break" || kind === "soft-hyphen";
2561
+ }
2562
+ function normalizeSimpleLineStartSegmentIndex(prepared, segmentIndex) {
2563
+ while (segmentIndex < prepared.widths.length) {
2564
+ const kind = prepared.kinds[segmentIndex];
2565
+ if (kind !== "space" && kind !== "zero-width-break" && kind !== "soft-hyphen") break;
2566
+ segmentIndex++;
2567
+ }
2568
+ return segmentIndex;
2569
+ }
2570
+ function getTabAdvance(lineWidth, tabStopAdvance) {
2571
+ if (tabStopAdvance <= 0) return 0;
2572
+ const remainder = lineWidth % tabStopAdvance;
2573
+ if (Math.abs(remainder) <= 1e-6) return tabStopAdvance;
2574
+ return tabStopAdvance - remainder;
2575
+ }
2576
+ function getBreakableAdvance(graphemeWidths, graphemePrefixWidths, graphemeIndex, preferPrefixWidths) {
2577
+ if (!preferPrefixWidths || graphemePrefixWidths === null) return graphemeWidths[graphemeIndex];
2578
+ return graphemePrefixWidths[graphemeIndex] - (graphemeIndex > 0 ? graphemePrefixWidths[graphemeIndex - 1] : 0);
2579
+ }
2580
+ function fitSoftHyphenBreak(graphemeWidths, initialWidth, maxWidth, lineFitEpsilon, discretionaryHyphenWidth, cumulativeWidths) {
2581
+ let fitCount = 0;
2582
+ let fittedWidth = initialWidth;
2583
+ while (fitCount < graphemeWidths.length) {
2584
+ const nextWidth = cumulativeWidths ? initialWidth + graphemeWidths[fitCount] : fittedWidth + graphemeWidths[fitCount];
2585
+ if ((fitCount + 1 < graphemeWidths.length ? nextWidth + discretionaryHyphenWidth : nextWidth) > maxWidth + lineFitEpsilon) break;
2586
+ fittedWidth = nextWidth;
2587
+ fitCount++;
2588
+ }
2589
+ return {
2590
+ fitCount,
2591
+ fittedWidth
2592
+ };
2593
+ }
2594
+ function walkPreparedLinesSimple(prepared, maxWidth, onLine) {
2595
+ const { widths, kinds, breakableWidths, breakablePrefixWidths } = prepared;
2596
+ if (widths.length === 0) return 0;
2597
+ const engineProfile = getEngineProfile();
2598
+ const lineFitEpsilon = engineProfile.lineFitEpsilon;
2599
+ let lineCount = 0;
2600
+ let lineW = 0;
2601
+ let hasContent = false;
2602
+ let lineStartSegmentIndex = 0;
2603
+ let lineStartGraphemeIndex = 0;
2604
+ let lineEndSegmentIndex = 0;
2605
+ let lineEndGraphemeIndex = 0;
2606
+ let pendingBreakSegmentIndex = -1;
2607
+ let pendingBreakPaintWidth = 0;
2608
+ function clearPendingBreak() {
2609
+ pendingBreakSegmentIndex = -1;
2610
+ pendingBreakPaintWidth = 0;
2611
+ }
2612
+ function emitCurrentLine(endSegmentIndex = lineEndSegmentIndex, endGraphemeIndex = lineEndGraphemeIndex, width = lineW) {
2613
+ lineCount++;
2614
+ onLine?.({
2615
+ startSegmentIndex: lineStartSegmentIndex,
2616
+ startGraphemeIndex: lineStartGraphemeIndex,
2617
+ endSegmentIndex,
2618
+ endGraphemeIndex,
2619
+ width
2620
+ });
2621
+ lineW = 0;
2622
+ hasContent = false;
2623
+ clearPendingBreak();
2624
+ }
2625
+ function startLineAtSegment(segmentIndex, width) {
2626
+ hasContent = true;
2627
+ lineStartSegmentIndex = segmentIndex;
2628
+ lineStartGraphemeIndex = 0;
2629
+ lineEndSegmentIndex = segmentIndex + 1;
2630
+ lineEndGraphemeIndex = 0;
2631
+ lineW = width;
2632
+ }
2633
+ function startLineAtGrapheme(segmentIndex, graphemeIndex, width) {
2634
+ hasContent = true;
2635
+ lineStartSegmentIndex = segmentIndex;
2636
+ lineStartGraphemeIndex = graphemeIndex;
2637
+ lineEndSegmentIndex = segmentIndex;
2638
+ lineEndGraphemeIndex = graphemeIndex + 1;
2639
+ lineW = width;
2640
+ }
2641
+ function appendWholeSegment(segmentIndex, width) {
2642
+ if (!hasContent) {
2643
+ startLineAtSegment(segmentIndex, width);
2644
+ return;
2645
+ }
2646
+ lineW += width;
2647
+ lineEndSegmentIndex = segmentIndex + 1;
2648
+ lineEndGraphemeIndex = 0;
2649
+ }
2650
+ function updatePendingBreak(segmentIndex, segmentWidth) {
2651
+ if (!canBreakAfter(kinds[segmentIndex])) return;
2652
+ pendingBreakSegmentIndex = segmentIndex + 1;
2653
+ pendingBreakPaintWidth = lineW - segmentWidth;
2654
+ }
2655
+ function appendBreakableSegment(segmentIndex) {
2656
+ appendBreakableSegmentFrom(segmentIndex, 0);
2657
+ }
2658
+ function appendBreakableSegmentFrom(segmentIndex, startGraphemeIndex) {
2659
+ const gWidths = breakableWidths[segmentIndex];
2660
+ const gPrefixWidths = breakablePrefixWidths[segmentIndex] ?? null;
2661
+ for (let g = startGraphemeIndex; g < gWidths.length; g++) {
2662
+ const gw = getBreakableAdvance(gWidths, gPrefixWidths, g, engineProfile.preferPrefixWidthsForBreakableRuns);
2663
+ if (!hasContent) {
2664
+ startLineAtGrapheme(segmentIndex, g, gw);
2665
+ continue;
2666
+ }
2667
+ if (lineW + gw > maxWidth + lineFitEpsilon) {
2668
+ emitCurrentLine();
2669
+ startLineAtGrapheme(segmentIndex, g, gw);
2670
+ } else {
2671
+ lineW += gw;
2672
+ lineEndSegmentIndex = segmentIndex;
2673
+ lineEndGraphemeIndex = g + 1;
2674
+ }
2675
+ }
2676
+ if (hasContent && lineEndSegmentIndex === segmentIndex && lineEndGraphemeIndex === gWidths.length) {
2677
+ lineEndSegmentIndex = segmentIndex + 1;
2678
+ lineEndGraphemeIndex = 0;
2679
+ }
2680
+ }
2681
+ let i = 0;
2682
+ while (i < widths.length) {
2683
+ if (!hasContent) {
2684
+ i = normalizeSimpleLineStartSegmentIndex(prepared, i);
2685
+ if (i >= widths.length) break;
2686
+ }
2687
+ const w = widths[i];
2688
+ const kind = kinds[i];
2689
+ if (!hasContent) {
2690
+ if (w > maxWidth && breakableWidths[i] !== null) appendBreakableSegment(i);
2691
+ else startLineAtSegment(i, w);
2692
+ updatePendingBreak(i, w);
2693
+ i++;
2694
+ continue;
2695
+ }
2696
+ if (lineW + w > maxWidth + lineFitEpsilon) {
2697
+ if (canBreakAfter(kind)) {
2698
+ appendWholeSegment(i, w);
2699
+ emitCurrentLine(i + 1, 0, lineW - w);
2700
+ i++;
2701
+ continue;
2702
+ }
2703
+ if (pendingBreakSegmentIndex >= 0) {
2704
+ if (lineEndSegmentIndex > pendingBreakSegmentIndex || lineEndSegmentIndex === pendingBreakSegmentIndex && lineEndGraphemeIndex > 0) {
2705
+ emitCurrentLine();
2706
+ continue;
2707
+ }
2708
+ emitCurrentLine(pendingBreakSegmentIndex, 0, pendingBreakPaintWidth);
2709
+ continue;
2710
+ }
2711
+ if (w > maxWidth && breakableWidths[i] !== null) {
2712
+ emitCurrentLine();
2713
+ appendBreakableSegment(i);
2714
+ i++;
2715
+ continue;
2716
+ }
2717
+ emitCurrentLine();
2718
+ continue;
2719
+ }
2720
+ appendWholeSegment(i, w);
2721
+ updatePendingBreak(i, w);
2722
+ i++;
2723
+ }
2724
+ if (hasContent) emitCurrentLine();
2725
+ return lineCount;
2726
+ }
2727
+ function walkPreparedLines(prepared, maxWidth, onLine) {
2728
+ if (prepared.simpleLineWalkFastPath) return walkPreparedLinesSimple(prepared, maxWidth, onLine);
2729
+ const { widths, lineEndFitAdvances, lineEndPaintAdvances, kinds, breakableWidths, breakablePrefixWidths, discretionaryHyphenWidth, tabStopAdvance, chunks } = prepared;
2730
+ if (widths.length === 0 || chunks.length === 0) return 0;
2731
+ const engineProfile = getEngineProfile();
2732
+ const lineFitEpsilon = engineProfile.lineFitEpsilon;
2733
+ let lineCount = 0;
2734
+ let lineW = 0;
2735
+ let hasContent = false;
2736
+ let lineStartSegmentIndex = 0;
2737
+ let lineStartGraphemeIndex = 0;
2738
+ let lineEndSegmentIndex = 0;
2739
+ let lineEndGraphemeIndex = 0;
2740
+ let pendingBreakSegmentIndex = -1;
2741
+ let pendingBreakFitWidth = 0;
2742
+ let pendingBreakPaintWidth = 0;
2743
+ let pendingBreakKind = null;
2744
+ function clearPendingBreak() {
2745
+ pendingBreakSegmentIndex = -1;
2746
+ pendingBreakFitWidth = 0;
2747
+ pendingBreakPaintWidth = 0;
2748
+ pendingBreakKind = null;
2749
+ }
2750
+ function emitCurrentLine(endSegmentIndex = lineEndSegmentIndex, endGraphemeIndex = lineEndGraphemeIndex, width = lineW) {
2751
+ lineCount++;
2752
+ onLine?.({
2753
+ startSegmentIndex: lineStartSegmentIndex,
2754
+ startGraphemeIndex: lineStartGraphemeIndex,
2755
+ endSegmentIndex,
2756
+ endGraphemeIndex,
2757
+ width
2758
+ });
2759
+ lineW = 0;
2760
+ hasContent = false;
2761
+ clearPendingBreak();
2762
+ }
2763
+ function startLineAtSegment(segmentIndex, width) {
2764
+ hasContent = true;
2765
+ lineStartSegmentIndex = segmentIndex;
2766
+ lineStartGraphemeIndex = 0;
2767
+ lineEndSegmentIndex = segmentIndex + 1;
2768
+ lineEndGraphemeIndex = 0;
2769
+ lineW = width;
2770
+ }
2771
+ function startLineAtGrapheme(segmentIndex, graphemeIndex, width) {
2772
+ hasContent = true;
2773
+ lineStartSegmentIndex = segmentIndex;
2774
+ lineStartGraphemeIndex = graphemeIndex;
2775
+ lineEndSegmentIndex = segmentIndex;
2776
+ lineEndGraphemeIndex = graphemeIndex + 1;
2777
+ lineW = width;
2778
+ }
2779
+ function appendWholeSegment(segmentIndex, width) {
2780
+ if (!hasContent) {
2781
+ startLineAtSegment(segmentIndex, width);
2782
+ return;
2783
+ }
2784
+ lineW += width;
2785
+ lineEndSegmentIndex = segmentIndex + 1;
2786
+ lineEndGraphemeIndex = 0;
2787
+ }
2788
+ function updatePendingBreakForWholeSegment(segmentIndex, segmentWidth) {
2789
+ if (!canBreakAfter(kinds[segmentIndex])) return;
2790
+ const fitAdvance = kinds[segmentIndex] === "tab" ? 0 : lineEndFitAdvances[segmentIndex];
2791
+ const paintAdvance = kinds[segmentIndex] === "tab" ? segmentWidth : lineEndPaintAdvances[segmentIndex];
2792
+ pendingBreakSegmentIndex = segmentIndex + 1;
2793
+ pendingBreakFitWidth = lineW - segmentWidth + fitAdvance;
2794
+ pendingBreakPaintWidth = lineW - segmentWidth + paintAdvance;
2795
+ pendingBreakKind = kinds[segmentIndex];
2796
+ }
2797
+ function appendBreakableSegment(segmentIndex) {
2798
+ appendBreakableSegmentFrom(segmentIndex, 0);
2799
+ }
2800
+ function appendBreakableSegmentFrom(segmentIndex, startGraphemeIndex) {
2801
+ const gWidths = breakableWidths[segmentIndex];
2802
+ const gPrefixWidths = breakablePrefixWidths[segmentIndex] ?? null;
2803
+ for (let g = startGraphemeIndex; g < gWidths.length; g++) {
2804
+ const gw = getBreakableAdvance(gWidths, gPrefixWidths, g, engineProfile.preferPrefixWidthsForBreakableRuns);
2805
+ if (!hasContent) {
2806
+ startLineAtGrapheme(segmentIndex, g, gw);
2807
+ continue;
2808
+ }
2809
+ if (lineW + gw > maxWidth + lineFitEpsilon) {
2810
+ emitCurrentLine();
2811
+ startLineAtGrapheme(segmentIndex, g, gw);
2812
+ } else {
2813
+ lineW += gw;
2814
+ lineEndSegmentIndex = segmentIndex;
2815
+ lineEndGraphemeIndex = g + 1;
2816
+ }
2817
+ }
2818
+ if (hasContent && lineEndSegmentIndex === segmentIndex && lineEndGraphemeIndex === gWidths.length) {
2819
+ lineEndSegmentIndex = segmentIndex + 1;
2820
+ lineEndGraphemeIndex = 0;
2821
+ }
2822
+ }
2823
+ function continueSoftHyphenBreakableSegment(segmentIndex) {
2824
+ if (pendingBreakKind !== "soft-hyphen") return false;
2825
+ const gWidths = breakableWidths[segmentIndex];
2826
+ if (gWidths === null) return false;
2827
+ const fitWidths = engineProfile.preferPrefixWidthsForBreakableRuns ? breakablePrefixWidths[segmentIndex] ?? gWidths : gWidths;
2828
+ const { fitCount, fittedWidth } = fitSoftHyphenBreak(fitWidths, lineW, maxWidth, lineFitEpsilon, discretionaryHyphenWidth, fitWidths !== gWidths);
2829
+ if (fitCount === 0) return false;
2830
+ lineW = fittedWidth;
2831
+ lineEndSegmentIndex = segmentIndex;
2832
+ lineEndGraphemeIndex = fitCount;
2833
+ clearPendingBreak();
2834
+ if (fitCount === gWidths.length) {
2835
+ lineEndSegmentIndex = segmentIndex + 1;
2836
+ lineEndGraphemeIndex = 0;
2837
+ return true;
2838
+ }
2839
+ emitCurrentLine(segmentIndex, fitCount, fittedWidth + discretionaryHyphenWidth);
2840
+ appendBreakableSegmentFrom(segmentIndex, fitCount);
2841
+ return true;
2842
+ }
2843
+ function emitEmptyChunk(chunk) {
2844
+ lineCount++;
2845
+ onLine?.({
2846
+ startSegmentIndex: chunk.startSegmentIndex,
2847
+ startGraphemeIndex: 0,
2848
+ endSegmentIndex: chunk.consumedEndSegmentIndex,
2849
+ endGraphemeIndex: 0,
2850
+ width: 0
2851
+ });
2852
+ clearPendingBreak();
2853
+ }
2854
+ for (let chunkIndex = 0; chunkIndex < chunks.length; chunkIndex++) {
2855
+ const chunk = chunks[chunkIndex];
2856
+ if (chunk.startSegmentIndex === chunk.endSegmentIndex) {
2857
+ emitEmptyChunk(chunk);
2858
+ continue;
2859
+ }
2860
+ hasContent = false;
2861
+ lineW = 0;
2862
+ lineStartSegmentIndex = chunk.startSegmentIndex;
2863
+ lineStartGraphemeIndex = 0;
2864
+ lineEndSegmentIndex = chunk.startSegmentIndex;
2865
+ lineEndGraphemeIndex = 0;
2866
+ clearPendingBreak();
2867
+ let i = chunk.startSegmentIndex;
2868
+ while (i < chunk.endSegmentIndex) {
2869
+ const kind = kinds[i];
2870
+ const w = kind === "tab" ? getTabAdvance(lineW, tabStopAdvance) : widths[i];
2871
+ if (kind === "soft-hyphen") {
2872
+ if (hasContent) {
2873
+ lineEndSegmentIndex = i + 1;
2874
+ lineEndGraphemeIndex = 0;
2875
+ pendingBreakSegmentIndex = i + 1;
2876
+ pendingBreakFitWidth = lineW + discretionaryHyphenWidth;
2877
+ pendingBreakPaintWidth = lineW + discretionaryHyphenWidth;
2878
+ pendingBreakKind = kind;
2879
+ }
2880
+ i++;
2881
+ continue;
2882
+ }
2883
+ if (!hasContent) {
2884
+ if (w > maxWidth && breakableWidths[i] !== null) appendBreakableSegment(i);
2885
+ else startLineAtSegment(i, w);
2886
+ updatePendingBreakForWholeSegment(i, w);
2887
+ i++;
2888
+ continue;
2889
+ }
2890
+ if (lineW + w > maxWidth + lineFitEpsilon) {
2891
+ const currentBreakFitWidth = lineW + (kind === "tab" ? 0 : lineEndFitAdvances[i]);
2892
+ const currentBreakPaintWidth = lineW + (kind === "tab" ? w : lineEndPaintAdvances[i]);
2893
+ if (pendingBreakKind === "soft-hyphen" && engineProfile.preferEarlySoftHyphenBreak && pendingBreakFitWidth <= maxWidth + lineFitEpsilon) {
2894
+ emitCurrentLine(pendingBreakSegmentIndex, 0, pendingBreakPaintWidth);
2895
+ continue;
2896
+ }
2897
+ if (pendingBreakKind === "soft-hyphen" && continueSoftHyphenBreakableSegment(i)) {
2898
+ i++;
2899
+ continue;
2900
+ }
2901
+ if (canBreakAfter(kind) && currentBreakFitWidth <= maxWidth + lineFitEpsilon) {
2902
+ appendWholeSegment(i, w);
2903
+ emitCurrentLine(i + 1, 0, currentBreakPaintWidth);
2904
+ i++;
2905
+ continue;
2906
+ }
2907
+ if (pendingBreakSegmentIndex >= 0 && pendingBreakFitWidth <= maxWidth + lineFitEpsilon) {
2908
+ if (lineEndSegmentIndex > pendingBreakSegmentIndex || lineEndSegmentIndex === pendingBreakSegmentIndex && lineEndGraphemeIndex > 0) {
2909
+ emitCurrentLine();
2910
+ continue;
2911
+ }
2912
+ const nextSegmentIndex = pendingBreakSegmentIndex;
2913
+ emitCurrentLine(nextSegmentIndex, 0, pendingBreakPaintWidth);
2914
+ i = nextSegmentIndex;
2915
+ continue;
2916
+ }
2917
+ if (w > maxWidth && breakableWidths[i] !== null) {
2918
+ emitCurrentLine();
2919
+ appendBreakableSegment(i);
2920
+ i++;
2921
+ continue;
2922
+ }
2923
+ emitCurrentLine();
2924
+ continue;
2925
+ }
2926
+ appendWholeSegment(i, w);
2927
+ updatePendingBreakForWholeSegment(i, w);
2928
+ i++;
2929
+ }
2930
+ if (hasContent) {
2931
+ const finalPaintWidth = pendingBreakSegmentIndex === chunk.consumedEndSegmentIndex ? pendingBreakPaintWidth : lineW;
2932
+ emitCurrentLine(chunk.consumedEndSegmentIndex, 0, finalPaintWidth);
2933
+ }
2934
+ }
2935
+ return lineCount;
2936
+ }
2937
+ //#endregion
2938
+ //#region node_modules/@chenglou/pretext/dist/layout.js
2939
+ var sharedGraphemeSegmenter$1 = null;
2940
+ var sharedLineTextCaches = /* @__PURE__ */ new WeakMap();
2941
+ function getSharedGraphemeSegmenter() {
2942
+ if (sharedGraphemeSegmenter$1 === null) sharedGraphemeSegmenter$1 = new Intl.Segmenter(void 0, { granularity: "grapheme" });
2943
+ return sharedGraphemeSegmenter$1;
2944
+ }
2945
+ function createEmptyPrepared(includeSegments) {
2946
+ if (includeSegments) return {
2947
+ widths: [],
2948
+ lineEndFitAdvances: [],
2949
+ lineEndPaintAdvances: [],
2950
+ kinds: [],
2951
+ simpleLineWalkFastPath: true,
2952
+ segLevels: null,
2953
+ breakableWidths: [],
2954
+ breakablePrefixWidths: [],
2955
+ discretionaryHyphenWidth: 0,
2956
+ tabStopAdvance: 0,
2957
+ chunks: [],
2958
+ segments: []
2959
+ };
2960
+ return {
2961
+ widths: [],
2962
+ lineEndFitAdvances: [],
2963
+ lineEndPaintAdvances: [],
2964
+ kinds: [],
2965
+ simpleLineWalkFastPath: true,
2966
+ segLevels: null,
2967
+ breakableWidths: [],
2968
+ breakablePrefixWidths: [],
2969
+ discretionaryHyphenWidth: 0,
2970
+ tabStopAdvance: 0,
2971
+ chunks: []
2972
+ };
2973
+ }
2974
+ function measureAnalysis(analysis, font, includeSegments) {
2975
+ const graphemeSegmenter = getSharedGraphemeSegmenter();
2976
+ const engineProfile = getEngineProfile();
2977
+ const { cache, emojiCorrection } = getFontMeasurementState(font, textMayContainEmoji(analysis.normalized));
2978
+ const discretionaryHyphenWidth = getCorrectedSegmentWidth("-", getSegmentMetrics("-", cache), emojiCorrection);
2979
+ const tabStopAdvance = getCorrectedSegmentWidth(" ", getSegmentMetrics(" ", cache), emojiCorrection) * 8;
2980
+ if (analysis.len === 0) return createEmptyPrepared(includeSegments);
2981
+ const widths = [];
2982
+ const lineEndFitAdvances = [];
2983
+ const lineEndPaintAdvances = [];
2984
+ const kinds = [];
2985
+ let simpleLineWalkFastPath = analysis.chunks.length <= 1;
2986
+ const segStarts = includeSegments ? [] : null;
2987
+ const breakableWidths = [];
2988
+ const breakablePrefixWidths = [];
2989
+ const segments = includeSegments ? [] : null;
2990
+ const preparedStartByAnalysisIndex = Array.from({ length: analysis.len });
2991
+ const preparedEndByAnalysisIndex = Array.from({ length: analysis.len });
2992
+ function pushMeasuredSegment(text, width, lineEndFitAdvance, lineEndPaintAdvance, kind, start, breakable, breakablePrefix) {
2993
+ if (kind !== "text" && kind !== "space" && kind !== "zero-width-break") simpleLineWalkFastPath = false;
2994
+ widths.push(width);
2995
+ lineEndFitAdvances.push(lineEndFitAdvance);
2996
+ lineEndPaintAdvances.push(lineEndPaintAdvance);
2997
+ kinds.push(kind);
2998
+ segStarts?.push(start);
2999
+ breakableWidths.push(breakable);
3000
+ breakablePrefixWidths.push(breakablePrefix);
3001
+ if (segments !== null) segments.push(text);
3002
+ }
3003
+ for (let mi = 0; mi < analysis.len; mi++) {
3004
+ preparedStartByAnalysisIndex[mi] = widths.length;
3005
+ const segText = analysis.texts[mi];
3006
+ const segWordLike = analysis.isWordLike[mi];
3007
+ const segKind = analysis.kinds[mi];
3008
+ const segStart = analysis.starts[mi];
3009
+ if (segKind === "soft-hyphen") {
3010
+ pushMeasuredSegment(segText, 0, discretionaryHyphenWidth, discretionaryHyphenWidth, segKind, segStart, null, null);
3011
+ preparedEndByAnalysisIndex[mi] = widths.length;
3012
+ continue;
3013
+ }
3014
+ if (segKind === "hard-break") {
3015
+ pushMeasuredSegment(segText, 0, 0, 0, segKind, segStart, null, null);
3016
+ preparedEndByAnalysisIndex[mi] = widths.length;
3017
+ continue;
3018
+ }
3019
+ if (segKind === "tab") {
3020
+ pushMeasuredSegment(segText, 0, 0, 0, segKind, segStart, null, null);
3021
+ preparedEndByAnalysisIndex[mi] = widths.length;
3022
+ continue;
3023
+ }
3024
+ const segMetrics = getSegmentMetrics(segText, cache);
3025
+ if (segKind === "text" && segMetrics.containsCJK) {
3026
+ let unitText = "";
3027
+ let unitStart = 0;
3028
+ for (const gs of graphemeSegmenter.segment(segText)) {
3029
+ const grapheme = gs.segment;
3030
+ if (unitText.length === 0) {
3031
+ unitText = grapheme;
3032
+ unitStart = gs.index;
3033
+ continue;
3034
+ }
3035
+ if (kinsokuEnd.has(unitText) || kinsokuStart.has(grapheme) || leftStickyPunctuation.has(grapheme) || engineProfile.carryCJKAfterClosingQuote && isCJK(grapheme) && endsWithClosingQuote(unitText)) {
3036
+ unitText += grapheme;
3037
+ continue;
3038
+ }
3039
+ const unitMetrics = getSegmentMetrics(unitText, cache);
3040
+ const w = getCorrectedSegmentWidth(unitText, unitMetrics, emojiCorrection);
3041
+ pushMeasuredSegment(unitText, w, w, w, "text", segStart + unitStart, null, null);
3042
+ unitText = grapheme;
3043
+ unitStart = gs.index;
3044
+ }
3045
+ if (unitText.length > 0) {
3046
+ const unitMetrics = getSegmentMetrics(unitText, cache);
3047
+ const w = getCorrectedSegmentWidth(unitText, unitMetrics, emojiCorrection);
3048
+ pushMeasuredSegment(unitText, w, w, w, "text", segStart + unitStart, null, null);
3049
+ }
3050
+ preparedEndByAnalysisIndex[mi] = widths.length;
3051
+ continue;
3052
+ }
3053
+ const w = getCorrectedSegmentWidth(segText, segMetrics, emojiCorrection);
3054
+ const lineEndFitAdvance = segKind === "space" || segKind === "preserved-space" || segKind === "zero-width-break" ? 0 : w;
3055
+ const lineEndPaintAdvance = segKind === "space" || segKind === "zero-width-break" ? 0 : w;
3056
+ if (segWordLike && segText.length > 1) pushMeasuredSegment(segText, w, lineEndFitAdvance, lineEndPaintAdvance, segKind, segStart, getSegmentGraphemeWidths(segText, segMetrics, cache, emojiCorrection), engineProfile.preferPrefixWidthsForBreakableRuns ? getSegmentGraphemePrefixWidths(segText, segMetrics, cache, emojiCorrection) : null);
3057
+ else pushMeasuredSegment(segText, w, lineEndFitAdvance, lineEndPaintAdvance, segKind, segStart, null, null);
3058
+ preparedEndByAnalysisIndex[mi] = widths.length;
3059
+ }
3060
+ const chunks = mapAnalysisChunksToPreparedChunks(analysis.chunks, preparedStartByAnalysisIndex, preparedEndByAnalysisIndex);
3061
+ const segLevels = segStarts === null ? null : computeSegmentLevels(analysis.normalized, segStarts);
3062
+ if (segments !== null) return {
3063
+ widths,
3064
+ lineEndFitAdvances,
3065
+ lineEndPaintAdvances,
3066
+ kinds,
3067
+ simpleLineWalkFastPath,
3068
+ segLevels,
3069
+ breakableWidths,
3070
+ breakablePrefixWidths,
3071
+ discretionaryHyphenWidth,
3072
+ tabStopAdvance,
3073
+ chunks,
3074
+ segments
3075
+ };
3076
+ return {
3077
+ widths,
3078
+ lineEndFitAdvances,
3079
+ lineEndPaintAdvances,
3080
+ kinds,
3081
+ simpleLineWalkFastPath,
3082
+ segLevels,
3083
+ breakableWidths,
3084
+ breakablePrefixWidths,
3085
+ discretionaryHyphenWidth,
3086
+ tabStopAdvance,
3087
+ chunks
3088
+ };
3089
+ }
3090
+ function mapAnalysisChunksToPreparedChunks(chunks, preparedStartByAnalysisIndex, preparedEndByAnalysisIndex) {
3091
+ const preparedChunks = [];
3092
+ for (let i = 0; i < chunks.length; i++) {
3093
+ const chunk = chunks[i];
3094
+ const startSegmentIndex = chunk.startSegmentIndex < preparedStartByAnalysisIndex.length ? preparedStartByAnalysisIndex[chunk.startSegmentIndex] : preparedEndByAnalysisIndex[preparedEndByAnalysisIndex.length - 1] ?? 0;
3095
+ const endSegmentIndex = chunk.endSegmentIndex < preparedStartByAnalysisIndex.length ? preparedStartByAnalysisIndex[chunk.endSegmentIndex] : preparedEndByAnalysisIndex[preparedEndByAnalysisIndex.length - 1] ?? 0;
3096
+ const consumedEndSegmentIndex = chunk.consumedEndSegmentIndex < preparedStartByAnalysisIndex.length ? preparedStartByAnalysisIndex[chunk.consumedEndSegmentIndex] : preparedEndByAnalysisIndex[preparedEndByAnalysisIndex.length - 1] ?? 0;
3097
+ preparedChunks.push({
3098
+ startSegmentIndex,
3099
+ endSegmentIndex,
3100
+ consumedEndSegmentIndex
3101
+ });
3102
+ }
3103
+ return preparedChunks;
3104
+ }
3105
+ function prepareInternal(text, font, includeSegments, options) {
3106
+ return measureAnalysis(analyzeText(text, getEngineProfile(), options?.whiteSpace), font, includeSegments);
3107
+ }
3108
+ function prepareWithSegments(text, font, options) {
3109
+ return prepareInternal(text, font, true, options);
3110
+ }
3111
+ function getInternalPrepared(prepared) {
3112
+ return prepared;
3113
+ }
3114
+ function getSegmentGraphemes(segmentIndex, segments, cache) {
3115
+ let graphemes = cache.get(segmentIndex);
3116
+ if (graphemes !== void 0) return graphemes;
3117
+ graphemes = [];
3118
+ const graphemeSegmenter = getSharedGraphemeSegmenter();
3119
+ for (const gs of graphemeSegmenter.segment(segments[segmentIndex])) graphemes.push(gs.segment);
3120
+ cache.set(segmentIndex, graphemes);
3121
+ return graphemes;
3122
+ }
3123
+ function getLineTextCache(prepared) {
3124
+ let cache = sharedLineTextCaches.get(prepared);
3125
+ if (cache !== void 0) return cache;
3126
+ cache = /* @__PURE__ */ new Map();
3127
+ sharedLineTextCaches.set(prepared, cache);
3128
+ return cache;
3129
+ }
3130
+ function lineHasDiscretionaryHyphen(kinds, startSegmentIndex, startGraphemeIndex, endSegmentIndex) {
3131
+ return endSegmentIndex > 0 && kinds[endSegmentIndex - 1] === "soft-hyphen" && !(startSegmentIndex === endSegmentIndex && startGraphemeIndex > 0);
3132
+ }
3133
+ function buildLineTextFromRange(segments, kinds, cache, startSegmentIndex, startGraphemeIndex, endSegmentIndex, endGraphemeIndex) {
3134
+ let text = "";
3135
+ const endsWithDiscretionaryHyphen = lineHasDiscretionaryHyphen(kinds, startSegmentIndex, startGraphemeIndex, endSegmentIndex);
3136
+ for (let i = startSegmentIndex; i < endSegmentIndex; i++) {
3137
+ if (kinds[i] === "soft-hyphen" || kinds[i] === "hard-break") continue;
3138
+ if (i === startSegmentIndex && startGraphemeIndex > 0) text += getSegmentGraphemes(i, segments, cache).slice(startGraphemeIndex).join("");
3139
+ else text += segments[i];
3140
+ }
3141
+ if (endGraphemeIndex > 0) {
3142
+ if (endsWithDiscretionaryHyphen) text += "-";
3143
+ text += getSegmentGraphemes(endSegmentIndex, segments, cache).slice(startSegmentIndex === endSegmentIndex ? startGraphemeIndex : 0, endGraphemeIndex).join("");
3144
+ } else if (endsWithDiscretionaryHyphen) text += "-";
3145
+ return text;
3146
+ }
3147
+ function createLayoutLine(prepared, cache, width, startSegmentIndex, startGraphemeIndex, endSegmentIndex, endGraphemeIndex) {
3148
+ return {
3149
+ text: buildLineTextFromRange(prepared.segments, prepared.kinds, cache, startSegmentIndex, startGraphemeIndex, endSegmentIndex, endGraphemeIndex),
3150
+ width,
3151
+ start: {
3152
+ segmentIndex: startSegmentIndex,
3153
+ graphemeIndex: startGraphemeIndex
3154
+ },
3155
+ end: {
3156
+ segmentIndex: endSegmentIndex,
3157
+ graphemeIndex: endGraphemeIndex
3158
+ }
3159
+ };
3160
+ }
3161
+ function materializeLayoutLine(prepared, cache, line) {
3162
+ return createLayoutLine(prepared, cache, line.width, line.startSegmentIndex, line.startGraphemeIndex, line.endSegmentIndex, line.endGraphemeIndex);
3163
+ }
3164
+ function layoutWithLines(prepared, maxWidth, lineHeight) {
3165
+ const lines = [];
3166
+ if (prepared.widths.length === 0) return {
3167
+ lineCount: 0,
3168
+ height: 0,
3169
+ lines
3170
+ };
3171
+ const graphemeCache = getLineTextCache(prepared);
3172
+ const lineCount = walkPreparedLines(getInternalPrepared(prepared), maxWidth, (line) => {
3173
+ lines.push(materializeLayoutLine(prepared, graphemeCache, line));
3174
+ });
3175
+ return {
3176
+ lineCount,
3177
+ height: lineCount * lineHeight,
3178
+ lines
3179
+ };
3180
+ }
3181
+ //#endregion
3182
+ //#region src/paFont/paragraphLayout.js
3183
+ var DEFAULT_LINE_HEIGHT_RATIO = 1.2;
3184
+ var HUGE_LAYOUT_WIDTH = 1e9;
3185
+ var JUSTIFY_EPSILON = 1e-6;
3186
+ var QUOTE_RE = /"/g;
3187
+ var sharedMeasureContext = null;
3188
+ var sharedWordSegmenter = null;
3189
+ var sharedGraphemeSegmenter = null;
3190
+ function layoutParagraph(fontInstance, text, options = {}, state = {}) {
3191
+ const normalized = normalizeParagraphOptions(fontInstance, options);
3192
+ const textValue = String(text ?? "");
3193
+ const layoutState = normalized.engine === "pretext" && normalized.overflowWrap !== "normal" ? layoutWithPretext(fontInstance, textValue, normalized, state) : layoutWithNative(fontInstance, textValue, normalized);
3194
+ const lines = positionLines(fontInstance, applyMaxLines(layoutState.lines, normalized, createTextMeasurer(fontInstance, normalized)), normalized);
3195
+ const bbox = combineRects(lines.map((line) => line.bbox)) ?? emptyRect();
3196
+ const width = lines.reduce((max, line) => Math.max(max, line.width), 0);
3197
+ return {
3198
+ options: normalized,
3199
+ lines,
3200
+ metrics: {
3201
+ x: normalized.x,
3202
+ y: normalized.y,
3203
+ width,
3204
+ height: lines.length * normalized.lineHeight,
3205
+ lineCount: lines.length,
3206
+ bbox
3207
+ },
3208
+ prepared: layoutState.prepared ?? null,
3209
+ preparedWhiteSpace: layoutState.preparedWhiteSpace ?? null,
3210
+ layoutEngine: layoutState.layoutEngine
3211
+ };
3212
+ }
3213
+ function normalizeParagraphOptions(fontInstance, options = {}) {
3214
+ if (options == null || typeof options !== "object" || Array.isArray(options)) throw new TypeError("font.paragraph() options must be an object.");
3215
+ const textOptions = normalizeTextOptions(options);
3216
+ const width = normalizePositive(options.width, 0);
3217
+ if (width <= 0) throw new TypeError("font.paragraph() option \"width\" must be a positive number.");
3218
+ const font = resolveCanvasFont(fontInstance, textOptions.size, options);
3219
+ const lineHeight = resolveLineHeight(options.lineHeight, textOptions.size);
3220
+ const align = normalizeEnum(options.align, [
3221
+ "left",
3222
+ "center",
3223
+ "right",
3224
+ "justify"
3225
+ ], "left");
3226
+ const whiteSpace = normalizeEnum(options.whiteSpace, [
3227
+ "normal",
3228
+ "pre-wrap",
3229
+ "nowrap"
3230
+ ], "normal");
3231
+ const overflowWrap = normalizeEnum(options.overflowWrap, [
3232
+ "normal",
3233
+ "break-word",
3234
+ "anywhere"
3235
+ ], "break-word");
3236
+ const engine = normalizeEnum(options.engine, ["pretext", "native"], "pretext");
3237
+ const maxLines = normalizeMaxLines(options.maxLines);
3238
+ const ellipsis = normalizeEllipsis(options.ellipsis);
3239
+ return {
3240
+ ...textOptions,
3241
+ width,
3242
+ lineHeight,
3243
+ align,
3244
+ whiteSpace,
3245
+ overflowWrap,
3246
+ maxLines,
3247
+ ellipsis,
3248
+ fontStyle: normalizeEnum(options.fontStyle, [
3249
+ "normal",
3250
+ "italic",
3251
+ "oblique"
3252
+ ], "normal"),
3253
+ fontWeight: typeof options.fontWeight === "string" || Number.isFinite(options.fontWeight) ? options.fontWeight : 400,
3254
+ fontFamily: resolveFontFamily(fontInstance, options.fontFamily),
3255
+ font,
3256
+ engine
3257
+ };
3258
+ }
3259
+ function resolveCanvasFont(fontInstance, size, options = {}) {
3260
+ if (typeof options.font === "string" && options.font.trim().length > 0) return options.font.trim();
3261
+ return `${normalizeEnum(options.fontStyle, [
3262
+ "normal",
3263
+ "italic",
3264
+ "oblique"
3265
+ ], "normal")} ${typeof options.fontWeight === "string" || Number.isFinite(options.fontWeight) ? String(options.fontWeight) : "400"} ${size}px ${formatFontFamily(resolveFontFamily(fontInstance, options.fontFamily))}`;
3266
+ }
3267
+ function canReusePreparedParagraphState(previousState, previousOptions, nextOptions) {
3268
+ return previousState?.prepared != null && previousState.preparedWhiteSpace === resolvePretextWhiteSpace(nextOptions.whiteSpace) && previousOptions?.font === nextOptions.font;
3269
+ }
3270
+ function layoutWithPretext(fontInstance, text, options, state) {
3271
+ const preparedWhiteSpace = resolvePretextWhiteSpace(options.whiteSpace);
3272
+ const prepared = state.prepared != null && state.preparedWhiteSpace === preparedWhiteSpace && state.font === options.font ? state.prepared : prepareWithSegments(text, options.font, { whiteSpace: preparedWhiteSpace });
3273
+ return {
3274
+ lines: layoutWithLines(prepared, options.whiteSpace === "nowrap" ? HUGE_LAYOUT_WIDTH : options.width, options.lineHeight).lines.map((line) => ({
3275
+ text: line.text,
3276
+ width: line.width,
3277
+ start: null,
3278
+ end: null,
3279
+ hardBreak: isHardBreak(prepared, line)
3280
+ })),
3281
+ prepared,
3282
+ preparedWhiteSpace,
3283
+ layoutEngine: "pretext",
3284
+ fontInstance
3285
+ };
3286
+ }
3287
+ function layoutWithNative(fontInstance, text, options) {
3288
+ const measuredWidth = createOpenTypeMeasurer(fontInstance, options);
3289
+ const source = normalizeNativeText(text, options.whiteSpace);
3290
+ if (source.length === 0) return {
3291
+ lines: [],
3292
+ prepared: null,
3293
+ preparedWhiteSpace: null,
3294
+ layoutEngine: "native"
3295
+ };
3296
+ if (options.whiteSpace === "nowrap") return {
3297
+ lines: [{
3298
+ text: source,
3299
+ width: measuredWidth(source),
3300
+ start: 0,
3301
+ end: source.length,
3302
+ hardBreak: false
3303
+ }],
3304
+ prepared: null,
3305
+ preparedWhiteSpace: null,
3306
+ layoutEngine: "native"
3307
+ };
3308
+ const tokens = tokenizeNativeText(source, options.whiteSpace, measuredWidth);
3309
+ const lines = [];
3310
+ let currentText = "";
3311
+ let currentWidth = 0;
3312
+ let currentStart = null;
3313
+ let currentEnd = null;
3314
+ const pushCurrentLine = (hardBreak, fallbackOffset) => {
3315
+ const textValue = options.whiteSpace === "pre-wrap" ? currentText : currentText.replace(/\s+$/u, "");
3316
+ const widthValue = textValue === currentText ? currentWidth : measuredWidth(textValue);
3317
+ lines.push({
3318
+ text: textValue,
3319
+ width: widthValue,
3320
+ start: currentStart ?? fallbackOffset,
3321
+ end: currentEnd ?? fallbackOffset,
3322
+ hardBreak
3323
+ });
3324
+ currentText = "";
3325
+ currentWidth = 0;
3326
+ currentStart = null;
3327
+ currentEnd = null;
3328
+ };
3329
+ const appendPiece = (piece) => {
3330
+ currentText += piece.text;
3331
+ currentWidth += piece.width;
3332
+ currentStart = currentStart == null ? piece.start : currentStart;
3333
+ currentEnd = piece.end;
3334
+ };
3335
+ const appendWrappedToken = (token) => {
3336
+ if (options.overflowWrap === "normal") {
3337
+ appendPiece(token);
3338
+ return;
3339
+ }
3340
+ splitIntoGraphemePieces(token, measuredWidth).forEach((piece) => {
3341
+ if (currentText.length > 0 && currentWidth + piece.width > options.width + JUSTIFY_EPSILON) pushCurrentLine(false, piece.start);
3342
+ appendPiece(piece);
3343
+ });
3344
+ };
3345
+ tokens.forEach((token) => {
3346
+ if (token.type === "hardBreak") {
3347
+ pushCurrentLine(true, token.start);
3348
+ return;
3349
+ }
3350
+ if (token.type === "space" && currentText.length === 0 && options.whiteSpace !== "pre-wrap") return;
3351
+ if (currentText.length === 0 || currentWidth + token.width <= options.width + JUSTIFY_EPSILON) {
3352
+ appendPiece(token);
3353
+ return;
3354
+ }
3355
+ if (token.type === "space") {
3356
+ pushCurrentLine(false, token.start);
3357
+ if (options.whiteSpace === "pre-wrap") appendWrappedToken(token);
3358
+ return;
3359
+ }
3360
+ pushCurrentLine(false, token.start);
3361
+ appendWrappedToken(token);
3362
+ });
3363
+ if (currentText.length > 0 || lines.length === 0) pushCurrentLine(false, source.length);
3364
+ return {
3365
+ lines,
3366
+ prepared: null,
3367
+ preparedWhiteSpace: null,
3368
+ layoutEngine: "native"
3369
+ };
3370
+ }
3371
+ function splitIntoGraphemePieces(token, measureWidth) {
3372
+ const pieces = [];
3373
+ const segmenter = getGraphemeSegmenter();
3374
+ for (const part of segmenter.segment(token.text)) {
3375
+ const text = part.segment;
3376
+ const start = token.start + part.index;
3377
+ const end = start + text.length;
3378
+ pieces.push({
3379
+ text,
3380
+ type: token.type,
3381
+ start,
3382
+ end,
3383
+ width: measureWidth(text)
3384
+ });
3385
+ }
3386
+ return pieces.length > 0 ? pieces : [token];
3387
+ }
3388
+ function applyMaxLines(lines, options, measureWidth) {
3389
+ if (options.maxLines == null || lines.length <= options.maxLines) return lines;
3390
+ const clipped = lines.slice(0, options.maxLines).map((line) => ({ ...line }));
3391
+ if (options.ellipsis !== false && clipped.length > 0) {
3392
+ const lastLine = clipped[clipped.length - 1];
3393
+ const suffix = options.ellipsis;
3394
+ const suffixWidth = measureWidth(suffix);
3395
+ const trimmed = trimTrailingWhitespace(lastLine.text);
3396
+ if (suffixWidth > options.width + JUSTIFY_EPSILON) {
3397
+ lastLine.text = "";
3398
+ lastLine.width = 0;
3399
+ } else {
3400
+ let nextText = trimmed;
3401
+ while (nextText.length > 0 && measureWidth(`${nextText}${suffix}`) > options.width + JUSTIFY_EPSILON) nextText = trimLastGrapheme(nextText);
3402
+ lastLine.text = `${nextText}${suffix}`;
3403
+ lastLine.width = measureWidth(lastLine.text);
3404
+ }
3405
+ lastLine.hardBreak = false;
3406
+ }
3407
+ return clipped;
3408
+ }
3409
+ function positionLines(fontInstance, lines, options) {
3410
+ const ascent = getFontAscender(fontInstance, options.size);
3411
+ const lineBoxHeight = options.lineHeight;
3412
+ const measureWidth = createTextMeasurer(fontInstance, options);
3413
+ let cursor = 0;
3414
+ return lines.map((line, index) => {
3415
+ const justified = shouldJustifyLine(line, index, lines.length, options);
3416
+ const offsetX = justified ? 0 : resolveAlignOffset(options.align, line.width, options.width);
3417
+ const x = options.x + offsetX;
3418
+ const y = options.y + index * lineBoxHeight;
3419
+ const baseline = y + ascent;
3420
+ const fragments = justified ? buildJustifiedFragments(line, options, measureWidth) : [{
3421
+ text: line.text,
3422
+ x,
3423
+ width: line.width,
3424
+ isWhitespace: false
3425
+ }];
3426
+ const width = justified ? options.width : line.width;
3427
+ const bbox = {
3428
+ x,
3429
+ y,
3430
+ w: width,
3431
+ h: lineBoxHeight
3432
+ };
3433
+ const positioned = {
3434
+ index,
3435
+ text: line.text,
3436
+ start: line.start ?? cursor,
3437
+ end: line.end ?? cursor + line.text.length,
3438
+ x,
3439
+ y,
3440
+ width,
3441
+ baseline,
3442
+ height: lineBoxHeight,
3443
+ bbox,
3444
+ hardBreak: line.hardBreak,
3445
+ fragments
3446
+ };
3447
+ cursor = positioned.end + (line.hardBreak ? 1 : 0);
3448
+ return positioned;
3449
+ });
3450
+ }
3451
+ function buildJustifiedFragments(line, options, measureWidth) {
3452
+ const tokens = splitPreservingWhitespace(line.text);
3453
+ const expandable = tokens.reduce((count, token, index) => {
3454
+ if (index > 0 && index < tokens.length - 1 && /\s/u.test(token)) return count + 1;
3455
+ return count;
3456
+ }, 0);
3457
+ if (expandable === 0) return [{
3458
+ text: line.text,
3459
+ x: options.x,
3460
+ width: line.width,
3461
+ isWhitespace: false
3462
+ }];
3463
+ const extraPerGap = Math.max(0, options.width - line.width) / expandable;
3464
+ const fragments = [];
3465
+ let cursorX = options.x;
3466
+ tokens.forEach((token, index) => {
3467
+ const isWhitespace = /\s/u.test(token);
3468
+ const isExpandable = isWhitespace && index > 0 && index < tokens.length - 1;
3469
+ const width = measureWidth(token) + (isExpandable ? extraPerGap : 0);
3470
+ fragments.push({
3471
+ text: token,
3472
+ x: cursorX,
3473
+ width,
3474
+ isWhitespace
3475
+ });
3476
+ cursorX += width;
3477
+ });
3478
+ return fragments;
3479
+ }
3480
+ function shouldJustifyLine(line, index, lineCount, options) {
3481
+ return options.align === "justify" && index < lineCount - 1 && !line.hardBreak && /\S\s+\S/u.test(line.text);
3482
+ }
3483
+ function resolveAlignOffset(align, lineWidth, maxWidth) {
3484
+ if (align === "center") return (maxWidth - lineWidth) * .5;
3485
+ if (align === "right") return maxWidth - lineWidth;
3486
+ return 0;
3487
+ }
3488
+ function resolveLineHeight(value, size) {
3489
+ if (!Number.isFinite(value) || value <= 0) return size * DEFAULT_LINE_HEIGHT_RATIO;
3490
+ if (value <= 10) return size * value;
3491
+ return value;
3492
+ }
3493
+ function normalizeEnum(value, supported, fallback) {
3494
+ return typeof value === "string" && supported.includes(value) ? value : fallback;
3495
+ }
3496
+ function normalizeEllipsis(value) {
3497
+ if (value === false || value == null) return false;
3498
+ return typeof value === "string" ? value : "…";
3499
+ }
3500
+ function normalizeMaxLines(value) {
3501
+ if (!Number.isFinite(value)) return null;
3502
+ const rounded = Math.floor(value);
3503
+ return rounded > 0 ? rounded : null;
3504
+ }
3505
+ function resolveFontFamily(fontInstance, fontFamily) {
3506
+ if (typeof fontFamily === "string" && fontFamily.trim().length > 0) return fontFamily.trim();
3507
+ const preferred = fontInstance?.font?.names?.fullName?.en ?? fontInstance?.font?.names?.fontFamily?.en ?? fontInstance?.font?.familyName;
3508
+ return typeof preferred === "string" && preferred.trim().length > 0 ? preferred.trim() : "sans-serif";
3509
+ }
3510
+ function formatFontFamily(value) {
3511
+ if (value.includes(",") || value.includes("\"") || value.includes("'")) return value;
3512
+ return /\s/u.test(value) ? `"${value.replace(QUOTE_RE, "\\\"")}"` : value;
3513
+ }
3514
+ function resolvePretextWhiteSpace(whiteSpace) {
3515
+ return whiteSpace === "pre-wrap" ? "pre-wrap" : "normal";
3516
+ }
3517
+ function isHardBreak(prepared, line) {
3518
+ return prepared.kinds?.[line.end.segmentIndex] === "hard-break";
3519
+ }
3520
+ function normalizeNativeText(text, whiteSpace) {
3521
+ if (whiteSpace === "pre-wrap") return String(text ?? "").replace(/\r\n/g, "\n").replace(/\r/g, "\n");
3522
+ return String(text ?? "").replace(/\s+/gu, " ").trim();
3523
+ }
3524
+ function tokenizeNativeText(text, whiteSpace, measureWidth) {
3525
+ const tokens = [];
3526
+ let index = 0;
3527
+ while (index < text.length) {
3528
+ const char = text[index];
3529
+ if (char === "\n") {
3530
+ tokens.push({
3531
+ text: "\n",
3532
+ type: "hardBreak",
3533
+ start: index,
3534
+ end: index + 1,
3535
+ width: 0
3536
+ });
3537
+ index += 1;
3538
+ continue;
3539
+ }
3540
+ if ((char === " " || char === " ") && whiteSpace === "pre-wrap") {
3541
+ const start = index;
3542
+ while (index < text.length && (text[index] === " " || text[index] === " ")) index += 1;
3543
+ tokens.push({
3544
+ text: text.slice(start, index),
3545
+ type: "space",
3546
+ start,
3547
+ end: index,
3548
+ width: measureWidth(text.slice(start, index))
3549
+ });
3550
+ continue;
3551
+ }
3552
+ if (char === " ") {
3553
+ tokens.push({
3554
+ text: " ",
3555
+ type: "space",
3556
+ start: index,
3557
+ end: index + 1,
3558
+ width: measureWidth(" ")
3559
+ });
3560
+ index += 1;
3561
+ continue;
3562
+ }
3563
+ const nextStop = findNextStop(text, index, whiteSpace);
3564
+ const chunk = text.slice(index, nextStop);
3565
+ for (const piece of segmentWords(chunk, index, measureWidth)) tokens.push(piece);
3566
+ index = nextStop;
3567
+ }
3568
+ return tokens;
3569
+ }
3570
+ function findNextStop(text, start, whiteSpace) {
3571
+ let index = start;
3572
+ while (index < text.length) {
3573
+ const char = text[index];
3574
+ if (char === "\n") break;
3575
+ if (char === " " || whiteSpace === "pre-wrap" && char === " ") break;
3576
+ index += 1;
3577
+ }
3578
+ return index;
3579
+ }
3580
+ function segmentWords(text, baseOffset, measureWidth) {
3581
+ const pieces = [];
3582
+ const segmenter = getWordSegmenter();
3583
+ for (const part of segmenter.segment(text)) {
3584
+ const value = part.segment;
3585
+ if (value.length === 0) continue;
3586
+ pieces.push({
3587
+ text: value,
3588
+ type: /\s/u.test(value) ? "space" : "text",
3589
+ start: baseOffset + part.index,
3590
+ end: baseOffset + part.index + value.length,
3591
+ width: measureWidth(value)
3592
+ });
3593
+ }
3594
+ return pieces.length > 0 ? pieces : [{
3595
+ text,
3596
+ type: "text",
3597
+ start: baseOffset,
3598
+ end: baseOffset + text.length,
3599
+ width: measureWidth(text)
3600
+ }];
3601
+ }
3602
+ function createTextMeasurer(fontInstance, options) {
3603
+ const cache = /* @__PURE__ */ new Map();
3604
+ const openTypeMeasurer = createOpenTypeMeasurer(fontInstance, options);
3605
+ const context = getMeasureContext();
3606
+ return (value) => {
3607
+ if (value.length === 0) return 0;
3608
+ if (cache.has(value)) return cache.get(value);
3609
+ let width;
3610
+ if (context) {
3611
+ context.font = options.font;
3612
+ width = context.measureText(value).width;
3613
+ } else width = openTypeMeasurer(value);
3614
+ cache.set(value, width);
3615
+ return width;
3616
+ };
3617
+ }
3618
+ function createOpenTypeMeasurer(fontInstance, options) {
3619
+ const cache = /* @__PURE__ */ new Map();
3620
+ const widthOptions = {
3621
+ x: 0,
3622
+ y: 0,
3623
+ size: options.size,
3624
+ flatten: options.flatten,
3625
+ edgeEpsilon: options.edgeEpsilon,
3626
+ kerning: options.kerning,
3627
+ letterSpacing: options.letterSpacing,
3628
+ tracking: options.tracking,
3629
+ script: options.script,
3630
+ language: options.language,
3631
+ features: options.features
3632
+ };
3633
+ return (value) => {
3634
+ if (value.length === 0) return 0;
3635
+ if (cache.has(value)) return cache.get(value);
3636
+ const width = measureAdvanceWidth(fontInstance.font, value, widthOptions);
3637
+ cache.set(value, width);
3638
+ return width;
3639
+ };
3640
+ }
3641
+ function getMeasureContext() {
3642
+ if (sharedMeasureContext) return sharedMeasureContext;
3643
+ if (typeof OffscreenCanvas !== "undefined") {
3644
+ sharedMeasureContext = new OffscreenCanvas(1, 1).getContext("2d");
3645
+ return sharedMeasureContext;
3646
+ }
3647
+ if (typeof document !== "undefined") {
3648
+ sharedMeasureContext = document.createElement("canvas").getContext("2d");
3649
+ return sharedMeasureContext;
3650
+ }
3651
+ return null;
3652
+ }
3653
+ function getWordSegmenter() {
3654
+ if (sharedWordSegmenter == null) sharedWordSegmenter = new Intl.Segmenter(void 0, { granularity: "word" });
3655
+ return sharedWordSegmenter;
3656
+ }
3657
+ function getGraphemeSegmenter() {
3658
+ if (sharedGraphemeSegmenter == null) sharedGraphemeSegmenter = new Intl.Segmenter(void 0, { granularity: "grapheme" });
3659
+ return sharedGraphemeSegmenter;
3660
+ }
3661
+ function trimTrailingWhitespace(value) {
3662
+ return value.replace(/\s+$/u, "");
3663
+ }
3664
+ function trimLastGrapheme(value) {
3665
+ const segmenter = getGraphemeSegmenter();
3666
+ const graphemes = Array.from(segmenter.segment(value), (segment) => segment.segment);
3667
+ graphemes.pop();
3668
+ return graphemes.join("");
3669
+ }
3670
+ function splitPreservingWhitespace(value) {
3671
+ return value.split(/(\s+)/u).filter((token) => token.length > 0);
3672
+ }
3673
+ function getFontAscender(fontInstance, size) {
3674
+ return normalizeNumber(fontInstance?.font?.ascender, fontInstance?.unitsPerEm ?? 1e3) / normalizePositive(fontInstance?.unitsPerEm, 1e3) * size;
3675
+ }
3676
+ //#endregion
3677
+ //#region src/paFont/paragraph.js
3678
+ var paParagraph = class paParagraph {
3679
+ constructor(fontInstance, text, options, state) {
3680
+ this._font = fontInstance;
3681
+ this._state = state;
3682
+ this.text = text;
3683
+ this.options = options;
3684
+ this.lines = state.lines.map((line) => ({
3685
+ index: line.index,
3686
+ text: line.text,
3687
+ start: line.start,
3688
+ end: line.end,
3689
+ x: line.x,
3690
+ y: line.y,
3691
+ width: line.width,
3692
+ baseline: line.baseline,
3693
+ height: line.height,
3694
+ bbox: { ...line.bbox }
3695
+ }));
3696
+ this.metrics = {
3697
+ ...state.metrics,
3698
+ bbox: { ...state.metrics.bbox }
3699
+ };
3700
+ this._cache = {
3701
+ baseShapes: /* @__PURE__ */ new Map(),
3702
+ layoutParagraphs: /* @__PURE__ */ new Map()
3703
+ };
3704
+ }
3705
+ relayout(next = {}) {
3706
+ const normalized = normalizeParagraphOptions(this._font, {
3707
+ ...this.options,
3708
+ ...next
3709
+ });
3710
+ const state = layoutParagraph(this._font, this.text, normalized, {
3711
+ prepared: canReusePreparedParagraphState(this._state, this.options, normalized) ? this._state.prepared : null,
3712
+ preparedWhiteSpace: this._state.preparedWhiteSpace,
3713
+ font: this.options.font
3714
+ });
3715
+ return new paParagraph(this._font, this.text, normalized, state);
3716
+ }
3717
+ line(index) {
3718
+ return this.lines[index];
3719
+ }
3720
+ drawText(ctx, options = {}) {
3721
+ if (!ctx || typeof ctx.fillText !== "function") throw new TypeError("drawText() expects a CanvasRenderingContext2D.");
3722
+ const fill = options.fill !== false;
3723
+ const stroke = options.stroke === true;
3724
+ if (!fill && !stroke) return;
3725
+ ctx.save();
3726
+ ctx.font = resolveCanvasFont(this._font, this.options.size, this.options);
3727
+ ctx.textAlign = "left";
3728
+ ctx.textBaseline = "alphabetic";
3729
+ if (options.fillStyle != null) ctx.fillStyle = options.fillStyle;
3730
+ if (options.strokeStyle != null) ctx.strokeStyle = options.strokeStyle;
3731
+ this._state.lines.forEach((line) => {
3732
+ line.fragments.forEach((fragment) => {
3733
+ if (fill) ctx.fillText(fragment.text, fragment.x, line.baseline);
3734
+ if (stroke) ctx.strokeText(fragment.text, fragment.x, line.baseline);
3735
+ });
3736
+ });
3737
+ ctx.restore();
3738
+ }
3739
+ toShape(options = {}) {
3740
+ const { layout = "current", ...shapeOptions } = normalizeParagraphShapeOptions(options, "toShape()");
3741
+ return this._getBaseShape(layout).toShape(shapeOptions);
3742
+ }
3743
+ toRegions(options = {}) {
3744
+ const { layout = "current", ...shapeOptions } = normalizeParagraphShapeOptions(options, "toRegions()");
3745
+ return this._getBaseShape(layout).toRegions(shapeOptions);
3746
+ }
3747
+ toPoints(options = {}) {
3748
+ const { layout = "current", ...pointOptions } = normalizeParagraphPointOptions(options);
3749
+ return this._getBaseShape(layout).toPoints(pointOptions);
3750
+ }
3751
+ _getBaseShape(layout) {
3752
+ const paragraph = this._resolveParagraph(layout);
3753
+ if (paragraph._cache.baseShapes.has("base")) return paragraph._cache.baseShapes.get("base");
3754
+ const baseShape = createTextShape(buildParagraphGlyphLayout(paragraph), paragraph.options, paragraph._font);
3755
+ paragraph._cache.baseShapes.set("base", baseShape);
3756
+ return baseShape;
3757
+ }
3758
+ _resolveParagraph(layout) {
3759
+ if (layout === "current") return this;
3760
+ if (layout === "native") return this._getLayoutParagraph("native");
3761
+ if (layout === "pretext") return this._getLayoutParagraph("pretext");
3762
+ throw new TypeError("Paragraph layout must be \"current\", \"pretext\", or \"native\".");
3763
+ }
3764
+ _getLayoutParagraph(engine) {
3765
+ if (this.options.engine === engine) return this;
3766
+ if (this._cache.layoutParagraphs.has(engine)) return this._cache.layoutParagraphs.get(engine);
3767
+ const paragraph = this.relayout({ engine });
3768
+ this._cache.layoutParagraphs.set(engine, paragraph);
3769
+ return paragraph;
3770
+ }
3771
+ };
3772
+ function createParagraph(fontInstance, text, options = {}, state = {}) {
3773
+ const normalized = normalizeParagraphOptions(fontInstance, options);
3774
+ return new paParagraph(fontInstance, text, normalized, layoutParagraph(fontInstance, text, normalized, state));
3775
+ }
3776
+ function buildParagraphGlyphLayout(paragraph) {
3777
+ const glyphs = [];
3778
+ const glyphOptions = {
3779
+ x: 0,
3780
+ y: 0,
3781
+ size: paragraph.options.size,
3782
+ flatten: paragraph.options.flatten,
3783
+ edgeEpsilon: paragraph.options.edgeEpsilon,
3784
+ kerning: paragraph.options.kerning,
3785
+ letterSpacing: paragraph.options.letterSpacing,
3786
+ tracking: paragraph.options.tracking,
3787
+ script: paragraph.options.script,
3788
+ language: paragraph.options.language,
3789
+ features: paragraph.options.features
3790
+ };
3791
+ paragraph._state.lines.forEach((line) => {
3792
+ line.fragments.forEach((fragment) => {
3793
+ if (fragment.text.length === 0) return;
3794
+ const layout = paragraph._font._layoutText(fragment.text, {
3795
+ ...glyphOptions,
3796
+ x: fragment.x,
3797
+ y: line.baseline
3798
+ });
3799
+ glyphs.push(...layout.glyphs);
3800
+ });
3801
+ });
3802
+ return {
3803
+ text: paragraph.text,
3804
+ glyphs,
3805
+ metrics: {
3806
+ x: paragraph.metrics.x,
3807
+ y: paragraph.metrics.y,
3808
+ size: paragraph.options.size,
3809
+ width: paragraph.metrics.width
3810
+ }
3811
+ };
3812
+ }
3813
+ function normalizeParagraphShapeOptions(options = {}, callerName) {
3814
+ if (options == null) return { layout: "current" };
3815
+ if (typeof options !== "object" || Array.isArray(options)) throw new TypeError(`${callerName} expects an options object.`);
3816
+ return {
3817
+ ...options,
3818
+ layout: options.layout ?? "current"
3819
+ };
3820
+ }
3821
+ function normalizeParagraphPointOptions(options = {}) {
3822
+ if (options == null) return { layout: "current" };
3823
+ if (typeof options !== "object" || Array.isArray(options)) throw new TypeError("toPoints() expects an options object.");
3824
+ return {
3825
+ ...options,
3826
+ layout: options.layout ?? "current"
3827
+ };
3828
+ }
3829
+ //#endregion
1089
3830
  //#region src/paFont/paFont.js
1090
- var PAFont = class PAFont {
3831
+ var paFont = class paFont {
1091
3832
  constructor(font) {
1092
3833
  this.font = font;
1093
3834
  this.unitsPerEm = font.unitsPerEm ?? 1e3;
@@ -1096,10 +3837,10 @@ var PAFont = class PAFont {
1096
3837
  }
1097
3838
  static async load(source, options = {}) {
1098
3839
  const opts = normalizeLoadOptions(options);
1099
- if (source instanceof ArrayBuffer || ArrayBuffer.isView(source)) return new PAFont((0, opentype_js.parse)(toArrayBuffer(source)));
1100
- if (typeof window !== "undefined") return new PAFont((0, opentype_js.parse)(await fetchFontBytes(resolveBrowserFontSource(source, opts.base))));
3840
+ if (source instanceof ArrayBuffer || ArrayBuffer.isView(source)) return new paFont((0, opentype_js.parse)(toArrayBuffer(source)));
3841
+ if (typeof window !== "undefined") return new paFont((0, opentype_js.parse)(await fetchFontBytes(resolveBrowserFontSource(source, opts.base))));
1101
3842
  const { target, loadOptions } = await resolveNodeFontSource(source, opts.base);
1102
- return new PAFont(await (0, opentype_js.load)(target, void 0, loadOptions));
3843
+ return new paFont(await (0, opentype_js.load)(target, void 0, loadOptions));
1103
3844
  }
1104
3845
  text(value, options = {}) {
1105
3846
  const opts = normalizeTextOptions(options);
@@ -1129,6 +3870,9 @@ var PAFont = class PAFont {
1129
3870
  const opts = normalizeTextOptions(options);
1130
3871
  return measureText(this.font, String(value ?? ""), opts);
1131
3872
  }
3873
+ paragraph(value, options = {}) {
3874
+ return createParagraph(this, String(value ?? ""), options);
3875
+ }
1132
3876
  _getGlyphTopology(glyph) {
1133
3877
  const key = String(glyph.index);
1134
3878
  if (!this._glyphTopologyCache.has(key)) this._glyphTopologyCache.set(key, buildGlyphTopology(glyph, this.unitsPerEm));
@@ -1159,18 +3903,18 @@ async function fetchFontBytes(source) {
1159
3903
  }
1160
3904
  function normalizeLoadOptions(options = {}) {
1161
3905
  if (options == null) return {};
1162
- if (typeof options !== "object" || Array.isArray(options)) throw new TypeError("PAFont.load() options must be an object.");
3906
+ if (typeof options !== "object" || Array.isArray(options)) throw new TypeError("paFont.load() options must be an object.");
1163
3907
  return options;
1164
3908
  }
1165
3909
  function resolveBrowserFontSource(source, base) {
1166
3910
  if (source instanceof URL) return source.href;
1167
- if (typeof source !== "string") throw new TypeError("PAFont.load() expects a string, URL, ArrayBuffer, or ArrayBufferView.");
3911
+ if (typeof source !== "string") throw new TypeError("paFont.load() expects a string, URL, ArrayBuffer, or ArrayBufferView.");
1168
3912
  if (base == null) return source;
1169
3913
  return new URL(source, normalizeBase(base)).href;
1170
3914
  }
1171
3915
  async function resolveNodeFontSource(source, base) {
1172
3916
  if (source instanceof URL) return toNodeLoadTarget(source);
1173
- if (typeof source !== "string") throw new TypeError("PAFont.load() expects a string, URL, ArrayBuffer, or ArrayBufferView.");
3917
+ if (typeof source !== "string") throw new TypeError("paFont.load() expects a string, URL, ArrayBuffer, or ArrayBufferView.");
1174
3918
  if (base == null) {
1175
3919
  if (isLoadableUrlString(source)) return toNodeLoadTarget(new URL(source));
1176
3920
  return {
@@ -1183,7 +3927,7 @@ async function resolveNodeFontSource(source, base) {
1183
3927
  function normalizeBase(base) {
1184
3928
  if (base instanceof URL) return base;
1185
3929
  if (typeof base === "string") return base;
1186
- throw new TypeError("PAFont.load() option \"base\" must be a string or URL.");
3930
+ throw new TypeError("paFont.load() option \"base\" must be a string or URL.");
1187
3931
  }
1188
3932
  async function toNodeLoadTarget(url) {
1189
3933
  if (url.protocol === "file:") return {
@@ -1214,8 +3958,9 @@ function isHtmlSignature(signature) {
1214
3958
  return signature === "<!do" || signature === "<htm";
1215
3959
  }
1216
3960
  //#endregion
1217
- exports.PAFont = PAFont;
1218
3961
  exports.PAShape = PAShape;
1219
- exports.default = PAFont;
3962
+ exports.default = paFont;
3963
+ exports.paFont = paFont;
3964
+ exports.paParagraph = paParagraph;
1220
3965
 
1221
3966
  //# sourceMappingURL=paFont.cjs.map