@sushichan044/kg-core 0.7.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +38 -4
- package/dist/{composed-manuscript-QgIdae8p.d.mts → composed-manuscript-C0f3mxKs.d.mts} +52 -52
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +1 -1
- package/dist/lint.d.mts +1 -1
- package/dist/{novel-composer-Cl6LJXOA.mjs → novel-composer-CtovfhET.mjs} +454 -108
- package/dist/{novel-composer-BCt9g172.d.mts → novel-composer-DhycV9B3.d.mts} +1 -1
- package/dist/plugin.d.mts +2 -2
- package/dist/plugin.mjs +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -84,18 +84,52 @@ a closing bracket, comma, or full stop, a quarter em on either side of a middle
|
|
|
84
84
|
a quarter em between Japanese and a numeral, unit symbol, or Western character, and
|
|
85
85
|
solid where two of those punctuation marks meet.
|
|
86
86
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
87
|
+
A western word space is not a character with a box but an アキ of its own: a third em
|
|
88
|
+
by rule rather than by measurement, nothing at all at a line head or a line end, and
|
|
89
|
+
the first _visible_ space either direction of the adjustment reaches for — only the
|
|
90
|
+
line-end space, which the reader cannot see at all, is spent ahead of it. It leaves the
|
|
91
|
+
composer as a `glue` item carrying the source text it came from.
|
|
92
|
+
|
|
93
|
+
An overflowing line is answered by reducing the space between characters, stage by
|
|
94
|
+
stage, or by hanging a comma or full stop; a short line is answered by opening up space
|
|
95
|
+
where JLReq 3.8.4 and appendix 表6
|
|
96
|
+
allow it — the word space first, up to a half, then the Japanese-to-Western quarter em,
|
|
97
|
+
also up to a half, then a solid pair between kana, kanji, and the marks that go with
|
|
98
|
+
them, up to a quarter. Space around
|
|
99
|
+
brackets, commas, full stops, middle dots, hyphens, and an ideographic space is never
|
|
100
|
+
opened up. A line that admits none of these is forced. The choice is made for the
|
|
101
|
+
paragraph rather than line by line,
|
|
90
102
|
minimizing in order the number of forced lines, then stretched, then hanging, then
|
|
91
103
|
shrunk lines that spent visible space. Which space may be reduced, and in which
|
|
92
104
|
order, follows JLReq 3.8.3 and appendix 表3: the space at the line end goes first and
|
|
93
105
|
costs the paragraph nothing, because it is invisible once that character lands there;
|
|
94
|
-
then a middle dot's quarters, then the half
|
|
106
|
+
then the word space, down to a quarter em; then a middle dot's quarters, then the half
|
|
107
|
+
em before an opening bracket or after a
|
|
95
108
|
closing bracket or comma, then the Japanese-to-Western quarter down to an eighth. The
|
|
96
109
|
half em after a mid-line full stop marks the end of a sentence and is never reduced,
|
|
97
110
|
and neither is the half em that keeps a line-head opening bracket off the edge.
|
|
98
111
|
|
|
112
|
+
JLReq puts the word space ahead of the line end; the composer swaps the two, because
|
|
113
|
+
a stage here says how visible an adjustment is and the line end is the only space the
|
|
114
|
+
reader cannot see at all.
|
|
115
|
+
|
|
116
|
+
The アキ at a line end is the one exception to spending a space in part. JLReq 3.1.9
|
|
117
|
+
admits the full アキ or ベタ組 and forbids anything between, so a line whose overflow is
|
|
118
|
+
smaller than it leaves it whole and finds the difference elsewhere — or breaks
|
|
119
|
+
elsewhere. A 中点類 at a line end gives up the quarter em before it together with the
|
|
120
|
+
quarter after, as one amount.
|
|
121
|
+
|
|
122
|
+
Every stage is spent 文字サイズ比で均等に, as JLReq 3.8.3 and 3.8.4 both put it: the amount
|
|
123
|
+
is split across all the spaces of that stage in proportion to what each of them can
|
|
124
|
+
give, rather than taken out of the earliest space until it runs dry. A stage opens
|
|
125
|
+
only once every stage before it is spent out.
|
|
126
|
+
|
|
127
|
+
JLReq 3.1.5 states the white before a line-head opening bracket as a pair of amounts:
|
|
128
|
+
one for 改行行頭, the head of a line that starts a paragraph, and one for 折返し行頭, the
|
|
129
|
+
head of a line the composer turned over. The specification lists three such pairs, and
|
|
130
|
+
the composer takes the one Japanese novels are set with — scheme ③: a half em at
|
|
131
|
+
改行行頭, and 天付き, flush against the edge, at 折返し行頭.
|
|
132
|
+
|
|
99
133
|
The composer also owns Japanese line-start and line-end restrictions, inseparable
|
|
100
134
|
punctuation, question/exclamation gap suppression, and ruby placement.
|
|
101
135
|
|
|
@@ -914,7 +914,7 @@ declare const ParsedManuscriptSchema: v.SchemaWithPipe<readonly [v.StrictObjectS
|
|
|
914
914
|
}>;
|
|
915
915
|
mark: string;
|
|
916
916
|
}>]>], undefined>, undefined>, v.ReadonlyAction<(Readonly<{
|
|
917
|
-
kind: "
|
|
917
|
+
kind: "italic";
|
|
918
918
|
readonly range: Readonly<{
|
|
919
919
|
source: Readonly<{
|
|
920
920
|
start: number;
|
|
@@ -929,18 +929,8 @@ declare const ParsedManuscriptSchema: v.SchemaWithPipe<readonly [v.StrictObjectS
|
|
|
929
929
|
end: number;
|
|
930
930
|
}> & v.Brand<"GraphemeRange">;
|
|
931
931
|
}>;
|
|
932
|
-
reading: Readonly<{
|
|
933
|
-
kind: "group";
|
|
934
|
-
text: string;
|
|
935
|
-
}> | Readonly<{
|
|
936
|
-
kind: "mono";
|
|
937
|
-
readonly segments: readonly string[];
|
|
938
|
-
}> | Readonly<{
|
|
939
|
-
kind: "jukugo";
|
|
940
|
-
readonly segments: readonly string[];
|
|
941
|
-
}>;
|
|
942
932
|
}> | Readonly<{
|
|
943
|
-
kind: "
|
|
933
|
+
kind: "ruby";
|
|
944
934
|
readonly range: Readonly<{
|
|
945
935
|
source: Readonly<{
|
|
946
936
|
start: number;
|
|
@@ -955,8 +945,18 @@ declare const ParsedManuscriptSchema: v.SchemaWithPipe<readonly [v.StrictObjectS
|
|
|
955
945
|
end: number;
|
|
956
946
|
}> & v.Brand<"GraphemeRange">;
|
|
957
947
|
}>;
|
|
948
|
+
reading: Readonly<{
|
|
949
|
+
kind: "group";
|
|
950
|
+
text: string;
|
|
951
|
+
}> | Readonly<{
|
|
952
|
+
kind: "mono";
|
|
953
|
+
readonly segments: readonly string[];
|
|
954
|
+
}> | Readonly<{
|
|
955
|
+
kind: "jukugo";
|
|
956
|
+
readonly segments: readonly string[];
|
|
957
|
+
}>;
|
|
958
958
|
}> | Readonly<{
|
|
959
|
-
kind: "
|
|
959
|
+
kind: "bold";
|
|
960
960
|
readonly range: Readonly<{
|
|
961
961
|
source: Readonly<{
|
|
962
962
|
start: number;
|
|
@@ -1010,7 +1010,7 @@ declare const ParsedManuscriptSchema: v.SchemaWithPipe<readonly [v.StrictObjectS
|
|
|
1010
1010
|
}>;
|
|
1011
1011
|
}>[];
|
|
1012
1012
|
readonly annotations: readonly (Readonly<{
|
|
1013
|
-
kind: "
|
|
1013
|
+
kind: "italic";
|
|
1014
1014
|
readonly range: Readonly<{
|
|
1015
1015
|
source: Readonly<{
|
|
1016
1016
|
start: number;
|
|
@@ -1025,18 +1025,8 @@ declare const ParsedManuscriptSchema: v.SchemaWithPipe<readonly [v.StrictObjectS
|
|
|
1025
1025
|
end: number;
|
|
1026
1026
|
}> & v.Brand<"GraphemeRange">;
|
|
1027
1027
|
}>;
|
|
1028
|
-
reading: Readonly<{
|
|
1029
|
-
kind: "group";
|
|
1030
|
-
text: string;
|
|
1031
|
-
}> | Readonly<{
|
|
1032
|
-
kind: "mono";
|
|
1033
|
-
readonly segments: readonly string[];
|
|
1034
|
-
}> | Readonly<{
|
|
1035
|
-
kind: "jukugo";
|
|
1036
|
-
readonly segments: readonly string[];
|
|
1037
|
-
}>;
|
|
1038
1028
|
}> | Readonly<{
|
|
1039
|
-
kind: "
|
|
1029
|
+
kind: "ruby";
|
|
1040
1030
|
readonly range: Readonly<{
|
|
1041
1031
|
source: Readonly<{
|
|
1042
1032
|
start: number;
|
|
@@ -1051,8 +1041,18 @@ declare const ParsedManuscriptSchema: v.SchemaWithPipe<readonly [v.StrictObjectS
|
|
|
1051
1041
|
end: number;
|
|
1052
1042
|
}> & v.Brand<"GraphemeRange">;
|
|
1053
1043
|
}>;
|
|
1044
|
+
reading: Readonly<{
|
|
1045
|
+
kind: "group";
|
|
1046
|
+
text: string;
|
|
1047
|
+
}> | Readonly<{
|
|
1048
|
+
kind: "mono";
|
|
1049
|
+
readonly segments: readonly string[];
|
|
1050
|
+
}> | Readonly<{
|
|
1051
|
+
kind: "jukugo";
|
|
1052
|
+
readonly segments: readonly string[];
|
|
1053
|
+
}>;
|
|
1054
1054
|
}> | Readonly<{
|
|
1055
|
-
kind: "
|
|
1055
|
+
kind: "bold";
|
|
1056
1056
|
readonly range: Readonly<{
|
|
1057
1057
|
source: Readonly<{
|
|
1058
1058
|
start: number;
|
|
@@ -1503,7 +1503,7 @@ declare const ParsedManuscript: {
|
|
|
1503
1503
|
}>;
|
|
1504
1504
|
mark: string;
|
|
1505
1505
|
}>]>], undefined>, undefined>, v.ReadonlyAction<(Readonly<{
|
|
1506
|
-
kind: "
|
|
1506
|
+
kind: "italic";
|
|
1507
1507
|
readonly range: Readonly<{
|
|
1508
1508
|
source: Readonly<{
|
|
1509
1509
|
start: number;
|
|
@@ -1518,18 +1518,8 @@ declare const ParsedManuscript: {
|
|
|
1518
1518
|
end: number;
|
|
1519
1519
|
}> & v.Brand<"GraphemeRange">;
|
|
1520
1520
|
}>;
|
|
1521
|
-
reading: Readonly<{
|
|
1522
|
-
kind: "group";
|
|
1523
|
-
text: string;
|
|
1524
|
-
}> | Readonly<{
|
|
1525
|
-
kind: "mono";
|
|
1526
|
-
readonly segments: readonly string[];
|
|
1527
|
-
}> | Readonly<{
|
|
1528
|
-
kind: "jukugo";
|
|
1529
|
-
readonly segments: readonly string[];
|
|
1530
|
-
}>;
|
|
1531
1521
|
}> | Readonly<{
|
|
1532
|
-
kind: "
|
|
1522
|
+
kind: "ruby";
|
|
1533
1523
|
readonly range: Readonly<{
|
|
1534
1524
|
source: Readonly<{
|
|
1535
1525
|
start: number;
|
|
@@ -1544,8 +1534,18 @@ declare const ParsedManuscript: {
|
|
|
1544
1534
|
end: number;
|
|
1545
1535
|
}> & v.Brand<"GraphemeRange">;
|
|
1546
1536
|
}>;
|
|
1537
|
+
reading: Readonly<{
|
|
1538
|
+
kind: "group";
|
|
1539
|
+
text: string;
|
|
1540
|
+
}> | Readonly<{
|
|
1541
|
+
kind: "mono";
|
|
1542
|
+
readonly segments: readonly string[];
|
|
1543
|
+
}> | Readonly<{
|
|
1544
|
+
kind: "jukugo";
|
|
1545
|
+
readonly segments: readonly string[];
|
|
1546
|
+
}>;
|
|
1547
1547
|
}> | Readonly<{
|
|
1548
|
-
kind: "
|
|
1548
|
+
kind: "bold";
|
|
1549
1549
|
readonly range: Readonly<{
|
|
1550
1550
|
source: Readonly<{
|
|
1551
1551
|
start: number;
|
|
@@ -1599,7 +1599,7 @@ declare const ParsedManuscript: {
|
|
|
1599
1599
|
}>;
|
|
1600
1600
|
}>[];
|
|
1601
1601
|
readonly annotations: readonly (Readonly<{
|
|
1602
|
-
kind: "
|
|
1602
|
+
kind: "italic";
|
|
1603
1603
|
readonly range: Readonly<{
|
|
1604
1604
|
source: Readonly<{
|
|
1605
1605
|
start: number;
|
|
@@ -1614,18 +1614,8 @@ declare const ParsedManuscript: {
|
|
|
1614
1614
|
end: number;
|
|
1615
1615
|
}> & v.Brand<"GraphemeRange">;
|
|
1616
1616
|
}>;
|
|
1617
|
-
reading: Readonly<{
|
|
1618
|
-
kind: "group";
|
|
1619
|
-
text: string;
|
|
1620
|
-
}> | Readonly<{
|
|
1621
|
-
kind: "mono";
|
|
1622
|
-
readonly segments: readonly string[];
|
|
1623
|
-
}> | Readonly<{
|
|
1624
|
-
kind: "jukugo";
|
|
1625
|
-
readonly segments: readonly string[];
|
|
1626
|
-
}>;
|
|
1627
1617
|
}> | Readonly<{
|
|
1628
|
-
kind: "
|
|
1618
|
+
kind: "ruby";
|
|
1629
1619
|
readonly range: Readonly<{
|
|
1630
1620
|
source: Readonly<{
|
|
1631
1621
|
start: number;
|
|
@@ -1640,8 +1630,18 @@ declare const ParsedManuscript: {
|
|
|
1640
1630
|
end: number;
|
|
1641
1631
|
}> & v.Brand<"GraphemeRange">;
|
|
1642
1632
|
}>;
|
|
1633
|
+
reading: Readonly<{
|
|
1634
|
+
kind: "group";
|
|
1635
|
+
text: string;
|
|
1636
|
+
}> | Readonly<{
|
|
1637
|
+
kind: "mono";
|
|
1638
|
+
readonly segments: readonly string[];
|
|
1639
|
+
}> | Readonly<{
|
|
1640
|
+
kind: "jukugo";
|
|
1641
|
+
readonly segments: readonly string[];
|
|
1642
|
+
}>;
|
|
1643
1643
|
}> | Readonly<{
|
|
1644
|
-
kind: "
|
|
1644
|
+
kind: "bold";
|
|
1645
1645
|
readonly range: Readonly<{
|
|
1646
1646
|
source: Readonly<{
|
|
1647
1647
|
start: number;
|
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { a as ManuscriptResult, c as DiagnosticSeverity, d as ManuscriptRangeInput, i as ParsedManuscript, l as DiagnosticOrigin, n as ValidationIssue, o as ManuscriptDiagnostic, r as NamespacedId, s as ManuscriptDiagnosticInput, t as ComposedManuscript, u as ManuscriptRange } from "./composed-manuscript-
|
|
2
|
-
import { d as NovelCompositionSettings, f as ManuscriptComposer, i as NovelLayout, l as VerticalTextPresentation, p as ManuscriptParser, r as novelComposer, t as NovelComposedManuscript, u as FontPresetId } from "./novel-composer-
|
|
1
|
+
import { a as ManuscriptResult, c as DiagnosticSeverity, d as ManuscriptRangeInput, i as ParsedManuscript, l as DiagnosticOrigin, n as ValidationIssue, o as ManuscriptDiagnostic, r as NamespacedId, s as ManuscriptDiagnosticInput, t as ComposedManuscript, u as ManuscriptRange } from "./composed-manuscript-C0f3mxKs.mjs";
|
|
2
|
+
import { d as NovelCompositionSettings, f as ManuscriptComposer, i as NovelLayout, l as VerticalTextPresentation, p as ManuscriptParser, r as novelComposer, t as NovelComposedManuscript, u as FontPresetId } from "./novel-composer-DhycV9B3.mjs";
|
|
3
3
|
import * as v from "valibot";
|
|
4
4
|
//#region src/parser/parse-error.d.ts
|
|
5
5
|
/**
|
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { c as ManuscriptResult, d as NamespacedId, f as ManuscriptRange, h as readonlyObject, l as ManuscriptDiagnostic, o as graphemeSegmenter, s as ValidationIssue } from "./question-or-exclamation-spacing-B5o23ZLf.mjs";
|
|
2
|
-
import { c as ManuscriptOffsets, d as FontSizePt, f as FontPresetId, i as PaperSize, l as ManuscriptAppearanceSettings, n as novelComposer, o as NovelCompositionSettings, p as ParsedManuscript, r as ManuscriptGeometry, s as NovelFlowSettings, u as PaperSizeId } from "./novel-composer-
|
|
2
|
+
import { c as ManuscriptOffsets, d as FontSizePt, f as FontPresetId, i as PaperSize, l as ManuscriptAppearanceSettings, n as novelComposer, o as NovelCompositionSettings, p as ParsedManuscript, r as ManuscriptGeometry, s as NovelFlowSettings, u as PaperSizeId } from "./novel-composer-CtovfhET.mjs";
|
|
3
3
|
import { n as assertNever, t as describeCause } from "./describe-cause-BV8mZ3Um.mjs";
|
|
4
4
|
import * as v from "valibot";
|
|
5
5
|
const Rejection = { schema: readonlyObject({ reason: v.string() }) };
|
package/dist/lint.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as ManuscriptResult, i as ParsedManuscript, n as ValidationIssue, o as ManuscriptDiagnostic, r as NamespacedId, t as ComposedManuscript } from "./composed-manuscript-
|
|
1
|
+
import { a as ManuscriptResult, i as ParsedManuscript, n as ValidationIssue, o as ManuscriptDiagnostic, r as NamespacedId, t as ComposedManuscript } from "./composed-manuscript-C0f3mxKs.mjs";
|
|
2
2
|
import * as v from "valibot";
|
|
3
3
|
//#region src/proofreading/invalid-rule-options.d.ts
|
|
4
4
|
/**
|
|
@@ -337,14 +337,17 @@ function classify({ value, presentation }) {
|
|
|
337
337
|
* The stages JLReq 3.8.3 spends inter-character space in, as glue priorities: the lower the number,
|
|
338
338
|
* the earlier the paragraph optimizer takes it.
|
|
339
339
|
*
|
|
340
|
-
* JLReq
|
|
341
|
-
*
|
|
340
|
+
* JLReq puts the western word space first and the line-end reduction second; this profile swaps the
|
|
341
|
+
* two, because a priority here says how visible an adjustment is rather than where 3.8.3's list
|
|
342
|
+
* puts it. Priority 0 is capacity the paragraph optimizer spends for nothing, and a line that spent
|
|
343
|
+
* only priority 0 reads as tightly set as an unadjusted one. That is true of the line end and of
|
|
344
|
+
* nothing else: the half em after a closing bracket, full stop or comma — and the quarter em after
|
|
345
|
+
* a middle dot — falls outside the text area once that character sits at the line end, so taking it
|
|
346
|
+
* changes nothing on the page (3.1.9). A 欧文間隔 sits between two words the reader is looking at, so
|
|
347
|
+
* narrowing it does change the page. Spending the invisible space first can only leave the word
|
|
348
|
+
* spaces nearer their 三分アキ than JLReq's order would, never further from it.
|
|
342
349
|
*
|
|
343
|
-
*
|
|
344
|
-
* what allows that: the half em after a closing bracket, full stop or comma — and the quarter em
|
|
345
|
-
* after a middle dot — cannot be seen once that character sits at the line end. JLReq orders it
|
|
346
|
-
* second rather than first, but only after the word space this profile does not adjust at all.
|
|
347
|
-
* Stages two and three are one priority here because a line has a single line end, so the two never
|
|
350
|
+
* Stages two and three are one priority because a line has a single line end, so the two never
|
|
348
351
|
* compete.
|
|
349
352
|
*/
|
|
350
353
|
const REDUCTION_STAGE = {
|
|
@@ -353,27 +356,65 @@ const REDUCTION_STAGE = {
|
|
|
353
356
|
*/
|
|
354
357
|
lineEnd: 0,
|
|
355
358
|
/**
|
|
359
|
+
* Stage 1: the western word space, from its 三分アキ down to a 四分アキ.
|
|
360
|
+
*/
|
|
361
|
+
wordSpace: 1,
|
|
362
|
+
/**
|
|
356
363
|
* Stage 4: the quarter em on either side of a mid-line middle dot.
|
|
357
364
|
*/
|
|
358
|
-
middleDot:
|
|
365
|
+
middleDot: 2,
|
|
359
366
|
/**
|
|
360
367
|
* Stage 5: the half em before an opening bracket and after a closing bracket or comma.
|
|
361
368
|
*/
|
|
362
|
-
punctuation:
|
|
369
|
+
punctuation: 3,
|
|
363
370
|
/**
|
|
364
371
|
* Stage 6: the quarter em between Japanese and a numeral, unit symbol or western character.
|
|
365
372
|
*/
|
|
366
|
-
mixedText:
|
|
373
|
+
mixedText: 4
|
|
367
374
|
};
|
|
368
375
|
/**
|
|
369
|
-
*
|
|
376
|
+
* The stages JLReq 3.8.4 adds inter-character space in, as glue priorities on the same reading as
|
|
377
|
+
* {@link REDUCTION_STAGE}: the lower the number, the earlier the paragraph optimizer takes it. A
|
|
378
|
+
* line is either reduced or expanded, never both, so the two scales never meet.
|
|
379
|
+
*
|
|
380
|
+
* Stage 4 spreads whatever a、b and c could not absorb across every character gap that is not
|
|
381
|
+
* unbreakable, and is deliberately not implemented: it would justify every line that currently
|
|
382
|
+
* falls short, which reads as prose combed out to the margin rather than a novel.
|
|
383
|
+
*
|
|
384
|
+
* Nothing here takes priority 0. Expanding is always visible, and priority 0 means an adjustment
|
|
385
|
+
* the optimizer is charged nothing for.
|
|
386
|
+
*/
|
|
387
|
+
const EXPANSION_STAGE = {
|
|
388
|
+
/**
|
|
389
|
+
* Stage 1: the western word space, from its 三分アキ up to a 二分アキ.
|
|
390
|
+
*/
|
|
391
|
+
wordSpace: 1,
|
|
392
|
+
/**
|
|
393
|
+
* Stage 2: the quarter em between Japanese and a numeral, unit symbol or western character.
|
|
394
|
+
*/
|
|
395
|
+
mixedText: 2,
|
|
396
|
+
/**
|
|
397
|
+
* Stage 3: everywhere else 表6 admits, from solid up to a quarter em.
|
|
398
|
+
*/
|
|
399
|
+
solid: 3
|
|
400
|
+
};
|
|
401
|
+
/**
|
|
402
|
+
* 無印 in 表1 with no colour in 表6: the pair is set solid and line adjustment leaves it alone.
|
|
370
403
|
*/
|
|
371
404
|
const SOLID = {
|
|
405
|
+
kind: "glue",
|
|
406
|
+
naturalWidthEm: 0
|
|
407
|
+
};
|
|
408
|
+
/**
|
|
409
|
+
* `1/4` in 表6: set solid, but line adjustment may open it up to a quarter em.
|
|
410
|
+
*/
|
|
411
|
+
const SOLID_EXPANDABLE = {
|
|
372
412
|
kind: "glue",
|
|
373
413
|
naturalWidthEm: 0,
|
|
374
414
|
stretch: {
|
|
375
|
-
priority:
|
|
376
|
-
amountEm: .25
|
|
415
|
+
priority: EXPANSION_STAGE.solid,
|
|
416
|
+
amountEm: .25,
|
|
417
|
+
granularity: "continuous"
|
|
377
418
|
}
|
|
378
419
|
};
|
|
379
420
|
/**
|
|
@@ -384,7 +425,8 @@ const HALF = {
|
|
|
384
425
|
naturalWidthEm: .5,
|
|
385
426
|
shrink: {
|
|
386
427
|
priority: REDUCTION_STAGE.punctuation,
|
|
387
|
-
amountEm: .5
|
|
428
|
+
amountEm: .5,
|
|
429
|
+
granularity: "continuous"
|
|
388
430
|
}
|
|
389
431
|
};
|
|
390
432
|
/**
|
|
@@ -402,7 +444,8 @@ const QUARTER = {
|
|
|
402
444
|
naturalWidthEm: .25,
|
|
403
445
|
shrink: {
|
|
404
446
|
priority: REDUCTION_STAGE.middleDot,
|
|
405
|
-
amountEm: .25
|
|
447
|
+
amountEm: .25,
|
|
448
|
+
granularity: "continuous"
|
|
406
449
|
}
|
|
407
450
|
};
|
|
408
451
|
/**
|
|
@@ -413,14 +456,34 @@ const FIXED_QUARTER = {
|
|
|
413
456
|
naturalWidthEm: .25
|
|
414
457
|
};
|
|
415
458
|
/**
|
|
416
|
-
* `1/4-1/8` in 表3: the Japanese-to-western quarter em, reducible to an eighth
|
|
459
|
+
* `1/4-1/8` in 表3 with `1/4-1/2` in 表6: the Japanese-to-western quarter em, reducible to an eighth
|
|
460
|
+
* and expandable to a half.
|
|
417
461
|
*/
|
|
418
462
|
const MIXED_TEXT_QUARTER = {
|
|
419
463
|
kind: "glue",
|
|
420
464
|
naturalWidthEm: .25,
|
|
421
465
|
shrink: {
|
|
422
466
|
priority: REDUCTION_STAGE.mixedText,
|
|
423
|
-
amountEm: .125
|
|
467
|
+
amountEm: .125,
|
|
468
|
+
granularity: "continuous"
|
|
469
|
+
},
|
|
470
|
+
stretch: {
|
|
471
|
+
priority: EXPANSION_STAGE.mixedText,
|
|
472
|
+
amountEm: .25,
|
|
473
|
+
granularity: "continuous"
|
|
474
|
+
}
|
|
475
|
+
};
|
|
476
|
+
/**
|
|
477
|
+
* The same quarter em where 表6 leaves it out of the expansion: between a unit symbol and the
|
|
478
|
+
* quantity on either side of it.
|
|
479
|
+
*/
|
|
480
|
+
const UNIT_SYMBOL_QUARTER = {
|
|
481
|
+
kind: "glue",
|
|
482
|
+
naturalWidthEm: .25,
|
|
483
|
+
shrink: {
|
|
484
|
+
priority: REDUCTION_STAGE.mixedText,
|
|
485
|
+
amountEm: .125,
|
|
486
|
+
granularity: "continuous"
|
|
424
487
|
}
|
|
425
488
|
};
|
|
426
489
|
/**
|
|
@@ -431,7 +494,8 @@ const TWO_QUARTERS = {
|
|
|
431
494
|
naturalWidthEm: .5,
|
|
432
495
|
shrink: {
|
|
433
496
|
priority: REDUCTION_STAGE.middleDot,
|
|
434
|
-
amountEm: .5
|
|
497
|
+
amountEm: .5,
|
|
498
|
+
granularity: "continuous"
|
|
435
499
|
}
|
|
436
500
|
};
|
|
437
501
|
/**
|
|
@@ -442,7 +506,8 @@ const FIXED_HALF_AND_QUARTER = {
|
|
|
442
506
|
naturalWidthEm: .75,
|
|
443
507
|
shrink: {
|
|
444
508
|
priority: REDUCTION_STAGE.middleDot,
|
|
445
|
-
amountEm: .25
|
|
509
|
+
amountEm: .25,
|
|
510
|
+
granularity: "continuous"
|
|
446
511
|
}
|
|
447
512
|
};
|
|
448
513
|
/**
|
|
@@ -457,7 +522,8 @@ const HALF_AND_QUARTER = {
|
|
|
457
522
|
naturalWidthEm: .75,
|
|
458
523
|
shrink: {
|
|
459
524
|
priority: REDUCTION_STAGE.punctuation,
|
|
460
|
-
amountEm: .75
|
|
525
|
+
amountEm: .75,
|
|
526
|
+
granularity: "continuous"
|
|
461
527
|
}
|
|
462
528
|
};
|
|
463
529
|
/**
|
|
@@ -468,6 +534,40 @@ const DASH_JOINT = {
|
|
|
468
534
|
naturalWidthEm: 0
|
|
469
535
|
};
|
|
470
536
|
/**
|
|
537
|
+
* 欧文間隔(cl-26)の三分アキ. JLReq states the western word space as a third em by rule rather than by
|
|
538
|
+
* measurement, and makes it the first space a line adjustment reaches for: down to a 四分アキ when the
|
|
539
|
+
* line is over (3.8.3 a), up to a 二分アキ when it is short (3.8.4 a).
|
|
540
|
+
*
|
|
541
|
+
* The two capacities are written as differences from the limits rather than as `1/12` and `1/6`,
|
|
542
|
+
* because a third of an em is not representable in binary while the limits JLReq states are: this
|
|
543
|
+
* way `naturalWidthEm - shrink.amountEm` is exactly 0.25 and `naturalWidthEm + stretch.amountEm`
|
|
544
|
+
* exactly 0.5.
|
|
545
|
+
*/
|
|
546
|
+
const WORD_SPACE = {
|
|
547
|
+
kind: "glue",
|
|
548
|
+
naturalWidthEm: 1 / 3,
|
|
549
|
+
shrink: {
|
|
550
|
+
priority: REDUCTION_STAGE.wordSpace,
|
|
551
|
+
amountEm: 1 / 3 - 1 / 4,
|
|
552
|
+
granularity: "continuous"
|
|
553
|
+
},
|
|
554
|
+
stretch: {
|
|
555
|
+
priority: EXPANSION_STAGE.wordSpace,
|
|
556
|
+
amountEm: 1 / 2 - 1 / 3,
|
|
557
|
+
granularity: "continuous"
|
|
558
|
+
}
|
|
559
|
+
};
|
|
560
|
+
/**
|
|
561
|
+
* 表1 注13 と 表3 注4: a word space at a line head or a line end has no width and is no adjustment
|
|
562
|
+
* opportunity, since the white would fall outside the text area where no reader can see it. 注13
|
|
563
|
+
* adds ただし,この部分が移動し,これとは異なる配置になった場合は,欧文間隔(cl-26)の空き量を確保する — which is why the answer is given per
|
|
564
|
+
* candidate line and not once per source line.
|
|
565
|
+
*/
|
|
566
|
+
const EDGE_WORD_SPACE = {
|
|
567
|
+
kind: "glue",
|
|
568
|
+
naturalWidthEm: 0
|
|
569
|
+
};
|
|
570
|
+
/**
|
|
471
571
|
* The classes that take a quarter em against a numeral, unit symbol or western character (3.2.6).
|
|
472
572
|
* Dividing punctuation is on the list in that direction only, because the space before a `?` or `!`
|
|
473
573
|
* is solid (3.1.6).
|
|
@@ -502,6 +602,39 @@ const PUNCTUATION_TAKING_SPACE_AFTER = [
|
|
|
502
602
|
"cl-07"
|
|
503
603
|
];
|
|
504
604
|
/**
|
|
605
|
+
* 表6 の色付きの小間: the classes a solid pair may be opened up between at stage 3. Neither list holds a
|
|
606
|
+
* bracket, a middle dot, a full stop, a comma, a hyphen, an ideographic space or a word space —
|
|
607
|
+
* JLReq opens none of those — and only the left-hand list holds a numeral, unit symbol or western
|
|
608
|
+
* character, since the space on their Japanese side belongs to stage 2 instead.
|
|
609
|
+
*/
|
|
610
|
+
const EXPANDABLE_BEFORE = [
|
|
611
|
+
"cl-08",
|
|
612
|
+
"cl-09",
|
|
613
|
+
"cl-10",
|
|
614
|
+
"cl-11",
|
|
615
|
+
"cl-12",
|
|
616
|
+
"cl-13",
|
|
617
|
+
"cl-15",
|
|
618
|
+
"cl-16",
|
|
619
|
+
"cl-19",
|
|
620
|
+
"cl-24",
|
|
621
|
+
"cl-25",
|
|
622
|
+
"cl-27",
|
|
623
|
+
"cl-30"
|
|
624
|
+
];
|
|
625
|
+
const EXPANDABLE_AFTER = [
|
|
626
|
+
"cl-08",
|
|
627
|
+
"cl-09",
|
|
628
|
+
"cl-10",
|
|
629
|
+
"cl-11",
|
|
630
|
+
"cl-12",
|
|
631
|
+
"cl-13",
|
|
632
|
+
"cl-15",
|
|
633
|
+
"cl-16",
|
|
634
|
+
"cl-19",
|
|
635
|
+
"cl-30"
|
|
636
|
+
];
|
|
637
|
+
/**
|
|
505
638
|
* The classes JLReq 3.1.2 sets on a half em rather than a full one.
|
|
506
639
|
*/
|
|
507
640
|
const HALF_EM_CLASSES = /* @__PURE__ */ new Set([
|
|
@@ -517,6 +650,44 @@ const PAIR_SPACING_TABLE = new Map([
|
|
|
517
650
|
right: "any",
|
|
518
651
|
spacing: SOLID
|
|
519
652
|
},
|
|
653
|
+
{
|
|
654
|
+
left: EXPANDABLE_BEFORE,
|
|
655
|
+
right: EXPANDABLE_AFTER,
|
|
656
|
+
spacing: SOLID_EXPANDABLE
|
|
657
|
+
},
|
|
658
|
+
{
|
|
659
|
+
left: [
|
|
660
|
+
"cl-08",
|
|
661
|
+
"cl-12",
|
|
662
|
+
"cl-13"
|
|
663
|
+
],
|
|
664
|
+
right: NUMERAL_UNIT_OR_WESTERN,
|
|
665
|
+
spacing: SOLID_EXPANDABLE
|
|
666
|
+
},
|
|
667
|
+
{
|
|
668
|
+
left: ["cl-08"],
|
|
669
|
+
right: ["cl-24", "cl-25"],
|
|
670
|
+
spacing: SOLID
|
|
671
|
+
},
|
|
672
|
+
{
|
|
673
|
+
left: ["cl-12"],
|
|
674
|
+
right: ["cl-24"],
|
|
675
|
+
spacing: SOLID
|
|
676
|
+
},
|
|
677
|
+
{
|
|
678
|
+
left: ["cl-24"],
|
|
679
|
+
right: ["cl-13"],
|
|
680
|
+
spacing: SOLID
|
|
681
|
+
},
|
|
682
|
+
{
|
|
683
|
+
left: [
|
|
684
|
+
"cl-09",
|
|
685
|
+
"cl-10",
|
|
686
|
+
"cl-11"
|
|
687
|
+
],
|
|
688
|
+
right: ["cl-08"],
|
|
689
|
+
spacing: SOLID
|
|
690
|
+
},
|
|
520
691
|
{
|
|
521
692
|
left: "any",
|
|
522
693
|
right: ["cl-01"],
|
|
@@ -545,12 +716,12 @@ const PAIR_SPACING_TABLE = new Map([
|
|
|
545
716
|
{
|
|
546
717
|
left: ["cl-25"],
|
|
547
718
|
right: ["cl-24"],
|
|
548
|
-
spacing:
|
|
719
|
+
spacing: UNIT_SYMBOL_QUARTER
|
|
549
720
|
},
|
|
550
721
|
{
|
|
551
722
|
left: ["cl-27"],
|
|
552
723
|
right: ["cl-25"],
|
|
553
|
-
spacing:
|
|
724
|
+
spacing: UNIT_SYMBOL_QUARTER
|
|
554
725
|
},
|
|
555
726
|
{
|
|
556
727
|
left: ["cl-24"],
|
|
@@ -692,6 +863,10 @@ const defaultJapaneseTypesettingProfile = {
|
|
|
692
863
|
* trailing half of its em, a middle dot in the centre, and the rest in the leading half.
|
|
693
864
|
*/
|
|
694
865
|
boxMetrics: (characterClass, measuredAdvanceEm) => {
|
|
866
|
+
if (characterClass === "cl-26") return {
|
|
867
|
+
advanceEm: 0,
|
|
868
|
+
renderOffsetEm: 0
|
|
869
|
+
};
|
|
695
870
|
const advanceEm = HALF_EM_CLASSES.has(characterClass) ? Math.min(.5, measuredAdvanceEm) : measuredAdvanceEm;
|
|
696
871
|
const inkOverhangEm = measuredAdvanceEm - advanceEm;
|
|
697
872
|
return {
|
|
@@ -700,16 +875,18 @@ const defaultJapaneseTypesettingProfile = {
|
|
|
700
875
|
};
|
|
701
876
|
},
|
|
702
877
|
pairSpacing,
|
|
878
|
+
spacingCharacter: (characterClass, position) => characterClass !== "cl-26" ? null : position === "mid-line" ? WORD_SPACE : EDGE_WORD_SPACE,
|
|
703
879
|
/**
|
|
704
|
-
*
|
|
705
|
-
*
|
|
706
|
-
*
|
|
707
|
-
*
|
|
880
|
+
* The white before an opening bracket at a line head. JLReq 3.1.5 pairs 改行行頭 with 折返し行頭 and lists
|
|
881
|
+
* three schemes for the two together: ① 全角アキ and 天付き, ② 全角半アキ and 二分アキ, ③ 二分アキ and 天付き. This
|
|
882
|
+
* profile takes ③, the scheme 講談社, 新潮社, 文藝春秋, 中央公論新社 and 筑摩書房 set novels with; ① is what JIS X
|
|
883
|
+
* 4051 and 岩波書店 use. A line of dialogue therefore starts one half em in when it opens a
|
|
884
|
+
* paragraph, and flush against the edge when the composer turned it over.
|
|
708
885
|
*
|
|
709
886
|
* The white is not shrinkable. 表3 excludes the line head from line adjustment altogether, and
|
|
710
887
|
* shrinking it is exactly what pushed a line-head bracket flush against the edge in #94.
|
|
711
888
|
*/
|
|
712
|
-
lineStartSpacing: (first) => first === "cl-01" ? {
|
|
889
|
+
lineStartSpacing: (first, lineHead) => first === "cl-01" && lineHead === "paragraph-start" ? {
|
|
713
890
|
kind: "glue",
|
|
714
891
|
naturalWidthEm: .5
|
|
715
892
|
} : null,
|
|
@@ -717,21 +894,33 @@ const defaultJapaneseTypesettingProfile = {
|
|
|
717
894
|
* The half em trailing a closing bracket, full stop or comma at the line end, and the quarter em
|
|
718
895
|
* trailing a middle dot there (JLReq 3.1.9). Priority 0: spending it changes nothing the reader
|
|
719
896
|
* can see.
|
|
897
|
+
*
|
|
898
|
+
* Both are all-or-nothing. 3.1.9 admits the full アキ or ベタ組 and forbids anything in between, so a
|
|
899
|
+
* line whose overflow is smaller than the アキ has to find the difference somewhere else — or break
|
|
900
|
+
* elsewhere. A line-end middle dot takes the quarter before it into the same amount (表3 注5).
|
|
720
901
|
*/
|
|
721
902
|
lineEndSpacing: (last) => last === "cl-02" || last === "cl-06" || last === "cl-07" ? {
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
903
|
+
spacing: {
|
|
904
|
+
kind: "glue",
|
|
905
|
+
naturalWidthEm: .5,
|
|
906
|
+
shrink: {
|
|
907
|
+
priority: REDUCTION_STAGE.lineEnd,
|
|
908
|
+
amountEm: .5,
|
|
909
|
+
granularity: "all-or-nothing"
|
|
910
|
+
}
|
|
911
|
+
},
|
|
912
|
+
absorbsPrecedingEm: 0
|
|
728
913
|
} : last === "cl-05" ? {
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
914
|
+
spacing: {
|
|
915
|
+
kind: "glue",
|
|
916
|
+
naturalWidthEm: .25,
|
|
917
|
+
shrink: {
|
|
918
|
+
priority: REDUCTION_STAGE.lineEnd,
|
|
919
|
+
amountEm: .25,
|
|
920
|
+
granularity: "all-or-nothing"
|
|
921
|
+
}
|
|
922
|
+
},
|
|
923
|
+
absorbsPrecedingEm: .25
|
|
735
924
|
} : null,
|
|
736
925
|
breakPenalty,
|
|
737
926
|
/**
|
|
@@ -747,6 +936,13 @@ const EPSILON = 1e-9;
|
|
|
747
936
|
* Spending capacity at this priority changes nothing the reader can see.
|
|
748
937
|
*/
|
|
749
938
|
const FREE_SHRINK_PRIORITY = 0;
|
|
939
|
+
/**
|
|
940
|
+
* Interleave the two kinds onto one number so the allocator can key a plain `Map` without building
|
|
941
|
+
* a string per boundary — this runs once per adjustment unit of every candidate line.
|
|
942
|
+
*/
|
|
943
|
+
function slotKey(slot) {
|
|
944
|
+
return slot.kind === "gap" ? slot.boundary * 2 : slot.index * 2 + 1;
|
|
945
|
+
}
|
|
750
946
|
function skipSourceGaps(atoms, start) {
|
|
751
947
|
let cursor = start;
|
|
752
948
|
while (atoms[cursor]?.sourceGap === true) cursor += 1;
|
|
@@ -755,14 +951,31 @@ function skipSourceGaps(atoms, start) {
|
|
|
755
951
|
function previousVisible(atoms, end) {
|
|
756
952
|
for (let index = end - 1; index >= 0; index -= 1) if (atoms[index]?.sourceGap === false) return index;
|
|
757
953
|
}
|
|
758
|
-
function opportunities(atoms, classes, start, end, profile) {
|
|
954
|
+
function opportunities(atoms, classes, start, end, profile, lineHead) {
|
|
759
955
|
const result = [];
|
|
760
956
|
const firstClass = classes[start];
|
|
761
|
-
const startSpacing = firstClass === void 0 ? null : profile.lineStartSpacing(firstClass);
|
|
957
|
+
const startSpacing = firstClass === void 0 ? null : profile.lineStartSpacing(firstClass, lineHead);
|
|
762
958
|
if (startSpacing !== null) result.push({
|
|
763
|
-
|
|
764
|
-
|
|
959
|
+
slot: {
|
|
960
|
+
kind: "gap",
|
|
961
|
+
boundary: start
|
|
962
|
+
},
|
|
963
|
+
spacing: startSpacing,
|
|
964
|
+
absorbsPrecedingEm: 0
|
|
765
965
|
});
|
|
966
|
+
for (let index = start; index < end; index += 1) {
|
|
967
|
+
const characterClass = classes[index];
|
|
968
|
+
if (characterClass === void 0 || atoms[index]?.sourceGap === true) continue;
|
|
969
|
+
const spacing = profile.spacingCharacter(characterClass, index === start || index === end - 1 ? "line-edge" : "mid-line");
|
|
970
|
+
if (spacing !== null) result.push({
|
|
971
|
+
slot: {
|
|
972
|
+
kind: "character",
|
|
973
|
+
index
|
|
974
|
+
},
|
|
975
|
+
spacing,
|
|
976
|
+
absorbsPrecedingEm: 0
|
|
977
|
+
});
|
|
978
|
+
}
|
|
766
979
|
for (let right = start + 1; right < end; right += 1) {
|
|
767
980
|
const leftAtom = atoms[right - 1];
|
|
768
981
|
const rightAtom = atoms[right];
|
|
@@ -770,73 +983,203 @@ function opportunities(atoms, classes, start, end, profile) {
|
|
|
770
983
|
const rightClass = classes[right];
|
|
771
984
|
if (leftAtom === void 0 || rightAtom === void 0 || leftClass === void 0 || rightClass === void 0 || leftAtom.sourceGap || rightAtom.sourceGap || !leftAtom.pairSpacingAfter) continue;
|
|
772
985
|
result.push({
|
|
773
|
-
|
|
774
|
-
|
|
986
|
+
slot: {
|
|
987
|
+
kind: "gap",
|
|
988
|
+
boundary: right
|
|
989
|
+
},
|
|
990
|
+
spacing: profile.pairSpacing(leftClass, rightClass),
|
|
991
|
+
absorbsPrecedingEm: 0
|
|
775
992
|
});
|
|
776
993
|
}
|
|
777
994
|
const lastClass = classes[end - 1];
|
|
778
995
|
const endSpacing = lastClass === void 0 ? null : profile.lineEndSpacing(lastClass);
|
|
779
996
|
if (endSpacing !== null) result.push({
|
|
780
|
-
|
|
781
|
-
|
|
997
|
+
slot: {
|
|
998
|
+
kind: "gap",
|
|
999
|
+
boundary: end
|
|
1000
|
+
},
|
|
1001
|
+
spacing: endSpacing.spacing,
|
|
1002
|
+
absorbsPrecedingEm: endSpacing.absorbsPrecedingEm
|
|
782
1003
|
});
|
|
783
1004
|
return result;
|
|
784
1005
|
}
|
|
785
|
-
|
|
786
|
-
|
|
1006
|
+
/**
|
|
1007
|
+
* Break the line's spaces into the indivisible amounts the line adjustment may spend.
|
|
1008
|
+
*
|
|
1009
|
+
* A line end the profile says absorbs the space before it claims that much of the preceding
|
|
1010
|
+
* boundary's capacity, and the two become one unit at the line-end stage — JLReq 3.8.3's third
|
|
1011
|
+
* stage, which is free. Whatever the preceding boundary has left over stays an opportunity of its
|
|
1012
|
+
* own stage, so the half em of a `、` before a line-end `・` is still spent where 3.8.3 puts it.
|
|
1013
|
+
*/
|
|
1014
|
+
function adjustmentUnits(values, adjustment) {
|
|
1015
|
+
const select = (spacing) => adjustment === "shrink" ? spacing.shrink : spacing.stretch;
|
|
1016
|
+
const units = [];
|
|
1017
|
+
const straddle = values.find(({ absorbsPrecedingEm }) => absorbsPrecedingEm > 0);
|
|
1018
|
+
let absorbedBoundary;
|
|
1019
|
+
let absorbedEm = 0;
|
|
1020
|
+
let absorbingBoundary;
|
|
1021
|
+
const straddleSlot = straddle?.slot.kind === "gap" ? straddle.slot : void 0;
|
|
1022
|
+
const straddleOwn = straddle === void 0 ? void 0 : select(straddle.spacing);
|
|
1023
|
+
if (straddle !== void 0 && straddleSlot !== void 0 && straddleOwn !== void 0) {
|
|
1024
|
+
const precedingBoundary = straddleSlot.boundary - 1;
|
|
1025
|
+
const preceding = values.find(({ slot }) => slot.kind === "gap" && slot.boundary === precedingBoundary);
|
|
1026
|
+
const availableEm = preceding === void 0 ? 0 : select(preceding.spacing)?.amountEm ?? 0;
|
|
1027
|
+
absorbingBoundary = straddleSlot.boundary;
|
|
1028
|
+
if (availableEm + EPSILON >= straddle.absorbsPrecedingEm) {
|
|
1029
|
+
absorbedBoundary = precedingBoundary;
|
|
1030
|
+
absorbedEm = straddle.absorbsPrecedingEm;
|
|
1031
|
+
units.push({
|
|
1032
|
+
parts: [{
|
|
1033
|
+
slot: {
|
|
1034
|
+
kind: "gap",
|
|
1035
|
+
boundary: precedingBoundary
|
|
1036
|
+
},
|
|
1037
|
+
amountEm: absorbedEm
|
|
1038
|
+
}, {
|
|
1039
|
+
slot: straddleSlot,
|
|
1040
|
+
amountEm: straddleOwn.amountEm
|
|
1041
|
+
}],
|
|
1042
|
+
priority: straddleOwn.priority,
|
|
1043
|
+
amountEm: absorbedEm + straddleOwn.amountEm,
|
|
1044
|
+
granularity: straddleOwn.granularity
|
|
1045
|
+
});
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
for (const { slot, spacing } of values) {
|
|
1049
|
+
if (slot.kind === "gap" && slot.boundary === absorbingBoundary) continue;
|
|
1050
|
+
const own = select(spacing);
|
|
1051
|
+
if (own === void 0) continue;
|
|
1052
|
+
const claimedEm = slot.kind === "gap" && slot.boundary === absorbedBoundary ? absorbedEm : 0;
|
|
1053
|
+
const amountEm = own.amountEm - claimedEm;
|
|
1054
|
+
if (amountEm <= EPSILON) continue;
|
|
1055
|
+
units.push({
|
|
1056
|
+
parts: [{
|
|
1057
|
+
slot,
|
|
1058
|
+
amountEm
|
|
1059
|
+
}],
|
|
1060
|
+
priority: own.priority,
|
|
1061
|
+
amountEm,
|
|
1062
|
+
granularity: own.granularity
|
|
1063
|
+
});
|
|
1064
|
+
}
|
|
1065
|
+
return units;
|
|
787
1066
|
}
|
|
788
|
-
|
|
789
|
-
|
|
1067
|
+
/**
|
|
1068
|
+
* Spend `amountEm` over the line's units of adjustment, stage by stage.
|
|
1069
|
+
*
|
|
1070
|
+
* Every stage of JLReq 3.8.3 and 3.8.4 is stated as 文字サイズ比で均等に — the English text of 3.8.3 a puts
|
|
1071
|
+
* it as "The same width reduction is applied to all spaces on the target line at the same time."
|
|
1072
|
+
* Within a stage the amount is therefore split in proportion to what each space can give, rather
|
|
1073
|
+
* than taken out of the earliest space until it runs dry. Across stages the order stays a
|
|
1074
|
+
* waterfall: a later stage is reached only once every stage before it is spent out. The composer
|
|
1075
|
+
* sets one character size, so a share of the stage's capacity is a share by character size.
|
|
1076
|
+
*
|
|
1077
|
+
* An all-or-nothing unit larger than what is still needed is skipped rather than partly spent, and
|
|
1078
|
+
* never revisited: `remaining` only falls, so a unit that did not fit at its own stage cannot fit
|
|
1079
|
+
* at a later one. One forward pass is therefore exact and no search over subsets is needed.
|
|
1080
|
+
*/
|
|
1081
|
+
function resolveSpacings(values, units, adjustment, amountEm) {
|
|
1082
|
+
const usedBySlot = /* @__PURE__ */ new Map();
|
|
790
1083
|
let remaining = amountEm;
|
|
791
1084
|
let priorityCost = 0;
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
}
|
|
798
|
-
|
|
799
|
-
|
|
1085
|
+
let freeEm = 0;
|
|
1086
|
+
const spend = (unit, fraction) => {
|
|
1087
|
+
for (const { slot, amountEm: partEm } of unit.parts) {
|
|
1088
|
+
const key = slotKey(slot);
|
|
1089
|
+
usedBySlot.set(key, (usedBySlot.get(key) ?? 0) + partEm * fraction);
|
|
1090
|
+
}
|
|
1091
|
+
const spentEm = unit.amountEm * fraction;
|
|
1092
|
+
priorityCost += spentEm * unit.priority;
|
|
1093
|
+
if (unit.priority === FREE_SHRINK_PRIORITY) freeEm += spentEm;
|
|
1094
|
+
remaining -= spentEm;
|
|
1095
|
+
};
|
|
1096
|
+
const stages = /* @__PURE__ */ new Map();
|
|
1097
|
+
for (const unit of units) {
|
|
1098
|
+
const stage = stages.get(unit.priority);
|
|
1099
|
+
if (stage === void 0) stages.set(unit.priority, [unit]);
|
|
1100
|
+
else stage.push(unit);
|
|
1101
|
+
}
|
|
1102
|
+
for (const priority of [...stages.keys()].sort((left, right) => left - right)) {
|
|
800
1103
|
if (remaining <= EPSILON) break;
|
|
801
|
-
const
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
remaining
|
|
1104
|
+
const stage = stages.get(priority) ?? [];
|
|
1105
|
+
let stageCapacityEm = 0;
|
|
1106
|
+
for (const unit of stage) if (unit.granularity === "continuous") stageCapacityEm += unit.amountEm;
|
|
1107
|
+
else if (unit.amountEm <= remaining + EPSILON) spend(unit, 1);
|
|
1108
|
+
if (stageCapacityEm <= EPSILON || remaining <= EPSILON) continue;
|
|
1109
|
+
const fraction = Math.min(remaining, stageCapacityEm) / stageCapacityEm;
|
|
1110
|
+
for (const unit of stage) if (unit.granularity === "continuous") spend(unit, fraction);
|
|
805
1111
|
}
|
|
806
1112
|
return {
|
|
807
|
-
spacings: values
|
|
808
|
-
const used =
|
|
809
|
-
return
|
|
810
|
-
boundary,
|
|
811
|
-
kind: spacing.kind,
|
|
812
|
-
naturalWidthEm: spacing.naturalWidthEm,
|
|
813
|
-
widthEm: adjustment === "shrink" ? spacing.naturalWidthEm - used : spacing.naturalWidthEm + used
|
|
814
|
-
};
|
|
1113
|
+
spacings: splitBySlot(values, (slot, spacing) => {
|
|
1114
|
+
const used = usedBySlot.get(slotKey(slot)) ?? 0;
|
|
1115
|
+
return adjustment === "shrink" ? spacing.naturalWidthEm - used : spacing.naturalWidthEm + used;
|
|
815
1116
|
}),
|
|
816
|
-
priorityCost
|
|
1117
|
+
priorityCost,
|
|
1118
|
+
freeEm,
|
|
1119
|
+
unabsorbedEm: Math.max(0, remaining)
|
|
1120
|
+
};
|
|
1121
|
+
}
|
|
1122
|
+
function splitBySlot(values, widthOf) {
|
|
1123
|
+
const pairSpacings = [];
|
|
1124
|
+
const characterSpacings = [];
|
|
1125
|
+
for (const { slot, spacing } of values) {
|
|
1126
|
+
const resolved = {
|
|
1127
|
+
kind: spacing.kind,
|
|
1128
|
+
naturalWidthEm: spacing.naturalWidthEm,
|
|
1129
|
+
widthEm: widthOf(slot, spacing)
|
|
1130
|
+
};
|
|
1131
|
+
if (slot.kind === "gap") pairSpacings.push({
|
|
1132
|
+
boundary: slot.boundary,
|
|
1133
|
+
...resolved
|
|
1134
|
+
});
|
|
1135
|
+
else characterSpacings.push({
|
|
1136
|
+
index: slot.index,
|
|
1137
|
+
...resolved
|
|
1138
|
+
});
|
|
1139
|
+
}
|
|
1140
|
+
return {
|
|
1141
|
+
pairSpacings,
|
|
1142
|
+
characterSpacings
|
|
1143
|
+
};
|
|
1144
|
+
}
|
|
1145
|
+
/**
|
|
1146
|
+
* The same spaces at the width the profile gives them, for a line no adjustment reaches.
|
|
1147
|
+
*/
|
|
1148
|
+
function naturalSpacings(values) {
|
|
1149
|
+
return splitBySlot(values, (_slot, spacing) => spacing.naturalWidthEm);
|
|
1150
|
+
}
|
|
1151
|
+
/**
|
|
1152
|
+
* The same, minus the アキ the line end would have taken: a hanging character sits outside the text
|
|
1153
|
+
* area and the space that would have followed it goes with it.
|
|
1154
|
+
*/
|
|
1155
|
+
function hangingSpacings(values, end) {
|
|
1156
|
+
const { pairSpacings, characterSpacings } = naturalSpacings(values);
|
|
1157
|
+
return {
|
|
1158
|
+
pairSpacings: pairSpacings.filter(({ boundary }) => boundary !== end),
|
|
1159
|
+
characterSpacings
|
|
817
1160
|
};
|
|
818
1161
|
}
|
|
819
1162
|
function candidate(atoms, classes, start, end, lineLengthEm, profile) {
|
|
820
1163
|
const contentStart = skipSourceGaps(atoms, start);
|
|
821
1164
|
const suppressedIndexes = Array.from({ length: contentStart - start }, (_, index) => start + index);
|
|
822
|
-
const pairValues = opportunities(atoms, classes, contentStart, end, profile);
|
|
1165
|
+
const pairValues = opportunities(atoms, classes, contentStart, end, profile, start === 0 ? "paragraph-start" : "turned-over");
|
|
823
1166
|
const naturalSizeEm = atoms.slice(contentStart, end).reduce((total, atom) => total + atom.boxAdvanceEm, 0) + pairValues.reduce((total, value) => total + value.spacing.naturalWidthEm, 0);
|
|
824
1167
|
const terminal = skipSourceGaps(atoms, end) === atoms.length;
|
|
825
|
-
const shrinkCapacity = capacity(pairValues, ({ shrink }) => shrink);
|
|
826
|
-
const freeShrinkCapacity = capacity(pairValues, ({ shrink }) => shrink?.priority === FREE_SHRINK_PRIORITY ? shrink : void 0);
|
|
827
|
-
const stretchCapacity = capacity(pairValues, ({ stretch }) => stretch);
|
|
828
1168
|
const overflow = naturalSizeEm - lineLengthEm;
|
|
829
1169
|
const underflow = lineLengthEm - naturalSizeEm;
|
|
1170
|
+
const units = adjustmentUnits(pairValues, overflow > 0 ? "shrink" : "stretch");
|
|
1171
|
+
const capacity = units.reduce((total, { amountEm }) => total + amountEm, 0);
|
|
1172
|
+
const freeShrinkCapacity = units.reduce((total, { priority, amountEm }) => total + (priority === FREE_SHRINK_PRIORITY ? amountEm : 0), 0);
|
|
830
1173
|
const lastVisible = previousVisible(atoms, end);
|
|
831
1174
|
const lastClass = lastVisible === void 0 ? void 0 : classes[lastVisible];
|
|
832
1175
|
const lastAdvance = lastVisible === void 0 ? 0 : atoms[lastVisible]?.boxAdvanceEm ?? 0;
|
|
833
|
-
const trailingSpacing = pairValues.find(({
|
|
1176
|
+
const trailingSpacing = pairValues.find(({ slot }) => slot.kind === "gap" && slot.boundary === end)?.spacing.naturalWidthEm ?? 0;
|
|
834
1177
|
if (terminal && overflow <= EPSILON) return {
|
|
835
1178
|
start,
|
|
836
1179
|
contentStart,
|
|
837
1180
|
end,
|
|
838
1181
|
suppressedIndexes,
|
|
839
|
-
|
|
1182
|
+
...naturalSpacings(pairValues),
|
|
840
1183
|
inlineSizeEm: naturalSizeEm,
|
|
841
1184
|
break: { kind: "paragraph-end" },
|
|
842
1185
|
hangingIndex: null,
|
|
@@ -848,28 +1191,28 @@ function candidate(atoms, classes, start, end, lineLengthEm, profile) {
|
|
|
848
1191
|
contentStart,
|
|
849
1192
|
end,
|
|
850
1193
|
suppressedIndexes,
|
|
851
|
-
|
|
1194
|
+
...naturalSpacings(pairValues),
|
|
852
1195
|
inlineSizeEm: naturalSizeEm,
|
|
853
1196
|
break: { kind: "natural" },
|
|
854
1197
|
hangingIndex: null,
|
|
855
1198
|
deformationRatio: 0,
|
|
856
1199
|
priorityCost: 0
|
|
857
1200
|
};
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
const chargeableCapacity =
|
|
861
|
-
const chargeableShrink = Math.max(0, overflow -
|
|
1201
|
+
const shrunk = overflow > 0 && overflow <= capacity + EPSILON ? resolveSpacings(pairValues, units, "shrink", overflow) : null;
|
|
1202
|
+
if (shrunk !== null && shrunk.unabsorbedEm <= EPSILON) {
|
|
1203
|
+
const chargeableCapacity = capacity - freeShrinkCapacity;
|
|
1204
|
+
const chargeableShrink = Math.max(0, overflow - shrunk.freeEm);
|
|
862
1205
|
return {
|
|
863
1206
|
start,
|
|
864
1207
|
contentStart,
|
|
865
1208
|
end,
|
|
866
1209
|
suppressedIndexes,
|
|
867
|
-
|
|
1210
|
+
...shrunk.spacings,
|
|
868
1211
|
inlineSizeEm: lineLengthEm,
|
|
869
1212
|
break: { kind: "shrunk" },
|
|
870
1213
|
hangingIndex: null,
|
|
871
1214
|
deformationRatio: chargeableCapacity <= EPSILON ? 0 : chargeableShrink / chargeableCapacity,
|
|
872
|
-
priorityCost:
|
|
1215
|
+
priorityCost: shrunk.priorityCost
|
|
873
1216
|
};
|
|
874
1217
|
}
|
|
875
1218
|
if (!terminal && overflow > 0 && lastVisible !== void 0 && lastClass !== void 0 && profile.canHang(lastClass) && naturalSizeEm - lastAdvance - trailingSpacing <= lineLengthEm + EPSILON) return {
|
|
@@ -877,34 +1220,32 @@ function candidate(atoms, classes, start, end, lineLengthEm, profile) {
|
|
|
877
1220
|
contentStart,
|
|
878
1221
|
end,
|
|
879
1222
|
suppressedIndexes,
|
|
880
|
-
|
|
1223
|
+
...hangingSpacings(pairValues, end),
|
|
881
1224
|
inlineSizeEm: naturalSizeEm - lastAdvance - trailingSpacing,
|
|
882
1225
|
break: { kind: "hanging" },
|
|
883
1226
|
hangingIndex: lastVisible,
|
|
884
1227
|
deformationRatio: overflow / Math.max(lastAdvance, EPSILON),
|
|
885
1228
|
priorityCost: 0
|
|
886
1229
|
};
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
};
|
|
901
|
-
}
|
|
1230
|
+
const stretched = !terminal && underflow > EPSILON && underflow <= capacity + EPSILON ? resolveSpacings(pairValues, units, "stretch", underflow) : null;
|
|
1231
|
+
if (stretched !== null && stretched.unabsorbedEm <= EPSILON) return {
|
|
1232
|
+
start,
|
|
1233
|
+
contentStart,
|
|
1234
|
+
end,
|
|
1235
|
+
suppressedIndexes,
|
|
1236
|
+
...stretched.spacings,
|
|
1237
|
+
inlineSizeEm: lineLengthEm,
|
|
1238
|
+
break: { kind: "stretched" },
|
|
1239
|
+
hangingIndex: null,
|
|
1240
|
+
deformationRatio: capacity === 0 ? 0 : underflow / capacity,
|
|
1241
|
+
priorityCost: stretched.priorityCost
|
|
1242
|
+
};
|
|
902
1243
|
return {
|
|
903
1244
|
start,
|
|
904
1245
|
contentStart,
|
|
905
1246
|
end,
|
|
906
1247
|
suppressedIndexes,
|
|
907
|
-
|
|
1248
|
+
...naturalSpacings(pairValues),
|
|
908
1249
|
inlineSizeEm: naturalSizeEm,
|
|
909
1250
|
break: { kind: "forced" },
|
|
910
1251
|
hangingIndex: null,
|
|
@@ -1644,7 +1985,9 @@ function positionedLine(atoms, sourceGapIndexes, plan) {
|
|
|
1644
1985
|
}];
|
|
1645
1986
|
});
|
|
1646
1987
|
const spacings = new Map(plan.pairSpacings.map((spacing) => [spacing.boundary, spacing]));
|
|
1988
|
+
const characterSpacings = new Map(plan.characterSpacings.map((spacing) => [spacing.index, spacing]));
|
|
1647
1989
|
let offsetEm = 0;
|
|
1990
|
+
const adjustmentOf = (widthEm, naturalWidthEm) => widthEm < naturalWidthEm ? "shrunk" : widthEm > naturalWidthEm ? "stretched" : "natural";
|
|
1648
1991
|
const positionSpacing = (spacing) => {
|
|
1649
1992
|
if (spacing.kind === "kern") items.push({
|
|
1650
1993
|
kind: "kern",
|
|
@@ -1657,7 +2000,7 @@ function positionedLine(atoms, sourceGapIndexes, plan) {
|
|
|
1657
2000
|
offsetEm,
|
|
1658
2001
|
widthEm: spacing.widthEm,
|
|
1659
2002
|
naturalWidthEm: spacing.naturalWidthEm,
|
|
1660
|
-
adjustment: spacing.widthEm
|
|
2003
|
+
adjustment: adjustmentOf(spacing.widthEm, spacing.naturalWidthEm)
|
|
1661
2004
|
});
|
|
1662
2005
|
offsetEm += spacing.widthEm;
|
|
1663
2006
|
};
|
|
@@ -1666,18 +2009,21 @@ function positionedLine(atoms, sourceGapIndexes, plan) {
|
|
|
1666
2009
|
if (spacing !== void 0) positionSpacing(spacing);
|
|
1667
2010
|
const atom = atoms[index];
|
|
1668
2011
|
if (atom === void 0) continue;
|
|
1669
|
-
|
|
2012
|
+
const characterSpacing = characterSpacings.get(index);
|
|
2013
|
+
if (sourceGapIndexes.has(index) || characterSpacing !== void 0) {
|
|
2014
|
+
const naturalWidthEm = characterSpacing?.naturalWidthEm ?? atom.boxAdvanceEm;
|
|
2015
|
+
const widthEm = characterSpacing?.widthEm ?? atom.boxAdvanceEm;
|
|
1670
2016
|
items.push({
|
|
1671
2017
|
kind: "glue",
|
|
1672
2018
|
origin: "source",
|
|
1673
2019
|
value: atom.grapheme.value,
|
|
1674
2020
|
range: atom.grapheme.range,
|
|
1675
2021
|
offsetEm,
|
|
1676
|
-
widthEm
|
|
1677
|
-
naturalWidthEm
|
|
1678
|
-
adjustment:
|
|
2022
|
+
widthEm,
|
|
2023
|
+
naturalWidthEm,
|
|
2024
|
+
adjustment: adjustmentOf(widthEm, naturalWidthEm)
|
|
1679
2025
|
});
|
|
1680
|
-
offsetEm +=
|
|
2026
|
+
offsetEm += widthEm;
|
|
1681
2027
|
continue;
|
|
1682
2028
|
}
|
|
1683
2029
|
const hanging = plan.hangingIndex === index;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as ManuscriptResult, i as ParsedManuscript, t as ComposedManuscript } from "./composed-manuscript-
|
|
1
|
+
import { a as ManuscriptResult, i as ParsedManuscript, t as ComposedManuscript } from "./composed-manuscript-C0f3mxKs.mjs";
|
|
2
2
|
import * as v from "valibot";
|
|
3
3
|
//#region src/result/rejection.d.ts
|
|
4
4
|
declare const RejectionSchema: v.SchemaWithPipe<readonly [v.StrictObjectSchema<{
|
package/dist/plugin.d.mts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { a as ManuscriptResult, i as ParsedManuscript, n as ValidationIssue, r as NamespacedId } from "./composed-manuscript-
|
|
2
|
-
import { a as InlineMeasureRequest, c as logicalInlineMeasurer, f as ManuscriptComposer, m as Rejection, n as createNovelComposer, o as InlineMeasurement, p as ManuscriptParser, s as InlineMeasurer } from "./novel-composer-
|
|
1
|
+
import { a as ManuscriptResult, i as ParsedManuscript, n as ValidationIssue, r as NamespacedId } from "./composed-manuscript-C0f3mxKs.mjs";
|
|
2
|
+
import { a as InlineMeasureRequest, c as logicalInlineMeasurer, f as ManuscriptComposer, m as Rejection, n as createNovelComposer, o as InlineMeasurement, p as ManuscriptParser, s as InlineMeasurer } from "./novel-composer-DhycV9B3.mjs";
|
|
3
3
|
export { type InlineMeasureRequest, type InlineMeasurement, type InlineMeasurer, type ManuscriptComposer, type ManuscriptParser, ManuscriptResult, NamespacedId, ParsedManuscript, type Rejection, ValidationIssue, createNovelComposer, logicalInlineMeasurer };
|
package/dist/plugin.mjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { c as ManuscriptResult, d as NamespacedId, s as ValidationIssue } from "./question-or-exclamation-spacing-B5o23ZLf.mjs";
|
|
2
|
-
import { a as logicalInlineMeasurer, p as ParsedManuscript, t as createNovelComposer } from "./novel-composer-
|
|
2
|
+
import { a as logicalInlineMeasurer, p as ParsedManuscript, t as createNovelComposer } from "./novel-composer-CtovfhET.mjs";
|
|
3
3
|
export { ManuscriptResult, NamespacedId, ParsedManuscript, ValidationIssue, createNovelComposer, logicalInlineMeasurer };
|