Files
edentext/tests/unit/odf-para-inheritance.test.ts
T
Steffen Becker aff3eaa812 fix: a named style's line spacing is the percentage the file declares
The style-registry path divided an ODF fo:line-height by 1.15 where the
block path, both DOCX paths and the export all treat it as the plain
percentage, so every paragraph whose spacing came from its style
rendered 15% too tight. Its suppression yardstick is the block's own
style too, so a 100% under a 115% style stays as direct formatting.
2026-08-09 20:43:34 +00:00

63 lines
3.4 KiB
TypeScript

import { describe, it, expect } from 'vitest';
import { zipSync, strToU8 } from 'fflate';
import { importOdt } from '../../src/lib/import/odt';
// Probed against LibreOffice with the OPG fixture's own styles: it holds a paragraph's
// two vertical margins in one item, so a style declaring just one of them takes the
// *default* style's value for the other instead of the parent chain's.
const NS =
'xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" ' +
'xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" ' +
'xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" ' +
'xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0"';
// `Normal` sets both margins and 115% spacing; the block's automatic style overrides one.
function odt(autoProps: string): Uint8Array {
const styles = `<?xml version="1.0"?><office:document-styles ${NS}><office:styles>
<style:default-style style:family="paragraph">
<style:paragraph-properties fo:margin-bottom="8pt"/>
<style:text-properties fo:font-size="12pt" fo:font-family="Liberation Serif"/>
</style:default-style>
<style:style style:name="Standard" style:family="paragraph"/>
<style:style style:name="Normal" style:display-name="Normal" style:family="paragraph">
<style:paragraph-properties fo:margin-top="20pt" fo:margin-bottom="12pt" fo:line-height="115%"/>
</style:style>
</office:styles></office:document-styles>`;
const content = `<?xml version="1.0"?><office:document-content ${NS}><office:automatic-styles>
<style:style style:name="P1" style:parent-style-name="Normal" style:family="paragraph">
<style:paragraph-properties ${autoProps}/>
</style:style>
</office:automatic-styles><office:body><office:text>
<text:p text:style-name="P1">Absatz</text:p>
</office:text></office:body></office:document-content>`;
return zipSync({ 'content.xml': strToU8(content), 'styles.xml': strToU8(styles) });
}
const block = (autoProps: string) =>
(importOdt(odt(autoProps)).content.content?.[0] as { attrs: Record<string, unknown> }).attrs;
describe('ODF paragraph property inheritance', () => {
it('drops the inherited margin the other half of the pair replaced', () => {
// Declaring only the top margin resets the bottom one to the default style's 8pt…
expect(block('fo:margin-top="2.4pt"')).toMatchObject({ spaceBefore: 2.4, spaceAfter: 8 });
// …and the other way round; the parent's 20pt does not survive.
expect(block('fo:margin-bottom="6pt"')).toMatchObject({ spaceBefore: 0, spaceAfter: 6 });
});
it('keeps both when the style declares neither', () => {
// Nothing overrides the pair, so both stay with the style and the block adds nothing.
const attrs = block('fo:text-align="center"');
expect(attrs.spaceBefore).toBeUndefined();
expect(attrs.spaceAfter).toBeUndefined();
expect(importOdt(odt('fo:text-align="center"')).styles.paragraph['Normal']?.para)
.toMatchObject({ spaceBefore: 20, spaceAfter: 12 });
});
it('reads a line spacing as the ODF percentage, against the style it overrides', () => {
expect(importOdt(odt('fo:text-align="center"')).styles.paragraph['Normal']?.para.lineHeight).toBe('1.15');
// 100% under a 115% style is direct formatting, not the editor's implicit default.
expect(block('fo:line-height="100%"').lineHeight).toBe('1');
expect(block('fo:line-height="115%"').lineHeight).toBeUndefined();
});
});