arrolio 0.1.0 → 0.1.2
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.
- checksums.yaml +4 -4
- data/.rubocop.yml +2 -2
- data/Rakefile +15 -0
- data/TODO.complete/56-generic-adapter-mece-decomposition.md +20 -0
- data/TODO.complete/60-adapter-fidelity-footnotes-notes-examples.md +92 -55
- data/TODO.complete/70-ttf-font-metrics-parity.md +68 -0
- data/TODO.complete/71-cover-layout-parity.md +45 -0
- data/TODO.complete/72-table-layout-parity.md +49 -0
- data/TODO.complete/73-toc-leaders-parity.md +55 -0
- data/TODO.complete/74-svg-image-rendering-parity.md +76 -0
- data/TODO.complete/75-mathml-formula-rendering-parity.md +52 -0
- data/TODO.complete/76-odd-even-page-templates-parity.md +50 -0
- data/TODO.complete/77-text-formatting-parity.md +55 -0
- data/TODO.complete/78-bibliography-formatting-parity.md +62 -0
- data/TODO.complete/79-line-breaking-parity.md +80 -0
- data/TODO.complete/80-parity-diff-harness-in-ci.md +68 -0
- data/TODO.complete/81-conditional-style-refinements.md +70 -0
- data/TODO.complete/82-semantic-presentation-dedup.md +65 -0
- data/TODO.complete/83-nested-list-support.md +74 -0
- data/TODO.complete/84-figure-svg-rendering.md +70 -0
- data/TODO.complete/85-locality-reference-formatting.md +48 -0
- data/TODO.complete/86-list-item-text-overflow.md +80 -0
- data/TODO.complete/87-odd-even-page-templates.md +55 -0
- data/TODO.complete/88-hyphenation-support.md +52 -0
- data/TODO.complete/89-table-rowspan.md +49 -0
- data/TODO.complete/90-mathml-stacked-layout.md +65 -0
- data/TODO.complete/91-parity-diff-diagnostics.md +50 -0
- data/TODO.complete/92-table-cell-height-parity.md +66 -0
- data/TODO.complete/93-figure-text-searchability.md +49 -0
- data/TODO.complete/94-fo-space-resolution.md +63 -0
- data/TODO.complete/95-title-header-placement.md +55 -0
- data/TODO.complete/96-pagination-drift-parity.md +517 -0
- data/TODO.complete/README.md +58 -38
- data/lib/arrolio/config_driven_pipeline.rb +45 -1
- data/lib/arrolio/content/document.rb +4 -3
- data/lib/arrolio/content/section.rb +3 -0
- data/lib/arrolio/content/table.rb +25 -14
- data/lib/arrolio/embedded_block_extractor.rb +54 -0
- data/lib/arrolio/engine/paged.rb +109 -28
- data/lib/arrolio/flavor/manifest.rb +0 -1
- data/lib/arrolio/flavor/registry.rb +1 -1
- data/lib/arrolio/flowable.rb +21 -0
- data/lib/arrolio/flowables/figure_flowable.rb +17 -0
- data/lib/arrolio/flowables/footnote_marker_flowable.rb +32 -13
- data/lib/arrolio/flowables/group_flowable.rb +57 -0
- data/lib/arrolio/flowables/list_flowable.rb +46 -18
- data/lib/arrolio/flowables/note_flowable.rb +84 -9
- data/lib/arrolio/flowables/page_sequence_start.rb +8 -4
- data/lib/arrolio/flowables/positioned_block.rb +55 -0
- data/lib/arrolio/flowables/rotated_text.rb +51 -0
- data/lib/arrolio/flowables/table_flowable.rb +303 -71
- data/lib/arrolio/flowables/text_flowable.rb +67 -11
- data/lib/arrolio/flowables/toc_line_flowable.rb +70 -7
- data/lib/arrolio/flowables.rb +4 -0
- data/lib/arrolio/font/embedder.rb +22 -3
- data/lib/arrolio/font_scanner.rb +133 -0
- data/lib/arrolio/generic_adapter/document_extraction.rb +101 -0
- data/lib/arrolio/generic_adapter/footnote_extraction.rb +81 -0
- data/lib/arrolio/generic_adapter/heading_extraction.rb +92 -0
- data/lib/arrolio/generic_adapter/inline_run_collection.rb +136 -0
- data/lib/arrolio/generic_adapter/list_conversion.rb +70 -0
- data/lib/arrolio/generic_adapter/metadata_extraction.rb +46 -0
- data/lib/arrolio/generic_adapter/table_conversion.rb +105 -0
- data/lib/arrolio/generic_adapter.rb +263 -317
- data/lib/arrolio/generic_flow_builder/bibliography.rb +47 -0
- data/lib/arrolio/generic_flow_builder/figures.rb +90 -0
- data/lib/arrolio/generic_flow_builder/lists.rb +66 -0
- data/lib/arrolio/generic_flow_builder/notes.rb +61 -0
- data/lib/arrolio/generic_flow_builder/sequences.rb +102 -0
- data/lib/arrolio/generic_flow_builder/tables.rb +28 -0
- data/lib/arrolio/generic_flow_builder/terms.rb +89 -0
- data/lib/arrolio/generic_flow_builder.rb +92 -187
- data/lib/arrolio/glyph_measurer.rb +16 -5
- data/lib/arrolio/inline_run.rb +2 -1
- data/lib/arrolio/layout_spec/page_template.rb +14 -8
- data/lib/arrolio/math_ml/inline_run_extractor.rb +272 -0
- data/lib/arrolio/math_ml.rb +7 -0
- data/lib/arrolio/output/page.rb +5 -4
- data/lib/arrolio/output/placed_box.rb +5 -2
- data/lib/arrolio/renderer/pdf/assets.rb +94 -0
- data/lib/arrolio/renderer/pdf/font_embedding.rb +77 -0
- data/lib/arrolio/renderer/pdf/metadata.rb +52 -0
- data/lib/arrolio/renderer/pdf.rb +140 -215
- data/lib/arrolio/style/definition.rb +7 -3
- data/lib/arrolio/style/registry.rb +6 -1
- data/lib/arrolio/table/auto_layout.rb +19 -10
- data/lib/arrolio/table/grid.rb +105 -0
- data/lib/arrolio/table.rb +1 -0
- data/lib/arrolio/text_layout/greedy.rb +15 -1
- data/lib/arrolio/text_layout/knuth_plass/breaker.rb +216 -92
- data/lib/arrolio/text_layout/knuth_plass/item_builder.rb +31 -17
- data/lib/arrolio/text_layout/knuth_plass.rb +3 -0
- data/lib/arrolio/text_layout/line.rb +10 -2
- data/lib/arrolio/version.rb +1 -1
- data/lib/arrolio.rb +8 -4
- data/scripts/parity_check.rb +52 -0
- data/scripts/parity_diff.rb +89 -0
- data/scripts/xsl_profiles/standoc.yml +8 -2
- data/scripts/xsl_to_config.rb +28 -1
- metadata +91 -5
|
@@ -0,0 +1,517 @@
|
|
|
1
|
+
---
|
|
2
|
+
priority: P1
|
|
3
|
+
impact: high
|
|
4
|
+
depends_on: [89, 92]
|
|
5
|
+
layer: layout
|
|
6
|
+
status: in progress
|
|
7
|
+
est: 3d
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## Problem
|
|
11
|
+
|
|
12
|
+
With table geometry correct, the remaining parity gap is accumulated
|
|
13
|
+
pagination drift: our body text consumes slightly different vertical
|
|
14
|
+
space than the reference, page by page, so headings and tables land
|
|
15
|
+
on different pages. Whole-table moves (correct FOP semantics) turn
|
|
16
|
+
small drift into large whitespace gaps.
|
|
17
|
+
|
|
18
|
+
## Progress (2026-08-18, 59.12% after geometry corrections)
|
|
19
|
+
|
|
20
|
+
Fixed (all verified against the reference):
|
|
21
|
+
- **Body region geometry**: the body was shrunk by the before/after
|
|
22
|
+
region extents — XSL-FO keeps the body at the full content
|
|
23
|
+
rectangle; extents only size the header/footer strips inside the
|
|
24
|
+
margins. The old geometry wasted 102pt per page and "matched" the
|
|
25
|
+
reference only by accident.
|
|
26
|
+
- **Section levels** derive from the autonumber's dotted depth
|
|
27
|
+
("5.1.1" → 3); the fmt-title depth attribute is absent for most
|
|
28
|
+
sections, so nearly every heading resolved to level 1 and its
|
|
29
|
+
level-specific margins never applied.
|
|
30
|
+
- **Term entry spacing**: +12pt before each entry's number, +6pt
|
|
31
|
+
after the preferred term (reference pitch 109.5pt vs our 87.5pt).
|
|
32
|
+
Terms region now within ±0.2pg.
|
|
33
|
+
- **Heading margins**: heading_2 margin-bottom 20pt, heading_3 8pt
|
|
34
|
+
(reference level-2/3 gaps measured 25pt vs our 17pt).
|
|
35
|
+
- **Header/footer offsets** calibrated to the reference (10.34mm /
|
|
36
|
+
13.6mm from the body margin).
|
|
37
|
+
|
|
38
|
+
The metric moved 64.07% → 59.12% because the corrected geometry
|
|
39
|
+
re-flowed every page; the old alignment was built on a wrong body
|
|
40
|
+
position. Remaining deficits are now clean and quantified below.
|
|
41
|
+
|
|
42
|
+
## Drift map (heading absolute-position delta, 2026-08-18, 59.12%)
|
|
43
|
+
|
|
44
|
+
Measured as (our page×770 + y) − (ref page×770 + y) per heading;
|
|
45
|
+
page breaks reset drift to ~0, so each region is independent:
|
|
46
|
+
|
|
47
|
+
| Region | Drift | Notes |
|
|
48
|
+
|--------|-------|-------|
|
|
49
|
+
| 2.1–3.1 (intro/foreword) | +0.7..1.0pg | we consume more space (open) |
|
|
50
|
+
| 3.2–3.9 (terms) | ±0.2pg | ALIGNED after term spacing fix |
|
|
51
|
+
| 5.1–5.1.5 | ±0.05pg | aligned |
|
|
52
|
+
| 5.1.5→5.1.6 | −103pt | Figure 4 block shorter than reference |
|
|
53
|
+
| 5.3.2→5.4 | −212pt | Two FEATURE gaps, not spacing (2026-08-18): (1) the footnote in 5.3.2's second paragraph renders INLINE in our output; the reference pins its body ("1) Associated with apportionment...") to the PAGE BOTTOM via the FOP footnote area, leaving 115pt of body space free on p19. (2) the "0.3 ≤ pLC ≤ 0.8" formula renders as a centered DISPLAY line in the reference; we inline it. See TODO 60 (page-bottom footnotes) and TODO 90 (display formulas) |
|
|
54
|
+
| 5.5.1→5.5.2 | −125pt | Example blocks fixed (2026-08-18): "Example:" block label + 35.4pt body indent; remainder is creep/math content |
|
|
55
|
+
| 5.6.1→5.6.2 | −127pt | open |
|
|
56
|
+
| 5.7.x | −70pt | open |
|
|
57
|
+
| 6.x–Annex | −0.8pg | follows from above |
|
|
58
|
+
|
|
59
|
+
Beware false anchors when measuring: cross-references in body text
|
|
60
|
+
("see 5.1.6 and 5.1.7") match the heading regex — validate monotonic
|
|
61
|
+
page order before trusting a span.
|
|
62
|
+
|
|
63
|
+
## Key discovery (2026-08-19): the forced breaks are compensating
|
|
64
|
+
|
|
65
|
+
The reference flows CONTINUOUSLY - no forced section breaks in the
|
|
66
|
+
XSL; its fresh-page section starts (3 Terminology, 5 Metrological,
|
|
67
|
+
Bibliography) are natural landings. Our content under continuous
|
|
68
|
+
flow is only 23-25 pages vs the reference's 28: a genuine ~3-5 page
|
|
69
|
+
DENSITY debt that the per-section forced breaks (insert_page_break_
|
|
70
|
+
before/after_section: true) mask by resyncing every region.
|
|
71
|
+
Continuous flow scores 33%, selective breaks (3/5/Bibliography)
|
|
72
|
+
score 42%, forced breaks score 64.53%.
|
|
73
|
+
|
|
74
|
+
Density-debt suspects, in priority order:
|
|
75
|
+
1. mfrac/msqrt flattened to "/" text (TODO 90 open piece): the 3.7
|
|
76
|
+
measurement/error terms contain fraction formulas the reference
|
|
77
|
+
renders as tall stacked blocks (2-3 lines each); ours render one
|
|
78
|
+
line. Likely 1-2 pages.
|
|
79
|
+
2. Line pitch: ours 13.0 vs reference 13.4 (~0.4pt/line = ~0.7pg).
|
|
80
|
+
3. Figures 1-4 sizing and the foreword/intro region's +0.7pg.
|
|
81
|
+
|
|
82
|
+
The builder now supports `section.page_break_before_numbers` for
|
|
83
|
+
landings-matched breaks once the density debt closes.
|
|
84
|
+
|
|
85
|
+
## Shipped (2026-08-25): measure overflows FIXED (three coupled bugs)
|
|
86
|
+
|
|
87
|
+
1. Per-run font measurement: the KP ItemBuilder and InlineRun#width
|
|
88
|
+
measured EVERY run with the paragraph's font — bold runs priced
|
|
89
|
+
at regular widths, so following runs' x_offsets under-allocated
|
|
90
|
+
and words OVERLAPPED ("(OIML , R)" corruption). GlyphMeasurer
|
|
91
|
+
gained an optional font_name on width_of_run/string; both call
|
|
92
|
+
sites pass run.style.font_name.
|
|
93
|
+
2. Justify SHRINK was never rendered: `extra_offset` accumulated
|
|
94
|
+
only when justify was positive, so compressed lines (ratio
|
|
95
|
+
> -1, legitimate TeX shrink — e.g. nat 456.5 vs measure 450.7)
|
|
96
|
+
drew at natural width, past the measure. Both gates fixed
|
|
97
|
+
(positive-only and the <= 0 early return in text_chunks).
|
|
98
|
+
3. List marker geometry now flavor config (list.geometry:
|
|
99
|
+
marker_indent 18.4, marker_width 19.8, item_spacing 5.6) and
|
|
100
|
+
unordered lists render the configured bullet, not the fmt-name
|
|
101
|
+
autonum placeholder ("—" -> "■", x=90.7, hang 110.5/113.2).
|
|
102
|
+
After: ZERO words past the measure document-wide (was 20+ on p4-8).
|
|
103
|
+
Metric 52.52 -> 46.72 on the knife edge (correct bold widths
|
|
104
|
+
re-wrap every bold-bearing paragraph and reshuffle mid-document
|
|
105
|
+
boundaries; p16 is a pure off-by-one shift, no content loss).
|
|
106
|
+
Recovery rides on the density debt below.
|
|
107
|
+
|
|
108
|
+
## Open: foreword lines overflow the measure (2026-08-24)
|
|
109
|
+
|
|
110
|
+
Rendered foreword lines reach x=545 vs the body right edge 523
|
|
111
|
+
(+22pt into the margin; one line +94pt of text beyond the layout
|
|
112
|
+
measure). The LAYOUT is verified correct: TextFlowable#emit
|
|
113
|
+
receives width=450.7 and the standalone KP layout wraps the same
|
|
114
|
+
runs within it (lines <=456). Suspect a renderer-vs-layout metric
|
|
115
|
+
mismatch on these paragraphs (justified word positioning computed
|
|
116
|
+
from different measurements than the layout used). Start here:
|
|
117
|
+
instrument render_line_runs' cursor vs the line's placed-run
|
|
118
|
+
x_offsets for a foreword line.
|
|
119
|
+
|
|
120
|
+
## Shipped (2026-08-24): forced line breaks honored
|
|
121
|
+
|
|
122
|
+
Two coupled bugs fixed: Ruby's split drops trailing empty segments,
|
|
123
|
+
so a lone "\n" run emitted NO forced-break penalty; and the KP
|
|
124
|
+
dynamic program let paths skip forced breaks entirely (the parfill
|
|
125
|
+
glue makes the spanning single line feasible), so even correct
|
|
126
|
+
penalties were ignored. Result: <br>-separated spans rendered GLUED
|
|
127
|
+
("class A;15 degC for class B") - text corruption. All 57 breaks in
|
|
128
|
+
the document now produce their lines (5.6.1.2's per-class pairs
|
|
129
|
+
match the reference structure). The metric drops 65.29 -> 52.93 on
|
|
130
|
+
the page-boundary knife edge (the ~18 in-paragraph breaks add ~250pt
|
|
131
|
+
mid-document and reshuffle every later page); the recovery is the
|
|
132
|
+
remaining density debt plus the foreword's indented measure (ref
|
|
133
|
+
wraps foreword text narrower - an open style item).
|
|
134
|
+
|
|
135
|
+
## New finding (2026-08-24): 5.6.1.2 unitsml list inlined
|
|
136
|
+
|
|
137
|
+
The reference renders 'for load cells of class A; / 5 degC' as
|
|
138
|
+
stacked line PAIRS (one per class); we inline the whole set as one
|
|
139
|
+
flowing line ('5 degC for load cells of class A; 15 degC for...').
|
|
140
|
+
The unitsml values now reserve stacked height (the +digit trigger),
|
|
141
|
+
but the LIST STRUCTURE itself is missing - the XML's list-ish
|
|
142
|
+
markup around these items needs adapter investigation. Same shape
|
|
143
|
+
likely in 5.6.1.3 ('2 degC for load cells of class A...') and the
|
|
144
|
+
6.x marking lists.
|
|
145
|
+
|
|
146
|
+
## Ruled out (2026-08-24)
|
|
147
|
+
|
|
148
|
+
- Note leading: styling note bodies :note applies the style's 8pt
|
|
149
|
+
paragraph margin between every body line (same trap as the
|
|
150
|
+
example fix) and any line_spacing lift (1.3-1.6 swept) drops the
|
|
151
|
+
metric 2-3pp. Notes are correctly spaced in aggregate.
|
|
152
|
+
- Figure sizing: reference figure 1's vector text spans 332pt wide
|
|
153
|
+
vs our placed 337pt - figures are at the right scale (the 0.75
|
|
154
|
+
sweep stands). The -324pt 2.3-to-3 continuous span is page-break
|
|
155
|
+
interaction around figure 1, not missing figure height.
|
|
156
|
+
|
|
157
|
+
## Remaining 3.1 ledger (2026-08-19, fifth pass)
|
|
158
|
+
|
|
159
|
+
After the note-lists restore, 3.1's entry gaps over 15pt:
|
|
160
|
+
after 3.1.3.1 -25, after 3.1.3.2 -40, after 3.1.8 -69, after
|
|
161
|
+
3.1.10 -19. Content verified COMPLETE (the apparent missing words
|
|
162
|
+
in 3.1.9 were a debug-display truncation artifact - always dump
|
|
163
|
+
full lines before diagnosing); the deficits are small spacing
|
|
164
|
+
deltas (note-to-source gaps ~6pt, bullet pitch 14 vs 17).
|
|
165
|
+
|
|
166
|
+
## Fixed (2026-08-19, fourth pass)
|
|
167
|
+
|
|
168
|
+
- Nested-term definition text no longer glues into the parent: the
|
|
169
|
+
raw <concept> element (refterm + renderterm + xref triple) is
|
|
170
|
+
skipped in favor of <fmt-concept> (the rendered form) - term
|
|
171
|
+
references now render once, with their number ("analogue-passive
|
|
172
|
+
load cell (3.1.3.1)"). 'term' joined the block-level skips and
|
|
173
|
+
definition paragraphs scope to the entry's own term.
|
|
174
|
+
- The verified drift map (labels now correct) shows: section 1
|
|
175
|
+
occupies a full page for us vs 414pt in the reference (the forced
|
|
176
|
+
break), 3.1.3.2/.3 spans were short from the (now fixed) missing
|
|
177
|
+
content, and after-section breaks were no-ops. Section-2 region
|
|
178
|
+
runs +44/+24pt per subsection (open).
|
|
179
|
+
|
|
180
|
+
## Correction (2026-08-19, third pass)
|
|
181
|
+
|
|
182
|
+
Section 3.7's per-entry pitch difference is 0pt TOTAL over all 21
|
|
183
|
+
entries - perfectly aligned. The earlier per-subsection span table
|
|
184
|
+
is UNRELIABLE: each row's span belongs to the PREVIOUS section
|
|
185
|
+
(off-by-one labeling), and it was measured on a scratch render.
|
|
186
|
+
Before fixing any more spacing, re-derive the drift map on the
|
|
187
|
+
current render with verified labeling (print heading + position for
|
|
188
|
+
both PDFs side by side). Also tested and REVERTED: a blanket
|
|
189
|
+
SCRIPT_LINE_FACTOR 1.4 on sub/sup-bearing lines (+2 pages overshoot
|
|
190
|
+
- script lines are far too numerous for blanket factors).
|
|
191
|
+
|
|
192
|
+
## Diagnostic notes (2026-08-19, second pass)
|
|
193
|
+
|
|
194
|
+
- Pages 18-20 (18-32%) are one page behind the reference from p17:
|
|
195
|
+
section 3's remaining ~470pt debt (3.7 -104, 3.8 -190, 3.9 -57,
|
|
196
|
+
3.5/3.6 -70 combined) pushes section 4 to a forced break a page
|
|
197
|
+
late. Section 4's own span is aligned (229 vs 237pt).
|
|
198
|
+
- 3.8's term entry pitch is ALIGNED (88 vs 89pt) and the "[1]"
|
|
199
|
+
source marker renders inline correctly (a superscript y-offset
|
|
200
|
+
makes pdftotext split it into a separate row - false alarm); the
|
|
201
|
+
-190pt accumulates later in the region (check its tables/notes).
|
|
202
|
+
- Terminology pitch overall: ours 89.4 vs ref 92.4 (~174pt total) -
|
|
203
|
+
a ~3pt/entry source-line or entry-gap remainder.
|
|
204
|
+
|
|
205
|
+
## Approach
|
|
206
|
+
|
|
207
|
+
1. Instrument per-region spacing: compare y-gaps of consecutive
|
|
208
|
+
headings/paragraphs ours vs reference (re-derive with
|
|
209
|
+
`pdftotext -bbox` + heading regex).
|
|
210
|
+
2. Terms (3.x): measured (2026-08-17) our entry pitch 87.5pt vs
|
|
211
|
+
reference 109.5pt. Per entry the reference has +6pt after the
|
|
212
|
+
preferred term and +12pt before the next entry's number
|
|
213
|
+
(number/preferred/definition lines otherwise identical). CAUTION:
|
|
214
|
+
closing this deficit is a knife edge — +6pt/entry already pushes
|
|
215
|
+
section 4/5 one page later and the overall metric drops ~15pp
|
|
216
|
+
(5.x/6.x misalign). The spacing must land so the region END
|
|
217
|
+
matches the reference exactly; apply the full +18-22pt/entry only
|
|
218
|
+
together with matching the region's non-term content (notes,
|
|
219
|
+
figures 1-3 spacing).
|
|
220
|
+
3. Intro/foreword (2.x): we run 0.8pg longer — compare note/paragraph
|
|
221
|
+
spacing on pages 5-6.
|
|
222
|
+
4. Figure caption spacing in 5.1.6-5.1.7 (~70pt excess).
|
|
223
|
+
5. Each closed gap re-aligns a table and compounds: fix regions in
|
|
224
|
+
document order, re-running parity after each.
|
|
225
|
+
|
|
226
|
+
## Done-When
|
|
227
|
+
|
|
228
|
+
- [ ] Every region in the drift map within ±0.1pg
|
|
229
|
+
- [ ] No table whole-jump leaves >40pt whitespace where the reference
|
|
230
|
+
splits or starts cleanly
|
|
231
|
+
- [ ] Overall parity ≥ 70%
|
|
232
|
+
|
|
233
|
+
## Shipped (2026-08-25): header parity + centered back-matter headings
|
|
234
|
+
|
|
235
|
+
- The TODO 87 `header_align_for` parity logic was unreachable: the
|
|
236
|
+
builder defaulted `header_align: right` and the OIML flavor YAML
|
|
237
|
+
pinned it. `nil` now means "no flavor opinion" through
|
|
238
|
+
PageSequenceStart/Output::Page, and the flavor drops the pin —
|
|
239
|
+
even pages get left headers (ref p4/p28 x=72.3 confirmed).
|
|
240
|
+
- header_offset 10.34 -> 11.68mm: header yMin 39.3 -> 35.55 (ref
|
|
241
|
+
35.54). The offset ADDS to the baseline height (page_h - top +
|
|
242
|
+
offset), so the earlier 10.34 was 3.8pt low.
|
|
243
|
+
- Preface level-1 + Bibliography headings are CENTERED (XSL
|
|
244
|
+
refine_title-style: preface ancestor + level 1 -> text-align
|
|
245
|
+
center) via the new flavor `title_styles: {preface_1: ...,
|
|
246
|
+
bibliography_1: ...}` map — context-driven, zero core flavor
|
|
247
|
+
knowledge. Foreword gap 33.4pt vs ref 33.6 ✓.
|
|
248
|
+
- The TODO 60 document-end marker bridge removed: markers now emit
|
|
249
|
+
ONLY at paragraph reference sites (FOP semantics; unreferenced
|
|
250
|
+
footnote bodies don't render). The bridge double-registered the
|
|
251
|
+
footnote on the last page.
|
|
252
|
+
- Continuous flow re-measured: 25pp vs ref 28. Fresh drift deltas:
|
|
253
|
+
3.1 -189, 3.7 -204 (tail entries), 5.3.2 -150, 5.5.1 -95,
|
|
254
|
+
5.6.1.3 -134, 5.7.2.4 -134, 6.2 -119, 6.2.3 -173; 3.2/5.1.2
|
|
255
|
+
run +100/+157 (we're longer there — table/figure regions to
|
|
256
|
+
re-check). Note: the parity harness is TEXT-based (position
|
|
257
|
+
insensitive) — geometry fixes only move it via page boundaries.
|
|
258
|
+
|
|
259
|
+
## Shipped (2026-08-25): term entry sibling spacing
|
|
260
|
+
|
|
261
|
+
FOP applies space-before BETWEEN sibling blocks: each term entry's
|
|
262
|
+
second+ definition paragraphs ("(For notes...)" line) and the
|
|
263
|
+
SOURCE line carry ~7.6pt space-before in the reference (gaps 21pt
|
|
264
|
+
vs our 14pt = ~10pt/entry deficit, x63 entries ~ 630pt). New
|
|
265
|
+
flavor config `term: {definition_paragraph_space_before: 7.6,
|
|
266
|
+
source_space_before: 7.5}`; the builder applies it after the first
|
|
267
|
+
definition paragraph and on the SOURCE flowable. Entry spans now
|
|
268
|
+
101pt vs ref 98 (3.1.4/.5 measured). Continuous flow 25 -> 26pp
|
|
269
|
+
(ref 28); forced-break render grows to 30pp (knife edge — metric
|
|
270
|
+
40.92% and IGNORED until debt closes; text-based harness punishes
|
|
271
|
+
boundary reshuffles only).
|
|
272
|
+
|
|
273
|
+
## Fresh drift map (2026-08-25, continuous, post term-spacing)
|
|
274
|
+
|
|
275
|
+
Drift is now nearly UNIFORM: -1165pt at 3.1 to -1507 at 5.7.2 —
|
|
276
|
+
i.e. ~1.5 pages consumed by the foreword/intro region, growing
|
|
277
|
+
slowly. Remaining per-region deltas:
|
|
278
|
+
- 5.1.2 span +419pt (WE RUN LONG): our Table 1 + Figure 4 + Table
|
|
279
|
+
2 blocks consume ~598pt where the ref packs ~180pt (heading-to-
|
|
280
|
+
heading, p15-16 vs p17). Needs table/figure height methodology
|
|
281
|
+
(row-pitch? whole-table keep_together page moves? figure scale).
|
|
282
|
+
Ref Table 1 rows pitch ~25-26pt (419/445/470) - same floor as
|
|
283
|
+
ours; suspect Figure 4 image height + page-break waste.
|
|
284
|
+
- 3.7 span -182pt (tail entries 3.7.16-.21 still unsampled).
|
|
285
|
+
- 5.3.2 span -218pt (the display formula + footnote region;
|
|
286
|
+
TODO 90 display-formula piece).
|
|
287
|
+
- 5.6.1.3 -69, 3.1 -72, 5.1.7 -116: smaller residues.
|
|
288
|
+
- 5.5.1 fixed (was -95, now -6); 5.6.1.3 improved (-134 -> -69).
|
|
289
|
+
|
|
290
|
+
## Shipped (2026-08-25, PR #97): table captions carry inline runs
|
|
291
|
+
|
|
292
|
+
extract_table_caption flattened via text_of — embedded stems
|
|
293
|
+
serialized to raw asciimath ('n_("LC")') and rendered across ~3
|
|
294
|
+
stacked pseudo-lines: Table 1's caption block was ~190pt vs the
|
|
295
|
+
reference's ~20pt (the bulk of the 5.1.2 +419pt deficit). Captions
|
|
296
|
+
now collect inline runs (math subscripts survive: "(nLC)" one
|
|
297
|
+
line). Pages 30 -> 29; parity 40.92 -> 41.82.
|
|
298
|
+
|
|
299
|
+
## Shipped (2026-08-26, PRs #99-#100): inline headers, atomic figures, keep-with-next
|
|
300
|
+
|
|
301
|
+
- Title-less numbered clauses are inline-header clauses (standoc
|
|
302
|
+
inline-header): the number prefixes the first paragraph
|
|
303
|
+
('2.1 This Recommendation ...' inline) instead of consuming a
|
|
304
|
+
heading line. Content::Section#heading? is now title-based.
|
|
305
|
+
- Figures are atomic: Flowables::FigureFlowable composes image +
|
|
306
|
+
caption (keep-together) so a caption can never orphan onto the
|
|
307
|
+
next page. caption_gap 28pt / block_gap 24pt from the reference.
|
|
308
|
+
- XSL-FO keep-with-next implemented in the engine: a heading that
|
|
309
|
+
fits but whose next flowable's min_keep_height would not moves
|
|
310
|
+
with it (orphaned '3.6' heading above moved Figure 3 was the
|
|
311
|
+
observed defect). Heading styles set keep_with_next: true.
|
|
312
|
+
- The 5.3.2 'display formula' is actually an INLINE stem
|
|
313
|
+
(block=false) that JEuclid gives stacked vertical extent - the
|
|
314
|
+
MATH_LINE_FACTOR model already covers it. The remaining 5.3.2
|
|
315
|
+
delta is footnote-zone + whole-table page-move interaction, not
|
|
316
|
+
a missing display-math feature.
|
|
317
|
+
- Terminology preamble (VIM/VIML/D9/D11/B18 list) matches the
|
|
318
|
+
reference's flowing-text shape already.
|
|
319
|
+
- REFERENCE NOTE: the ref breaks to a fresh page before '3
|
|
320
|
+
Terminology' (p6 ends at Figure 2's caption with ~260pt
|
|
321
|
+
whitespace) - a landings-matched break to apply via
|
|
322
|
+
page_break_before_numbers once debt closes.
|
|
323
|
+
|
|
324
|
+
Drift after: 3.1 -1111 (was -1345), 3.6 span +242 is page-move
|
|
325
|
+
whitespace (atomic figure), 3.7 -93, 5.3.2 -117, 5.6.1.3 -69.
|
|
326
|
+
Forced-break parity 42.23 -> 44.82; continuous 26pp vs ref 28.
|
|
327
|
+
|
|
328
|
+
## LANDINGS REACHED (2026-08-26, PRs #102-#105): 28/28 pages, 65.79%
|
|
329
|
+
|
|
330
|
+
- Landings-matched breaks shipped: '3 Terminology' and '5
|
|
331
|
+
Metrological' start fresh pages (the reference's only
|
|
332
|
+
top-of-page section landings); each preface clause gets its own
|
|
333
|
+
page (ToC p3, Foreword p4). insert_page_break_before: false.
|
|
334
|
+
- Example bodies: line_spacing 1.5 + 3pt item spacing (ref pitch
|
|
335
|
+
19-20pt; was 13.6/no gap) - ~130pt recovered in 5.5.1.
|
|
336
|
+
- Lead-in paragraphs keep with following TABLES and LISTS
|
|
337
|
+
(mn2pdf keep-with-next): TableFlowable#min_keep_height = caption
|
|
338
|
+
+ repeated header + first welded group; ListFlowable's = first
|
|
339
|
+
item. Figures EXCLUDED - the ref breaks freely between text and
|
|
340
|
+
figures. This reproduced the ref's page-end whitespace before
|
|
341
|
+
table sections and matched the page count.
|
|
342
|
+
- Per-kind list geometry: ordered markers at the margin (x=72,
|
|
343
|
+
hang 92), fixing 6.1/6.2 lists and nested depth.
|
|
344
|
+
- Part-title block space reserved (title.block_space 55pt): first
|
|
345
|
+
body page matches the ref exactly (title y70, heading y130).
|
|
346
|
+
- Trajectory today: 44.82 -> 57.61 -> 61.34 -> 65.22 -> 65.79.
|
|
347
|
+
|
|
348
|
+
Remaining offsets (page map): section 3 (p8-16) runs ~1-3 entries
|
|
349
|
+
ahead per page (terms region ~15-25pt/page denser than ref);
|
|
350
|
+
5.1-5.7 and 6.x run ~0.3 page ahead. Both realign at the landings.
|
|
351
|
+
Next: per-entry comparison in 3.2-3.5 (notes/lists/tables inside
|
|
352
|
+
terms) and the 5.1.x table-split row pitch.
|
|
353
|
+
|
|
354
|
+
## Shipped (2026-08-26, PRs #107-#108): KP line geometry exact
|
|
355
|
+
|
|
356
|
+
- Continuation lines no longer carry a phantom space indent:
|
|
357
|
+
build_placed_runs priced the first placed run from the
|
|
358
|
+
break-following item (the leading glue), shifting every wrapped
|
|
359
|
+
line +2.75pt (+5.5 after double spaces). Offsets are now relative
|
|
360
|
+
to the line's first box. Continuation x=72.28 = ref exact.
|
|
361
|
+
- Line#justify_stretch clamped at zero for overfull lines, so the
|
|
362
|
+
renderer's justify-SHRINK path (PR #95) could never fire:
|
|
363
|
+
compressed lines drew at natural width, up to 10pt past the
|
|
364
|
+
measure, 2.75pt spaces vs ref 2.15. Verified: gaps 2.13-2.18 vs
|
|
365
|
+
ref 2.15, line end 522.96 vs ref 522.99, zero overflows.
|
|
366
|
+
- Remaining wrap deltas (ours 5 lines where ref fits 4) trace to
|
|
367
|
+
inter-word natural width vs FOP's glue model - next calibration
|
|
368
|
+
if pursued.
|
|
369
|
+
|
|
370
|
+
## Shipped (2026-08-26, PR #110): FOP-faithful shrink pricing - 66.6%
|
|
371
|
+
|
|
372
|
+
FOP justifies every line to the measure after breaking: inter-word
|
|
373
|
+
shrink is not penalized. TeX's cubic badness made our DP prefer an
|
|
374
|
+
extra loose line where the reference compresses (5.6's paragraph:
|
|
375
|
+
ours 5 lines, ref 4, identical words/measure). Demerits now charge
|
|
376
|
+
zero for negative ratios; free shrink alone let the DP pack whole
|
|
377
|
+
paragraphs onto one line through the un-rejectable forced final
|
|
378
|
+
break, so overfull forced lines carry OVERFULL_FORCED_PENALTY as a
|
|
379
|
+
last resort. The 5.6 paragraph now wraps identically to the
|
|
380
|
+
reference.
|
|
381
|
+
|
|
382
|
+
## Residuals (post-#110 census, same-page entry spans)
|
|
383
|
+
|
|
384
|
+
Mostly +/-9-14pt (heading-gap + line-box differences):
|
|
385
|
+
- level-2 heading blocks: ours 33pt vs ref 25 (heading_2 line box
|
|
386
|
+
15.4 + mb 20 - collapsed term mt 12). Tested mb 20->12: OVERSHOT
|
|
387
|
+
(27pp, 51.66%) - the heading gap is NOT simply margin; likely
|
|
388
|
+
FOP's heading line-height (normal ~1.17 vs our inherited 1.24).
|
|
389
|
+
Next attempt: heading_2 line_spacing, not margin.
|
|
390
|
+
- 3.1.3.1 -19, 5.1 +35, 5.1.6 -19.
|
|
391
|
+
- p16's ~5-line lead accumulates across 3.5-3.8's cross-page
|
|
392
|
+
spans (page-end whitespace differences, not entry deficits).
|
|
393
|
+
|
|
394
|
+
## Negative results (2026-08-26 keep-rules investigation)
|
|
395
|
+
|
|
396
|
+
- WIDOWS/ORPHANS are NOT enforced by the reference: it ends p16
|
|
397
|
+
with the lone word 'conversion.' (a 1-line widow) and carries
|
|
398
|
+
single SOURCE lines to page tops. An orphans/widows guard in
|
|
399
|
+
TextFlowable#do_split was implemented, measured (29pp, 57.2),
|
|
400
|
+
and REVERTED. Do not re-add without new evidence.
|
|
401
|
+
- TERM ENTRIES are NOT atomic: the reference splits them
|
|
402
|
+
mid-entry on full pages (p9 ends mid-definition, p16 starts with
|
|
403
|
+
a carried SOURCE line). Blanket entry grouping measured 30pp and
|
|
404
|
+
was reverted. The narrower rule reproducing the ref's gaps at
|
|
405
|
+
p8/p11/p12/p13 (50-90pt page-end whitespace, next entry at page
|
|
406
|
+
top) is still unidentified - number+preferred+whole-first-def-
|
|
407
|
+
paragraph was the closest hypothesis (needs number/preferred
|
|
408
|
+
keep_with_next + widows=2 min_keep, which over-moved elsewhere).
|
|
409
|
+
- heading_2 correction (ours 33 vs ref 25 per heading, ~25 spots):
|
|
410
|
+
full fix (mb 12, spacing 1.13) lands at 27pp - it must ship
|
|
411
|
+
together with reproducing the ref's terms-region gaps (+~260pt)
|
|
412
|
+
or the bibliography-page whitespace; partial bisection is
|
|
413
|
+
bistable 27/29pp. Coupled landing documented, do not bisect.
|
|
414
|
+
- Keep-with-next is now CHAIN-aware (PR #113).
|
|
415
|
+
|
|
416
|
+
## Shipped (2026-08-27, PRs #115-#116): widows model + termnote attachment - 66.95%
|
|
417
|
+
|
|
418
|
+
- Style widows/orphans defaults 2 -> 1 (the reference does not
|
|
419
|
+
enforce them; evidence in the negative results below).
|
|
420
|
+
TextFlowable#min_keep_height honors style widows (first line at
|
|
421
|
+
the new default).
|
|
422
|
+
- <termnote> now belongs to its entry's definition (was a sibling
|
|
423
|
+
block), rendered between definition and SOURCE in document
|
|
424
|
+
order; term-embedded notes carry +7pt before/after
|
|
425
|
+
(term.note_spacing; measured 3.1.3.1: def->Note 21 vs our 14,
|
|
426
|
+
Note->list 20 vs 13). Wrapper group deliberately NOT
|
|
427
|
+
keep-together (29pp when it was).
|
|
428
|
+
- NOTE margin model finding: the engine's margin collapse is
|
|
429
|
+
min(prev_after, curr_before) and ListFlowable#height ignores
|
|
430
|
+
style margins entirely - note spacing only lands via explicit
|
|
431
|
+
Spacers. A margin-model cleanup is a candidate TODO.
|
|
432
|
+
- Remaining term-entry internals: Note->its-list still 13 vs 20
|
|
433
|
+
(the note's internal list spacing); list-in-note indent ours
|
|
434
|
+
118/138 vs ref 143/163 (~25pt deeper in the reference); entry
|
|
435
|
+
tail after a list 17 vs 28.
|
|
436
|
+
|
|
437
|
+
## Shipped (2026-08-27, PR #118): note-body list geometry - term internals exact
|
|
438
|
+
|
|
439
|
+
3.1.3.1's note block now matches the reference line-for-line:
|
|
440
|
+
text -> list 20pt (note.body_spacing 7), bullet marker o at
|
|
441
|
+
+43.7pt from the note column (x=144 vs ref 143.1) with 17pt pitch
|
|
442
|
+
(note.list overrides, note-nested lists only), note trailing 3pt
|
|
443
|
+
after (list -> next entry 28pt exact; leading stays 7pt for
|
|
444
|
+
def -> Note 21).
|
|
445
|
+
|
|
446
|
+
## Entry-head keeps: near-miss retry (2026-08-27, not shipped)
|
|
447
|
+
|
|
448
|
+
With the note fixes landed, retried term definition_widows 2 +
|
|
449
|
+
number/preferred keep_with_next TOGETHER with the heading_2
|
|
450
|
+
correction (mb 12, spacing 1.13): p8's gap REPRODUCED (ours ws 69
|
|
451
|
+
vs ref 76) and pages held 28/28 - but p17 collapsed (ws 670: the
|
|
452
|
+
section-4 tail interacts with the tightened headings) and p11/p12
|
|
453
|
+
still filled. 62.29 vs committed 66.59. NOT shipped; the missing
|
|
454
|
+
pieces are p11/p12's gaps and the section-4 tail interaction. The
|
|
455
|
+
recipe is recorded here - flip term definition_widows: 2, add
|
|
456
|
+
keep_with_next to the term number/preferred styles, and heading_2
|
|
457
|
+
mb 12 / spacing 1.13 - all three together.
|
|
458
|
+
|
|
459
|
+
## Recipe probe round 2 (2026-08-27): heading_1 gap found, p17 persists
|
|
460
|
+
|
|
461
|
+
Applied the full recipe PLUS heading_1 margin_bottom 9pt (new
|
|
462
|
+
finding: mid-flow level-1 headings run 17pt to first text vs the
|
|
463
|
+
reference's 26 - section 4 'Description of load cells' measured
|
|
464
|
+
directly). Result: section-3 same-page spans went CLEAN (only
|
|
465
|
+
3.5.2 +12 / 3.5.11 +15 left), p8's gap reproduced - but the
|
|
466
|
+
page-fill boundaries at p9-p15 still mismatch (ours fills where
|
|
467
|
+
ref leaves 23-73pt), p17 still collapses (ws 670: section 4's
|
|
468
|
+
last paragraph splits 1/2 lines onto a near-empty page before the
|
|
469
|
+
'5' landing), and 6.2.3 runs -56. Net 61.69 vs committed 66.59.
|
|
470
|
+
|
|
471
|
+
Remaining to unlock the landing (all measured):
|
|
472
|
+
- the ~23-73pt/page fill mismatch through p9-p15 despite clean
|
|
473
|
+
entry spans (the gaps sit at DIFFERENT entries than the ref's -
|
|
474
|
+
cumulative position offset upstream of each page)
|
|
475
|
+
- section 4's tail: needs ~17pt more than heading_1's +9 gave
|
|
476
|
+
- 6.2.3 -56pt
|
|
477
|
+
|
|
478
|
+
## Shipped (2026-08-27, PR #121): dead-zone line breaking - 66.8%
|
|
479
|
+
|
|
480
|
+
CONTENT BUG (not spacing): paragraphs with math rendered as ONE
|
|
481
|
+
overfull line clipped at the page edge - 6.2.3's items lost
|
|
482
|
+
'vmin, Y, where Y = (Emax-Emin)/vmin (see 3.5.15)'. Root causes:
|
|
483
|
+
(1) normalize_text stripped newline-boundary whitespace (glued
|
|
484
|
+
'whereY='); (2) with real fontist metrics the break candidates
|
|
485
|
+
sat in a DEAD ZONE (all over-shrunk or too-loose; the emergency
|
|
486
|
+
pass's fixed 20pt stretch cannot bridge 70-160pt-short
|
|
487
|
+
candidates) - it now accepts any stretch ratio; (3) the overfull
|
|
488
|
+
single line was CHEAPER than the loose fix (cubic badness 5.5e7
|
|
489
|
+
vs the 1e6 penalty) - OVERFULL_FORCED_PENALTY now 1e9.
|
|
490
|
+
Regression spec added (dead-zone paragraph >1 line).
|
|
491
|
+
|
|
492
|
+
DEBUGGING NOTE: standalone layout scripts that register
|
|
493
|
+
layout_spec.font_paths (empty for this flavor) silently use
|
|
494
|
+
ESTIMATE metrics - the pipeline resolves fonts via fontist
|
|
495
|
+
(FontScanner). Always register the fontist way in debug scripts
|
|
496
|
+
or the layout differs from the pipeline.
|
|
497
|
+
|
|
498
|
+
## Shipped (2026-08-28, PR #123): dl geometry + heading_1 space-before - 68.92%
|
|
499
|
+
|
|
500
|
+
- 3.9's abbreviation <dl>: 19pt/item vs ref 16pt (57pt on p16).
|
|
501
|
+
Definition lists carry their own kind (:definition) and geometry
|
|
502
|
+
(list.geometry.definition: marker at margin, 28pt column, 2.6pt
|
|
503
|
+
item spacing).
|
|
504
|
+
- heading_1 margin_top 16pt: mid-page level-1 headings sit 33pt
|
|
505
|
+
below the preceding block in the ref (ours 14); ref landing
|
|
506
|
+
pages apply it at page tops too ('3 Terminology' y90 not y75).
|
|
507
|
+
- CONTENT census clean: the remaining word-count deltas vs the ref
|
|
508
|
+
are all extraction artifacts (figure vector text = TODO 93, and
|
|
509
|
+
JEuclid's literal 'unitsml(degC)' alt text) - no dropped content
|
|
510
|
+
remains after the dead-zone fix.
|
|
511
|
+
- Recipe (heading_2 + term keeps) retested on this base: entry
|
|
512
|
+
gaps reproduce (p8 69 vs 76) but nets -6pp; still blocked by the
|
|
513
|
+
p9-p15 fill mismatch.
|
|
514
|
+
|
|
515
|
+
## Measurement
|
|
516
|
+
|
|
517
|
+
`bundle exec rake parity:check` — 64.07% (2026-08-17).
|
data/TODO.complete/README.md
CHANGED
|
@@ -1,48 +1,68 @@
|
|
|
1
1
|
# TODO.complete — Path to 100% mn2pdf parity
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
OIML output byte-equivalent (visually + textually) to mn2pdf's
|
|
5
|
-
output for the same input XML.
|
|
6
|
-
|
|
7
|
-
The reference rendering is `mn2pdf v2.55` (Java), invoked via
|
|
8
|
-
Metanorma. Reference PDF for r060/1 lives at
|
|
9
|
-
`~/src/mn/mn-samples-oiml/_site/documents/r060/1/document.pdf`
|
|
10
|
-
(28 pages, 744 KB, fonts: FuturaPT-Book/Demi/Light,
|
|
11
|
-
TimesNewRomanPSMT family, Cambria Math).
|
|
12
|
-
|
|
13
|
-
The authoritative style reference is
|
|
14
|
-
`~/src/mn/metanorma-taste/data/oiml/oiml.xsl` (767 lines).
|
|
15
|
-
|
|
16
|
-
## Front matter shape (every file)
|
|
17
|
-
|
|
18
|
-
```yaml
|
|
19
|
-
---
|
|
20
|
-
priority: P0 # P0 = blocks parity; P1 = important; P2 = polish; P3 = future
|
|
21
|
-
impact: high # high = >5% similarity swing; med = 1-5%; low = <1%
|
|
22
|
-
depends_on: [] # TODO numbers
|
|
23
|
-
layer: foundation | metrics | text | flowable | engine | output | render | adapter | harness
|
|
24
|
-
status: pending # pending | in_progress | done
|
|
25
|
-
est: 1d # 1d = single focused session; 5d = multi-week module
|
|
26
|
-
---
|
|
27
|
-
```
|
|
3
|
+
## Current baseline (2026-08-09)
|
|
28
4
|
|
|
29
|
-
|
|
5
|
+
`bundle exec rake parity:check` reports **62.7%** overall similarity
|
|
6
|
+
on the OIML r060/1 fixture, **28 pages vs 28 reference** (all pages PARTIAL+).
|
|
30
7
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
8
|
+
376 specs pass, 0 failures, 0 rubocop offenses across 174 files.
|
|
9
|
+
|
|
10
|
+
## Status snapshot
|
|
34
11
|
|
|
35
|
-
|
|
12
|
+
| TODO | Status | Layer | Impact | What shipped |
|
|
13
|
+
|------|--------|-------|--------|-------------|
|
|
14
|
+
| 60 | done | adapter | med | Per-page footnote model: marker, zone reservation, page-bottom bodies |
|
|
15
|
+
| 61 | done | engine | high | Typed errors with metadata, strict mode |
|
|
16
|
+
| 62 | done | font | low | FontScanner + FontMetrics::Registry auto-resolution |
|
|
17
|
+
| 63 | done | adapter | med | Zero flavor artifacts in lib/ (grep-enforced) |
|
|
18
|
+
| 64 | done | adapter | med | Selector-driven adapter specs |
|
|
19
|
+
| 66 | done | content | high | Content::Note, Example, TermEntry, FigureGroup, BibliographyItem |
|
|
20
|
+
| 70 | done | metrics | critical | TTF metrics via Fontisan, font embedding, subsetting |
|
|
21
|
+
| 71 | in progress | flowable | high | PositionedBlock, RotatedText, Style#text_transform shipped |
|
|
22
|
+
| 72 | in progress | flowable | high | colspan, bold headers, continuation caption, row min_height |
|
|
23
|
+
| 73 | done | flowable | med | Right-aligned page numbers, bold level-1, sub-indent |
|
|
24
|
+
| 74 | done | render | high | SVG rendering via rsvg-convert (covered by TODO 84) |
|
|
25
|
+
| 75 | done | adapter | high | MathML extraction from fmt-stem (recursive math element search) |
|
|
26
|
+
| 76 | in progress | engine | med | Odd/even header alignment shipped |
|
|
27
|
+
| 77 | done | adapter | low | Sub/sup baseline, note label suffix, hyperlink underline |
|
|
28
|
+
| 78 | done | adapter | low | Bibliography hanging indent + tag formatting |
|
|
29
|
+
| 79 | in progress | text | high | KP badness, emergency stretch, Glue merge, TOLERANCE |
|
|
30
|
+
| 80 | done | harness | critical | rake parity:check CI task |
|
|
31
|
+
| 81 | in progress | adapter | high | Conditional XSL style refinements (heading margins, term spacing) |
|
|
32
|
+
| 82 | done | adapter | high | Semantic/presentation element deduplication (xref, eref, identifier) || 83 | done | adapter | high | Nested list support (adapter + flow builder) |
|
|
33
|
+
| 84 | done | render | critical | Inline SVG extraction + rsvg-convert rasterization (parity 53→57%) |
|
|
34
|
+
| 85 | done | adapter | medium | Locality reference formatting (clause=X → clause X) |
|
|
35
|
+
| 86 | done | text | high | KP emergency pass, run-group styles, word-level justify (text loss fixed) |
|
|
36
|
+
| 87 | pending | engine | medium | Odd/even page template variants |
|
|
37
|
+
| 88 | pending | text | medium | Hyphenation support for long words |
|
|
38
|
+
| 89 | done | flowable | high | Rowspan via Table::Grid (welded groups, valign) |
|
|
39
|
+
| 90 | in progress | flowable | medium | Operator-math stacking shipped (1.9x); mfrac/msqrt open |
|
|
40
|
+
| 91 | done | harness | high | Parity diff diagnostics (rake parity:diff PAGE=N) |
|
|
41
|
+
| 92 | done | flowable | high | Table geometry config, footnotes, px→pt SVG, caption-in-table |
|
|
42
|
+
| 93 | pending | render | medium | Figure text searchability (SVG text overlay for pdftotext) |
|
|
43
|
+
| 94 | done | engine | critical | FO space resolution (max of consecutive spaces) — 28 pages! |
|
|
44
|
+
| 95 | done | engine | high | Title block as header static content |
|
|
45
|
+
| 96 | in progress | layout | high | Math stacking shipped (64.53%); intro + 3.6-4 region open |
|
|
36
46
|
|
|
37
|
-
##
|
|
47
|
+
## Architectural quality (audited 2026-08-10)
|
|
38
48
|
|
|
39
|
-
- `
|
|
40
|
-
- `
|
|
41
|
-
- `
|
|
42
|
-
- `
|
|
49
|
+
- Zero `require_relative` in lib/
|
|
50
|
+
- Zero internal `require 'arrolio/...'` in lib/ (autoload only)
|
|
51
|
+
- Zero `respond_to?` in lib/
|
|
52
|
+
- Zero `instance_variable_set/get` in lib/
|
|
53
|
+
- Zero `send` to private methods in lib/
|
|
54
|
+
- OCP: `CONVERTER_REGISTRY` (adapter), `ELEMENT_HANDLERS` (MathML)
|
|
55
|
+
- All value objects frozen with `==`/`eql?`/`hash`
|
|
56
|
+
- All internal code uses autoload (no require_relative)
|
|
57
|
+
- `public_send` only for dynamic dispatch on EXTERNAL Mml objects
|
|
58
|
+
|
|
59
|
+
## Critical path to 90%+
|
|
60
|
+
|
|
61
|
+
```
|
|
62
|
+
96 (drift map, region by region) → 71 (cover) → 88 (hyphenation) → 90 (mfrac)
|
|
63
|
+
```
|
|
43
64
|
|
|
44
65
|
## Verification
|
|
45
66
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
must not regress.
|
|
67
|
+
`bundle exec rake` (rspec + rubocop) must be green.
|
|
68
|
+
`bundle exec rake parity:check` similarity must not regress.
|