Commit Graph
24 Commits
Author SHA1 Message Date
Steffen Becker aa2f680ad7 docs: what the corpus still reports, and why 2026-08-10 11:42:41 +00:00
Steffen Becker ab83ad3c58 test: the parity runner compares a leader as one token, and polls
How many dots fill a tab or index leader is the fill's own business —
both engines end it at the same stop. The settle predicate now polls on a
timer: walking every spacer and index row each frame starved the layout
it was waiting for, and half the runs timed out.
2026-08-10 11:32:40 +00:00
Steffen Becker c26aac1bee fix: the index's leader fills its own gap, at LibreOffice's indents
Every row carried 200 dots — clipped on screen, but the clipboard, the
PDF and any measurement got all of them. Cut each to what its gap holds,
and step the levels by 0.5cm, which is what LibreOffice's Contents styles
use. The parity runner now waits for the page numbers to settle too.
2026-08-10 09:32:49 +00:00
Steffen Becker 4ad743dc28 test: the parity runner waits for the import, not just for a blank page
A 3.5MB fixture takes seconds to parse; until then the editor holds the
empty document, whose layout is stable enough to pass the settle window.
2026-08-10 08:17:40 +00:00
Steffen Becker 2e70e47912 test: the parity runner can reuse a dev server already listening
PARITY_PORT points it at an existing npm run dev instead of starting a
second one on 5199.
2026-08-10 07:38:31 +00:00
Steffen Becker 2f6e771f06 docs: record what a metafile brings and why a line still breaks early
The line-break finding is sharpened: LibreOffice compresses inter-word
spaces to fit a line (91 of 349 full-width justified lines on the
thesis, up to 0.83px per space), which CSS justification cannot. A
matching negative word-spacing was measured and reverted.
2026-08-09 05:59:58 +00:00
Steffen Becker c53bef8cae docs: record why a hoisted text box's offset was not measured back 2026-08-09 00:13:41 +00:00
Steffen Becker bf98014a3a feat: a chart is drawn from its own part instead of a placeholder
word/charts/chartN.xml → an SVG picture of the frame's size: series from the
producer's numCache, theme accent colours, titles, gridlines and the axis
bounds the file fixes. Bar, line, area, scatter and pie; the rest keep the box.
2026-08-08 23:49:13 +00:00
Steffen Becker 2fe762a594 fix: a citation inside a content control keeps its text
Word wraps every citation in a w:sdt; the inline walk ignored the wrapper, so
its runs never reached the paragraph. a fixture fixture lost every [7]-style
reference with them, and now matches LibreOffice's page count.
2026-08-08 23:17:47 +00:00
Steffen Becker 3154a9b7a2 feat: a list label takes the alignment the file gives it
Word's w:lvlJc / ODF's fo:text-align on the level: set right, the label grows
into the margin instead of over the text it labels. That is what the built-in
Roman numberings use, and a fixture's numerals collided without it.
2026-08-08 23:09:58 +00:00
Steffen Becker 2830e8a9d4 feat: a top-and-bottom frame is drawn where the file places it
Both anchor offsets now render: the x moves the rotor inside the full-width
float, the y the float's top. Chromium puts every line after such a float
below it, so a frame set below the paragraph top is sunk behind the text at
import and the node view measures what those lines already cover.
2026-08-08 22:51:39 +00:00
Steffen Becker 412701cdec docs: record what LibreOffice does with a nested level's text-indent 2026-08-08 17:47:17 +00:00
Steffen Becker ca3767f3d4 feat: a cell keeps its own margins where they differ from the table's
w:tcMar and an ODF cell style's own fo:padding were both dropped: the
table's value (in ODF the first cell's) stood for every cell.

cellPadding is now an attr on tableCell/tableHeader too, measured against
the table's rather than the editor default, so only a real disagreement
carries one. --cell-pad inherits, so the cell's own declaration wins for
its own td and the CSS is unchanged.

Checked against LibreOffice: a 1.2cm cell in a 0.3cm table puts its text
at 332.2pt where the grid column starts at 297.6.
2026-08-08 17:47:06 +00:00
Steffen Becker aad4e7a3e4 feat: a later section's header and footer can be edited
The zones already imported, rendered and exported per section, but only
section 1 was editable state — the rest were marked read-only.

Editing now targets the section of the page it starts on: a double-click
edits the zone under the pointer, the Layout-panel buttons the current
page's. zoneKey/writeZone route the single live editor's read and its
write-back to either the six per-zone props or the extraHfSections entry.
2026-08-08 17:00:49 +00:00
Steffen Becker 9d85ddb213 docs: record what dropping the block strut costs
Measured against LibreOffice on a purpose-built fixture the rule is
right, and on the corpus it is a loss: one fixture gains 22 issues.
The strut is also what holds up a line of no runs and, through the
reserved ::after, the paragraph mark on the last line.
2026-08-08 16:32:59 +00:00
Steffen Becker 7d5bc5e0c9 feat: a run of tabs too wide for its line carries on the next
LibreOffice restarts the tab grid on the continuation line; Chromium
hangs the leftover tabs and starts the next line at the margin, 160mm
away in the repro. The run is walked from the pen before it — the one
thing the fix can't move, so the answer holds whether or not the break
is already in place — and a break widget goes where the line runs out.

Measuring this turned up why the two disagreed in the first place: a
file that declares no tab interval falls back to ODF's own 2cm, not to
LibreOffice's UI default of 1.25, so leaving ours implied on export
rendered every tab elsewhere. Both exports declare it now, and both
imports fall back to their format's value rather than to ours.

Only runs of two or more are walked: reading a block's geometry forces
a reflow, and doing it per tab across 48 pages slowed pagination enough
that the parity harness compared a layout still settling.
2026-08-08 15:58:40 +00:00
Steffen Becker ae2290e22c feat: a header or footer keeps its own tab stops
The left⇥centre⇥right layout every letterhead uses rides on them, and a
zone had none: Word keeps them on the Header style, ODF on the zone
paragraph's, and neither was read or written.

An inactive zone is static generateHTML output no plugin reaches, so it
is measured straight on the DOM. Two bugs surfaced doing it: a segment
its own advance had pushed onto the next line measured as wrapped, and
the alignment then had no way back — it takes the sum of the segment's
client rects now. And the scale came from offsetHeight, which rounds
half a percent away on a zone one line tall.
2026-08-08 15:13:58 +00:00
Steffen Becker 6accc7268f feat: every section keeps its own header and footer
A document had one set for all of it, taken from whichever section
declared one last — so a fixture lost its letterhead entirely
and page 1 got the empty header its final section asked for.

A body block now carries `sectionBreak`, ordinal rather than indexed so
editing cannot desync it. Import resolves Word's "Link to Previous" per
reference type; export writes a set per sectPr, and on the ODF side each
section past the first gets its own master page, which the block switches
to. The layer picks a page's set from where pageBreaks reports the
sections starting, and each section's zone reaches ride
`--pb-section-reach` so a later section's letterhead makes room for
itself: page 10 of a fixture was 24.9mm out, now 1.1mm.

Editing still reaches section 1 only; the rest render read-only.
2026-08-08 14:02:36 +00:00
Steffen Becker dfa6f27638 docs: the marker is placed; its level's own hanging is not read 2026-08-08 12:57:44 +00:00
Steffen Becker eb4d15c02c docs: say which parity findings measurement did not support
Three of them described something the reference does not do. The trailing
space is counted by LibreOffice as well; neither engine breaks after a
slash or inside a run of dots; and the hard-break line was real but came
from the paragraph mark, not the break.

Each was checked against soffice on a one-page fixture, so the record now
says what was measured rather than what was inferred.
2026-08-08 12:40:26 +00:00
Steffen Becker e4b679ad8b test: find whichever Chromium build is installed
The parity runner pinned one build number, so a fresh
`playwright-core install chromium` left it pointing at a directory that
is not there. PARITY_CHROME still overrides.
2026-08-08 12:17:51 +00:00
Steffen Becker a4209fd622 docs: bring the parity findings up to date
The index draws its leader now, and a frame's vertical offset is a kept
value rather than an ignored one — with the measurement that decided it.
2026-08-08 10:32:29 +00:00
Steffen Becker 58607d92e9 docs: say what the parity harness cannot see
It compares where text sits, so a dropped highlight or fill reads as green;
the visual check that catches those, and its two traps, are written down.
Also records the list marker's ~4mm offset, which the tolerance hides.
2026-08-08 09:08:58 +00:00
Steffen Becker 361b14dc17 test: compare the rendered page against LibreOffice
Everything else in tests/ compares documents at the model level; nothing checked
what a page actually looks like. run.mjs renders the same file through soffice
and through the real app in Chromium, reads both back as word boxes, and diffs
page count, line count, line text and line position.

The corpus stays out of the repo: make-fixtures.mjs authors the baseline .docx
files, and real-world documents are whatever you drop into the gitignored
fixtures/ yourself.
2026-08-08 08:35:34 +00:00