zu fabricjs gewechselt, noch nicht funktionsfähig
This commit is contained in:
@@ -0,0 +1,133 @@
|
||||
import * as fabric from 'fabric';
|
||||
import { PDFDocument } from 'pdf-lib';
|
||||
|
||||
import { CANVAS_HEIGHT, CANVAS_WIDTH } from './schema';
|
||||
import { FabricRenderOptions, renderFabricLayout } from './DesignerCanvas';
|
||||
|
||||
const PDF_PAGE_SIZES: Record<string, { width: number; height: number }> = {
|
||||
a4: { width: 595.28, height: 841.89 },
|
||||
letter: { width: 612, height: 792 },
|
||||
};
|
||||
|
||||
export async function withFabricCanvas<T>(
|
||||
options: FabricRenderOptions,
|
||||
handler: (canvas: fabric.Canvas, element: HTMLCanvasElement) => Promise<T>,
|
||||
): Promise<T> {
|
||||
const canvasElement = document.createElement('canvas');
|
||||
canvasElement.width = CANVAS_WIDTH;
|
||||
canvasElement.height = CANVAS_HEIGHT;
|
||||
|
||||
const canvas = new fabric.Canvas(canvasElement, {
|
||||
selection: false,
|
||||
});
|
||||
|
||||
try {
|
||||
await renderFabricLayout(canvas, {
|
||||
...options,
|
||||
readOnly: true,
|
||||
selectedId: null,
|
||||
});
|
||||
return await handler(canvas, canvasElement);
|
||||
} finally {
|
||||
canvas.dispose();
|
||||
canvasElement.remove();
|
||||
}
|
||||
}
|
||||
|
||||
export async function generatePngDataUrl(
|
||||
options: FabricRenderOptions,
|
||||
multiplier = 2,
|
||||
): Promise<string> {
|
||||
return withFabricCanvas(options, async (canvas) =>
|
||||
canvas.toDataURL({ format: 'png', multiplier }),
|
||||
);
|
||||
}
|
||||
|
||||
export async function generatePdfBytes(
|
||||
options: FabricRenderOptions,
|
||||
paper: string,
|
||||
orientation: string,
|
||||
multiplier = 2,
|
||||
): Promise<Uint8Array> {
|
||||
const dataUrl = await generatePngDataUrl(options, multiplier);
|
||||
return createPdfFromPng(dataUrl, paper, orientation);
|
||||
}
|
||||
|
||||
export async function createPdfFromPng(
|
||||
dataUrl: string,
|
||||
paper: string,
|
||||
orientation: string,
|
||||
): Promise<Uint8Array> {
|
||||
const pdfDoc = await PDFDocument.create();
|
||||
const baseSize = PDF_PAGE_SIZES[paper.toLowerCase()] ?? PDF_PAGE_SIZES.a4;
|
||||
const landscape = orientation === 'landscape';
|
||||
const pageWidth = landscape ? baseSize.height : baseSize.width;
|
||||
const pageHeight = landscape ? baseSize.width : baseSize.height;
|
||||
|
||||
const page = pdfDoc.addPage([pageWidth, pageHeight]);
|
||||
const pngBytes = dataUrlToUint8Array(dataUrl);
|
||||
const pngImage = await pdfDoc.embedPng(pngBytes);
|
||||
|
||||
const imageWidth = pngImage.width;
|
||||
const imageHeight = pngImage.height;
|
||||
const scale = Math.min(pageWidth / imageWidth, pageHeight / imageHeight);
|
||||
const drawWidth = imageWidth * scale;
|
||||
const drawHeight = imageHeight * scale;
|
||||
|
||||
page.drawImage(pngImage, {
|
||||
x: (pageWidth - drawWidth) / 2,
|
||||
y: (pageHeight - drawHeight) / 2,
|
||||
width: drawWidth,
|
||||
height: drawHeight,
|
||||
});
|
||||
|
||||
return pdfDoc.save();
|
||||
}
|
||||
|
||||
export function triggerDownloadFromDataUrl(dataUrl: string, filename: string): Promise<void> {
|
||||
return fetch(dataUrl)
|
||||
.then((response) => response.blob())
|
||||
.then((blob) => triggerDownloadFromBlob(blob, filename));
|
||||
}
|
||||
|
||||
export function triggerDownloadFromBlob(blob: Blob, filename: string): void {
|
||||
const objectUrl = URL.createObjectURL(blob);
|
||||
const link = document.createElement('a');
|
||||
link.href = objectUrl;
|
||||
link.download = filename;
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
setTimeout(() => URL.revokeObjectURL(objectUrl), 1000);
|
||||
}
|
||||
|
||||
export async function openPdfInNewTab(pdfBytes: Uint8Array): Promise<void> {
|
||||
const blobUrl = URL.createObjectURL(new Blob([pdfBytes], { type: 'application/pdf' }));
|
||||
const printWindow = window.open(blobUrl, '_blank', 'noopener,noreferrer');
|
||||
|
||||
if (!printWindow) {
|
||||
URL.revokeObjectURL(blobUrl);
|
||||
throw new Error('window-blocked');
|
||||
}
|
||||
|
||||
printWindow.onload = () => {
|
||||
try {
|
||||
printWindow.focus();
|
||||
printWindow.print();
|
||||
} catch (error) {
|
||||
console.error('[FabricExport] Browser print failed', error);
|
||||
}
|
||||
};
|
||||
|
||||
setTimeout(() => URL.revokeObjectURL(blobUrl), 60_000);
|
||||
}
|
||||
|
||||
function dataUrlToUint8Array(dataUrl: string): Uint8Array {
|
||||
const [, base64] = dataUrl.split(',');
|
||||
const decoded = atob(base64 ?? '');
|
||||
const bytes = new Uint8Array(decoded.length);
|
||||
for (let index = 0; index < decoded.length; index += 1) {
|
||||
bytes[index] = decoded.charCodeAt(index);
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
Reference in New Issue
Block a user