100 lines
4.0 KiB
TypeScript
100 lines
4.0 KiB
TypeScript
|
|
/**
|
|||
|
|
* 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);
|
|||
|
|
});
|
|||
|
|
});
|