@sushichan044/kg-core 0.6.3 → 0.6.5
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 +21 -8
- package/dist/index.d.mts +24503 -20300
- package/dist/index.mjs +634 -155
- package/package.json +2 -2
package/dist/index.mjs
CHANGED
|
@@ -1004,7 +1004,7 @@ function composeManuscript(parsed, options) {
|
|
|
1004
1004
|
}
|
|
1005
1005
|
//#endregion
|
|
1006
1006
|
//#region src/composer/composed-annotation-fragment.ts
|
|
1007
|
-
const emLength
|
|
1007
|
+
const emLength = () => v.pipe(v.number(), v.finite(), v.minValue(0));
|
|
1008
1008
|
const continuation = v.union([
|
|
1009
1009
|
v.literal("whole"),
|
|
1010
1010
|
v.literal("start"),
|
|
@@ -1014,7 +1014,7 @@ const continuation = v.union([
|
|
|
1014
1014
|
const PositionedReadingGraphemeSchema = readonlyObject({
|
|
1015
1015
|
value: v.string(),
|
|
1016
1016
|
offsetEm: v.pipe(v.number(), v.finite()),
|
|
1017
|
-
advanceEm: emLength
|
|
1017
|
+
advanceEm: emLength()
|
|
1018
1018
|
});
|
|
1019
1019
|
const common$1 = {
|
|
1020
1020
|
annotationRange: ManuscriptRange.schema,
|
|
@@ -1039,8 +1039,8 @@ const ComposedAnnotationFragment = { schema: v.variant("kind", [
|
|
|
1039
1039
|
kind: v.literal("ruby"),
|
|
1040
1040
|
rubyKind: RubyKind.schema,
|
|
1041
1041
|
reading: v.string(),
|
|
1042
|
-
baseOffsetEm: emLength
|
|
1043
|
-
baseAdvanceEm: emLength
|
|
1042
|
+
baseOffsetEm: emLength(),
|
|
1043
|
+
baseAdvanceEm: emLength(),
|
|
1044
1044
|
readingGraphemes: readonlyArray(PositionedReadingGraphemeSchema),
|
|
1045
1045
|
...common$1
|
|
1046
1046
|
})
|
|
@@ -1139,8 +1139,88 @@ const CompositionStatistics = { schema: readonlyObject({
|
|
|
1139
1139
|
sourceLines: nonNegativeInteger(),
|
|
1140
1140
|
pages: nonNegativeInteger()
|
|
1141
1141
|
}) };
|
|
1142
|
+
const InlineSpan = { schema: readonlyObject({
|
|
1143
|
+
offsetEm: v.pipe(v.number(), v.finite()),
|
|
1144
|
+
advanceEm: v.pipe(v.number(), v.finite(), v.minValue(0))
|
|
1145
|
+
}) };
|
|
1146
|
+
//#endregion
|
|
1147
|
+
//#region src/composer/vertical-text-presentation.ts
|
|
1148
|
+
const common = { groupRange: ManuscriptRange.schema };
|
|
1149
|
+
const VerticalTextPresentation = { schema: v.variant("kind", [
|
|
1150
|
+
readonlyObject({
|
|
1151
|
+
kind: v.literal("mixed"),
|
|
1152
|
+
...common
|
|
1153
|
+
}),
|
|
1154
|
+
readonlyObject({
|
|
1155
|
+
kind: v.literal("upright"),
|
|
1156
|
+
...common
|
|
1157
|
+
}),
|
|
1158
|
+
readonlyObject({
|
|
1159
|
+
kind: v.literal("sideways"),
|
|
1160
|
+
...common
|
|
1161
|
+
}),
|
|
1162
|
+
readonlyObject({
|
|
1163
|
+
kind: v.literal("tate-chu-yoko"),
|
|
1164
|
+
...common
|
|
1165
|
+
})
|
|
1166
|
+
]) };
|
|
1142
1167
|
//#endregion
|
|
1143
|
-
//#region src/composer/inline-
|
|
1168
|
+
//#region src/composer/composed-inline-item.ts
|
|
1169
|
+
const signedEm = () => v.pipe(v.number(), v.finite());
|
|
1170
|
+
const GlyphItemSchema = readonlyObject({
|
|
1171
|
+
kind: v.literal("glyph"),
|
|
1172
|
+
value: v.string(),
|
|
1173
|
+
range: ManuscriptRange.schema,
|
|
1174
|
+
layoutSpan: InlineSpan.schema,
|
|
1175
|
+
renderSpan: InlineSpan.schema,
|
|
1176
|
+
disposition: v.union([v.literal("placed"), v.literal("hanging")]),
|
|
1177
|
+
presentation: VerticalTextPresentation.schema
|
|
1178
|
+
});
|
|
1179
|
+
const GeneratedGlueItemSchema = readonlyObject({
|
|
1180
|
+
kind: v.literal("glue"),
|
|
1181
|
+
origin: v.literal("generated"),
|
|
1182
|
+
offsetEm: signedEm(),
|
|
1183
|
+
widthEm: signedEm(),
|
|
1184
|
+
naturalWidthEm: signedEm(),
|
|
1185
|
+
adjustment: v.union([
|
|
1186
|
+
v.literal("natural"),
|
|
1187
|
+
v.literal("shrunk"),
|
|
1188
|
+
v.literal("stretched")
|
|
1189
|
+
])
|
|
1190
|
+
});
|
|
1191
|
+
const SourceGlueItemSchema = readonlyObject({
|
|
1192
|
+
kind: v.literal("glue"),
|
|
1193
|
+
origin: v.literal("source"),
|
|
1194
|
+
value: v.string(),
|
|
1195
|
+
range: ManuscriptRange.schema,
|
|
1196
|
+
offsetEm: signedEm(),
|
|
1197
|
+
widthEm: signedEm(),
|
|
1198
|
+
naturalWidthEm: signedEm(),
|
|
1199
|
+
adjustment: v.union([
|
|
1200
|
+
v.literal("natural"),
|
|
1201
|
+
v.literal("shrunk"),
|
|
1202
|
+
v.literal("stretched")
|
|
1203
|
+
])
|
|
1204
|
+
});
|
|
1205
|
+
const KernItemSchema = readonlyObject({
|
|
1206
|
+
kind: v.literal("kern"),
|
|
1207
|
+
offsetEm: signedEm(),
|
|
1208
|
+
widthEm: signedEm()
|
|
1209
|
+
});
|
|
1210
|
+
const SuppressedItemSchema = readonlyObject({
|
|
1211
|
+
kind: v.literal("suppressed"),
|
|
1212
|
+
value: v.string(),
|
|
1213
|
+
range: ManuscriptRange.schema,
|
|
1214
|
+
reason: v.literal("question-or-exclamation-gap")
|
|
1215
|
+
});
|
|
1216
|
+
const ComposedInlineItem = { schema: v.union([
|
|
1217
|
+
GlyphItemSchema,
|
|
1218
|
+
GeneratedGlueItemSchema,
|
|
1219
|
+
SourceGlueItemSchema,
|
|
1220
|
+
KernItemSchema,
|
|
1221
|
+
SuppressedItemSchema
|
|
1222
|
+
]) };
|
|
1223
|
+
const InlineMeasurement = { schema: readonlyObject({ advanceEm: v.pipe(v.number(), v.finite(), v.minValue(0)) }) };
|
|
1144
1224
|
function graphemeWidth(grapheme) {
|
|
1145
1225
|
let width = 1;
|
|
1146
1226
|
for (const character of grapheme) {
|
|
@@ -1155,9 +1235,17 @@ function graphemeWidth(grapheme) {
|
|
|
1155
1235
|
const logicalInlineMeasurer = (request) => {
|
|
1156
1236
|
const { text } = request;
|
|
1157
1237
|
const width = [...graphemeSegmenter.segment(text)].reduce((total, { segment }) => total + graphemeWidth(segment), 0);
|
|
1158
|
-
if (request.role === "ruby") return width / 2;
|
|
1159
|
-
return request.presentation === "upright" ? [...graphemeSegmenter.segment(text)].length : width;
|
|
1238
|
+
if (request.role === "ruby") return { advanceEm: width / 2 };
|
|
1239
|
+
return { advanceEm: request.presentation === "upright" ? [...graphemeSegmenter.segment(text)].length : width };
|
|
1160
1240
|
};
|
|
1241
|
+
const LineBreakResult = { schema: v.variant("kind", [
|
|
1242
|
+
readonlyObject({ kind: v.literal("natural") }),
|
|
1243
|
+
readonlyObject({ kind: v.literal("shrunk") }),
|
|
1244
|
+
readonlyObject({ kind: v.literal("stretched") }),
|
|
1245
|
+
readonlyObject({ kind: v.literal("hanging") }),
|
|
1246
|
+
readonlyObject({ kind: v.literal("forced") }),
|
|
1247
|
+
readonlyObject({ kind: v.literal("paragraph-end") })
|
|
1248
|
+
]) };
|
|
1161
1249
|
//#endregion
|
|
1162
1250
|
//#region src/composer/manuscript-geometry.ts
|
|
1163
1251
|
const STAGE_GAP_CELLS = 2;
|
|
@@ -1298,57 +1386,420 @@ function questionOrExclamationSpacings(line) {
|
|
|
1298
1386
|
return [...line.matchAll(MARKS)].map((marks) => analyzeMatch(line, marks)).filter((spacing) => spacing !== void 0);
|
|
1299
1387
|
}
|
|
1300
1388
|
//#endregion
|
|
1301
|
-
//#region src/composer/
|
|
1302
|
-
const
|
|
1303
|
-
const
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1389
|
+
//#region src/composer/internal/japanese-typesetting-profile.ts
|
|
1390
|
+
const OPENING = /* @__PURE__ */ new Set(`${OPENING_BRACKETS}【〘〝⦅«`);
|
|
1391
|
+
const CLOSING = /* @__PURE__ */ new Set(`${CLOSING_BRACKETS}】〙〟⦆»`);
|
|
1392
|
+
const HYPHENS = /* @__PURE__ */ new Set("‐‑⁃–");
|
|
1393
|
+
const DIVIDING_PUNCTUATION = /* @__PURE__ */ new Set("!?‼⁇⁈⁉");
|
|
1394
|
+
const MIDDLE_DOTS = /* @__PURE__ */ new Set("・:;");
|
|
1395
|
+
const SMALL_KANA = /* @__PURE__ */ new Set("ァィゥェォッャュョヮヵヶぁぃぅぇぉっゃゅょゎゕゖ");
|
|
1396
|
+
const ITERATION_MARKS = /* @__PURE__ */ new Set("ヽヾゝゞ々〻");
|
|
1397
|
+
const INSEPARABLE = /* @__PURE__ */ new Set("—―…‥─〳〵");
|
|
1398
|
+
const PREFIXED_ABBREVIATIONS = /* @__PURE__ */ new Set("¥$£##");
|
|
1399
|
+
const POSTFIXED_ABBREVIATIONS = /* @__PURE__ */ new Set("°′″%‰");
|
|
1400
|
+
const UNIT_SYMBOLS = /* @__PURE__ */ new Set("℃℉㎀㎁㎂㎃㎄㎅㎆㎇㎈㎉㎊㎋㎌㎍㎎㎏㎐㎑㎒㎓㎔㎕㎖㎗㎘㎙㎚㎛㎜㎝㎞㎟㎠㎡㎢");
|
|
1401
|
+
const GROUPED_NUMERAL = /^[0-90-9]$/u;
|
|
1402
|
+
const HIRAGANA = /^\p{Script=Hiragana}$/u;
|
|
1403
|
+
const KATAKANA = /^\p{Script=Katakana}$/u;
|
|
1404
|
+
const WESTERN = /^[\p{Script=Latin}\p{Script=Greek}\p{Script=Cyrillic}]$/u;
|
|
1405
|
+
function classify({ value, presentation }) {
|
|
1406
|
+
if (presentation === "tate-chu-yoko") return "cl-30";
|
|
1407
|
+
if (OPENING.has(value)) return "cl-01";
|
|
1408
|
+
if (CLOSING.has(value)) return "cl-02";
|
|
1409
|
+
if (HYPHENS.has(value)) return "cl-03";
|
|
1410
|
+
if (DIVIDING_PUNCTUATION.has(value)) return "cl-04";
|
|
1411
|
+
if (MIDDLE_DOTS.has(value)) return "cl-05";
|
|
1412
|
+
if (value === "。" || value === ".") return "cl-06";
|
|
1413
|
+
if (value === "、" || value === ",") return "cl-07";
|
|
1414
|
+
if (INSEPARABLE.has(value)) return "cl-08";
|
|
1415
|
+
if (ITERATION_MARKS.has(value)) return "cl-09";
|
|
1416
|
+
if (value === "ー") return "cl-10";
|
|
1417
|
+
if (SMALL_KANA.has(value)) return "cl-11";
|
|
1418
|
+
if (PREFIXED_ABBREVIATIONS.has(value)) return "cl-12";
|
|
1419
|
+
if (POSTFIXED_ABBREVIATIONS.has(value)) return "cl-13";
|
|
1420
|
+
if (value === " ") return "cl-14";
|
|
1421
|
+
if (HIRAGANA.test(value)) return "cl-15";
|
|
1422
|
+
if (KATAKANA.test(value)) return "cl-16";
|
|
1423
|
+
if (GROUPED_NUMERAL.test(value)) return "cl-24";
|
|
1424
|
+
if (UNIT_SYMBOLS.has(value)) return "cl-25";
|
|
1425
|
+
if (value === " ") return "cl-26";
|
|
1426
|
+
if (WESTERN.test(value)) return "cl-27";
|
|
1427
|
+
return "cl-19";
|
|
1428
|
+
}
|
|
1429
|
+
function pairSpacing(left, right) {
|
|
1430
|
+
if (left === "cl-08" && right === "cl-08") return {
|
|
1431
|
+
kind: "kern",
|
|
1432
|
+
naturalWidthEm: 0
|
|
1433
|
+
};
|
|
1434
|
+
if (left === "cl-01" && right === "cl-01" || left === "cl-02" && right === "cl-02") return {
|
|
1435
|
+
kind: "kern",
|
|
1436
|
+
naturalWidthEm: -.5
|
|
1437
|
+
};
|
|
1438
|
+
if (left === "cl-07" || left === "cl-06") return {
|
|
1439
|
+
kind: "glue",
|
|
1440
|
+
naturalWidthEm: .5,
|
|
1441
|
+
shrink: {
|
|
1442
|
+
priority: 1,
|
|
1443
|
+
amountEm: .5
|
|
1444
|
+
}
|
|
1445
|
+
};
|
|
1446
|
+
if (left === "cl-02" || right === "cl-01") return {
|
|
1447
|
+
kind: "glue",
|
|
1448
|
+
naturalWidthEm: .5,
|
|
1449
|
+
shrink: {
|
|
1450
|
+
priority: 2,
|
|
1451
|
+
amountEm: .5
|
|
1452
|
+
}
|
|
1453
|
+
};
|
|
1454
|
+
return {
|
|
1455
|
+
kind: "glue",
|
|
1456
|
+
naturalWidthEm: 0,
|
|
1457
|
+
stretch: {
|
|
1458
|
+
priority: 3,
|
|
1459
|
+
amountEm: .25
|
|
1460
|
+
}
|
|
1461
|
+
};
|
|
1462
|
+
}
|
|
1463
|
+
function breakPenalty(left, right) {
|
|
1464
|
+
if (left === "cl-01" || left === "cl-12") return null;
|
|
1465
|
+
if (right === "cl-02" || right === "cl-03" || right === "cl-04" || right === "cl-05" || right === "cl-06" || right === "cl-07" || right === "cl-09" || right === "cl-10" || right === "cl-11" || right === "cl-13") return null;
|
|
1466
|
+
if (left === "cl-08" && right === "cl-08") return null;
|
|
1467
|
+
return 0;
|
|
1468
|
+
}
|
|
1469
|
+
const defaultJapaneseTypesettingProfile = {
|
|
1470
|
+
classify,
|
|
1471
|
+
boxMetrics: (characterClass, measuredAdvanceEm) => ({
|
|
1472
|
+
advanceEm: characterClass === "cl-01" || characterClass === "cl-02" || characterClass === "cl-06" || characterClass === "cl-07" ? Math.min(.5, measuredAdvanceEm) : measuredAdvanceEm,
|
|
1473
|
+
renderOffsetEm: characterClass === "cl-01" ? -.5 : 0
|
|
1315
1474
|
}),
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1475
|
+
pairSpacing,
|
|
1476
|
+
lineStartSpacing: (first) => first === "cl-01" ? {
|
|
1477
|
+
kind: "glue",
|
|
1478
|
+
naturalWidthEm: .5,
|
|
1479
|
+
shrink: {
|
|
1480
|
+
priority: 2,
|
|
1481
|
+
amountEm: .5
|
|
1482
|
+
}
|
|
1483
|
+
} : null,
|
|
1484
|
+
lineEndSpacing: (last) => last === "cl-02" || last === "cl-06" || last === "cl-07" ? {
|
|
1485
|
+
kind: "glue",
|
|
1486
|
+
naturalWidthEm: .5,
|
|
1487
|
+
shrink: {
|
|
1488
|
+
priority: 0,
|
|
1489
|
+
amountEm: .5
|
|
1490
|
+
}
|
|
1491
|
+
} : null,
|
|
1492
|
+
breakPenalty,
|
|
1493
|
+
canHang: (characterClass) => characterClass === "cl-07" || characterClass === "cl-06"
|
|
1494
|
+
};
|
|
1321
1495
|
//#endregion
|
|
1322
|
-
//#region src/composer/
|
|
1323
|
-
const
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
}
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1496
|
+
//#region src/composer/internal/paragraph-layout.ts
|
|
1497
|
+
const EPSILON = 1e-9;
|
|
1498
|
+
/**
|
|
1499
|
+
* Spending capacity at this priority changes nothing the reader can see.
|
|
1500
|
+
*/
|
|
1501
|
+
const FREE_SHRINK_PRIORITY = 0;
|
|
1502
|
+
function skipSourceGaps(atoms, start) {
|
|
1503
|
+
let cursor = start;
|
|
1504
|
+
while (atoms[cursor]?.sourceGap === true) cursor += 1;
|
|
1505
|
+
return cursor;
|
|
1506
|
+
}
|
|
1507
|
+
function previousVisible(atoms, end) {
|
|
1508
|
+
for (let index = end - 1; index >= 0; index -= 1) if (atoms[index]?.sourceGap === false) return index;
|
|
1509
|
+
}
|
|
1510
|
+
function opportunities(atoms, classes, start, end, profile) {
|
|
1511
|
+
const result = [];
|
|
1512
|
+
const firstClass = classes[start];
|
|
1513
|
+
const startSpacing = firstClass === void 0 ? null : profile.lineStartSpacing(firstClass);
|
|
1514
|
+
if (startSpacing !== null) result.push({
|
|
1515
|
+
boundary: start,
|
|
1516
|
+
spacing: startSpacing
|
|
1517
|
+
});
|
|
1518
|
+
for (let right = start + 1; right < end; right += 1) {
|
|
1519
|
+
const leftAtom = atoms[right - 1];
|
|
1520
|
+
const rightAtom = atoms[right];
|
|
1521
|
+
const leftClass = classes[right - 1];
|
|
1522
|
+
const rightClass = classes[right];
|
|
1523
|
+
if (leftAtom === void 0 || rightAtom === void 0 || leftClass === void 0 || rightClass === void 0 || leftAtom.sourceGap || rightAtom.sourceGap || !leftAtom.pairSpacingAfter) continue;
|
|
1524
|
+
result.push({
|
|
1525
|
+
boundary: right,
|
|
1526
|
+
spacing: profile.pairSpacing(leftClass, rightClass)
|
|
1527
|
+
});
|
|
1528
|
+
}
|
|
1529
|
+
const lastClass = classes[end - 1];
|
|
1530
|
+
const endSpacing = lastClass === void 0 ? null : profile.lineEndSpacing(lastClass);
|
|
1531
|
+
if (endSpacing !== null) result.push({
|
|
1532
|
+
boundary: end,
|
|
1533
|
+
spacing: endSpacing
|
|
1534
|
+
});
|
|
1535
|
+
return result;
|
|
1536
|
+
}
|
|
1537
|
+
function capacity(values, select) {
|
|
1538
|
+
return values.reduce((total, opportunity) => total + (select(opportunity.spacing)?.amountEm ?? 0), 0);
|
|
1539
|
+
}
|
|
1540
|
+
function resolveSpacings(values, adjustment, amountEm) {
|
|
1541
|
+
const remainingByBoundary = /* @__PURE__ */ new Map();
|
|
1542
|
+
let remaining = amountEm;
|
|
1543
|
+
let priorityCost = 0;
|
|
1544
|
+
const capacities = values.flatMap((opportunity) => {
|
|
1545
|
+
const selected = adjustment === "shrink" ? opportunity.spacing.shrink : opportunity.spacing.stretch;
|
|
1546
|
+
return selected === void 0 ? [] : [{
|
|
1547
|
+
...opportunity,
|
|
1548
|
+
capacity: selected
|
|
1549
|
+
}];
|
|
1550
|
+
}).sort((left, right) => left.capacity.priority - right.capacity.priority || left.boundary - right.boundary);
|
|
1551
|
+
for (const opportunity of capacities) {
|
|
1552
|
+
if (remaining <= EPSILON) break;
|
|
1553
|
+
const used = Math.min(remaining, opportunity.capacity.amountEm);
|
|
1554
|
+
remainingByBoundary.set(opportunity.boundary, used);
|
|
1555
|
+
priorityCost += used * opportunity.capacity.priority;
|
|
1556
|
+
remaining -= used;
|
|
1557
|
+
}
|
|
1558
|
+
return {
|
|
1559
|
+
spacings: values.map(({ boundary, spacing }) => {
|
|
1560
|
+
const used = remainingByBoundary.get(boundary) ?? 0;
|
|
1561
|
+
return {
|
|
1562
|
+
boundary,
|
|
1563
|
+
kind: spacing.kind,
|
|
1564
|
+
naturalWidthEm: spacing.naturalWidthEm,
|
|
1565
|
+
widthEm: adjustment === "shrink" ? spacing.naturalWidthEm - used : spacing.naturalWidthEm + used
|
|
1566
|
+
};
|
|
1567
|
+
}),
|
|
1568
|
+
priorityCost
|
|
1569
|
+
};
|
|
1570
|
+
}
|
|
1571
|
+
function candidate(atoms, classes, start, end, lineLengthEm, profile) {
|
|
1572
|
+
const contentStart = skipSourceGaps(atoms, start);
|
|
1573
|
+
const suppressedIndexes = Array.from({ length: contentStart - start }, (_, index) => start + index);
|
|
1574
|
+
const pairValues = opportunities(atoms, classes, contentStart, end, profile);
|
|
1575
|
+
const naturalSizeEm = atoms.slice(contentStart, end).reduce((total, atom) => total + atom.boxAdvanceEm, 0) + pairValues.reduce((total, value) => total + value.spacing.naturalWidthEm, 0);
|
|
1576
|
+
const terminal = skipSourceGaps(atoms, end) === atoms.length;
|
|
1577
|
+
const shrinkCapacity = capacity(pairValues, ({ shrink }) => shrink);
|
|
1578
|
+
const freeShrinkCapacity = capacity(pairValues, ({ shrink }) => shrink?.priority === FREE_SHRINK_PRIORITY ? shrink : void 0);
|
|
1579
|
+
const stretchCapacity = capacity(pairValues, ({ stretch }) => stretch);
|
|
1580
|
+
const overflow = naturalSizeEm - lineLengthEm;
|
|
1581
|
+
const underflow = lineLengthEm - naturalSizeEm;
|
|
1582
|
+
const lastVisible = previousVisible(atoms, end);
|
|
1583
|
+
const lastClass = lastVisible === void 0 ? void 0 : classes[lastVisible];
|
|
1584
|
+
const lastAdvance = lastVisible === void 0 ? 0 : atoms[lastVisible]?.boxAdvanceEm ?? 0;
|
|
1585
|
+
const trailingSpacing = pairValues.find(({ boundary }) => boundary === end)?.spacing.naturalWidthEm ?? 0;
|
|
1586
|
+
if (terminal && overflow <= EPSILON) return {
|
|
1587
|
+
start,
|
|
1588
|
+
contentStart,
|
|
1589
|
+
end,
|
|
1590
|
+
suppressedIndexes,
|
|
1591
|
+
pairSpacings: resolveSpacings(pairValues, "natural", 0).spacings,
|
|
1592
|
+
inlineSizeEm: naturalSizeEm,
|
|
1593
|
+
break: { kind: "paragraph-end" },
|
|
1594
|
+
hangingIndex: null,
|
|
1595
|
+
deformationRatio: 0,
|
|
1596
|
+
priorityCost: 0
|
|
1597
|
+
};
|
|
1598
|
+
if (Math.abs(overflow) <= EPSILON) return {
|
|
1599
|
+
start,
|
|
1600
|
+
contentStart,
|
|
1601
|
+
end,
|
|
1602
|
+
suppressedIndexes,
|
|
1603
|
+
pairSpacings: resolveSpacings(pairValues, "natural", 0).spacings,
|
|
1604
|
+
inlineSizeEm: naturalSizeEm,
|
|
1605
|
+
break: { kind: "natural" },
|
|
1606
|
+
hangingIndex: null,
|
|
1607
|
+
deformationRatio: 0,
|
|
1608
|
+
priorityCost: 0
|
|
1609
|
+
};
|
|
1610
|
+
if (overflow > 0 && overflow <= shrinkCapacity + EPSILON) {
|
|
1611
|
+
const resolved = resolveSpacings(pairValues, "shrink", overflow);
|
|
1612
|
+
const chargeableCapacity = shrinkCapacity - freeShrinkCapacity;
|
|
1613
|
+
const chargeableShrink = Math.max(0, overflow - freeShrinkCapacity);
|
|
1614
|
+
return {
|
|
1615
|
+
start,
|
|
1616
|
+
contentStart,
|
|
1617
|
+
end,
|
|
1618
|
+
suppressedIndexes,
|
|
1619
|
+
pairSpacings: resolved.spacings,
|
|
1620
|
+
inlineSizeEm: lineLengthEm,
|
|
1621
|
+
break: { kind: "shrunk" },
|
|
1622
|
+
hangingIndex: null,
|
|
1623
|
+
deformationRatio: chargeableCapacity <= EPSILON ? 0 : chargeableShrink / chargeableCapacity,
|
|
1624
|
+
priorityCost: resolved.priorityCost
|
|
1625
|
+
};
|
|
1626
|
+
}
|
|
1627
|
+
if (!terminal && overflow > 0 && lastVisible !== void 0 && lastClass !== void 0 && profile.canHang(lastClass) && naturalSizeEm - lastAdvance - trailingSpacing <= lineLengthEm + EPSILON) return {
|
|
1628
|
+
start,
|
|
1629
|
+
contentStart,
|
|
1630
|
+
end,
|
|
1631
|
+
suppressedIndexes,
|
|
1632
|
+
pairSpacings: resolveSpacings(pairValues, "natural", 0).spacings.filter(({ boundary }) => boundary !== end),
|
|
1633
|
+
inlineSizeEm: naturalSizeEm - lastAdvance - trailingSpacing,
|
|
1634
|
+
break: { kind: "hanging" },
|
|
1635
|
+
hangingIndex: lastVisible,
|
|
1636
|
+
deformationRatio: overflow / Math.max(lastAdvance, EPSILON),
|
|
1637
|
+
priorityCost: 0
|
|
1638
|
+
};
|
|
1639
|
+
if (!terminal && underflow > EPSILON && underflow <= stretchCapacity + EPSILON) {
|
|
1640
|
+
const resolved = resolveSpacings(pairValues, "stretch", underflow);
|
|
1641
|
+
return {
|
|
1642
|
+
start,
|
|
1643
|
+
contentStart,
|
|
1644
|
+
end,
|
|
1645
|
+
suppressedIndexes,
|
|
1646
|
+
pairSpacings: resolved.spacings,
|
|
1647
|
+
inlineSizeEm: lineLengthEm,
|
|
1648
|
+
break: { kind: "stretched" },
|
|
1649
|
+
hangingIndex: null,
|
|
1650
|
+
deformationRatio: stretchCapacity === 0 ? 0 : underflow / stretchCapacity,
|
|
1651
|
+
priorityCost: resolved.priorityCost
|
|
1652
|
+
};
|
|
1653
|
+
}
|
|
1654
|
+
return {
|
|
1655
|
+
start,
|
|
1656
|
+
contentStart,
|
|
1657
|
+
end,
|
|
1658
|
+
suppressedIndexes,
|
|
1659
|
+
pairSpacings: resolveSpacings(pairValues, "natural", 0).spacings,
|
|
1660
|
+
inlineSizeEm: naturalSizeEm,
|
|
1661
|
+
break: { kind: "forced" },
|
|
1662
|
+
hangingIndex: null,
|
|
1663
|
+
deformationRatio: Math.abs(naturalSizeEm - lineLengthEm) / Math.max(lineLengthEm, EPSILON),
|
|
1664
|
+
priorityCost: 0
|
|
1665
|
+
};
|
|
1666
|
+
}
|
|
1667
|
+
function scoreFor(line, previousFitness) {
|
|
1668
|
+
const mode = line.break.kind;
|
|
1669
|
+
const fitness = Math.min(3, Math.floor(line.deformationRatio * 4));
|
|
1670
|
+
const transition = Math.abs(previousFitness - fitness) > 1 ? 1 : 0;
|
|
1671
|
+
const modeCounts = {
|
|
1672
|
+
forced: mode === "forced" ? 1 : 0,
|
|
1673
|
+
stretched: mode === "stretched" ? 1 : 0,
|
|
1674
|
+
hanging: mode === "hanging" ? 1 : 0,
|
|
1675
|
+
shrunk: mode === "shrunk" && line.priorityCost > EPSILON ? 1 : 0
|
|
1676
|
+
};
|
|
1677
|
+
return {
|
|
1678
|
+
score: [
|
|
1679
|
+
modeCounts.forced,
|
|
1680
|
+
modeCounts.stretched,
|
|
1681
|
+
modeCounts.hanging,
|
|
1682
|
+
modeCounts.shrunk,
|
|
1683
|
+
line.priorityCost,
|
|
1684
|
+
line.deformationRatio ** 3,
|
|
1685
|
+
transition
|
|
1686
|
+
],
|
|
1687
|
+
fitness
|
|
1688
|
+
};
|
|
1689
|
+
}
|
|
1690
|
+
function addScore(left, right) {
|
|
1691
|
+
return [
|
|
1692
|
+
left[0] + right[0],
|
|
1693
|
+
left[1] + right[1],
|
|
1694
|
+
left[2] + right[2],
|
|
1695
|
+
left[3] + right[3],
|
|
1696
|
+
left[4] + right[4],
|
|
1697
|
+
left[5] + right[5],
|
|
1698
|
+
left[6] + right[6]
|
|
1699
|
+
];
|
|
1700
|
+
}
|
|
1701
|
+
function compareBreaks(left, right) {
|
|
1702
|
+
const length = Math.min(left.length, right.length);
|
|
1703
|
+
for (let index = 0; index < length; index += 1) {
|
|
1704
|
+
const difference = (right[index] ?? 0) - (left[index] ?? 0);
|
|
1705
|
+
if (difference !== 0) return difference;
|
|
1706
|
+
}
|
|
1707
|
+
return left.length - right.length;
|
|
1708
|
+
}
|
|
1709
|
+
function isBetter(candidateState, current) {
|
|
1710
|
+
if (current === void 0) return true;
|
|
1711
|
+
for (let index = 0; index < candidateState.score.length; index += 1) {
|
|
1712
|
+
const difference = (candidateState.score[index] ?? 0) - (current.score[index] ?? 0);
|
|
1713
|
+
if (Math.abs(difference) > EPSILON) return difference < 0;
|
|
1714
|
+
}
|
|
1715
|
+
return compareBreaks(candidateState.breaks, current.breaks) < 0;
|
|
1716
|
+
}
|
|
1717
|
+
function layoutParagraph(atoms, lineLengthEm, profile, boundaryAllowed) {
|
|
1718
|
+
if (atoms.length === 0) return [];
|
|
1719
|
+
const classes = atoms.map(({ characterClass }) => characterClass);
|
|
1720
|
+
const states = /* @__PURE__ */ new Map([[0, /* @__PURE__ */ new Map([[0, {
|
|
1721
|
+
score: [
|
|
1722
|
+
0,
|
|
1723
|
+
0,
|
|
1724
|
+
0,
|
|
1725
|
+
0,
|
|
1726
|
+
0,
|
|
1727
|
+
0,
|
|
1728
|
+
0
|
|
1729
|
+
],
|
|
1730
|
+
fitness: 0,
|
|
1731
|
+
previous: null,
|
|
1732
|
+
line: null,
|
|
1733
|
+
breaks: []
|
|
1734
|
+
}]])]]);
|
|
1735
|
+
for (let start = 0; start < atoms.length; start += 1) {
|
|
1736
|
+
const activeStates = states.get(start);
|
|
1737
|
+
if (activeStates === void 0) continue;
|
|
1738
|
+
const contentStart = skipSourceGaps(atoms, start);
|
|
1739
|
+
if (contentStart === atoms.length) {
|
|
1740
|
+
const terminalStates = states.get(atoms.length) ?? /* @__PURE__ */ new Map();
|
|
1741
|
+
for (const state of activeStates.values()) if (isBetter(state, terminalStates.get(state.fitness))) terminalStates.set(state.fitness, state);
|
|
1742
|
+
states.set(atoms.length, terminalStates);
|
|
1743
|
+
continue;
|
|
1744
|
+
}
|
|
1745
|
+
for (const state of activeStates.values()) for (let end = contentStart + 1; end <= atoms.length; end += 1) {
|
|
1746
|
+
const right = skipSourceGaps(atoms, end);
|
|
1747
|
+
const left = previousVisible(atoms, end);
|
|
1748
|
+
if (atoms[end - 1]?.sourceGap === true) continue;
|
|
1749
|
+
if (end < atoms.length && right < atoms.length && (left === void 0 || !boundaryAllowed(left, right))) continue;
|
|
1750
|
+
const line = candidate(atoms, classes, start, end, lineLengthEm, profile);
|
|
1751
|
+
if (line.break.kind === "forced" && line.inlineSizeEm > lineLengthEm + EPSILON && Array.from({ length: end - contentStart - 1 }, (_, index) => contentStart + index + 1).some((boundary) => boundaryAllowed(boundary - 1, boundary))) {
|
|
1752
|
+
if (line.inlineSizeEm > lineLengthEm * 2 && end > contentStart + 1) break;
|
|
1753
|
+
continue;
|
|
1754
|
+
}
|
|
1755
|
+
const lineScore = scoreFor(line, state.fitness);
|
|
1756
|
+
const nextState = {
|
|
1757
|
+
score: addScore(state.score, lineScore.score),
|
|
1758
|
+
fitness: lineScore.fitness,
|
|
1759
|
+
previous: {
|
|
1760
|
+
index: start,
|
|
1761
|
+
fitness: state.fitness
|
|
1762
|
+
},
|
|
1763
|
+
line,
|
|
1764
|
+
breaks: [...state.breaks, end]
|
|
1765
|
+
};
|
|
1766
|
+
const statesAtEnd = states.get(end) ?? /* @__PURE__ */ new Map();
|
|
1767
|
+
if (isBetter(nextState, statesAtEnd.get(nextState.fitness))) {
|
|
1768
|
+
statesAtEnd.set(nextState.fitness, nextState);
|
|
1769
|
+
states.set(end, statesAtEnd);
|
|
1770
|
+
}
|
|
1771
|
+
if (line.inlineSizeEm > lineLengthEm * 2 && line.break.kind === "forced" && end > contentStart + 1) break;
|
|
1772
|
+
}
|
|
1773
|
+
}
|
|
1774
|
+
const terminal = [...states.get(atoms.length)?.values() ?? []].reduce((best, state) => isBetter(state, best) ? state : best, void 0);
|
|
1775
|
+
if (terminal === void 0) return [];
|
|
1776
|
+
const lines = [];
|
|
1777
|
+
let cursor = terminal;
|
|
1778
|
+
while (cursor?.line !== null && cursor?.line !== void 0) {
|
|
1779
|
+
lines.push(cursor.line);
|
|
1780
|
+
cursor = cursor.previous === null ? void 0 : states.get(cursor.previous.index)?.get(cursor.previous.fitness);
|
|
1781
|
+
}
|
|
1782
|
+
lines.reverse();
|
|
1783
|
+
const last = lines.at(-1);
|
|
1784
|
+
if (last !== void 0 && last.end < atoms.length) lines[lines.length - 1] = {
|
|
1785
|
+
...last,
|
|
1786
|
+
suppressedIndexes: [...last.suppressedIndexes, ...Array.from({ length: atoms.length - last.end }, (_, index) => last.end + index)]
|
|
1787
|
+
};
|
|
1788
|
+
return lines;
|
|
1789
|
+
}
|
|
1339
1790
|
const NovelLine = {
|
|
1340
1791
|
schema: readonlyObject({
|
|
1341
1792
|
range: v.nullable(ManuscriptRange.schema),
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1793
|
+
inlineSizeEm: v.pipe(v.number(), v.finite(), v.minValue(0)),
|
|
1794
|
+
items: readonlyArray(ComposedInlineItem.schema),
|
|
1795
|
+
break: LineBreakResult.schema,
|
|
1345
1796
|
annotations: readonlyArray(ComposedAnnotationFragment.schema)
|
|
1346
1797
|
}),
|
|
1347
1798
|
empty: () => ({
|
|
1348
1799
|
range: null,
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1800
|
+
inlineSizeEm: 0,
|
|
1801
|
+
items: [],
|
|
1802
|
+
break: { kind: "paragraph-end" },
|
|
1352
1803
|
annotations: []
|
|
1353
1804
|
})
|
|
1354
1805
|
};
|
|
@@ -1380,15 +1831,7 @@ const NovelLayout = { schema: readonlyObject({
|
|
|
1380
1831
|
//#endregion
|
|
1381
1832
|
//#region src/composer/novel-composer.ts
|
|
1382
1833
|
const COMPOSER_ID = NamespacedId.of("kg/novel");
|
|
1383
|
-
const
|
|
1384
|
-
const LINE_END_PROHIBITED = /* @__PURE__ */ new Set(`${OPENING_BRACKETS}【〘〝⦅«`);
|
|
1385
|
-
const HANGING_PUNCTUATION = /* @__PURE__ */ new Set("、。,.!?");
|
|
1386
|
-
const INSEPARABLE_PAIRS = /* @__PURE__ */ new Set([
|
|
1387
|
-
"……",
|
|
1388
|
-
"――",
|
|
1389
|
-
"──",
|
|
1390
|
-
"〳〵"
|
|
1391
|
-
]);
|
|
1834
|
+
const TYPESETTING_PROFILE = defaultJapaneseTypesettingProfile;
|
|
1392
1835
|
const ASCII_ALPHANUMERIC = /^[A-Za-z0-9]$/u;
|
|
1393
1836
|
const ASCII_TWO_DIGITS = /^[0-9]{2}$/u;
|
|
1394
1837
|
const UPRIGHT_LATIN_ABBREVIATION = /^(?:[A-Z]+|[A-Z][a-z]{1,2})$/u;
|
|
@@ -1462,7 +1905,7 @@ function measure(measurer, text, role, settings, presentationKind) {
|
|
|
1462
1905
|
fontPreset: settings.appearance.fontPreset,
|
|
1463
1906
|
writingMode: "vertical-rl"
|
|
1464
1907
|
};
|
|
1465
|
-
const
|
|
1908
|
+
const measurement = role === "base" ? measurer({
|
|
1466
1909
|
...context,
|
|
1467
1910
|
role,
|
|
1468
1911
|
presentation: presentationKind ?? "mixed"
|
|
@@ -1470,14 +1913,25 @@ function measure(measurer, text, role, settings, presentationKind) {
|
|
|
1470
1913
|
...context,
|
|
1471
1914
|
role
|
|
1472
1915
|
});
|
|
1473
|
-
|
|
1916
|
+
const parsed = v.safeParse(InlineMeasurement.schema, measurement);
|
|
1917
|
+
if (!parsed.success) return void 0;
|
|
1918
|
+
const { advanceEm } = parsed.output;
|
|
1919
|
+
return advanceEm;
|
|
1474
1920
|
}
|
|
1475
1921
|
function measureSourceLine(sourceLine, annotations, settings, measurer) {
|
|
1476
1922
|
const mutable = presentedSourceLine(sourceLine).map(({ grapheme, presentation }) => {
|
|
1477
|
-
const
|
|
1478
|
-
|
|
1923
|
+
const renderAdvanceEm = measure(measurer, grapheme.value, "base", settings, presentation.kind);
|
|
1924
|
+
if (renderAdvanceEm === void 0) return void 0;
|
|
1925
|
+
const characterClass = TYPESETTING_PROFILE.classify({
|
|
1926
|
+
value: grapheme.value,
|
|
1927
|
+
presentation: presentation.kind
|
|
1928
|
+
});
|
|
1929
|
+
const metrics = TYPESETTING_PROFILE.boxMetrics(characterClass, renderAdvanceEm);
|
|
1930
|
+
return {
|
|
1479
1931
|
grapheme,
|
|
1480
|
-
advanceEm,
|
|
1932
|
+
boxAdvanceEm: metrics.advanceEm,
|
|
1933
|
+
renderAdvanceEm,
|
|
1934
|
+
renderOffsetEm: metrics.renderOffsetEm,
|
|
1481
1935
|
presentation
|
|
1482
1936
|
};
|
|
1483
1937
|
});
|
|
@@ -1491,11 +1945,11 @@ function measureSourceLine(sourceLine, annotations, settings, measurer) {
|
|
|
1491
1945
|
if (annotation.reading.kind === "group") {
|
|
1492
1946
|
const readingAdvance = measure(measurer, annotation.reading.text, "ruby", settings);
|
|
1493
1947
|
if (readingAdvance === void 0) return void 0;
|
|
1494
|
-
const baseAdvance = indexes.reduce((total, index) => total + (atoms[index]?.
|
|
1948
|
+
const baseAdvance = indexes.reduce((total, index) => total + (atoms[index]?.boxAdvanceEm ?? 0), 0);
|
|
1495
1949
|
const extra = Math.max(0, readingAdvance - baseAdvance) / indexes.length;
|
|
1496
1950
|
for (const index of indexes) {
|
|
1497
1951
|
const atom = atoms[index];
|
|
1498
|
-
if (atom !== void 0) atom.
|
|
1952
|
+
if (atom !== void 0) atom.boxAdvanceEm += extra;
|
|
1499
1953
|
}
|
|
1500
1954
|
continue;
|
|
1501
1955
|
}
|
|
@@ -1505,7 +1959,7 @@ function measureSourceLine(sourceLine, annotations, settings, measurer) {
|
|
|
1505
1959
|
if (segment === void 0 || atom === void 0) continue;
|
|
1506
1960
|
const readingAdvance = measure(measurer, segment, "ruby", settings);
|
|
1507
1961
|
if (readingAdvance === void 0) return void 0;
|
|
1508
|
-
atom.
|
|
1962
|
+
atom.boxAdvanceEm = Math.max(atom.boxAdvanceEm, readingAdvance);
|
|
1509
1963
|
}
|
|
1510
1964
|
}
|
|
1511
1965
|
return {
|
|
@@ -1520,17 +1974,23 @@ function breakInsideFittableGroupRuby(atoms, annotations, boundary, lineLengthEm
|
|
|
1520
1974
|
return annotations.some((annotation) => {
|
|
1521
1975
|
if (annotation.kind !== "ruby" || annotation.reading.kind !== "group") return false;
|
|
1522
1976
|
if (graphemeIndex <= annotation.range.graphemes.start || graphemeIndex >= annotation.range.graphemes.end) return false;
|
|
1523
|
-
return indicesInside(atoms, annotation).reduce((total, index) => total + (atoms[index]?.
|
|
1977
|
+
return indicesInside(atoms, annotation).reduce((total, index) => total + (atoms[index]?.boxAdvanceEm ?? 0), 0) <= lineLengthEm;
|
|
1524
1978
|
});
|
|
1525
1979
|
}
|
|
1526
1980
|
function samePresentationGroup(left, right) {
|
|
1527
1981
|
return left.presentation.groupRange.graphemes.start === right.presentation.groupRange.graphemes.start && left.presentation.groupRange.graphemes.end === right.presentation.groupRange.graphemes.end;
|
|
1528
1982
|
}
|
|
1529
|
-
function legalBoundary(atoms, annotations,
|
|
1530
|
-
const left = atoms[
|
|
1531
|
-
const right = atoms[
|
|
1983
|
+
function legalBoundary(atoms, annotations, leftIndex, rightIndex, lineLengthEm, profile) {
|
|
1984
|
+
const left = atoms[leftIndex];
|
|
1985
|
+
const right = atoms[rightIndex];
|
|
1532
1986
|
if (left === void 0 || right === void 0) return true;
|
|
1533
|
-
return !samePresentationGroup(left, right) &&
|
|
1987
|
+
return !samePresentationGroup(left, right) && profile.breakPenalty(profile.classify({
|
|
1988
|
+
value: left.grapheme.value,
|
|
1989
|
+
presentation: left.presentation.kind
|
|
1990
|
+
}), profile.classify({
|
|
1991
|
+
value: right.grapheme.value,
|
|
1992
|
+
presentation: right.presentation.kind
|
|
1993
|
+
})) !== null && !breakInsideFittableGroupRuby(atoms, annotations, rightIndex, lineLengthEm);
|
|
1534
1994
|
}
|
|
1535
1995
|
function continuationFor(annotation, range) {
|
|
1536
1996
|
const starts = range.graphemes.start === annotation.range.graphemes.start;
|
|
@@ -1602,14 +2062,18 @@ function positionReading(text, baseOffsetEm, baseAdvanceEm, settings, measurer)
|
|
|
1602
2062
|
return positioned;
|
|
1603
2063
|
});
|
|
1604
2064
|
}
|
|
1605
|
-
function annotationFragmentRange(graphemes, suppressed, annotation) {
|
|
1606
|
-
return ManuscriptRange.merge([
|
|
2065
|
+
function annotationFragmentRange(graphemes, suppressed, sourceGlues, annotation) {
|
|
2066
|
+
return ManuscriptRange.merge([
|
|
2067
|
+
...graphemes.filter(({ range }) => ManuscriptRange.overlaps(range, annotation.range)).map(({ range }) => range),
|
|
2068
|
+
...suppressed.filter(({ range }) => ManuscriptRange.overlaps(range, annotation.range)).map(({ range }) => range),
|
|
2069
|
+
...sourceGlues.filter(({ range }) => ManuscriptRange.overlaps(range, annotation.range)).map(({ range }) => range)
|
|
2070
|
+
]);
|
|
1607
2071
|
}
|
|
1608
|
-
function annotationFragments(graphemes, suppressed, annotations, settings, measurer, fragmentPlansByAnnotation) {
|
|
2072
|
+
function annotationFragments(graphemes, suppressed, sourceGlues, annotations, settings, measurer, fragmentPlansByAnnotation) {
|
|
1609
2073
|
const fragments = [];
|
|
1610
2074
|
for (const annotation of annotations) {
|
|
1611
2075
|
const covered = graphemes.filter(({ range }) => ManuscriptRange.overlaps(range, annotation.range));
|
|
1612
|
-
const fragmentRange = annotationFragmentRange(graphemes, suppressed, annotation);
|
|
2076
|
+
const fragmentRange = annotationFragmentRange(graphemes, suppressed, sourceGlues, annotation);
|
|
1613
2077
|
const first = covered[0];
|
|
1614
2078
|
const last = covered.at(-1);
|
|
1615
2079
|
if (fragmentRange === null || first === void 0 || last === void 0) continue;
|
|
@@ -1636,8 +2100,8 @@ function annotationFragments(graphemes, suppressed, annotations, settings, measu
|
|
|
1636
2100
|
case "ruby": {
|
|
1637
2101
|
const fragmentPlan = fragmentPlansByAnnotation.get(annotation);
|
|
1638
2102
|
if (fragmentPlan === void 0) break;
|
|
1639
|
-
const baseOffsetEm = first.offsetEm;
|
|
1640
|
-
const baseAdvanceEm = last.offsetEm + last.advanceEm - baseOffsetEm;
|
|
2103
|
+
const baseOffsetEm = first.layoutSpan.offsetEm;
|
|
2104
|
+
const baseAdvanceEm = last.layoutSpan.offsetEm + last.layoutSpan.advanceEm - baseOffsetEm;
|
|
1641
2105
|
const reading = readingForFragment(annotation, fragmentRange, fragmentPlan);
|
|
1642
2106
|
fragments.push({
|
|
1643
2107
|
kind: "ruby",
|
|
@@ -1654,88 +2118,103 @@ function annotationFragments(graphemes, suppressed, annotations, settings, measu
|
|
|
1654
2118
|
}
|
|
1655
2119
|
return fragments;
|
|
1656
2120
|
}
|
|
1657
|
-
function positionedLine(atoms,
|
|
2121
|
+
function positionedLine(atoms, sourceGapIndexes, plan) {
|
|
2122
|
+
const items = plan.suppressedIndexes.flatMap((index) => {
|
|
2123
|
+
const atom = atoms[index];
|
|
2124
|
+
return atom === void 0 ? [] : [{
|
|
2125
|
+
kind: "suppressed",
|
|
2126
|
+
value: atom.grapheme.value,
|
|
2127
|
+
range: atom.grapheme.range,
|
|
2128
|
+
reason: "question-or-exclamation-gap"
|
|
2129
|
+
}];
|
|
2130
|
+
});
|
|
2131
|
+
const spacings = new Map(plan.pairSpacings.map((spacing) => [spacing.boundary, spacing]));
|
|
1658
2132
|
let offsetEm = 0;
|
|
1659
|
-
const
|
|
1660
|
-
|
|
1661
|
-
kind: "
|
|
1662
|
-
value: grapheme.value,
|
|
1663
|
-
range: grapheme.range,
|
|
2133
|
+
const positionSpacing = (spacing) => {
|
|
2134
|
+
if (spacing.kind === "kern") items.push({
|
|
2135
|
+
kind: "kern",
|
|
1664
2136
|
offsetEm,
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
suppressed,
|
|
1677
|
-
annotations: []
|
|
2137
|
+
widthEm: spacing.widthEm
|
|
2138
|
+
});
|
|
2139
|
+
else items.push({
|
|
2140
|
+
kind: "glue",
|
|
2141
|
+
origin: "generated",
|
|
2142
|
+
offsetEm,
|
|
2143
|
+
widthEm: spacing.widthEm,
|
|
2144
|
+
naturalWidthEm: spacing.naturalWidthEm,
|
|
2145
|
+
adjustment: spacing.widthEm < spacing.naturalWidthEm ? "shrunk" : spacing.widthEm > spacing.naturalWidthEm ? "stretched" : "natural"
|
|
2146
|
+
});
|
|
2147
|
+
offsetEm += spacing.widthEm;
|
|
1678
2148
|
};
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
if (atom !== void 0) suppressed.push({
|
|
1689
|
-
kind: "suppressed",
|
|
2149
|
+
for (let index = plan.contentStart; index < plan.end; index += 1) {
|
|
2150
|
+
const spacing = spacings.get(index);
|
|
2151
|
+
if (spacing !== void 0) positionSpacing(spacing);
|
|
2152
|
+
const atom = atoms[index];
|
|
2153
|
+
if (atom === void 0) continue;
|
|
2154
|
+
if (sourceGapIndexes.has(index)) {
|
|
2155
|
+
items.push({
|
|
2156
|
+
kind: "glue",
|
|
2157
|
+
origin: "source",
|
|
1690
2158
|
value: atom.grapheme.value,
|
|
1691
2159
|
range: atom.grapheme.range,
|
|
1692
|
-
|
|
2160
|
+
offsetEm,
|
|
2161
|
+
widthEm: atom.boxAdvanceEm,
|
|
2162
|
+
naturalWidthEm: atom.boxAdvanceEm,
|
|
2163
|
+
adjustment: "natural"
|
|
1693
2164
|
});
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
if (cursor >= sourceLine.atoms.length) {
|
|
1697
|
-
const previous = lines.at(-1);
|
|
1698
|
-
if (previous !== void 0 && suppressed.length > 0) lines[lines.length - 1] = {
|
|
1699
|
-
...previous,
|
|
1700
|
-
range: ManuscriptRange.merge([...previous.range === null ? [] : [previous.range], ...suppressed.map(({ range }) => range)]),
|
|
1701
|
-
suppressed: [...previous.suppressed, ...suppressed]
|
|
1702
|
-
};
|
|
1703
|
-
break;
|
|
1704
|
-
}
|
|
1705
|
-
const start = cursor;
|
|
1706
|
-
let advance = 0;
|
|
1707
|
-
while (cursor < sourceLine.atoms.length) {
|
|
1708
|
-
const atom = sourceLine.atoms[cursor];
|
|
1709
|
-
if (atom === void 0) break;
|
|
1710
|
-
const previous = sourceLine.atoms[cursor - 1];
|
|
1711
|
-
const beginsPresentationGroup = previous === void 0 || !samePresentationGroup(previous, atom);
|
|
1712
|
-
if (cursor > start && beginsPresentationGroup) {
|
|
1713
|
-
let groupAdvance = 0;
|
|
1714
|
-
for (let index = cursor; index < sourceLine.atoms.length; index += 1) {
|
|
1715
|
-
const member = sourceLine.atoms[index];
|
|
1716
|
-
if (member === void 0 || !samePresentationGroup(atom, member)) break;
|
|
1717
|
-
groupAdvance += member.advanceEm;
|
|
1718
|
-
}
|
|
1719
|
-
if (advance + groupAdvance > settings.flow.lineLengthEm) break;
|
|
1720
|
-
}
|
|
1721
|
-
if (cursor > start && advance + atom.advanceEm > settings.flow.lineLengthEm && (previous === void 0 || !samePresentationGroup(previous, atom))) break;
|
|
1722
|
-
advance += atom.advanceEm;
|
|
1723
|
-
cursor += 1;
|
|
2165
|
+
offsetEm += atom.boxAdvanceEm;
|
|
2166
|
+
continue;
|
|
1724
2167
|
}
|
|
1725
|
-
|
|
1726
|
-
const
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
2168
|
+
const hanging = plan.hangingIndex === index;
|
|
2169
|
+
const layoutAdvanceEm = hanging ? 0 : atom.boxAdvanceEm;
|
|
2170
|
+
items.push({
|
|
2171
|
+
kind: "glyph",
|
|
2172
|
+
value: atom.grapheme.value,
|
|
2173
|
+
range: atom.grapheme.range,
|
|
2174
|
+
layoutSpan: {
|
|
2175
|
+
offsetEm,
|
|
2176
|
+
advanceEm: layoutAdvanceEm
|
|
2177
|
+
},
|
|
2178
|
+
renderSpan: {
|
|
2179
|
+
offsetEm: offsetEm + atom.renderOffsetEm,
|
|
2180
|
+
advanceEm: atom.renderAdvanceEm
|
|
2181
|
+
},
|
|
2182
|
+
disposition: hanging ? "hanging" : "placed",
|
|
2183
|
+
presentation: atom.presentation
|
|
2184
|
+
});
|
|
2185
|
+
offsetEm += layoutAdvanceEm;
|
|
1735
2186
|
}
|
|
2187
|
+
const trailingSpacing = spacings.get(plan.end);
|
|
2188
|
+
if (trailingSpacing !== void 0) positionSpacing(trailingSpacing);
|
|
2189
|
+
const ranges = items.flatMap((item) => item.kind === "glyph" || item.kind === "suppressed" || item.kind === "glue" && item.origin === "source" ? [item.range] : []);
|
|
2190
|
+
return {
|
|
2191
|
+
range: ManuscriptRange.merge(ranges),
|
|
2192
|
+
inlineSizeEm: Math.max(0, plan.inlineSizeEm),
|
|
2193
|
+
items,
|
|
2194
|
+
break: plan.break,
|
|
2195
|
+
annotations: []
|
|
2196
|
+
};
|
|
2197
|
+
}
|
|
2198
|
+
function wrapSourceLine(sourceLine, annotations, settings, measurer, baseAdvances) {
|
|
2199
|
+
if (sourceLine.atoms.length === 0) return [NovelLine.empty()];
|
|
2200
|
+
const profile = TYPESETTING_PROFILE;
|
|
2201
|
+
const lines = layoutParagraph(sourceLine.atoms.map(({ grapheme, boxAdvanceEm, presentation }, index) => {
|
|
2202
|
+
const current = sourceLine.atoms[index];
|
|
2203
|
+
const next = sourceLine.atoms[index + 1];
|
|
2204
|
+
return {
|
|
2205
|
+
value: grapheme.value,
|
|
2206
|
+
boxAdvanceEm,
|
|
2207
|
+
sourceGap: sourceLine.suppressedIndexes.has(index),
|
|
2208
|
+
characterClass: profile.classify({
|
|
2209
|
+
value: grapheme.value,
|
|
2210
|
+
presentation: presentation.kind
|
|
2211
|
+
}),
|
|
2212
|
+
pairSpacingAfter: current === void 0 || next === void 0 || !samePresentationGroup(current, next) && !breakInsideFittableGroupRuby(sourceLine.atoms, annotations, index + 1, settings.flow.lineLengthEm)
|
|
2213
|
+
};
|
|
2214
|
+
}), settings.flow.lineLengthEm, profile, (leftIndex, rightIndex) => legalBoundary(sourceLine.atoms, annotations, leftIndex, rightIndex, settings.flow.lineLengthEm, profile)).map((plan) => positionedLine(sourceLine.atoms, sourceLine.suppressedIndexes, plan));
|
|
1736
2215
|
const fragmentPlansByAnnotation = new Map(annotations.map((annotation) => {
|
|
1737
2216
|
const ranges = lines.flatMap((line) => {
|
|
1738
|
-
const range = annotationFragmentRange(line.
|
|
2217
|
+
const range = annotationFragmentRange(line.items.filter((item) => item.kind === "glyph"), line.items.filter((item) => item.kind === "suppressed"), line.items.flatMap((item) => item.kind === "glue" && item.origin === "source" ? [item] : []), annotation);
|
|
1739
2218
|
return range === null ? [] : [range];
|
|
1740
2219
|
});
|
|
1741
2220
|
return [annotation, {
|
|
@@ -1745,10 +2224,10 @@ function wrapSourceLine(sourceLine, annotations, settings, measurer, baseAdvance
|
|
|
1745
2224
|
}));
|
|
1746
2225
|
return lines.map((line) => ({
|
|
1747
2226
|
range: line.range,
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
annotations: annotationFragments(line.
|
|
2227
|
+
inlineSizeEm: line.inlineSizeEm,
|
|
2228
|
+
items: line.items,
|
|
2229
|
+
break: line.break,
|
|
2230
|
+
annotations: annotationFragments(line.items.filter((item) => item.kind === "glyph"), line.items.filter((item) => item.kind === "suppressed"), line.items.flatMap((item) => item.kind === "glue" && item.origin === "source" ? [item] : []), annotations, settings, measurer, fragmentPlansByAnnotation)
|
|
1752
2231
|
}));
|
|
1753
2232
|
}
|
|
1754
2233
|
function blankLines(count) {
|
|
@@ -1789,7 +2268,7 @@ function createCompose(measurer) {
|
|
|
1789
2268
|
const measured = sourceLines.map((line) => measureSourceLine(line, manuscript.annotations, settings, measurer));
|
|
1790
2269
|
if (measured.some((line) => line === void 0)) return ManuscriptResult.fail({ reason: "inline measurer returned a negative or non-finite value" });
|
|
1791
2270
|
const baseAdvances = /* @__PURE__ */ new Map();
|
|
1792
|
-
for (const line of measured) for (const atom of line?.atoms ?? []) baseAdvances.set(atom.grapheme.range.graphemes.start, atom.
|
|
2271
|
+
for (const line of measured) for (const atom of line?.atoms ?? []) baseAdvances.set(atom.grapheme.range.graphemes.start, atom.boxAdvanceEm);
|
|
1793
2272
|
const manuscriptLines = measured.flatMap((line) => line === void 0 ? [] : wrapSourceLine(line, manuscript.annotations, settings, measurer, baseAdvances));
|
|
1794
2273
|
const pages = buildPages([
|
|
1795
2274
|
...blankLines(settings.offsets.document.leading),
|
|
@@ -2561,4 +3040,4 @@ function createDefaultProofreadingRules() {
|
|
|
2561
3040
|
];
|
|
2562
3041
|
}
|
|
2563
3042
|
//#endregion
|
|
2564
|
-
export { BoldAnnotation, ComposeError, ComposedAnnotationFragment, CompositionStatistics, DiagnosticOrigin, DiagnosticSeverity, DisplayRange, EmphasisAnnotation, FontPreset, FontPresetId, FontSizePt, GraphemeRange, InvalidRuleOptions, ItalicAnnotation, LineOffset, ManuscriptAnnotation, ManuscriptAppearanceSettings, ManuscriptDiagnostic, ManuscriptGeometry, ManuscriptOffsets, ManuscriptRange, ManuscriptResult, NamespacedId, NovelCompositionSettings, NovelFlowSettings, NovelLayout, NovelLine, NovelPage, NovelStage, PaperSize, PaperSizeId, ParseError, ParsedGrapheme, ParsedManuscript,
|
|
3043
|
+
export { BoldAnnotation, ComposeError, ComposedAnnotationFragment, ComposedInlineItem, CompositionStatistics, DiagnosticOrigin, DiagnosticSeverity, DisplayRange, EmphasisAnnotation, FontPreset, FontPresetId, FontSizePt, GraphemeRange, InlineMeasurement, InlineSpan, InvalidRuleOptions, ItalicAnnotation, LineBreakResult, LineOffset, ManuscriptAnnotation, ManuscriptAppearanceSettings, ManuscriptDiagnostic, ManuscriptGeometry, ManuscriptOffsets, ManuscriptRange, ManuscriptResult, NamespacedId, NovelCompositionSettings, NovelFlowSettings, NovelLayout, NovelLine, NovelPage, NovelStage, PaperSize, PaperSizeId, ParseError, ParsedGrapheme, ParsedManuscript, ProofreadError, ProofreadingReport, ProofreadingRuleMeta, Rejection, RubyAnnotation, RubyKind, RubyReading, SourcePosition, SourceRange, TextRange, ValidationIssue, VerticalTextPresentation, assertNever, composeManuscript, consistentKanjiOpeningRule, consistentLatinWidthRule, consistentNumeralWidthRule, createConsistentKanjiOpeningRule, createDashRule, createDefaultProofreadingRules, createMaxArabicNumeralDigitsRule, createNovelComposer, createParagraphOpeningRule, dashRule, ellipsisCharacterRule, evenEllipsisRule, fullwidthJapanesePunctuationRule, kakuyomuParser, logicalInlineMeasurer, maxArabicNumeralDigitsRule, minusBeforeNumberRule, mmToPt, noConsecutiveInterpunctRule, noConsecutivePunctuationRule, novelComposer, paragraphOpeningRule, parseManuscript, pixivParser, plainTextParser, proofreadManuscript, ptToMm, punctuationBeforeClosingQuoteRule, spaceAfterQuestionOrExclamationRule, variantCharacterRule };
|