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.
Files changed (55) hide show
  1. package/LICENSE +40 -0
  2. package/README.md +102 -0
  3. package/dist/agent/vendor/pretext/LICENSE +21 -0
  4. package/dist/agent/vendor/pretext/NOTICE +15 -0
  5. package/dist/agent/vendor/pretext/SOURCE.json +85 -0
  6. package/dist/agent/vendor/pretext/analysis.js +1234 -0
  7. package/dist/agent/vendor/pretext/bidi.js +151 -0
  8. package/dist/agent/vendor/pretext/generated/bidi-data.js +3831 -0
  9. package/dist/agent/vendor/pretext/layout.js +308 -0
  10. package/dist/agent/vendor/pretext/line-break.js +636 -0
  11. package/dist/agent/vendor/pretext/line-text.js +44 -0
  12. package/dist/agent/vendor/pretext/measurement.js +189 -0
  13. package/dist/agent/vendor/pretext/package.json +3 -0
  14. package/dist/agent/vendor/pretext/rich-inline.js +291 -0
  15. package/dist/agent/will.mjs +167 -0
  16. package/dist/kit/assets.mjs +3 -0
  17. package/dist/kit/conformance/01-inline.expected.json +82 -0
  18. package/dist/kit/conformance/01-inline.md +10 -0
  19. package/dist/kit/conformance/02-width-x-align.expected.json +83 -0
  20. package/dist/kit/conformance/02-width-x-align.md +12 -0
  21. package/dist/kit/conformance/03-wrap-around-silhouette.expected.json +177 -0
  22. package/dist/kit/conformance/03-wrap-around-silhouette.md +12 -0
  23. package/dist/kit/conformance/04-wrap-box.expected.json +173 -0
  24. package/dist/kit/conformance/04-wrap-box.md +12 -0
  25. package/dist/kit/conformance/05-behind.expected.json +122 -0
  26. package/dist/kit/conformance/05-behind.md +12 -0
  27. package/dist/kit/conformance/06-front.expected.json +122 -0
  28. package/dist/kit/conformance/06-front.md +12 -0
  29. package/dist/kit/conformance/07-rotate-raster.expected.json +179 -0
  30. package/dist/kit/conformance/07-rotate-raster.md +12 -0
  31. package/dist/kit/conformance/08-drawing-rotation.expected.json +188 -0
  32. package/dist/kit/conformance/08-drawing-rotation.md +12 -0
  33. package/dist/kit/conformance/09-drawing-ring-interior.expected.json +229 -0
  34. package/dist/kit/conformance/09-drawing-ring-interior.md +12 -0
  35. package/dist/kit/conformance/10-both-sides.expected.json +221 -0
  36. package/dist/kit/conformance/10-both-sides.md +10 -0
  37. package/dist/kit/conformance/11-neighbour-skips-image.expected.json +160 -0
  38. package/dist/kit/conformance/11-neighbour-skips-image.md +13 -0
  39. package/dist/kit/conformance/12-heading-barrier.expected.json +164 -0
  40. package/dist/kit/conformance/12-heading-barrier.md +12 -0
  41. package/dist/kit/conformance/13-rtl-text.expected.json +179 -0
  42. package/dist/kit/conformance/13-rtl-text.md +8 -0
  43. package/dist/kit/conformance/README.md +99 -0
  44. package/dist/kit/conformance/measurer.mjs +0 -0
  45. package/dist/kit/conformance/run.mjs +123 -0
  46. package/dist/kit/index.mjs +6 -0
  47. package/dist/kit/layout.mjs +2 -0
  48. package/dist/kit/marks.mjs +2 -0
  49. package/dist/kit/model.mjs +3 -0
  50. package/dist/kit/will.mjs +2 -0
  51. package/dist/layout/model.mjs +364 -0
  52. package/dist/spec/md-assets.mjs +323 -0
  53. package/dist/spec/md-layout.mjs +117 -0
  54. package/dist/spec/md-marks.mjs +78 -0
  55. 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.
@@ -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
+ }
@@ -0,0 +1,6 @@
1
+ // SPDX-License-Identifier: MIT
2
+ export * from './layout.mjs';
3
+ export * from './marks.mjs';
4
+ export * from './model.mjs';
5
+ export * from './will.mjs';
6
+ export * from './assets.mjs';
@@ -0,0 +1,2 @@
1
+ // SPDX-License-Identifier: MIT
2
+ export * from '../spec/md-layout.mjs';
@@ -0,0 +1,2 @@
1
+ // SPDX-License-Identifier: MIT
2
+ export * from '../spec/md-marks.mjs';
@@ -0,0 +1,3 @@
1
+ // SPDX-License-Identifier: MIT
2
+ export * from '../layout/model.mjs';
3
+ export {prepareRichInline, materializeRichInlineLineRange} from '../agent/vendor/pretext/rich-inline.js';
@@ -0,0 +1,2 @@
1
+ // SPDX-License-Identifier: MIT
2
+ export * from '../agent/will.mjs';