export const usePdfExport = () => { const generatePDF = async ( element: HTMLElement, filename: string = "document.pdf", options: any = {} ): Promise => { // Default options optimized for document templates const defaultOptions = { margin: [0.5, 0.5, 0.5, 0.5], // top, left, bottom, right in inches filename, image: { type: "jpeg", quality: 0.98 }, html2canvas: { scale: 2, // Higher scale for better quality useCORS: true, allowTaint: false, letterRendering: true, logging: false, dpi: 300, height: undefined, width: undefined, }, jsPDF: { unit: "in", format: "letter", orientation: "portrait", compress: true, }, pagebreak: { mode: ["avoid-all", "css", "legacy"], before: ".page-break-before", after: ".page-break-after", avoid: ".no-page-break", }, }; // Merge provided options with defaults const finalOptions = { ...defaultOptions, ...options, html2canvas: { ...defaultOptions.html2canvas, ...(options.html2canvas || {}), }, jsPDF: { ...defaultOptions.jsPDF, ...(options.jsPDF || {}), }, pagebreak: { ...defaultOptions.pagebreak, ...(options.pagebreak || {}), }, }; try { // Dynamic import for client-side only if (process.server) { throw new Error("PDF generation is only available on the client side"); } // Import html2pdf dynamically with better error handling let html2pdf; try { const module = await import("html2pdf.js"); html2pdf = module.default || module; } catch (importError) { console.error("Failed to import html2pdf.js:", importError); throw new Error( "Failed to load PDF library. Please refresh the page and try again." ); } // Clone the element to avoid modifying the original const clonedElement = element.cloneNode(true) as HTMLElement; // Apply PDF-specific styling to the clone while preserving font choices const currentFontClass = element.className.match(/font-[\w-]+/)?.[0]; let fontFamily = '"Times New Roman", "Times", serif'; // default // Preserve selected font for PDF if (currentFontClass) { if (currentFontClass.includes("source-serif")) { fontFamily = '"Source Serif 4", "Times New Roman", serif'; } else if (currentFontClass.includes("ubuntu")) { fontFamily = '"Ubuntu", -apple-system, BlinkMacSystemFont, sans-serif'; } else if (currentFontClass.includes("inter")) { fontFamily = '"Inter", -apple-system, BlinkMacSystemFont, sans-serif'; } } clonedElement.style.fontFamily = fontFamily; clonedElement.style.fontSize = "11pt"; clonedElement.style.lineHeight = "1.5"; clonedElement.style.color = "#000000"; clonedElement.style.backgroundColor = "#ffffff"; clonedElement.style.width = "8.5in"; clonedElement.style.maxWidth = "8.5in"; clonedElement.style.padding = "0.5in"; clonedElement.style.boxSizing = "border-box"; // Hide export controls in the clone const exportControls = clonedElement.querySelector(".export-controls"); if (exportControls) { (exportControls as HTMLElement).style.display = "none"; } // Ensure proper font loading by adding a slight delay await new Promise((resolve) => setTimeout(resolve, 500)); // Generate the PDF with better error handling console.log("Starting PDF generation with options:", finalOptions); if (typeof html2pdf !== "function") { throw new Error( "html2pdf is not a function. Library may not have loaded correctly." ); } const pdfInstance = html2pdf(); if (!pdfInstance || typeof pdfInstance.set !== "function") { throw new Error("html2pdf instance is invalid"); } await pdfInstance.set(finalOptions).from(clonedElement).save(); } catch (error: any) { console.error("PDF generation failed:", error); const errorMessage = error?.message || error?.toString() || "Unknown error"; throw new Error(`PDF generation failed: ${errorMessage}`); } }; const generatePDFFromContent = async ( htmlContent: string, filename: string = "document.pdf", options: any = {} ): Promise => { if (process.server) { throw new Error("PDF generation is only available on the client side"); } // Create a temporary element with the HTML content const tempDiv = document.createElement("div"); tempDiv.innerHTML = htmlContent; tempDiv.style.position = "absolute"; tempDiv.style.left = "-9999px"; tempDiv.style.top = "-9999px"; tempDiv.style.width = "8.5in"; tempDiv.style.fontFamily = '"Times New Roman", "Times", serif'; tempDiv.style.fontSize = "11pt"; tempDiv.style.lineHeight = "1.5"; tempDiv.style.color = "#000000"; tempDiv.style.backgroundColor = "#ffffff"; document.body.appendChild(tempDiv); try { await generatePDF(tempDiv, filename, options); } finally { document.body.removeChild(tempDiv); } }; const exportDocumentAsPDF = async ( selector: string = ".document-page", filename?: string ): Promise => { if (process.server) { throw new Error("PDF generation is only available on the client side"); } const element = document.querySelector(selector) as HTMLElement; if (!element) { throw new Error(`Element with selector "${selector}" not found`); } // Generate filename based on current date if not provided const defaultFilename = `document-${ new Date().toISOString().split("T")[0] }.pdf`; await generatePDF(element, filename || defaultFilename); }; return { generatePDF, generatePDFFromContent, exportDocumentAsPDF, }; };