Files
edentext/tests/unit/text-box-valign.test.ts
T
Steffen Becker f33e708b84 feat: a text box anchors its text top, middle or bottom
Both formats carry a vertical anchor on the box body — ODF
draw:textarea-vertical-align, DOCX wps:bodyPr/@anchor — and both
sides of the I/O hard-coded "top", so an imported box dropped its
centering and a saved one never got it. New textVAlign attr, read
and written in both formats, rendered by flexing the rotor (the
handles are absolute and stay out of that flow), set in the Shape
Format tab beside the vertical-text button.
2026-09-12 02:35:02 +02:00

50 lines
2.1 KiB
TypeScript

// Where the text sits in a box taller than it is: ODF's draw:textarea-vertical-align,
// Word's wps:bodyPr/@anchor.
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 doc = (attrs: N): N => ({
type: 'doc',
content: [{ type: 'paragraph', content: [{ type: 'text', text: 'davor' }] },
{ type: 'paragraph', content: [{
type: 'textBox',
attrs: { width: 200, height: 160, ...attrs },
content: [{ type: 'paragraph', content: [{ type: 'text', text: '53%' }] }],
}] }],
});
const margins = { top: 2, bottom: 2, left: 2, right: 2 };
const boxOf = (r: N): N => {
const find = (n: N): N => n?.type === 'textBox' ? n : (n?.content ?? []).map(find).find(Boolean);
return find((r.content as N));
};
describe('a vertically anchored text box', () => {
it('round-trips through ODF', async () => {
const bytes = await buildOdt(doc({ textVAlign: 'middle' }), margins, 'portrait');
expect(strFromU8(unzipSync(bytes)['content.xml'])).toContain('draw:textarea-vertical-align="middle"');
expect(boxOf(importOdt(bytes))?.attrs.textVAlign).toBe('middle');
});
it('round-trips through DOCX', async () => {
const bytes = await buildDocx(doc({ textVAlign: 'bottom' }), margins, 'portrait');
expect(strFromU8(unzipSync(bytes)['word/document.xml'])).toContain('anchor="b"');
expect(boxOf(importDocx(bytes))?.attrs.textVAlign).toBe('bottom');
});
it('leaves a top-anchored box alone', async () => {
const odt = await buildOdt(doc({}), margins, 'portrait');
expect(strFromU8(unzipSync(odt)['content.xml'])).toContain('draw:textarea-vertical-align="top"');
expect(boxOf(importOdt(odt))?.attrs.textVAlign).toBeUndefined();
const docx = await buildDocx(doc({}), margins, 'portrait');
expect(strFromU8(unzipSync(docx)['word/document.xml'])).toContain('anchor="t"');
expect(boxOf(importDocx(docx))?.attrs.textVAlign).toBeUndefined();
});
});