pdf export coming soon.

This commit is contained in:
Nate Kelley 2025-08-08 11:39:43 -06:00
parent edabcac7f2
commit d3dd804a3f
No known key found for this signature in database
GPG Key ID: FD90372AB8D98B4F
1 changed files with 1 additions and 113 deletions

View File

@ -79,119 +79,7 @@ export const useExportReport = () => {
}; };
const exportToPdf = async (editor: PlateEditor) => { const exportToPdf = async (editor: PlateEditor) => {
try { openInfoMessage('PDF export coming soon...');
const { default: html2canvas } = await import('html2canvas-pro');
const editorNode = editor.api.toDOMNode(editor);
if (!editorNode) throw new Error('Editor not found');
// Clone the editor DOM to a sandbox so we can mutate it for printing
const clonedRoot = editorNode.cloneNode(true) as HTMLElement;
// Sandbox: attach off-screen to compute sizes and rasterize metrics
const sandbox = document.createElement('div');
sandbox.setAttribute('data-report-print-sandbox', 'true');
sandbox.style.position = 'fixed';
sandbox.style.left = '-10000px';
sandbox.style.top = '0';
sandbox.style.width = '850px';
sandbox.style.pointerEvents = 'none';
sandbox.appendChild(clonedRoot);
document.body.appendChild(sandbox);
// Ensure consistent fonts in the clone
clonedRoot.style.fontFamily =
'-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif';
// Replace metric figures with rasterized images so only charts become images in PDF
const figureNodes = Array.from(
clonedRoot.querySelectorAll<HTMLElement>('figure[data-metric-figure]')
);
await Promise.all(
figureNodes.map(async (figure) => {
// Render the figure to a canvas, then replace with an image element
const canvas = await html2canvas(figure, {
backgroundColor: '#ffffff',
useCORS: true
});
const image = document.createElement('img');
image.src = canvas.toDataURL('image/png');
image.style.width = `${canvas.width}px`;
image.style.height = 'auto';
image.setAttribute('data-exported-metric-image', 'true');
figure.replaceWith(image);
})
);
// Build printable HTML document
const printWindow = window.open('', '_blank');
if (!printWindow) throw new Error('Unable to open print window');
const headStyles = Array.from(document.querySelectorAll('link[rel="stylesheet"], style'))
.map((n) => n.outerHTML)
.join('\n');
const printHtml = `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
${headStyles}
<style>
@page { size: A4; margin: 16mm; }
html, body { -webkit-print-color-adjust: exact; print-color-adjust: exact; }
body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; }
.report-print-root { width: 850px; margin: 0 auto; }
/* Hide interactive cursors/placeholders if any slipped in */
[contenteditable="true"] { outline: none; }
</style>
</head>
<body>
<div class="report-print-root">${clonedRoot.outerHTML}</div>
</body>
</html>`;
printWindow.document.open();
printWindow.document.write(printHtml);
printWindow.document.close();
// Wait for images to load in the print window before printing
await new Promise<void>((resolve) => {
const imgs = Array.from(printWindow.document.images);
if (imgs.length === 0) return resolve();
let loaded = 0;
imgs.forEach((img) => {
if (img.complete) {
loaded++;
if (loaded === imgs.length) resolve();
} else {
img.onload = () => {
loaded++;
if (loaded === imgs.length) resolve();
};
img.onerror = () => {
loaded++;
if (loaded === imgs.length) resolve();
};
}
});
});
// Cleanup sandbox
sandbox.remove();
// Print. User can select "Save as PDF" to get a true text PDF with charts as images.
printWindow.focus();
printWindow.print();
openInfoMessage(NodeTypeLabels.pdfExportedSuccessfully.label);
} catch (error) {
console.error(error);
openErrorMessage(NodeTypeLabels.failedToExportPdf.label);
}
}; };
const exportToImage = async (editor: PlateEditor) => { const exportToImage = async (editor: PlateEditor) => {