fix: a zone's own margins survive the ODF round trip

The Header/Footer style carries them, and the band that grows to hold them
is rebuilt from there on the way back in.
This commit is contained in:
Steffen Becker committed 2026-08-10 21:31:38 +00:00
1 parent 0fe1a8395d
commit 13e026fb8b
2 files changed
+39

No files matched your search

+5
View File
@@ -3637,6 +3637,11 @@ function hfParaPropsXml(para: TiptapNode): string {
if (align) props.push(`fo:text-align="${align}"`);
const s = paraStyleFromAttrs(para.attrs);
if (s.background) props.push(`fo:background-color="${s.background}"`);
// The zone's own margins, which grow the band on the way back in (import/CLAUDE.md).
for (const [attr, side] of [['spaceBefore', 'top'], ['spaceAfter', 'bottom']] as const) {
const v = para.attrs?.[attr];
if (typeof v === 'number' && v) props.push(`fo:margin-${side}="${v}pt"`);
}
for (const [attr, side] of [
['borderTop', 'top'], ['borderRight', 'right'], ['borderBottom', 'bottom'], ['borderLeft', 'left'],
] as const) {
+34
View File
@@ -0,0 +1,34 @@
// A header/footer zone's own vertical margins: LibreOffice keeps them on the Header /
// Footer paragraph style, and a footer's space above is what grows the band.
import { describe, it, expect } from 'vitest';
import { unzipSync, strFromU8 } from 'fflate';
import { buildOdt } from '../../src/lib/export/odt';
import { importOdt } from '../../src/lib/import/odt';
type N = any;
const doc: N = { type: 'doc', content: [{ type: 'paragraph', content: [{ type: 'text', text: 'Body' }] }] };
const zone = (attrs: N, text: string): N => ({
type: 'doc',
content: [{ type: 'paragraph', attrs, content: [{ type: 'text', text }] }],
});
const hf = {
header: zone({ spaceAfter: 34 }, 'Kopf'),
footer: zone({ spaceBefore: 24 }, 'Fuss'),
headerFirst: null, footerFirst: null, differentFirstPage: false,
headerEven: null, footerEven: null, differentOddEven: false,
pageCount: 1,
};
describe('header/footer zone margins', () => {
it('ride the ODF zone styles and come back', async () => {
const bytes = await buildOdt(doc, undefined, 'portrait', hf);
const styles = strFromU8(unzipSync(bytes)['styles.xml']);
expect(styles).toContain('fo:margin-bottom="34pt"');
expect(styles).toContain('fo:margin-top="24pt"');
const back = await importOdt(bytes);
expect(back.header?.content?.[0]?.attrs?.spaceAfter).toBeCloseTo(34, 1);
expect(back.footer?.content?.[0]?.attrs?.spaceBefore).toBeCloseTo(24, 1);
});
});