@sushichan044/kg-core 0.6.3 → 0.6.4
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 +18 -8
- package/dist/index.d.mts +24503 -20300
- package/dist/index.mjs +627 -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,413 @@ 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: 2,
|
|
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
|
-
const
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1496
|
+
//#region src/composer/internal/paragraph-layout.ts
|
|
1497
|
+
const EPSILON = 1e-9;
|
|
1498
|
+
function skipSourceGaps(atoms, start) {
|
|
1499
|
+
let cursor = start;
|
|
1500
|
+
while (atoms[cursor]?.sourceGap === true) cursor += 1;
|
|
1501
|
+
return cursor;
|
|
1502
|
+
}
|
|
1503
|
+
function previousVisible(atoms, end) {
|
|
1504
|
+
for (let index = end - 1; index >= 0; index -= 1) if (atoms[index]?.sourceGap === false) return index;
|
|
1505
|
+
}
|
|
1506
|
+
function opportunities(atoms, classes, start, end, profile) {
|
|
1507
|
+
const result = [];
|
|
1508
|
+
const firstClass = classes[start];
|
|
1509
|
+
const startSpacing = firstClass === void 0 ? null : profile.lineStartSpacing(firstClass);
|
|
1510
|
+
if (startSpacing !== null) result.push({
|
|
1511
|
+
boundary: start,
|
|
1512
|
+
spacing: startSpacing
|
|
1513
|
+
});
|
|
1514
|
+
for (let right = start + 1; right < end; right += 1) {
|
|
1515
|
+
const leftAtom = atoms[right - 1];
|
|
1516
|
+
const rightAtom = atoms[right];
|
|
1517
|
+
const leftClass = classes[right - 1];
|
|
1518
|
+
const rightClass = classes[right];
|
|
1519
|
+
if (leftAtom === void 0 || rightAtom === void 0 || leftClass === void 0 || rightClass === void 0 || leftAtom.sourceGap || rightAtom.sourceGap || !leftAtom.pairSpacingAfter) continue;
|
|
1520
|
+
result.push({
|
|
1521
|
+
boundary: right,
|
|
1522
|
+
spacing: profile.pairSpacing(leftClass, rightClass)
|
|
1523
|
+
});
|
|
1524
|
+
}
|
|
1525
|
+
const lastClass = classes[end - 1];
|
|
1526
|
+
const endSpacing = lastClass === void 0 ? null : profile.lineEndSpacing(lastClass);
|
|
1527
|
+
if (endSpacing !== null) result.push({
|
|
1528
|
+
boundary: end,
|
|
1529
|
+
spacing: endSpacing
|
|
1530
|
+
});
|
|
1531
|
+
return result;
|
|
1532
|
+
}
|
|
1533
|
+
function capacity(values, select) {
|
|
1534
|
+
return values.reduce((total, opportunity) => total + (select(opportunity.spacing)?.amountEm ?? 0), 0);
|
|
1535
|
+
}
|
|
1536
|
+
function resolveSpacings(values, adjustment, amountEm) {
|
|
1537
|
+
const remainingByBoundary = /* @__PURE__ */ new Map();
|
|
1538
|
+
let remaining = amountEm;
|
|
1539
|
+
let priorityCost = 0;
|
|
1540
|
+
const capacities = values.flatMap((opportunity) => {
|
|
1541
|
+
const selected = adjustment === "shrink" ? opportunity.spacing.shrink : opportunity.spacing.stretch;
|
|
1542
|
+
return selected === void 0 ? [] : [{
|
|
1543
|
+
...opportunity,
|
|
1544
|
+
capacity: selected
|
|
1545
|
+
}];
|
|
1546
|
+
}).sort((left, right) => left.capacity.priority - right.capacity.priority || left.boundary - right.boundary);
|
|
1547
|
+
for (const opportunity of capacities) {
|
|
1548
|
+
if (remaining <= EPSILON) break;
|
|
1549
|
+
const used = Math.min(remaining, opportunity.capacity.amountEm);
|
|
1550
|
+
remainingByBoundary.set(opportunity.boundary, used);
|
|
1551
|
+
priorityCost += used * opportunity.capacity.priority;
|
|
1552
|
+
remaining -= used;
|
|
1553
|
+
}
|
|
1554
|
+
return {
|
|
1555
|
+
spacings: values.map(({ boundary, spacing }) => {
|
|
1556
|
+
const used = remainingByBoundary.get(boundary) ?? 0;
|
|
1557
|
+
return {
|
|
1558
|
+
boundary,
|
|
1559
|
+
kind: spacing.kind,
|
|
1560
|
+
naturalWidthEm: spacing.naturalWidthEm,
|
|
1561
|
+
widthEm: adjustment === "shrink" ? spacing.naturalWidthEm - used : spacing.naturalWidthEm + used
|
|
1562
|
+
};
|
|
1563
|
+
}),
|
|
1564
|
+
priorityCost
|
|
1565
|
+
};
|
|
1566
|
+
}
|
|
1567
|
+
function candidate(atoms, classes, start, end, lineLengthEm, profile) {
|
|
1568
|
+
const contentStart = skipSourceGaps(atoms, start);
|
|
1569
|
+
const suppressedIndexes = Array.from({ length: contentStart - start }, (_, index) => start + index);
|
|
1570
|
+
const pairValues = opportunities(atoms, classes, contentStart, end, profile);
|
|
1571
|
+
const naturalSizeEm = atoms.slice(contentStart, end).reduce((total, atom) => total + atom.boxAdvanceEm, 0) + pairValues.reduce((total, value) => total + value.spacing.naturalWidthEm, 0);
|
|
1572
|
+
const terminal = skipSourceGaps(atoms, end) === atoms.length;
|
|
1573
|
+
const shrinkCapacity = capacity(pairValues, ({ shrink }) => shrink);
|
|
1574
|
+
const stretchCapacity = capacity(pairValues, ({ stretch }) => stretch);
|
|
1575
|
+
const overflow = naturalSizeEm - lineLengthEm;
|
|
1576
|
+
const underflow = lineLengthEm - naturalSizeEm;
|
|
1577
|
+
const lastVisible = previousVisible(atoms, end);
|
|
1578
|
+
const lastClass = lastVisible === void 0 ? void 0 : classes[lastVisible];
|
|
1579
|
+
const lastAdvance = lastVisible === void 0 ? 0 : atoms[lastVisible]?.boxAdvanceEm ?? 0;
|
|
1580
|
+
const trailingSpacing = pairValues.find(({ boundary }) => boundary === end)?.spacing.naturalWidthEm ?? 0;
|
|
1581
|
+
if (terminal && overflow <= EPSILON) return {
|
|
1582
|
+
start,
|
|
1583
|
+
contentStart,
|
|
1584
|
+
end,
|
|
1585
|
+
suppressedIndexes,
|
|
1586
|
+
pairSpacings: resolveSpacings(pairValues, "natural", 0).spacings,
|
|
1587
|
+
inlineSizeEm: naturalSizeEm,
|
|
1588
|
+
break: { kind: "paragraph-end" },
|
|
1589
|
+
hangingIndex: null,
|
|
1590
|
+
deformationRatio: 0,
|
|
1591
|
+
priorityCost: 0
|
|
1592
|
+
};
|
|
1593
|
+
if (Math.abs(overflow) <= EPSILON) return {
|
|
1594
|
+
start,
|
|
1595
|
+
contentStart,
|
|
1596
|
+
end,
|
|
1597
|
+
suppressedIndexes,
|
|
1598
|
+
pairSpacings: resolveSpacings(pairValues, "natural", 0).spacings,
|
|
1599
|
+
inlineSizeEm: naturalSizeEm,
|
|
1600
|
+
break: { kind: "natural" },
|
|
1601
|
+
hangingIndex: null,
|
|
1602
|
+
deformationRatio: 0,
|
|
1603
|
+
priorityCost: 0
|
|
1604
|
+
};
|
|
1605
|
+
if (overflow > 0 && overflow <= shrinkCapacity + EPSILON) {
|
|
1606
|
+
const resolved = resolveSpacings(pairValues, "shrink", overflow);
|
|
1607
|
+
return {
|
|
1608
|
+
start,
|
|
1609
|
+
contentStart,
|
|
1610
|
+
end,
|
|
1611
|
+
suppressedIndexes,
|
|
1612
|
+
pairSpacings: resolved.spacings,
|
|
1613
|
+
inlineSizeEm: lineLengthEm,
|
|
1614
|
+
break: { kind: "shrunk" },
|
|
1615
|
+
hangingIndex: null,
|
|
1616
|
+
deformationRatio: shrinkCapacity === 0 ? 0 : overflow / shrinkCapacity,
|
|
1617
|
+
priorityCost: resolved.priorityCost
|
|
1618
|
+
};
|
|
1619
|
+
}
|
|
1620
|
+
if (!terminal && overflow > 0 && lastVisible !== void 0 && lastClass !== void 0 && profile.canHang(lastClass) && naturalSizeEm - lastAdvance - trailingSpacing <= lineLengthEm + EPSILON) return {
|
|
1621
|
+
start,
|
|
1622
|
+
contentStart,
|
|
1623
|
+
end,
|
|
1624
|
+
suppressedIndexes,
|
|
1625
|
+
pairSpacings: resolveSpacings(pairValues, "natural", 0).spacings.filter(({ boundary }) => boundary !== end),
|
|
1626
|
+
inlineSizeEm: naturalSizeEm - lastAdvance - trailingSpacing,
|
|
1627
|
+
break: { kind: "hanging" },
|
|
1628
|
+
hangingIndex: lastVisible,
|
|
1629
|
+
deformationRatio: overflow / Math.max(lastAdvance, EPSILON),
|
|
1630
|
+
priorityCost: 0
|
|
1631
|
+
};
|
|
1632
|
+
if (!terminal && underflow > EPSILON && underflow <= stretchCapacity + EPSILON) {
|
|
1633
|
+
const resolved = resolveSpacings(pairValues, "stretch", underflow);
|
|
1634
|
+
return {
|
|
1635
|
+
start,
|
|
1636
|
+
contentStart,
|
|
1637
|
+
end,
|
|
1638
|
+
suppressedIndexes,
|
|
1639
|
+
pairSpacings: resolved.spacings,
|
|
1640
|
+
inlineSizeEm: lineLengthEm,
|
|
1641
|
+
break: { kind: "stretched" },
|
|
1642
|
+
hangingIndex: null,
|
|
1643
|
+
deformationRatio: stretchCapacity === 0 ? 0 : underflow / stretchCapacity,
|
|
1644
|
+
priorityCost: resolved.priorityCost
|
|
1645
|
+
};
|
|
1646
|
+
}
|
|
1647
|
+
return {
|
|
1648
|
+
start,
|
|
1649
|
+
contentStart,
|
|
1650
|
+
end,
|
|
1651
|
+
suppressedIndexes,
|
|
1652
|
+
pairSpacings: resolveSpacings(pairValues, "natural", 0).spacings,
|
|
1653
|
+
inlineSizeEm: naturalSizeEm,
|
|
1654
|
+
break: { kind: "forced" },
|
|
1655
|
+
hangingIndex: null,
|
|
1656
|
+
deformationRatio: Math.abs(naturalSizeEm - lineLengthEm) / Math.max(lineLengthEm, EPSILON),
|
|
1657
|
+
priorityCost: 0
|
|
1658
|
+
};
|
|
1659
|
+
}
|
|
1660
|
+
function scoreFor(line, previousFitness) {
|
|
1661
|
+
const mode = line.break.kind;
|
|
1662
|
+
const fitness = Math.min(3, Math.floor(line.deformationRatio * 4));
|
|
1663
|
+
const transition = Math.abs(previousFitness - fitness) > 1 ? 1 : 0;
|
|
1664
|
+
const modeCounts = {
|
|
1665
|
+
forced: mode === "forced" ? 1 : 0,
|
|
1666
|
+
stretched: mode === "stretched" ? 1 : 0,
|
|
1667
|
+
hanging: mode === "hanging" ? 1 : 0,
|
|
1668
|
+
shrunk: mode === "shrunk" ? 1 : 0
|
|
1669
|
+
};
|
|
1670
|
+
return {
|
|
1671
|
+
score: [
|
|
1672
|
+
modeCounts.forced,
|
|
1673
|
+
modeCounts.stretched,
|
|
1674
|
+
modeCounts.hanging,
|
|
1675
|
+
modeCounts.shrunk,
|
|
1676
|
+
line.priorityCost,
|
|
1677
|
+
line.deformationRatio ** 3,
|
|
1678
|
+
transition
|
|
1679
|
+
],
|
|
1680
|
+
fitness
|
|
1681
|
+
};
|
|
1682
|
+
}
|
|
1683
|
+
function addScore(left, right) {
|
|
1684
|
+
return [
|
|
1685
|
+
left[0] + right[0],
|
|
1686
|
+
left[1] + right[1],
|
|
1687
|
+
left[2] + right[2],
|
|
1688
|
+
left[3] + right[3],
|
|
1689
|
+
left[4] + right[4],
|
|
1690
|
+
left[5] + right[5],
|
|
1691
|
+
left[6] + right[6]
|
|
1692
|
+
];
|
|
1693
|
+
}
|
|
1694
|
+
function compareBreaks(left, right) {
|
|
1695
|
+
const length = Math.min(left.length, right.length);
|
|
1696
|
+
for (let index = 0; index < length; index += 1) {
|
|
1697
|
+
const difference = (right[index] ?? 0) - (left[index] ?? 0);
|
|
1698
|
+
if (difference !== 0) return difference;
|
|
1699
|
+
}
|
|
1700
|
+
return left.length - right.length;
|
|
1701
|
+
}
|
|
1702
|
+
function isBetter(candidateState, current) {
|
|
1703
|
+
if (current === void 0) return true;
|
|
1704
|
+
for (let index = 0; index < candidateState.score.length; index += 1) {
|
|
1705
|
+
const difference = (candidateState.score[index] ?? 0) - (current.score[index] ?? 0);
|
|
1706
|
+
if (Math.abs(difference) > EPSILON) return difference < 0;
|
|
1707
|
+
}
|
|
1708
|
+
return compareBreaks(candidateState.breaks, current.breaks) < 0;
|
|
1709
|
+
}
|
|
1710
|
+
function layoutParagraph(atoms, lineLengthEm, profile, boundaryAllowed) {
|
|
1711
|
+
if (atoms.length === 0) return [];
|
|
1712
|
+
const classes = atoms.map(({ characterClass }) => characterClass);
|
|
1713
|
+
const states = /* @__PURE__ */ new Map([[0, /* @__PURE__ */ new Map([[0, {
|
|
1714
|
+
score: [
|
|
1715
|
+
0,
|
|
1716
|
+
0,
|
|
1717
|
+
0,
|
|
1718
|
+
0,
|
|
1719
|
+
0,
|
|
1720
|
+
0,
|
|
1721
|
+
0
|
|
1722
|
+
],
|
|
1723
|
+
fitness: 0,
|
|
1724
|
+
previous: null,
|
|
1725
|
+
line: null,
|
|
1726
|
+
breaks: []
|
|
1727
|
+
}]])]]);
|
|
1728
|
+
for (let start = 0; start < atoms.length; start += 1) {
|
|
1729
|
+
const activeStates = states.get(start);
|
|
1730
|
+
if (activeStates === void 0) continue;
|
|
1731
|
+
const contentStart = skipSourceGaps(atoms, start);
|
|
1732
|
+
if (contentStart === atoms.length) {
|
|
1733
|
+
const terminalStates = states.get(atoms.length) ?? /* @__PURE__ */ new Map();
|
|
1734
|
+
for (const state of activeStates.values()) if (isBetter(state, terminalStates.get(state.fitness))) terminalStates.set(state.fitness, state);
|
|
1735
|
+
states.set(atoms.length, terminalStates);
|
|
1736
|
+
continue;
|
|
1737
|
+
}
|
|
1738
|
+
for (const state of activeStates.values()) for (let end = contentStart + 1; end <= atoms.length; end += 1) {
|
|
1739
|
+
const right = skipSourceGaps(atoms, end);
|
|
1740
|
+
const left = previousVisible(atoms, end);
|
|
1741
|
+
if (atoms[end - 1]?.sourceGap === true) continue;
|
|
1742
|
+
if (end < atoms.length && right < atoms.length && (left === void 0 || !boundaryAllowed(left, right))) continue;
|
|
1743
|
+
const line = candidate(atoms, classes, start, end, lineLengthEm, profile);
|
|
1744
|
+
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))) {
|
|
1745
|
+
if (line.inlineSizeEm > lineLengthEm * 2 && end > contentStart + 1) break;
|
|
1746
|
+
continue;
|
|
1747
|
+
}
|
|
1748
|
+
const lineScore = scoreFor(line, state.fitness);
|
|
1749
|
+
const nextState = {
|
|
1750
|
+
score: addScore(state.score, lineScore.score),
|
|
1751
|
+
fitness: lineScore.fitness,
|
|
1752
|
+
previous: {
|
|
1753
|
+
index: start,
|
|
1754
|
+
fitness: state.fitness
|
|
1755
|
+
},
|
|
1756
|
+
line,
|
|
1757
|
+
breaks: [...state.breaks, end]
|
|
1758
|
+
};
|
|
1759
|
+
const statesAtEnd = states.get(end) ?? /* @__PURE__ */ new Map();
|
|
1760
|
+
if (isBetter(nextState, statesAtEnd.get(nextState.fitness))) {
|
|
1761
|
+
statesAtEnd.set(nextState.fitness, nextState);
|
|
1762
|
+
states.set(end, statesAtEnd);
|
|
1763
|
+
}
|
|
1764
|
+
if (line.inlineSizeEm > lineLengthEm * 2 && line.break.kind === "forced" && end > contentStart + 1) break;
|
|
1765
|
+
}
|
|
1766
|
+
}
|
|
1767
|
+
const terminal = [...states.get(atoms.length)?.values() ?? []].reduce((best, state) => isBetter(state, best) ? state : best, void 0);
|
|
1768
|
+
if (terminal === void 0) return [];
|
|
1769
|
+
const lines = [];
|
|
1770
|
+
let cursor = terminal;
|
|
1771
|
+
while (cursor?.line !== null && cursor?.line !== void 0) {
|
|
1772
|
+
lines.push(cursor.line);
|
|
1773
|
+
cursor = cursor.previous === null ? void 0 : states.get(cursor.previous.index)?.get(cursor.previous.fitness);
|
|
1774
|
+
}
|
|
1775
|
+
lines.reverse();
|
|
1776
|
+
const last = lines.at(-1);
|
|
1777
|
+
if (last !== void 0 && last.end < atoms.length) lines[lines.length - 1] = {
|
|
1778
|
+
...last,
|
|
1779
|
+
suppressedIndexes: [...last.suppressedIndexes, ...Array.from({ length: atoms.length - last.end }, (_, index) => last.end + index)]
|
|
1780
|
+
};
|
|
1781
|
+
return lines;
|
|
1782
|
+
}
|
|
1339
1783
|
const NovelLine = {
|
|
1340
1784
|
schema: readonlyObject({
|
|
1341
1785
|
range: v.nullable(ManuscriptRange.schema),
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1786
|
+
inlineSizeEm: v.pipe(v.number(), v.finite(), v.minValue(0)),
|
|
1787
|
+
items: readonlyArray(ComposedInlineItem.schema),
|
|
1788
|
+
break: LineBreakResult.schema,
|
|
1345
1789
|
annotations: readonlyArray(ComposedAnnotationFragment.schema)
|
|
1346
1790
|
}),
|
|
1347
1791
|
empty: () => ({
|
|
1348
1792
|
range: null,
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1793
|
+
inlineSizeEm: 0,
|
|
1794
|
+
items: [],
|
|
1795
|
+
break: { kind: "paragraph-end" },
|
|
1352
1796
|
annotations: []
|
|
1353
1797
|
})
|
|
1354
1798
|
};
|
|
@@ -1380,15 +1824,7 @@ const NovelLayout = { schema: readonlyObject({
|
|
|
1380
1824
|
//#endregion
|
|
1381
1825
|
//#region src/composer/novel-composer.ts
|
|
1382
1826
|
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
|
-
]);
|
|
1827
|
+
const TYPESETTING_PROFILE = defaultJapaneseTypesettingProfile;
|
|
1392
1828
|
const ASCII_ALPHANUMERIC = /^[A-Za-z0-9]$/u;
|
|
1393
1829
|
const ASCII_TWO_DIGITS = /^[0-9]{2}$/u;
|
|
1394
1830
|
const UPRIGHT_LATIN_ABBREVIATION = /^(?:[A-Z]+|[A-Z][a-z]{1,2})$/u;
|
|
@@ -1462,7 +1898,7 @@ function measure(measurer, text, role, settings, presentationKind) {
|
|
|
1462
1898
|
fontPreset: settings.appearance.fontPreset,
|
|
1463
1899
|
writingMode: "vertical-rl"
|
|
1464
1900
|
};
|
|
1465
|
-
const
|
|
1901
|
+
const measurement = role === "base" ? measurer({
|
|
1466
1902
|
...context,
|
|
1467
1903
|
role,
|
|
1468
1904
|
presentation: presentationKind ?? "mixed"
|
|
@@ -1470,14 +1906,25 @@ function measure(measurer, text, role, settings, presentationKind) {
|
|
|
1470
1906
|
...context,
|
|
1471
1907
|
role
|
|
1472
1908
|
});
|
|
1473
|
-
|
|
1909
|
+
const parsed = v.safeParse(InlineMeasurement.schema, measurement);
|
|
1910
|
+
if (!parsed.success) return void 0;
|
|
1911
|
+
const { advanceEm } = parsed.output;
|
|
1912
|
+
return advanceEm;
|
|
1474
1913
|
}
|
|
1475
1914
|
function measureSourceLine(sourceLine, annotations, settings, measurer) {
|
|
1476
1915
|
const mutable = presentedSourceLine(sourceLine).map(({ grapheme, presentation }) => {
|
|
1477
|
-
const
|
|
1478
|
-
|
|
1916
|
+
const renderAdvanceEm = measure(measurer, grapheme.value, "base", settings, presentation.kind);
|
|
1917
|
+
if (renderAdvanceEm === void 0) return void 0;
|
|
1918
|
+
const characterClass = TYPESETTING_PROFILE.classify({
|
|
1919
|
+
value: grapheme.value,
|
|
1920
|
+
presentation: presentation.kind
|
|
1921
|
+
});
|
|
1922
|
+
const metrics = TYPESETTING_PROFILE.boxMetrics(characterClass, renderAdvanceEm);
|
|
1923
|
+
return {
|
|
1479
1924
|
grapheme,
|
|
1480
|
-
advanceEm,
|
|
1925
|
+
boxAdvanceEm: metrics.advanceEm,
|
|
1926
|
+
renderAdvanceEm,
|
|
1927
|
+
renderOffsetEm: metrics.renderOffsetEm,
|
|
1481
1928
|
presentation
|
|
1482
1929
|
};
|
|
1483
1930
|
});
|
|
@@ -1491,11 +1938,11 @@ function measureSourceLine(sourceLine, annotations, settings, measurer) {
|
|
|
1491
1938
|
if (annotation.reading.kind === "group") {
|
|
1492
1939
|
const readingAdvance = measure(measurer, annotation.reading.text, "ruby", settings);
|
|
1493
1940
|
if (readingAdvance === void 0) return void 0;
|
|
1494
|
-
const baseAdvance = indexes.reduce((total, index) => total + (atoms[index]?.
|
|
1941
|
+
const baseAdvance = indexes.reduce((total, index) => total + (atoms[index]?.boxAdvanceEm ?? 0), 0);
|
|
1495
1942
|
const extra = Math.max(0, readingAdvance - baseAdvance) / indexes.length;
|
|
1496
1943
|
for (const index of indexes) {
|
|
1497
1944
|
const atom = atoms[index];
|
|
1498
|
-
if (atom !== void 0) atom.
|
|
1945
|
+
if (atom !== void 0) atom.boxAdvanceEm += extra;
|
|
1499
1946
|
}
|
|
1500
1947
|
continue;
|
|
1501
1948
|
}
|
|
@@ -1505,7 +1952,7 @@ function measureSourceLine(sourceLine, annotations, settings, measurer) {
|
|
|
1505
1952
|
if (segment === void 0 || atom === void 0) continue;
|
|
1506
1953
|
const readingAdvance = measure(measurer, segment, "ruby", settings);
|
|
1507
1954
|
if (readingAdvance === void 0) return void 0;
|
|
1508
|
-
atom.
|
|
1955
|
+
atom.boxAdvanceEm = Math.max(atom.boxAdvanceEm, readingAdvance);
|
|
1509
1956
|
}
|
|
1510
1957
|
}
|
|
1511
1958
|
return {
|
|
@@ -1520,17 +1967,23 @@ function breakInsideFittableGroupRuby(atoms, annotations, boundary, lineLengthEm
|
|
|
1520
1967
|
return annotations.some((annotation) => {
|
|
1521
1968
|
if (annotation.kind !== "ruby" || annotation.reading.kind !== "group") return false;
|
|
1522
1969
|
if (graphemeIndex <= annotation.range.graphemes.start || graphemeIndex >= annotation.range.graphemes.end) return false;
|
|
1523
|
-
return indicesInside(atoms, annotation).reduce((total, index) => total + (atoms[index]?.
|
|
1970
|
+
return indicesInside(atoms, annotation).reduce((total, index) => total + (atoms[index]?.boxAdvanceEm ?? 0), 0) <= lineLengthEm;
|
|
1524
1971
|
});
|
|
1525
1972
|
}
|
|
1526
1973
|
function samePresentationGroup(left, right) {
|
|
1527
1974
|
return left.presentation.groupRange.graphemes.start === right.presentation.groupRange.graphemes.start && left.presentation.groupRange.graphemes.end === right.presentation.groupRange.graphemes.end;
|
|
1528
1975
|
}
|
|
1529
|
-
function legalBoundary(atoms, annotations,
|
|
1530
|
-
const left = atoms[
|
|
1531
|
-
const right = atoms[
|
|
1976
|
+
function legalBoundary(atoms, annotations, leftIndex, rightIndex, lineLengthEm, profile) {
|
|
1977
|
+
const left = atoms[leftIndex];
|
|
1978
|
+
const right = atoms[rightIndex];
|
|
1532
1979
|
if (left === void 0 || right === void 0) return true;
|
|
1533
|
-
return !samePresentationGroup(left, right) &&
|
|
1980
|
+
return !samePresentationGroup(left, right) && profile.breakPenalty(profile.classify({
|
|
1981
|
+
value: left.grapheme.value,
|
|
1982
|
+
presentation: left.presentation.kind
|
|
1983
|
+
}), profile.classify({
|
|
1984
|
+
value: right.grapheme.value,
|
|
1985
|
+
presentation: right.presentation.kind
|
|
1986
|
+
})) !== null && !breakInsideFittableGroupRuby(atoms, annotations, rightIndex, lineLengthEm);
|
|
1534
1987
|
}
|
|
1535
1988
|
function continuationFor(annotation, range) {
|
|
1536
1989
|
const starts = range.graphemes.start === annotation.range.graphemes.start;
|
|
@@ -1602,14 +2055,18 @@ function positionReading(text, baseOffsetEm, baseAdvanceEm, settings, measurer)
|
|
|
1602
2055
|
return positioned;
|
|
1603
2056
|
});
|
|
1604
2057
|
}
|
|
1605
|
-
function annotationFragmentRange(graphemes, suppressed, annotation) {
|
|
1606
|
-
return ManuscriptRange.merge([
|
|
2058
|
+
function annotationFragmentRange(graphemes, suppressed, sourceGlues, annotation) {
|
|
2059
|
+
return ManuscriptRange.merge([
|
|
2060
|
+
...graphemes.filter(({ range }) => ManuscriptRange.overlaps(range, annotation.range)).map(({ range }) => range),
|
|
2061
|
+
...suppressed.filter(({ range }) => ManuscriptRange.overlaps(range, annotation.range)).map(({ range }) => range),
|
|
2062
|
+
...sourceGlues.filter(({ range }) => ManuscriptRange.overlaps(range, annotation.range)).map(({ range }) => range)
|
|
2063
|
+
]);
|
|
1607
2064
|
}
|
|
1608
|
-
function annotationFragments(graphemes, suppressed, annotations, settings, measurer, fragmentPlansByAnnotation) {
|
|
2065
|
+
function annotationFragments(graphemes, suppressed, sourceGlues, annotations, settings, measurer, fragmentPlansByAnnotation) {
|
|
1609
2066
|
const fragments = [];
|
|
1610
2067
|
for (const annotation of annotations) {
|
|
1611
2068
|
const covered = graphemes.filter(({ range }) => ManuscriptRange.overlaps(range, annotation.range));
|
|
1612
|
-
const fragmentRange = annotationFragmentRange(graphemes, suppressed, annotation);
|
|
2069
|
+
const fragmentRange = annotationFragmentRange(graphemes, suppressed, sourceGlues, annotation);
|
|
1613
2070
|
const first = covered[0];
|
|
1614
2071
|
const last = covered.at(-1);
|
|
1615
2072
|
if (fragmentRange === null || first === void 0 || last === void 0) continue;
|
|
@@ -1636,8 +2093,8 @@ function annotationFragments(graphemes, suppressed, annotations, settings, measu
|
|
|
1636
2093
|
case "ruby": {
|
|
1637
2094
|
const fragmentPlan = fragmentPlansByAnnotation.get(annotation);
|
|
1638
2095
|
if (fragmentPlan === void 0) break;
|
|
1639
|
-
const baseOffsetEm = first.offsetEm;
|
|
1640
|
-
const baseAdvanceEm = last.offsetEm + last.advanceEm - baseOffsetEm;
|
|
2096
|
+
const baseOffsetEm = first.layoutSpan.offsetEm;
|
|
2097
|
+
const baseAdvanceEm = last.layoutSpan.offsetEm + last.layoutSpan.advanceEm - baseOffsetEm;
|
|
1641
2098
|
const reading = readingForFragment(annotation, fragmentRange, fragmentPlan);
|
|
1642
2099
|
fragments.push({
|
|
1643
2100
|
kind: "ruby",
|
|
@@ -1654,88 +2111,103 @@ function annotationFragments(graphemes, suppressed, annotations, settings, measu
|
|
|
1654
2111
|
}
|
|
1655
2112
|
return fragments;
|
|
1656
2113
|
}
|
|
1657
|
-
function positionedLine(atoms,
|
|
2114
|
+
function positionedLine(atoms, sourceGapIndexes, plan) {
|
|
2115
|
+
const items = plan.suppressedIndexes.flatMap((index) => {
|
|
2116
|
+
const atom = atoms[index];
|
|
2117
|
+
return atom === void 0 ? [] : [{
|
|
2118
|
+
kind: "suppressed",
|
|
2119
|
+
value: atom.grapheme.value,
|
|
2120
|
+
range: atom.grapheme.range,
|
|
2121
|
+
reason: "question-or-exclamation-gap"
|
|
2122
|
+
}];
|
|
2123
|
+
});
|
|
2124
|
+
const spacings = new Map(plan.pairSpacings.map((spacing) => [spacing.boundary, spacing]));
|
|
1658
2125
|
let offsetEm = 0;
|
|
1659
|
-
const
|
|
1660
|
-
|
|
1661
|
-
kind: "
|
|
1662
|
-
value: grapheme.value,
|
|
1663
|
-
range: grapheme.range,
|
|
2126
|
+
const positionSpacing = (spacing) => {
|
|
2127
|
+
if (spacing.kind === "kern") items.push({
|
|
2128
|
+
kind: "kern",
|
|
1664
2129
|
offsetEm,
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
suppressed,
|
|
1677
|
-
annotations: []
|
|
2130
|
+
widthEm: spacing.widthEm
|
|
2131
|
+
});
|
|
2132
|
+
else items.push({
|
|
2133
|
+
kind: "glue",
|
|
2134
|
+
origin: "generated",
|
|
2135
|
+
offsetEm,
|
|
2136
|
+
widthEm: spacing.widthEm,
|
|
2137
|
+
naturalWidthEm: spacing.naturalWidthEm,
|
|
2138
|
+
adjustment: spacing.widthEm < spacing.naturalWidthEm ? "shrunk" : spacing.widthEm > spacing.naturalWidthEm ? "stretched" : "natural"
|
|
2139
|
+
});
|
|
2140
|
+
offsetEm += spacing.widthEm;
|
|
1678
2141
|
};
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
if (atom !== void 0) suppressed.push({
|
|
1689
|
-
kind: "suppressed",
|
|
2142
|
+
for (let index = plan.contentStart; index < plan.end; index += 1) {
|
|
2143
|
+
const spacing = spacings.get(index);
|
|
2144
|
+
if (spacing !== void 0) positionSpacing(spacing);
|
|
2145
|
+
const atom = atoms[index];
|
|
2146
|
+
if (atom === void 0) continue;
|
|
2147
|
+
if (sourceGapIndexes.has(index)) {
|
|
2148
|
+
items.push({
|
|
2149
|
+
kind: "glue",
|
|
2150
|
+
origin: "source",
|
|
1690
2151
|
value: atom.grapheme.value,
|
|
1691
2152
|
range: atom.grapheme.range,
|
|
1692
|
-
|
|
2153
|
+
offsetEm,
|
|
2154
|
+
widthEm: atom.boxAdvanceEm,
|
|
2155
|
+
naturalWidthEm: atom.boxAdvanceEm,
|
|
2156
|
+
adjustment: "natural"
|
|
1693
2157
|
});
|
|
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;
|
|
2158
|
+
offsetEm += atom.boxAdvanceEm;
|
|
2159
|
+
continue;
|
|
1724
2160
|
}
|
|
1725
|
-
|
|
1726
|
-
const
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
2161
|
+
const hanging = plan.hangingIndex === index;
|
|
2162
|
+
const layoutAdvanceEm = hanging ? 0 : atom.boxAdvanceEm;
|
|
2163
|
+
items.push({
|
|
2164
|
+
kind: "glyph",
|
|
2165
|
+
value: atom.grapheme.value,
|
|
2166
|
+
range: atom.grapheme.range,
|
|
2167
|
+
layoutSpan: {
|
|
2168
|
+
offsetEm,
|
|
2169
|
+
advanceEm: layoutAdvanceEm
|
|
2170
|
+
},
|
|
2171
|
+
renderSpan: {
|
|
2172
|
+
offsetEm: offsetEm + atom.renderOffsetEm,
|
|
2173
|
+
advanceEm: atom.renderAdvanceEm
|
|
2174
|
+
},
|
|
2175
|
+
disposition: hanging ? "hanging" : "placed",
|
|
2176
|
+
presentation: atom.presentation
|
|
2177
|
+
});
|
|
2178
|
+
offsetEm += layoutAdvanceEm;
|
|
1735
2179
|
}
|
|
2180
|
+
const trailingSpacing = spacings.get(plan.end);
|
|
2181
|
+
if (trailingSpacing !== void 0) positionSpacing(trailingSpacing);
|
|
2182
|
+
const ranges = items.flatMap((item) => item.kind === "glyph" || item.kind === "suppressed" || item.kind === "glue" && item.origin === "source" ? [item.range] : []);
|
|
2183
|
+
return {
|
|
2184
|
+
range: ManuscriptRange.merge(ranges),
|
|
2185
|
+
inlineSizeEm: Math.max(0, plan.inlineSizeEm),
|
|
2186
|
+
items,
|
|
2187
|
+
break: plan.break,
|
|
2188
|
+
annotations: []
|
|
2189
|
+
};
|
|
2190
|
+
}
|
|
2191
|
+
function wrapSourceLine(sourceLine, annotations, settings, measurer, baseAdvances) {
|
|
2192
|
+
if (sourceLine.atoms.length === 0) return [NovelLine.empty()];
|
|
2193
|
+
const profile = TYPESETTING_PROFILE;
|
|
2194
|
+
const lines = layoutParagraph(sourceLine.atoms.map(({ grapheme, boxAdvanceEm, presentation }, index) => {
|
|
2195
|
+
const current = sourceLine.atoms[index];
|
|
2196
|
+
const next = sourceLine.atoms[index + 1];
|
|
2197
|
+
return {
|
|
2198
|
+
value: grapheme.value,
|
|
2199
|
+
boxAdvanceEm,
|
|
2200
|
+
sourceGap: sourceLine.suppressedIndexes.has(index),
|
|
2201
|
+
characterClass: profile.classify({
|
|
2202
|
+
value: grapheme.value,
|
|
2203
|
+
presentation: presentation.kind
|
|
2204
|
+
}),
|
|
2205
|
+
pairSpacingAfter: current === void 0 || next === void 0 || !samePresentationGroup(current, next) && !breakInsideFittableGroupRuby(sourceLine.atoms, annotations, index + 1, settings.flow.lineLengthEm)
|
|
2206
|
+
};
|
|
2207
|
+
}), 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
2208
|
const fragmentPlansByAnnotation = new Map(annotations.map((annotation) => {
|
|
1737
2209
|
const ranges = lines.flatMap((line) => {
|
|
1738
|
-
const range = annotationFragmentRange(line.
|
|
2210
|
+
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
2211
|
return range === null ? [] : [range];
|
|
1740
2212
|
});
|
|
1741
2213
|
return [annotation, {
|
|
@@ -1745,10 +2217,10 @@ function wrapSourceLine(sourceLine, annotations, settings, measurer, baseAdvance
|
|
|
1745
2217
|
}));
|
|
1746
2218
|
return lines.map((line) => ({
|
|
1747
2219
|
range: line.range,
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
annotations: annotationFragments(line.
|
|
2220
|
+
inlineSizeEm: line.inlineSizeEm,
|
|
2221
|
+
items: line.items,
|
|
2222
|
+
break: line.break,
|
|
2223
|
+
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
2224
|
}));
|
|
1753
2225
|
}
|
|
1754
2226
|
function blankLines(count) {
|
|
@@ -1789,7 +2261,7 @@ function createCompose(measurer) {
|
|
|
1789
2261
|
const measured = sourceLines.map((line) => measureSourceLine(line, manuscript.annotations, settings, measurer));
|
|
1790
2262
|
if (measured.some((line) => line === void 0)) return ManuscriptResult.fail({ reason: "inline measurer returned a negative or non-finite value" });
|
|
1791
2263
|
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.
|
|
2264
|
+
for (const line of measured) for (const atom of line?.atoms ?? []) baseAdvances.set(atom.grapheme.range.graphemes.start, atom.boxAdvanceEm);
|
|
1793
2265
|
const manuscriptLines = measured.flatMap((line) => line === void 0 ? [] : wrapSourceLine(line, manuscript.annotations, settings, measurer, baseAdvances));
|
|
1794
2266
|
const pages = buildPages([
|
|
1795
2267
|
...blankLines(settings.offsets.document.leading),
|
|
@@ -2561,4 +3033,4 @@ function createDefaultProofreadingRules() {
|
|
|
2561
3033
|
];
|
|
2562
3034
|
}
|
|
2563
3035
|
//#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,
|
|
3036
|
+
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 };
|