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