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