rapier-markdown-kit 1.1.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/LICENSE +40 -0
- package/README.md +102 -0
- package/dist/agent/vendor/pretext/LICENSE +21 -0
- package/dist/agent/vendor/pretext/NOTICE +15 -0
- package/dist/agent/vendor/pretext/SOURCE.json +85 -0
- package/dist/agent/vendor/pretext/analysis.js +1234 -0
- package/dist/agent/vendor/pretext/bidi.js +151 -0
- package/dist/agent/vendor/pretext/generated/bidi-data.js +3831 -0
- package/dist/agent/vendor/pretext/layout.js +308 -0
- package/dist/agent/vendor/pretext/line-break.js +636 -0
- package/dist/agent/vendor/pretext/line-text.js +44 -0
- package/dist/agent/vendor/pretext/measurement.js +189 -0
- package/dist/agent/vendor/pretext/package.json +3 -0
- package/dist/agent/vendor/pretext/rich-inline.js +291 -0
- package/dist/agent/will.mjs +167 -0
- package/dist/kit/assets.mjs +3 -0
- package/dist/kit/conformance/01-inline.expected.json +82 -0
- package/dist/kit/conformance/01-inline.md +10 -0
- package/dist/kit/conformance/02-width-x-align.expected.json +83 -0
- package/dist/kit/conformance/02-width-x-align.md +12 -0
- package/dist/kit/conformance/03-wrap-around-silhouette.expected.json +177 -0
- package/dist/kit/conformance/03-wrap-around-silhouette.md +12 -0
- package/dist/kit/conformance/04-wrap-box.expected.json +173 -0
- package/dist/kit/conformance/04-wrap-box.md +12 -0
- package/dist/kit/conformance/05-behind.expected.json +122 -0
- package/dist/kit/conformance/05-behind.md +12 -0
- package/dist/kit/conformance/06-front.expected.json +122 -0
- package/dist/kit/conformance/06-front.md +12 -0
- package/dist/kit/conformance/07-rotate-raster.expected.json +179 -0
- package/dist/kit/conformance/07-rotate-raster.md +12 -0
- package/dist/kit/conformance/08-drawing-rotation.expected.json +188 -0
- package/dist/kit/conformance/08-drawing-rotation.md +12 -0
- package/dist/kit/conformance/09-drawing-ring-interior.expected.json +229 -0
- package/dist/kit/conformance/09-drawing-ring-interior.md +12 -0
- package/dist/kit/conformance/10-both-sides.expected.json +221 -0
- package/dist/kit/conformance/10-both-sides.md +10 -0
- package/dist/kit/conformance/11-neighbour-skips-image.expected.json +160 -0
- package/dist/kit/conformance/11-neighbour-skips-image.md +13 -0
- package/dist/kit/conformance/12-heading-barrier.expected.json +164 -0
- package/dist/kit/conformance/12-heading-barrier.md +12 -0
- package/dist/kit/conformance/13-rtl-text.expected.json +179 -0
- package/dist/kit/conformance/13-rtl-text.md +8 -0
- package/dist/kit/conformance/README.md +99 -0
- package/dist/kit/conformance/measurer.mjs +0 -0
- package/dist/kit/conformance/run.mjs +123 -0
- package/dist/kit/index.mjs +6 -0
- package/dist/kit/layout.mjs +2 -0
- package/dist/kit/marks.mjs +2 -0
- package/dist/kit/model.mjs +3 -0
- package/dist/kit/will.mjs +2 -0
- package/dist/layout/model.mjs +364 -0
- package/dist/spec/md-assets.mjs +323 -0
- package/dist/spec/md-layout.mjs +117 -0
- package/dist/spec/md-marks.mjs +78 -0
- package/package.json +33 -0
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
{
|
|
2
|
+
"document": "12-heading-barrier.md",
|
|
3
|
+
"measurer": {
|
|
4
|
+
"font": "normal 400 17.6px/30.8px Geist, system-ui, sans-serif",
|
|
5
|
+
"letterSpacing": 0,
|
|
6
|
+
"lineHeight": 30.8,
|
|
7
|
+
"direction": "ltr",
|
|
8
|
+
"columnWidth": 354
|
|
9
|
+
},
|
|
10
|
+
"anchorText": "This paragraph comes before the picture and stays the anchor whenever the block right after the picture cannot be one -- another image paragraph, or a heading -- so the words below keep their own separate life.",
|
|
11
|
+
"anchorFlowed": true,
|
|
12
|
+
"picture": {
|
|
13
|
+
"natural": {
|
|
14
|
+
"width": 60,
|
|
15
|
+
"height": 40
|
|
16
|
+
},
|
|
17
|
+
"occupancy": null,
|
|
18
|
+
"layout": "<!--md-layout:v1 width=35% wrap=around x=75%-->",
|
|
19
|
+
"rect": {
|
|
20
|
+
"left": 203.546875,
|
|
21
|
+
"top": 0.015625,
|
|
22
|
+
"right": 327.4375,
|
|
23
|
+
"bottom": 82.609375
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"profileHint": null,
|
|
27
|
+
"lines": [
|
|
28
|
+
{
|
|
29
|
+
"left": 0,
|
|
30
|
+
"top": 0,
|
|
31
|
+
"right": 193.55,
|
|
32
|
+
"bottom": 30.8
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"left": 0,
|
|
36
|
+
"top": 30.8,
|
|
37
|
+
"right": 193.55,
|
|
38
|
+
"bottom": 61.59
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
"left": 0,
|
|
42
|
+
"top": 61.59,
|
|
43
|
+
"right": 193.55,
|
|
44
|
+
"bottom": 92.39
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
"left": 0,
|
|
48
|
+
"top": 92.39,
|
|
49
|
+
"right": 193.55,
|
|
50
|
+
"bottom": 123.19
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"left": 0,
|
|
54
|
+
"top": 123.19,
|
|
55
|
+
"right": 354,
|
|
56
|
+
"bottom": 153.98
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
"left": 0,
|
|
60
|
+
"top": 154,
|
|
61
|
+
"right": 354,
|
|
62
|
+
"bottom": 184.8
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
"left": 0,
|
|
66
|
+
"top": 184.8,
|
|
67
|
+
"right": 354,
|
|
68
|
+
"bottom": 215.59
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
"left": 0,
|
|
72
|
+
"top": 215.59,
|
|
73
|
+
"right": 354,
|
|
74
|
+
"bottom": 246.39
|
|
75
|
+
}
|
|
76
|
+
],
|
|
77
|
+
"glyphWidths": {
|
|
78
|
+
"T": 10.747,
|
|
79
|
+
"h": 11.151,
|
|
80
|
+
"i": 4.888,
|
|
81
|
+
"s": 9.166,
|
|
82
|
+
" ": 5.593,
|
|
83
|
+
"p": 11.168,
|
|
84
|
+
"a": 10.781,
|
|
85
|
+
"r": 7.233,
|
|
86
|
+
"g": 11.168,
|
|
87
|
+
"c": 9.673,
|
|
88
|
+
"o": 10.764,
|
|
89
|
+
"m": 17.138,
|
|
90
|
+
"e": 10.824,
|
|
91
|
+
"b": 11.168,
|
|
92
|
+
"f": 6.194,
|
|
93
|
+
"t": 6.898,
|
|
94
|
+
"u": 11.151,
|
|
95
|
+
"n": 11.151,
|
|
96
|
+
"d": 11.168,
|
|
97
|
+
"y": 10.412,
|
|
98
|
+
"w": 14.389,
|
|
99
|
+
"v": 10.412,
|
|
100
|
+
"l": 4.888,
|
|
101
|
+
"k": 10.189,
|
|
102
|
+
"-": 6.349,
|
|
103
|
+
",": 5.593,
|
|
104
|
+
".": 5.593
|
|
105
|
+
},
|
|
106
|
+
"measurements": {
|
|
107
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000-": 6.349,
|
|
108
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000 ": 5.593,
|
|
109
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000This": 35.952,
|
|
110
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000T": 10.747,
|
|
111
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000h": 11.151,
|
|
112
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000i": 4.888,
|
|
113
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000s": 9.166,
|
|
114
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000paragraph": 91.465,
|
|
115
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000p": 11.168,
|
|
116
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000a": 10.781,
|
|
117
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000r": 7.233,
|
|
118
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000g": 11.168,
|
|
119
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000comes": 57.566,
|
|
120
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000c": 9.673,
|
|
121
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000o": 10.764,
|
|
122
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000m": 17.138,
|
|
123
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000e": 10.824,
|
|
124
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000before": 56.621,
|
|
125
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000b": 11.168,
|
|
126
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000f": 6.194,
|
|
127
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000the": 28.873,
|
|
128
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000t": 6.898,
|
|
129
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000picture": 61.449,
|
|
130
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000u": 11.151,
|
|
131
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000and": 33.1,
|
|
132
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000n": 11.151,
|
|
133
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000d": 11.168,
|
|
134
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000stays": 46.424,
|
|
135
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000y": 10.412,
|
|
136
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000anchor": 60.753,
|
|
137
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000whenever": 86.809,
|
|
138
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000w": 14.389,
|
|
139
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000v": 10.412,
|
|
140
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000block": 46.682,
|
|
141
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000l": 4.888,
|
|
142
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000k": 10.189,
|
|
143
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000right": 41.338,
|
|
144
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000after": 41.622,
|
|
145
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000cannot": 60.418,
|
|
146
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000be": 21.992,
|
|
147
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000one": 32.739,
|
|
148
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000another": 68.803,
|
|
149
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000image": 54.8,
|
|
150
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000paragraph,": 97.058,
|
|
151
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000,": 5.593,
|
|
152
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000or": 17.998,
|
|
153
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000heading": 71.131,
|
|
154
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000so": 19.93,
|
|
155
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000words": 52.412,
|
|
156
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000below": 52.034,
|
|
157
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000keep": 42.378,
|
|
158
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000their": 40.995,
|
|
159
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000own": 36.304,
|
|
160
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000separate": 77.677,
|
|
161
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000life.": 32.387,
|
|
162
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000.": 5.593
|
|
163
|
+
}
|
|
164
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
|
|
2
|
+
# The neighbour search stops at a heading
|
|
3
|
+
|
|
4
|
+
This paragraph comes before the picture and stays the anchor whenever the block right after the picture cannot be one -- another image paragraph, or a heading -- so the words below keep their own separate life.
|
|
5
|
+
|
|
6
|
+
![The picture under test][opaque] <!--md-layout:v1 width=35% wrap=around x=75%-->
|
|
7
|
+
|
|
8
|
+
## A heading right after the picture
|
|
9
|
+
|
|
10
|
+
This paragraph comes after the picture and is its ordinary anchor whenever nothing else stands in the way, wrapping beside it line by line the way the fixture is built to show plainly.
|
|
11
|
+
|
|
12
|
+
[opaque]: data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADwAAAAoCAYAAACiu5n/AAAAQ0lEQVR42u3PMQEAAAQAMHl0095PC48dC7DI6vkkhIWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFby0f7s8anCQmZQAAAABJRU5ErkJggg==
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
{
|
|
2
|
+
"document": "13-rtl-text.md",
|
|
3
|
+
"measurer": {
|
|
4
|
+
"font": "normal 400 17.6px/30.8px Geist, system-ui, sans-serif",
|
|
5
|
+
"letterSpacing": 0,
|
|
6
|
+
"lineHeight": 30.8,
|
|
7
|
+
"direction": "rtl",
|
|
8
|
+
"columnWidth": 354
|
|
9
|
+
},
|
|
10
|
+
"anchorText": "هذه فقرة قصيرة باللغة العربية تلتف حول الصورة المجاورة لها في هذا المستند، وتُستخدم للتحقق من أن اتجاه الكتابة من اليمين إلى اليسار يُحترم عند التفاف النص حول صورة موضوعة بعرض جزئي من العمود.",
|
|
11
|
+
"anchorFlowed": true,
|
|
12
|
+
"picture": {
|
|
13
|
+
"natural": {
|
|
14
|
+
"width": 64,
|
|
15
|
+
"height": 64
|
|
16
|
+
},
|
|
17
|
+
"occupancy": null,
|
|
18
|
+
"layout": "<!--md-layout:v1 width=35% wrap=around x=20%-->",
|
|
19
|
+
"rect": {
|
|
20
|
+
"left": 8.84375,
|
|
21
|
+
"top": 0,
|
|
22
|
+
"right": 132.734375,
|
|
23
|
+
"bottom": 123.890625
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"profileHint": {
|
|
27
|
+
"kind": "bands",
|
|
28
|
+
"descriptor": "||0.4792-0.5208|0.4583-0.5417|0.4375-0.5625|0.4167-0.5833|0.3958-0.6042|0.375-0.625|0.3542-0.6458|0.3333-0.6667|0.3125-0.6875|0.2917-0.7083|0.2708-0.7292|0.25-0.75|0.2292-0.7708|0.2083-0.7917|0.1875-0.8125|0.1667-0.8333|0.1458-0.8542|0.125-0.875|0.1042-0.8958|0.0833-0.9167|0.0625-0.9375|0.0417-0.9583|0.0417-0.9583|0.0625-0.9375|0.0833-0.9167|0.1042-0.8958|0.125-0.875|0.1458-0.8542|0.1667-0.8333|0.1875-0.8125|0.2083-0.7917|0.2292-0.7708|0.25-0.75|0.2708-0.7292|0.2917-0.7083|0.3125-0.6875|0.3333-0.6667|0.3542-0.6458|0.375-0.625|0.3958-0.6042|0.4167-0.5833|0.4375-0.5625|0.4583-0.5417|0.4792-0.5208||",
|
|
29
|
+
"note": "the same diamond.png as fixture 03"
|
|
30
|
+
},
|
|
31
|
+
"lines": [
|
|
32
|
+
{
|
|
33
|
+
"left": 122.09,
|
|
34
|
+
"top": 0,
|
|
35
|
+
"right": 353.98,
|
|
36
|
+
"bottom": 30.8
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"left": 142.75,
|
|
40
|
+
"top": 30.8,
|
|
41
|
+
"right": 354,
|
|
42
|
+
"bottom": 61.59
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"left": 142.75,
|
|
46
|
+
"top": 61.59,
|
|
47
|
+
"right": 354,
|
|
48
|
+
"bottom": 92.39
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
"left": 124.67,
|
|
52
|
+
"top": 92.39,
|
|
53
|
+
"right": 353.98,
|
|
54
|
+
"bottom": 123.19
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
"left": 93.7,
|
|
58
|
+
"top": 123.19,
|
|
59
|
+
"right": 353.98,
|
|
60
|
+
"bottom": 153.98
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
"left": 0,
|
|
64
|
+
"top": 154,
|
|
65
|
+
"right": 354,
|
|
66
|
+
"bottom": 184.8
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
"left": 0,
|
|
70
|
+
"top": 184.8,
|
|
71
|
+
"right": 354,
|
|
72
|
+
"bottom": 215.59
|
|
73
|
+
}
|
|
74
|
+
],
|
|
75
|
+
"glyphWidths": {
|
|
76
|
+
"ه": 9.218,
|
|
77
|
+
"ذ": 7.835,
|
|
78
|
+
" ": 5.593,
|
|
79
|
+
"ف": 18.238,
|
|
80
|
+
"ق": 13.651,
|
|
81
|
+
"ر": 8.496,
|
|
82
|
+
"ة": 9.218,
|
|
83
|
+
"ص": 21.271,
|
|
84
|
+
"ي": 13.771,
|
|
85
|
+
"ب": 16.563,
|
|
86
|
+
"ا": 4.888,
|
|
87
|
+
"ل": 12.783,
|
|
88
|
+
"غ": 10.498,
|
|
89
|
+
"ع": 10.498,
|
|
90
|
+
"ت": 16.563,
|
|
91
|
+
"ح": 11.357,
|
|
92
|
+
"و": 8.496,
|
|
93
|
+
"م": 10.893,
|
|
94
|
+
"ج": 11.357,
|
|
95
|
+
"س": 21.477,
|
|
96
|
+
"ن": 12.92,
|
|
97
|
+
"د": 7.835,
|
|
98
|
+
"،": 5.678,
|
|
99
|
+
"ُ": 0,
|
|
100
|
+
"خ": 11.357,
|
|
101
|
+
"أ": 4.888,
|
|
102
|
+
"ك": 14.501,
|
|
103
|
+
"إ": 4.888,
|
|
104
|
+
"ى": 13.771,
|
|
105
|
+
"ض": 21.271,
|
|
106
|
+
"ز": 8.496,
|
|
107
|
+
"ئ": 13.771,
|
|
108
|
+
".": 5.593
|
|
109
|
+
},
|
|
110
|
+
"measurements": {
|
|
111
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000-": 6.349,
|
|
112
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000 ": 5.593,
|
|
113
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000هذه": 27.731,
|
|
114
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000ه": 9.218,
|
|
115
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000ذ": 7.835,
|
|
116
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000فقرة": 36.236,
|
|
117
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000ف": 18.238,
|
|
118
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000ق": 13.651,
|
|
119
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000ر": 8.496,
|
|
120
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000ة": 9.218,
|
|
121
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000قصيرة": 47.902,
|
|
122
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000ص": 21.271,
|
|
123
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000ي": 13.771,
|
|
124
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000باللغة": 39.363,
|
|
125
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000ب": 16.563,
|
|
126
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000ا": 4.888,
|
|
127
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000ل": 12.783,
|
|
128
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000غ": 10.498,
|
|
129
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000العربية": 48.082,
|
|
130
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000ع": 10.498,
|
|
131
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000تلتف": 34.243,
|
|
132
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000ت": 16.563,
|
|
133
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000حول": 32.748,
|
|
134
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000ح": 11.357,
|
|
135
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000و": 8.496,
|
|
136
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000الصورة": 52.309,
|
|
137
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000المجاورة": 63.348,
|
|
138
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000م": 10.893,
|
|
139
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000ج": 11.357,
|
|
140
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000لها": 18.831,
|
|
141
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000في": 23.075,
|
|
142
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000هذا": 23.401,
|
|
143
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000المستند،": 61.647,
|
|
144
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000س": 21.477,
|
|
145
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000ن": 12.92,
|
|
146
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000د": 7.835,
|
|
147
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000،": 5.678,
|
|
148
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000وتُستخدم": 65.882,
|
|
149
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000تُ": 16.563,
|
|
150
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000خ": 11.357,
|
|
151
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000للتحقق": 51.424,
|
|
152
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000من": 22.817,
|
|
153
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000أن": 17.809,
|
|
154
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000أ": 4.888,
|
|
155
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000اتجاه": 35.72,
|
|
156
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000الكتابة": 44.964,
|
|
157
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000ك": 14.501,
|
|
158
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000اليمين": 44.431,
|
|
159
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000إلى": 24.913,
|
|
160
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000إ": 4.888,
|
|
161
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000ى": 13.771,
|
|
162
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000اليسار": 45.11,
|
|
163
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000يُحترم": 42.163,
|
|
164
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000يُ": 13.771,
|
|
165
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000عند": 25.042,
|
|
166
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000التفاف": 48.056,
|
|
167
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000النص": 37.112,
|
|
168
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000صورة": 41.742,
|
|
169
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000موضوعة": 62.472,
|
|
170
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000ض": 21.271,
|
|
171
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000بعرض": 44.362,
|
|
172
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000جزئي": 40.144,
|
|
173
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000ز": 8.496,
|
|
174
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000ئ": 13.771,
|
|
175
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000العمود.": 51.424,
|
|
176
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000.": 5.593,
|
|
177
|
+
"normal 400 17.6px Geist, system-ui, sans-serif\u0000ُ": 0
|
|
178
|
+
}
|
|
179
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
|
|
2
|
+
# RTL text wrapping around a picture
|
|
3
|
+
|
|
4
|
+
![صورة][diamond] <!--md-layout:v1 width=35% wrap=around x=20%-->
|
|
5
|
+
|
|
6
|
+
هذه فقرة قصيرة باللغة العربية تلتف حول الصورة المجاورة لها في هذا المستند، وتُستخدم للتحقق من أن اتجاه الكتابة من اليمين إلى اليسار يُحترم عند التفاف النص حول صورة موضوعة بعرض جزئي من العمود.
|
|
7
|
+
|
|
8
|
+
[diamond]: data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAA40lEQVR42u3bSQ6DMBAAQZ7N7zvXSNmAYHuWPtQ9akUJ2DPbvu9bZ5sB1n8IOgfgSbsAvNEmAF+UD8ABZQNwQrkAXFAmAH9IH4AbpA3AjdIFYIA0ARgofAAmCBuAicIFYIEwAVhoeQACWBaAQKYHIKBpAQhseAASGBaARG4PQEK3BSCxvwNQwOUAFHI6AAUdDkBhPwPQwMcANPISgIb8Bvgb4L+AzwE+Cfou4Nug5wGeCHkm6Kmw9wLeDHk36O2w8wFOiDgj5JSYc4JOijor7LS4+wJujLgz5NaYe4Nujro73MEDip6/b+F9FD0AAAAASUVORK5CYII=
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# The conformance kit
|
|
2
|
+
|
|
3
|
+
Thirteen small documents, each covering one placement fact from [the Markdown
|
|
4
|
+
standard](../../docs/markdown-standard.md): inline (no layout comment), width/x/align, `wrap=around`
|
|
5
|
+
with a real alpha silhouette, `wrap=box`, `behind`, `front`, `rotate=` on a plain raster, a foreign
|
|
6
|
+
SVG "drawing" already turned in its own bytes, a ring drawing whose unfilled interior takes a line of
|
|
7
|
+
text, both-sides wrapping around a centred picture, the neighbour search skipping a second, unrelated
|
|
8
|
+
picture, the neighbour search stopping at a heading, and RTL (Arabic) text.
|
|
9
|
+
|
|
10
|
+
## What each document's JSON carries
|
|
11
|
+
|
|
12
|
+
`<NN>-<name>.expected.json`, committed beside `<NN>-<name>.md`:
|
|
13
|
+
|
|
14
|
+
- `measurer` — the anchor paragraph's own resolved `font` (canvas-shaped: style/weight/size/family),
|
|
15
|
+
`letterSpacing`, `lineHeight`, `direction` and `columnWidth` — the facts an implementer needs to
|
|
16
|
+
reproduce the same lines with the same measurer.
|
|
17
|
+
- `anchorText` / `anchorFlowed` — which paragraph is the anchor, and whether a picture obstacle
|
|
18
|
+
reached it at all (a plain, `behind`, or `front` document has none).
|
|
19
|
+
- `picture` — the picture's own natural size, its layout comment, and (when `anchorFlowed`) its
|
|
20
|
+
reserved rectangle *relative to the anchor paragraph's own content box* — the same coordinate frame
|
|
21
|
+
`pictureSlices`/`flowLines` expect.
|
|
22
|
+
- `profileHint` — which of the kit's calls reconstructs the obstacle's shape: `{kind: "bands",
|
|
23
|
+
descriptor}` for `parseProfile`, `{kind: "polygon", corners}` for `polygonProfile`, `{kind:
|
|
24
|
+
"rasterTilt", width, height, radiusDeg}` for `rasterTiltProfile`, or `null` for the plain
|
|
25
|
+
conservative-rectangle fallback (`pictureSlices(null, …)`) — an opaque raster's own alpha profile
|
|
26
|
+
is `null` too (`alphaProfile` refuses a fully opaque image on purpose; `wrap=box` with no drawing
|
|
27
|
+
recipe and no rotation is the same fallback). The `bands`/`polygon` fixtures' hints are the exact
|
|
28
|
+
alpha shape of the fixture's own picture, computed independently in Node from how the picture was
|
|
29
|
+
built (see the generation notes at the foot of this file) — not read back from Rapier — since these
|
|
30
|
+
are plain rasters/foreign SVGs and Rapier's export only serializes `data-rapier-occupancy` for its
|
|
31
|
+
own drawing recipes (`docs/architecture.md`, "Layout and images").
|
|
32
|
+
- `lines` — the anchor's own line boxes (`left`/`top`/`right`/`bottom`), relative to its own content
|
|
33
|
+
box, read from Rapier's styled export: from the reflow script's own `<span>` boxes when the
|
|
34
|
+
paragraph is flowed (their `right` is the reserved *slot*'s edge, `x + width` — the script sizes
|
|
35
|
+
each span to its slot and left-aligns ragged text inside it, so that is the honest thing to compare,
|
|
36
|
+
not a text-measurement guess), or from `Range.getClientRects()` on the plain text otherwise (which
|
|
37
|
+
*is* the glyphs' own tight extent).
|
|
38
|
+
- `glyphWidths` — every character the anchor's text uses, with its own measured width in that exact
|
|
39
|
+
font, from this browser's `measureText`, called in isolation on one character at a time. Always
|
|
40
|
+
present; the fallback `measurer.mjs` (R77) reaches for when a query is not in `measurements` below.
|
|
41
|
+
- `measurements` (R77) — every `(font, text)` pair Pretext's own `getMeasureContext()` was actually
|
|
42
|
+
asked to measure while *this fixture's own export* ran its real reflow script, each with the exact
|
|
43
|
+
width the browser gave it — captured from the exported page's own reflow as it ran.
|
|
44
|
+
This is Rapier's own measurement, not a stand-in: a caller replaying the same font and text later
|
|
45
|
+
gets back the *same* number, kerning and shaping included, because it is looked up, not re-derived.
|
|
46
|
+
**Which fixtures carry a real trace:** only one whose picture actually pushes a flow obstacle into
|
|
47
|
+
the anchor paragraph — `layout/interchange.js`'s own `prepare()` calls Pretext exclusively for a
|
|
48
|
+
paragraph an obstacle reaches, so `01-inline` and `02-width-x-align` (no `wrap`, no obstacle at
|
|
49
|
+
all) and `05-behind`/`06-front` (`behind`/`front` are out-of-flow by design and never push one
|
|
50
|
+
either) never run Pretext during export and so have an empty `measurements` — every other fixture
|
|
51
|
+
(`wrap=around`/`box`) does. `measurer.mjs`'s own `createMeasurer` merges every document's
|
|
52
|
+
`measurements` and `glyphWidths` into one process-wide lookup, matching `run.mjs`'s pre-existing
|
|
53
|
+
merge-once-upfront discipline (Pretext's `getMeasureContext()` memoizes its context once per
|
|
54
|
+
process, so installing a fresh measurer per document would silently keep serving the first
|
|
55
|
+
document's data to every later one).
|
|
56
|
+
|
|
57
|
+
## The expectations
|
|
58
|
+
|
|
59
|
+
`<name>.expected.json` is compared against the exported page's own reflow within 1.5 px; a missing one is a failure. `profileHint` is hand-computed from how each fixture's picture bytes were built ("Generation notes" below) and is restored by hand if a file is regenerated.
|
|
60
|
+
|
|
61
|
+
## Running the comparison
|
|
62
|
+
|
|
63
|
+
```sh
|
|
64
|
+
node run.mjs # the kit itself, driven by measurer.mjs (R77)
|
|
65
|
+
node run.mjs --impl path/to/other-kit/index.mjs --tolerance 4
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Prints two separate tables (preservation and presentation are different claims):
|
|
69
|
+
|
|
70
|
+
- **Preservation** — does each fixture's own layout comment round-trip exactly through
|
|
71
|
+
`parseLayout`/`formatLayout`. No measurer, no font: either exactly right, or a real bug. 12/12.
|
|
72
|
+
- **Presentation** — does the planned line count and each line's left/right agree with the recorded
|
|
73
|
+
fact, within `--tolerance` pixels (default 2). This depends entirely on the measurer supplied;
|
|
74
|
+
`run.mjs`'s own default (`measurer.mjs`, R77) replays Rapier's real Pretext trace wherever one was
|
|
75
|
+
captured, falling back to the character-glyph sum only where none exists. Current result: **4/13
|
|
76
|
+
AGREE outright** (`04-wrap-box`, `07-rotate-raster`, `10-both-sides`, `12-heading-barrier` — every
|
|
77
|
+
one a case where the compared edge is pure obstacle-slot geometry, unaffected by text fidelity
|
|
78
|
+
either way), **9 CLOSE, 0 DIFFERS**, each CLOSE row's cause named rather than left as "close
|
|
79
|
+
enough": `01-inline`/`02-width-x-align`/`05-behind`/`06-front` have no real trace at all (see
|
|
80
|
+
`measurements` above) so still rest on the character-sum fallback; `03-wrap-around-silhouette`,
|
|
81
|
+
`09-drawing-ring-interior`, `11-neighbour-skips-image` and `13-rtl-text` run Pretext for real during
|
|
82
|
+
export (`13-rtl-text` at 100% — every query of its own answered from the real trace) yet still
|
|
83
|
+
carry a small, constant left-edge delta (5.18px for 03/11/13, 6.63px for 09) that is a
|
|
84
|
+
picture-obstacle *geometry* fact, not a text one: their `profileHint` is a hand-computed, quantized
|
|
85
|
+
alpha-band descriptor for a picture Rapier's own export never serializes an occupancy descriptor
|
|
86
|
+
for, and that descriptor's own precision — not the measurer — is the residual gap. Before this
|
|
87
|
+
measurer existed (a per-character glyph-sum with no real trace at all), the same run reported 4
|
|
88
|
+
AGREE / 8 CLOSE / 1 DIFFERS — `13-rtl-text` disagreed by a whole line (7 expected, 8 planned): real
|
|
89
|
+
Arabic shaping (ligatures, diacritic combination) changes a run's total measured width in a way an
|
|
90
|
+
isolated-character sum cannot reproduce, and that was enough to move one word to the next line. The
|
|
91
|
+
real trace above fixes exactly that: `13-rtl-text` now plans the correct 7 lines.
|
|
92
|
+
|
|
93
|
+
## Generation notes (how the pictures were built)
|
|
94
|
+
|
|
95
|
+
`opaque.png` (60×40, fully opaque blue) and `diamond.png` (64×64, a Manhattan-diamond alpha mask) are
|
|
96
|
+
hand-built PNGs (no canvas library — a raw RGBA buffer through `zlib.deflateSync`, wrapped in
|
|
97
|
+
IHDR/IDAT/IEND chunks). `ring.svg` (a 200×200 stroked circle, `fill="none"`, leaving a real transparent
|
|
98
|
+
hole) and `rotated.svg` (a 140×100 rectangle already rotated 20° in its own `transform`) are hand-written
|
|
99
|
+
SVG. None of this is shipped; it only explains where each fixture's bytes and `profileHint` came from.
|
|
Binary file
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
// Usage: node run.mjs [--impl path/to/module.mjs] [--tolerance 2]
|
|
4
|
+
import {resolve} from 'node:path';
|
|
5
|
+
import {dirname} from 'node:path';
|
|
6
|
+
import {fileURLToPath} from 'node:url';
|
|
7
|
+
import {loadDocuments, installMeasurer} from './measurer.mjs';
|
|
8
|
+
|
|
9
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
10
|
+
|
|
11
|
+
function parseArgs(argv) {
|
|
12
|
+
const out = {impl: resolve(here, '../index.mjs'), tolerance: 2};
|
|
13
|
+
for (let i = 0; i < argv.length; i++) {
|
|
14
|
+
if (argv[i] === '--impl') out.impl = resolve(process.cwd(), argv[++i]);
|
|
15
|
+
else if (argv[i] === '--tolerance') out.tolerance = Number(argv[++i]);
|
|
16
|
+
}
|
|
17
|
+
return out;
|
|
18
|
+
}
|
|
19
|
+
const args = parseArgs(process.argv.slice(2));
|
|
20
|
+
const impl = await import(args.impl.startsWith('file://') ? args.impl : 'file://' + args.impl);
|
|
21
|
+
|
|
22
|
+
const need = ['parseLayout', 'formatLayout', 'prepareRun', 'prepareRichInline', 'flowLines', 'pictureSlices', 'bandsProfile', 'polygonProfile', 'rasterTiltProfile', 'parseProfile', 'wrapColumnFloor'];
|
|
23
|
+
const missing = need.filter(name => typeof impl[name] !== 'function');
|
|
24
|
+
if (missing.length) { console.error('run.mjs: implementation at ' + args.impl + ' is missing: ' + missing.join(', ')); process.exit(2); }
|
|
25
|
+
|
|
26
|
+
function buildProfile(hint) {
|
|
27
|
+
if (!hint) return null;
|
|
28
|
+
if (hint.kind === 'bands') return impl.parseProfile(hint.descriptor);
|
|
29
|
+
if (hint.kind === 'polygon') return impl.polygonProfile(hint.corners);
|
|
30
|
+
if (hint.kind === 'rasterTilt') return impl.rasterTiltProfile(hint.width, hint.height, hint.radiusDeg * Math.PI / 180);
|
|
31
|
+
throw new Error('unknown profileHint.kind: ' + hint.kind);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const documents = loadDocuments(here);
|
|
35
|
+
// Install once over merged data: Pretext memoizes its context (measurer.mjs).
|
|
36
|
+
const measurer = installMeasurer(documents);
|
|
37
|
+
|
|
38
|
+
// Preservation and presentation are separate claims; preservation is exact or a bug.
|
|
39
|
+
const preservationRows = documents.map(expected => {
|
|
40
|
+
const comment = expected.picture?.layout;
|
|
41
|
+
if (!comment) return {document: expected.document, verdict: 'n/a', detail: "this fixture's picture carries no md-layout comment"};
|
|
42
|
+
try {
|
|
43
|
+
const value = impl.parseLayout(comment);
|
|
44
|
+
if (!value) return {document: expected.document, verdict: 'FAIL', detail: 'parseLayout refused a comment the fixture actually carries: ' + comment};
|
|
45
|
+
const back = impl.formatLayout(value);
|
|
46
|
+
return back === comment
|
|
47
|
+
? {document: expected.document, verdict: 'EXACT', detail: comment}
|
|
48
|
+
: {document: expected.document, verdict: 'FAIL', detail: `parsed ${JSON.stringify(value)} then reformatted to ${JSON.stringify(back)}, source was ${JSON.stringify(comment)}`};
|
|
49
|
+
} catch (error) { return {document: expected.document, verdict: 'FAIL', detail: error.message}; }
|
|
50
|
+
});
|
|
51
|
+
{
|
|
52
|
+
const width = Math.max(...preservationRows.map(r => r.document.length));
|
|
53
|
+
console.log('Preservation (source facts: exact, no measurer involved)\n');
|
|
54
|
+
console.log('document'.padEnd(width) + ' verdict detail');
|
|
55
|
+
for (const row of preservationRows) console.log(row.document.padEnd(width) + ' ' + row.verdict.padEnd(7) + ' ' + row.detail);
|
|
56
|
+
const exact = preservationRows.filter(r => r.verdict === 'EXACT').length, applicable = preservationRows.filter(r => r.verdict !== 'n/a').length;
|
|
57
|
+
console.log(`\n${exact}/${applicable} documents with a layout comment round-trip it exactly through parseLayout/formatLayout.\n`);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
console.log('Presentation (line boxes; hits/misses show how much of each row rests on the real trace vs. the character-sum fallback)\n');
|
|
61
|
+
const rows = [];
|
|
62
|
+
for (const expected of documents) {
|
|
63
|
+
const font = expected.measurer.font.replace(/\/[^ ]+ /, ' '); // canvas fonts take no line-height
|
|
64
|
+
const before = {hits: measurer.stats.hits, misses: measurer.stats.misses};
|
|
65
|
+
const run = impl.prepareRun(expected.anchorText, font, expected.measurer.letterSpacing || 0);
|
|
66
|
+
if (!run) { rows.push({document: expected.document, verdict: 'ERROR', detail: 'prepareRun refused this font/text/letterSpacing'}); continue; }
|
|
67
|
+
const flow = impl.prepareRichInline([{text: run.raw, font, letterSpacing: expected.measurer.letterSpacing || 0}]);
|
|
68
|
+
let obstacles = [];
|
|
69
|
+
if (expected.picture?.rect) {
|
|
70
|
+
const profile = buildProfile(expected.profileHint);
|
|
71
|
+
const {left, top, right, bottom} = expected.picture.rect;
|
|
72
|
+
obstacles = impl.pictureSlices(profile, left, top, right - left, bottom - top);
|
|
73
|
+
}
|
|
74
|
+
const fontSize = parseFloat(/(\d+(?:\.\d+)?)px/.exec(expected.measurer.font)?.[1]) || 16;
|
|
75
|
+
const minWidth = impl.wrapColumnFloor(fontSize);
|
|
76
|
+
const plan = impl.flowLines(flow, expected.measurer.columnWidth, 0, obstacles, expected.measurer.lineHeight, minWidth, expected.measurer.direction);
|
|
77
|
+
if (!plan) { rows.push({document: expected.document, verdict: 'ERROR', detail: 'flowLines returned null (no plan fits)'}); continue; }
|
|
78
|
+
|
|
79
|
+
const lineCountAgrees = plan.lines.length === expected.lines.length;
|
|
80
|
+
let maxLeftDelta = 0, maxRightDelta = 0;
|
|
81
|
+
for (let i = 0; i < Math.min(plan.lines.length, expected.lines.length); i++) {
|
|
82
|
+
const got = plan.lines[i], want = expected.lines[i];
|
|
83
|
+
// Flowed lines compare slot geometry (x+width); unflowed lines compare the measured tight extent.
|
|
84
|
+
const right = expected.anchorFlowed ? got.x + got.width : got.x + measurer.measure(font, got.fragments.map(f => f.text).join(''));
|
|
85
|
+
maxLeftDelta = Math.max(maxLeftDelta, Math.abs(got.x - want.left));
|
|
86
|
+
maxRightDelta = Math.max(maxRightDelta, Math.abs(right - want.right));
|
|
87
|
+
}
|
|
88
|
+
const withinTolerance = lineCountAgrees && maxLeftDelta <= args.tolerance && maxRightDelta <= args.tolerance;
|
|
89
|
+
rows.push({
|
|
90
|
+
document: expected.document,
|
|
91
|
+
verdict: withinTolerance ? 'AGREE' : lineCountAgrees ? 'CLOSE' : 'DIFFERS',
|
|
92
|
+
expectedLines: expected.lines.length, gotLines: plan.lines.length,
|
|
93
|
+
maxLeftDelta: Math.round(maxLeftDelta * 100) / 100, maxRightDelta: Math.round(maxRightDelta * 100) / 100,
|
|
94
|
+
hits: measurer.stats.hits - before.hits, misses: measurer.stats.misses - before.misses,
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const width = Math.max(...rows.map(r => r.document.length));
|
|
99
|
+
console.log('document'.padEnd(width) + ' verdict lines(exp/got) maxΔleft maxΔright measurer(hits/misses)');
|
|
100
|
+
for (const row of rows) {
|
|
101
|
+
if (row.verdict === 'ERROR') { console.log(row.document.padEnd(width) + ' ERROR ' + row.detail); continue; }
|
|
102
|
+
console.log(row.document.padEnd(width) + ' ' + row.verdict.padEnd(8) + ' ' + `${row.expectedLines}/${row.gotLines}`.padEnd(14) + ' ' +
|
|
103
|
+
`${row.maxLeftDelta}px`.padEnd(8) + ' ' + `${row.maxRightDelta}px`.padEnd(9) + ' ' + `${row.hits}/${row.misses}`);
|
|
104
|
+
}
|
|
105
|
+
const agree = rows.filter(r => r.verdict === 'AGREE').length;
|
|
106
|
+
const close = rows.filter(r => r.verdict === 'CLOSE').length;
|
|
107
|
+
const differs = rows.filter(r => r.verdict === 'DIFFERS').length;
|
|
108
|
+
console.log(`\n${agree}/${rows.length} documents AGREE within ${args.tolerance}px, ${close} CLOSE, ${differs} DIFFERS.`);
|
|
109
|
+
console.log(`Measurer: ${measurer.stats.hits} queries answered from Rapier's own real browser trace, ${measurer.stats.misses} fell back to a character-glyph sum (no kerning/shaping).`);
|
|
110
|
+
console.log(`A row's own hits/misses can undercount its real coverage: several fixtures share one identical anchor sentence, and Pretext's own per-process`);
|
|
111
|
+
console.log(`segment cache (agent/vendor/pretext/measurement.js) serves a repeated (font, segment) query straight from that cache without calling`);
|
|
112
|
+
console.log(`measureText again at all -- real data, just not counted against the SECOND document to ask for it. Two named, separate reasons a row still`);
|
|
113
|
+
console.log(`reads CLOSE rather than AGREE, neither of them "close enough": (a) no real trace exists at all for 01-inline/02-width-x-align/05-behind/06-front`);
|
|
114
|
+
console.log(`-- their picture pushes no flow obstacle, so layout/interchange.js's own prepare() never calls Pretext for this paragraph during export (see`);
|
|
115
|
+
console.log(`measurer.mjs) -- the from-scratch character-sum approximation disagrees with real shaping by a bounded, named amount; (b) 03/09/11/13 DO run`);
|
|
116
|
+
console.log(`Pretext for real (hits > 0, 13-rtl-text at 100% -- every query answered from the real trace) yet still carry a small, constant left-edge delta`);
|
|
117
|
+
console.log(`(5.18px for 03/11/13, 6.63px for 09, unchanged from before this measurer existed) -- a picture-obstacle geometry fact, not a text one: these`);
|
|
118
|
+
console.log(`fixtures' profileHint is a hand-computed, quantized alpha band descriptor for a raster Rapier's own export never serialized an occupancy`);
|
|
119
|
+
console.log(`descriptor for (conformance/README.md, "profileHint"), and that descriptor's own precision -- not this lane's measurer -- is the residual gap.`);
|
|
120
|
+
if (measurer.stats.missedQueries.length) {
|
|
121
|
+
console.log(`\nFirst ${Math.min(10, measurer.stats.missedQueries.length)} of ${measurer.stats.misses} missed (font, text) queries (fell back to the character sum):`);
|
|
122
|
+
for (const {font, text} of measurer.stats.missedQueries.slice(0, 10)) console.log(` ${JSON.stringify(text)} (${font})`);
|
|
123
|
+
}
|