fix: a column gap wider than the text is no gap at all

w:cols/@w:space is twips, and a producer writing EMU there (360000 = 1cm
in EMU) had the importer clamp its 635cm to the model's 5cm ceiling: a
two-column section came out with 51.3mm columns on a 152.4mm text width,
its second column 24.9mm right of where LibreOffice draws it. Below the
text width LibreOffice lays the declared gap out literally (probed from
720 to 8000 twips), so only the unholdable value is dropped — both sides
now start the second column at 108.0mm.
This commit is contained in:
Steffen Becker committed 2026-08-13 19:15:21 +02:00
1 parent 1a1505071c
commit ab25abd492
2 files changed
+12 -1

No files matched your search

+5
View File
@@ -75,6 +75,11 @@ docDefaults ← the table style ← the cell paragraph's own style chain ← dir
style name; read as an override the way a table style zeroing `w:after` reads, every table
loses the space after its cells' paragraphs and a long document comes out pages short.
**A `w:cols` gap the section's text cannot hold is no gap at all.** `w:space` is twips, and
a producer writing EMU there (360000 = 1cm in EMU, 635cm read as twips) would otherwise
leave columns of no width. Under that width LibreOffice lays the declared gap out literally
— probed to 10.16cm on a 15.24cm text — so only the unholdable value is dropped.
Body text with no resolved font falls back to the *document's own theme minor font*
(`docx.ts` `runMarks`), not the editor default — Word's implicit body default. Headings don't
(they keep the editor heading default); DOCX page margins default to Word's 2.54cm.
+7 -1
View File
@@ -23,6 +23,7 @@ import { applyUniformRunFont, pairAlignedFrames, sinkOffsetFrames, type OdtImpor
import { chartDataUrl } from './chart';
import { deobfuscateOdttf, type EmbeddedFont } from '../fonts/embeddedFonts';
import { cellPaddingAttr, DEFAULT_CELL_PADDING, type CellPadding } from '../editor/extensions/tableCellPadding';
import { clampColumnGap } from '../editor/extensions/columns';
import { astToLatex } from '../math/latex';
import { parseOmml, OMML_NS } from '../math/omml';
@@ -2089,7 +2090,12 @@ function sectPrColumns(sectPr: Element | null, ctx: Ctx): { count: number; gapCm
count = 3;
}
const space = intAttr(cols, W, 'space') ?? (colEls[0] ? intAttr(colEls[0], W, 'space') : null) ?? 283;
return { count, gapCm: Math.min(5, Math.max(0, round2(twipToCm(space)))) };
// A gap the section's text has no room for is a producer's unit slip — EMU written into
// a twips attribute — and lays out as no gap at all, not as columns too narrow to hold a
// word. Below that LibreOffice honours the declared value literally (probed).
const gapCm = round2(twipToCm(space));
const fits = gapCm > 0 && (count - 1) * gapCm < sectionContentWidthCm(sectPr);
return { count, gapCm: fits ? clampColumnGap(gapCm) : 0 };
}
// Block types a columns node can contain (mirrors import/odt.ts).