mirror of https://github.com/buster-so/buster.git
pdf export coming soon.
This commit is contained in:
parent
edabcac7f2
commit
d3dd804a3f
|
@ -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) => {
|
||||||
|
|
Loading…
Reference in New Issue