The mirror of the left indent (fo:margin-right, w:ind w:right) with its own marker on the ruler. ODF export rides the PBX sentinel: odf-kit has a paragraph option for the left indent but none for the right one. Clearing direct formatting now resets the first-line and right indents as well — no named style governs either, so both are always direct formatting.
61 lines
2.3 KiB
TypeScript
61 lines
2.3 KiB
TypeScript
// Paragraph indents: the first line (split into w:firstLine / w:hanging by sign in
|
|
// OOXML, one signed fo:text-indent in ODF) and the right indent. jsdom (vitest
|
|
// `environment`) supplies the global DOMParser.
|
|
import { describe, it, expect } from 'vitest';
|
|
import { unzipSync, strFromU8 } from 'fflate';
|
|
import { buildOdt } from '../../src/lib/export/odt';
|
|
import { buildDocx } from '../../src/lib/export/docx';
|
|
import { importOdt } from '../../src/lib/import/odt';
|
|
import { importDocx } from '../../src/lib/import/docx';
|
|
|
|
type N = any;
|
|
|
|
const P = (text: string, attrs?: N): N =>
|
|
({ type: 'paragraph', ...(attrs ? { attrs } : {}), content: [{ type: 'text', text }] });
|
|
|
|
const doc: N = {
|
|
type: 'doc',
|
|
content: [
|
|
P('plain'),
|
|
P('hanging', { indent: 2.5, indentFirst: -2.5 }),
|
|
P('first line in', { indentFirst: 1.25 }),
|
|
P('narrowed', { indent: 1, indentRight: 3 }),
|
|
],
|
|
};
|
|
|
|
const margins = { top: 2, bottom: 2, left: 2, right: 2 };
|
|
const firstOf = (d: N, i: number) => d.content[i].attrs?.indentFirst ?? null;
|
|
const rightOf = (d: N, i: number) => d.content[i].attrs?.indentRight ?? null;
|
|
|
|
describe('first-line indent', () => {
|
|
it('round-trips through ODF', async () => {
|
|
const bytes = await buildOdt(doc, margins, 'portrait');
|
|
expect(strFromU8(unzipSync(bytes)['content.xml'])).toMatch(/fo:text-indent="-2\.5cm"/);
|
|
|
|
// odf-kit has no right-indent option, so it rides the PBX sentinel's minted style.
|
|
expect(strFromU8(unzipSync(bytes)['content.xml'])).toMatch(/fo:margin-right="3cm"/);
|
|
|
|
const back = importOdt(bytes).content as N;
|
|
expect(firstOf(back, 0)).toBe(null);
|
|
expect(firstOf(back, 1)).toBe(-2.5);
|
|
expect(firstOf(back, 2)).toBe(1.25);
|
|
expect(rightOf(back, 3)).toBe(3);
|
|
expect(rightOf(back, 0)).toBe(null);
|
|
});
|
|
|
|
it('round-trips through DOCX', async () => {
|
|
const bytes = await buildDocx(doc, margins, 'portrait');
|
|
const xml = strFromU8(unzipSync(bytes)['word/document.xml']);
|
|
expect(xml).toMatch(/w:hanging="1417"/);
|
|
expect(xml).toMatch(/w:firstLine="709"/);
|
|
expect(xml).toMatch(/w:right="1701"/);
|
|
|
|
const back = importDocx(bytes).content as N;
|
|
expect(firstOf(back, 0)).toBe(null);
|
|
expect(firstOf(back, 1)).toBe(-2.5);
|
|
expect(firstOf(back, 2)).toBe(1.25);
|
|
expect(rightOf(back, 3)).toBe(3);
|
|
expect(rightOf(back, 0)).toBe(null);
|
|
});
|
|
});
|