task(G4): layout PDF export - viewport-scaled A4 landscape, elementToPrimitives rendering, layer line widths + dash patterns, monochrome option, title block inclusion
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
/**
|
||||
* Task G4 – PDF-Export je Layout im Ausgabemaßstab.
|
||||
*
|
||||
* exportLayoutPDF rendert den Layout-Viewport (center/scale) auf eine A4-
|
||||
* Seite (landscape), skaliert Weltkoordinaten in den Ausgabemaßstab,
|
||||
* berücksichtigt Linienbreiten aus Layern (lineType) und unterstützt
|
||||
* eine monochrome Option. Titelblock (frameBlockRef) wird mitgezeichnet.
|
||||
* Rückgabe: PDF-Bytes, per PDFDocument.load verifizierbar.
|
||||
*/
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { PDFDocument } from 'pdf-lib';
|
||||
import { CADDocument } from '../src/kernel/document/CADDocument';
|
||||
import { createInMemoryDoc } from './helpers/inMemoryDoc';
|
||||
import { exportLayoutPDF } from '../src/services/pdfExport';
|
||||
import type { CADElement, LayoutDefinition } from '../src/types/cad.types';
|
||||
|
||||
function line(id: string, x1: number, y1: number, x2: number, y2: number, layerId = 'layer-0'): CADElement {
|
||||
return {
|
||||
id, type: 'line', layerId,
|
||||
x: Math.min(x1, x2), y: Math.min(y1, y2),
|
||||
width: Math.abs(x2 - x1), height: Math.abs(y2 - y1),
|
||||
properties: { x1, y1, x2, y2, stroke: '#ff0000' },
|
||||
} as CADElement;
|
||||
}
|
||||
|
||||
function mkDoc(els: CADElement[], layouts: LayoutDefinition[]): CADDocument {
|
||||
const { ydoc } = createInMemoryDoc();
|
||||
const doc = new CADDocument(ydoc);
|
||||
for (const e of els) doc.addElement(e);
|
||||
for (const l of layouts) doc.addLayout(l);
|
||||
return doc;
|
||||
}
|
||||
|
||||
const LAYOUT: LayoutDefinition = {
|
||||
id: 'layout-1', name: 'A4 Plan',
|
||||
viewport: { center: { x: 50, y: 50 }, scale: 1 },
|
||||
};
|
||||
|
||||
describe('G4: exportLayoutPDF', () => {
|
||||
it('liefert valides PDF mit genau einer A4-landscape-Seite', async () => {
|
||||
const doc = mkDoc([line('l1', 0, 0, 100, 100)], [LAYOUT]);
|
||||
const bytes = await exportLayoutPDF(doc, 'layout-1');
|
||||
expect(bytes.byteLength).toBeGreaterThan(500);
|
||||
const pdf = await PDFDocument.load(bytes);
|
||||
expect(pdf.getPageCount()).toBe(1);
|
||||
const { width, height } = pdf.getPage(0).getSize();
|
||||
expect(width).toBe(842); // A4 landscape pt
|
||||
expect(height).toBe(595);
|
||||
});
|
||||
|
||||
it('Titelblock-Elemente (frameBlockRef) werden mitgezeichnet', async () => {
|
||||
const doc = mkDoc([line('l1', 0, 0, 100, 0)], [LAYOUT]);
|
||||
// Titelblock-Definition anlegen und referenzieren
|
||||
const blockId = 'title-1';
|
||||
doc.addBlock({
|
||||
id: blockId, name: 'Titelblock', description: '', category: 'frame',
|
||||
elements: [line('tb1', 0, 0, 50, 0)],
|
||||
});
|
||||
doc.updateLayout('layout-1', { frameBlockRef: blockId });
|
||||
const bytes = await exportLayoutPDF(doc, 'layout-1');
|
||||
const pdf = await PDFDocument.load(bytes);
|
||||
expect(pdf.getPageCount()).toBe(1);
|
||||
// Strukturelle Prüfung: Export läuft fehlerfrei mit Frame-Block
|
||||
expect(bytes.byteLength).toBeGreaterThan(500);
|
||||
});
|
||||
|
||||
it('monochrome Option exportiert ohne Farbinformationen zu verlieren', async () => {
|
||||
const doc = mkDoc([line('l1', 0, 0, 100, 100)], [LAYOUT]);
|
||||
const bytes = await exportLayoutPDF(doc, 'layout-1', { monochrome: true });
|
||||
const pdf = await PDFDocument.load(bytes);
|
||||
expect(pdf.getPageCount()).toBe(1);
|
||||
});
|
||||
|
||||
it('leeres Layout (keine Elemente im Viewport) exportiert trotzdem eine Seite', async () => {
|
||||
const doc = mkDoc([], [LAYOUT]);
|
||||
const bytes = await exportLayoutPDF(doc, 'layout-1');
|
||||
const pdf = await PDFDocument.load(bytes);
|
||||
expect(pdf.getPageCount()).toBe(1);
|
||||
});
|
||||
|
||||
it('Layout ohne existierende ID → throw', async () => {
|
||||
const doc = mkDoc([], []);
|
||||
await expect(exportLayoutPDF(doc, 'gibts-nicht')).rejects.toThrow();
|
||||
});
|
||||
|
||||
it('unsichtbare Layer werden übersprungen (Export läuft fehlerfrei)', async () => {
|
||||
const { ydoc } = createInMemoryDoc();
|
||||
const doc = new CADDocument(ydoc);
|
||||
ydoc.data.layers.set('layer-0', {
|
||||
id: 'layer-0', name: '0', visible: false, locked: false,
|
||||
color: '#ffffff', lineType: 'solid', transparency: 0, sortOrder: 0, parentId: null,
|
||||
});
|
||||
doc.addElement(line('l1', 0, 0, 100, 100));
|
||||
doc.addLayout(LAYOUT);
|
||||
const bytes = await exportLayoutPDF(doc, 'layout-1');
|
||||
const pdf = await PDFDocument.load(bytes);
|
||||
expect(pdf.getPageCount()).toBe(1);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user