Files
edentext/tests/unit/hf-tab-stops.test.ts
T
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

44 lines
1.9 KiB
TypeScript

// A header/footer zone's tab stops — what a left⇥centre⇥right header rides on. Word
// puts them on the Header style, ODF on the zone paragraph's.
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';
import { buildDocx } from '../../src/lib/export/docx';
import { importDocx } from '../../src/lib/import/docx';
type N = any;
const doc: N = { type: 'doc', content: [{ type: 'paragraph', content: [{ type: 'text', text: 'Body' }] }] };
const zone = (stops: string, text: string): N => ({
type: 'doc',
content: [{ type: 'paragraph', attrs: { tabStops: stops }, content: [{ type: 'text', text }] }],
});
const hf = {
header: zone('8.5c;17r', 'Links\tMitte\tRechts'),
footer: zone('17r.', 'Kapitel\tSeite'),
headerFirst: null, footerFirst: null, differentFirstPage: false,
headerEven: null, footerEven: null, differentOddEven: false,
pageCount: 1,
};
const stopsOf = (d: N) => d?.content?.[0]?.attrs?.tabStops ?? null;
describe('header/footer tab stops', () => {
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('<style:tab-stop style:position="8.5cm" style:type="center"/>');
expect(styles).toContain('style:leader-style="dotted" style:leader-text="."');
const back = await importOdt(bytes);
expect(stopsOf(back.header)).toBe('8.5c;17r');
expect(stopsOf(back.footer)).toBe('17r.');
});
it('ride the DOCX zone paragraphs and come back', async () => {
const back = importDocx(await buildDocx(doc, undefined, 'portrait', hf));
expect(stopsOf(back.header)).toBe('8.5c;17r');
expect(stopsOf(back.footer)).toBe('17r.');
});
});