import type { DocPageLayout } from "@/lib/drive/doc-page-setup" import type { DocsExportSnapshot } from "@/lib/drive/docs-export-snapshot" import { captureDocsPagesFromCanvas } from "@/lib/drive/docs-page-capture" const PRINT_IFRAME_ID = "docs-print-iframe" function buildPageRule(pageLayout: DocPageLayout): string { const wMm = pageLayout.format.widthMm const hMm = pageLayout.format.heightMm const landscape = pageLayout.format.widthMm > pageLayout.format.heightMm const size = landscape ? `${hMm}mm ${wMm}mm` : `${wMm}mm ${hMm}mm` return `@page { size: ${size}; margin: 0; }` } function buildPrintStylesheet(pageLayout: DocPageLayout): string { const pageWidth = pageLayout.widthPx const pageHeight = pageLayout.heightPx return ` ${buildPageRule(pageLayout)} html, body { margin: 0; padding: 0; background: white; } .docs-print-page { width: ${pageWidth}px; height: ${pageHeight}px; overflow: hidden; break-after: page; page-break-after: always; print-color-adjust: exact; -webkit-print-color-adjust: exact; } .docs-print-page:last-child { break-after: auto; page-break-after: auto; } .docs-print-page img { display: block; width: 100%; height: 100%; } ` } function canvasToBlob(canvas: HTMLCanvasElement): Promise { return new Promise((resolve, reject) => { canvas.toBlob( (blob) => { if (blob) resolve(blob) else reject(new Error("Impossible de convertir la page en image")) }, "image/jpeg", 0.92 ) }) } function removePrintIframe(): void { document.getElementById(PRINT_IFRAME_ID)?.remove() } async function mountPrintDocument( doc: Document, canvases: HTMLCanvasElement[], pageLayout: DocPageLayout ): Promise<() => void> { const pageWidth = pageLayout.widthPx const pageHeight = pageLayout.heightPx const objectUrls: string[] = [] doc.documentElement.lang = "fr" doc.head.replaceChildren() doc.body.replaceChildren() const style = doc.createElement("style") style.textContent = buildPrintStylesheet(pageLayout) doc.head.appendChild(style) for (const canvas of canvases) { const blob = await canvasToBlob(canvas) const url = URL.createObjectURL(blob) objectUrls.push(url) const page = doc.createElement("div") page.className = "docs-print-page" page.style.width = `${pageWidth}px` page.style.height = `${pageHeight}px` const img = doc.createElement("img") img.alt = "" img.width = pageWidth img.height = pageHeight img.src = url page.appendChild(img) doc.body.appendChild(page) await img.decode().catch(() => undefined) } return () => { for (const url of objectUrls) { URL.revokeObjectURL(url) } } } async function printPageCanvasesInIframe( canvases: HTMLCanvasElement[], pageLayout: DocPageLayout ): Promise { if (canvases.length === 0) { throw new Error("Aucune page à imprimer") } removePrintIframe() const iframe = document.createElement("iframe") iframe.id = PRINT_IFRAME_ID iframe.setAttribute("aria-hidden", "true") iframe.style.cssText = "position:fixed;left:-100000px;top:0;width:1px;height:1px;border:0;opacity:0;pointer-events:none" const docReady = new Promise((resolve, reject) => { iframe.onload = () => { const doc = iframe.contentDocument if (doc) resolve(doc) else reject(new Error("Impossible de préparer la fenêtre d'impression")) } iframe.onerror = () => reject(new Error("Impossible de préparer la fenêtre d'impression")) }) iframe.src = "about:blank" document.body.appendChild(iframe) const doc = await docReady const win = iframe.contentWindow if (!win) { removePrintIframe() throw new Error("Impossible de préparer la fenêtre d'impression") } let revokeObjectUrls = () => {} try { revokeObjectUrls = await mountPrintDocument(doc, canvases, pageLayout) await new Promise((resolve) => requestAnimationFrame(() => resolve())) if (doc.fonts?.ready) { await doc.fonts.ready } await new Promise((resolve, reject) => { const cleanup = () => { win.removeEventListener("afterprint", onAfterPrint) revokeObjectUrls() removePrintIframe() } const onAfterPrint = () => { cleanup() resolve() } win.addEventListener("afterprint", onAfterPrint) try { win.focus() win.print() } catch (error) { cleanup() reject(error) return } window.setTimeout(() => { if (document.getElementById(PRINT_IFRAME_ID)) { cleanup() resolve() } }, 2000) }) } catch (error) { revokeObjectUrls() removePrintIframe() throw error } } export type DocsPrintMode = "print" | "pdf-capture" /** @deprecated Live DOM is no longer mutated; kept for PDF export compatibility. */ export async function prepareDocsPrintEnvironment( _snapshot: DocsExportSnapshot, _mode: DocsPrintMode = "print" ): Promise<() => void> { return () => {} } export async function printDocsDocument(snapshot: DocsExportSnapshot): Promise { const canvases = await captureDocsPagesFromCanvas(snapshot, { scale: 2 }) await printPageCanvasesInIframe(canvases, snapshot.pageLayout) }