export const usePdfExportSafe = () => { const exportToPDF = async (elementSelector: string, filename: string) => { // Only run on client side if (process.server || typeof window === "undefined") { throw new Error("PDF generation is only available on the client side"); } try { // Dynamic import for client-side only const html2pdf = (await import("html2pdf.js")).default; // Get the element to export const element = document.querySelector(elementSelector); if (!element) { throw new Error(`Element with selector "${elementSelector}" not found`); } // Create a completely clean version of the content const createCleanContent = () => { const cleanDiv = document.createElement("div"); cleanDiv.style.cssText = ` width: 8.5in; padding: 0.5in; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; font-size: 12px; line-height: 1.5; color: #000000; background: #ffffff; `; // Extract text content and basic structure const extractContent = (sourceEl: Element, targetEl: HTMLElement) => { const children = sourceEl.children; for (let i = 0; i < children.length; i++) { const child = children[i]; // Skip export controls if ( child.classList.contains("export-controls") || child.classList.contains("no-pdf") || child.classList.contains("no-print") ) { continue; } let newEl: HTMLElement; // Create appropriate element based on tag switch (child.tagName.toLowerCase()) { case "h1": newEl = document.createElement("h1"); newEl.style.cssText = "font-size: 24px; font-weight: bold; margin: 20px 0 10px 0; color: #000;"; break; case "h2": newEl = document.createElement("h2"); newEl.style.cssText = "font-size: 20px; font-weight: bold; margin: 16px 0 8px 0; color: #000;"; break; case "h3": newEl = document.createElement("h3"); newEl.style.cssText = "font-size: 16px; font-weight: bold; margin: 12px 0 6px 0; color: #000;"; break; case "p": newEl = document.createElement("p"); newEl.style.cssText = "margin: 8px 0; color: #000;"; break; case "ul": newEl = document.createElement("ul"); newEl.style.cssText = "margin: 8px 0; padding-left: 20px; color: #000;"; break; case "ol": newEl = document.createElement("ol"); newEl.style.cssText = "margin: 8px 0; padding-left: 20px; color: #000;"; break; case "li": newEl = document.createElement("li"); newEl.style.cssText = "margin: 4px 0; color: #000;"; break; case "input": newEl = document.createElement("span"); const inputEl = child as HTMLInputElement; newEl.textContent = inputEl.value || "[_____]"; newEl.style.cssText = "border-bottom: 1px solid #000; padding: 2px; color: #000;"; break; case "textarea": newEl = document.createElement("span"); const textareaEl = child as HTMLTextAreaElement; newEl.textContent = textareaEl.value || "[_____]"; newEl.style.cssText = "border: 1px solid #000; padding: 4px; display: inline-block; color: #000;"; break; case "select": newEl = document.createElement("span"); const selectEl = child as HTMLSelectElement; newEl.textContent = selectEl.value || "[_____]"; newEl.style.cssText = "border: 1px solid #000; padding: 2px; color: #000;"; break; default: newEl = document.createElement("div"); newEl.style.cssText = "color: #000;"; } // Set text content for elements that should have it if ( child.tagName.toLowerCase() !== "input" && child.tagName.toLowerCase() !== "textarea" && child.tagName.toLowerCase() !== "select" ) { // Get only direct text content, not from children const directText = Array.from(child.childNodes) .filter((node) => node.nodeType === Node.TEXT_NODE) .map((node) => node.textContent) .join(""); if (directText.trim()) { newEl.appendChild(document.createTextNode(directText)); } } targetEl.appendChild(newEl); // Recursively process children if (child.children.length > 0) { extractContent(child, newEl); } } }; extractContent(element, cleanDiv); return cleanDiv; }; const cleanElement = createCleanContent(); // Temporarily add to DOM cleanElement.style.position = "absolute"; cleanElement.style.left = "-9999px"; cleanElement.style.top = "-9999px"; document.body.appendChild(cleanElement); // Simple options - no complex CSS processing const options = { margin: 0.5, filename: filename, image: { type: "jpeg", quality: 0.98 }, html2canvas: { scale: 2, useCORS: true, allowTaint: false, logging: false, backgroundColor: "#ffffff", }, jsPDF: { unit: "in", format: "letter", orientation: "portrait", }, }; console.log("Generating PDF with clean content..."); try { // Generate PDF from the clean element await html2pdf().set(options).from(cleanElement).save(); console.log("PDF generated successfully!"); } finally { // Clean up if (cleanElement.parentNode) { document.body.removeChild(cleanElement); } } } catch (error: any) { console.error("PDF generation error:", error); throw new Error(`PDF generation failed: ${error.message || error}`); } }; return { exportToPDF }; };