From b65920f7b73a7745119660aa44efbfbc8ce7c595 Mon Sep 17 00:00:00 2001 From: Steffen Becker Date: Mon, 17 Aug 2026 09:17:05 +0200 Subject: [PATCH] fix: a cell or list paragraph is measured against the default style MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ODF importer's yardstick for a block that can carry no style name was the editor's own fallback, not the default paragraph style — which is what `p:not([data-style])` re-applies. So a cell paragraph overriding its 115% line height back to 100% was dropped as "same as the default" and rendered at 115% anyway: 0.7mm too much on every table row. Its spacing is the one exception, zeroed by a rule that outranks the style's, for a paragraph but not a heading. Two fixtures' table rows: pitch 5.8mm against LibreOffice's 5.1, then 5.1. --- src/lib/import/odt.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/lib/import/odt.ts b/src/lib/import/odt.ts index 614c4ba..606c3b5 100644 --- a/src/lib/import/odt.ts +++ b/src/lib/import/odt.ts @@ -1558,8 +1558,14 @@ function convertParaLike(el: Element, ctx: Ctx, kind: BlockKind, boldByDefault = const named = resolver.namedAncestor(styleName); // Only a body block carries its style's name (below); anywhere else — a cell, a text // box — the formatting has to become direct, so it is measured against the default - // style instead: the only one re-applied on import. - const defaults = blockDefaults(resolver, kind === 'body' ? named : null, isHeading ? level : null, boldByDefault); + // style instead: the one `p:not([data-style])` re-applies (styleSheet.ts). A heading + // keeps its level's yardstick, which is what `hN:not([data-style])` re-applies. + const yardstick = kind === 'body' ? named : isHeading ? null : DEFAULT_STYLE; + const defaults = blockDefaults(resolver, yardstick, isHeading ? level : null, boldByDefault); + // A cell paragraph's spacing is the exception: editor.css zeroes it whatever the default + // style declares (only that rule outranks the style's — not the `li p` one, and not for a + // heading), so the file's own margins there are direct formatting. + if (kind === 'cell' && !isHeading) { defaults.marginTopPt = 0; defaults.marginBottomPt = 0; } const attrs = blockAttrs(paraProps, baseTextProps, defaults, kind); // A style:master-page-name switches the page master, which is how ODF gives a section