Files
edentext/tests/unit/vertical-text-box.test.ts
T
Steffen Becker 35059d8126 fix: export plain text boxes as Writer text frames
Without a parent chain to the Frame style LibreOffice read the box as a
drawing object, whose text loses lists, nested images and borders. The
frame takes fo:background-color/fo:border and its writing mode in the
graphic properties; an inline box's centring rides the anchor paragraph.
The import reads the frame spellings back and drops LO's from-top/0
respelling of the default alignment.
2026-08-30 10:06:43 +00:00

57 lines
2.7 KiB
TypeScript

// A text box whose text runs top-to-bottom. ODF keeps it as the style's own writing
// mode — a text frame in its graphic properties (where LibreOffice writes it), a
// drawing shape in its style's paragraph properties (probed both) — Word as `vert`.
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 box = (attrs: N): N => ({
type: 'textBox',
attrs: { width: 200, height: 160, ...attrs },
content: [{ type: 'paragraph', content: [{ type: 'text', text: 'Senkrecht' }] }],
});
const doc: N = {
type: 'doc',
content: [{ type: 'paragraph', content: [{ type: 'text', text: 'davor' }] }, box({ textVertical: true })],
};
const margins = { top: 2, bottom: 2, left: 2, right: 2 };
const boxOf = (r: N): N => ((r.content as N).content ?? []).find((n: N) => n.type === 'textBox');
describe('a text box with vertical text', () => {
it('round-trips through ODF', async () => {
const bytes = await buildOdt(doc, margins, 'portrait');
const xml = strFromU8(unzipSync(bytes)['content.xml']);
expect(xml).toMatch(/<style:graphic-properties [^>]*style:writing-mode="tb-rl"/);
expect(boxOf(importOdt(bytes))?.attrs.textVertical).toBe(true);
});
it('keeps the writing mode of a shape in its paragraph properties', async () => {
const shaped: N = { ...doc, content: [doc.content[0], box({ textVertical: true, shapeKind: 'roundRect' })] };
const xml = strFromU8(unzipSync(await buildOdt(shaped, margins, 'portrait'))['content.xml']);
expect(xml).toContain('<style:paragraph-properties style:writing-mode="tb-rl"/>');
expect(boxOf(importOdt(await buildOdt(shaped, margins, 'portrait')))?.attrs.textVertical).toBe(true);
});
it('round-trips through DOCX', async () => {
const bytes = await buildDocx(doc, margins, 'portrait');
const xml = strFromU8(unzipSync(bytes)['word/document.xml']);
expect(xml).toContain('vert="vert"');
expect(boxOf(importDocx(bytes))?.attrs.textVertical).toBe(true);
});
it('leaves a horizontal box alone', async () => {
const flat: N = { ...doc, content: [doc.content[0], box({})] };
const odt = strFromU8(unzipSync(await buildOdt(flat, margins, 'portrait'))['content.xml']);
expect(odt).not.toContain('writing-mode="tb-rl"');
const docx = strFromU8(unzipSync(await buildDocx(flat, margins, 'portrait'))['word/document.xml']);
expect(docx).toContain('vert="horz"');
expect(boxOf(importOdt(await buildOdt(flat, margins, 'portrait')))?.attrs.textVertical).toBeUndefined();
});
});