Full document, spreadsheet, slideshow, and diagram tooling
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 95 lines 3.0 kB view raw
1/** 2 * PDF Export module for Atmosphere Docs. 3 * 4 * Uses html2pdf.js to convert the editor content into a downloadable PDF. 5 * Always exports in light mode regardless of current theme. 6 */ 7import type { PdfExportOptions } from './types.js'; 8 9/** 10 * Build a self-contained HTML string suitable for PDF rendering. 11 * Extracted as a pure function for testability. 12 */ 13export function buildPdfHtml(editorHtml: string, title: string): string { 14 return `<!DOCTYPE html> 15<html lang="en"> 16<head> 17<meta charset="UTF-8"> 18<style> 19 body { 20 font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; 21 max-width: 100%; 22 margin: 0; 23 padding: 0 0.5in; 24 line-height: 1.6; 25 color: #1a1815; 26 background: #fff; 27 } 28 h1, h2, h3 { margin-top: 1.5em; margin-bottom: 0.5em; } 29 blockquote { border-left: 3px solid #ccc; margin-left: 0; padding-left: 1em; color: #555; } 30 pre { background: #f5f5f5; padding: 1em; border-radius: 4px; overflow-x: auto; } 31 code { background: #f5f5f5; padding: 0.2em 0.4em; border-radius: 3px; font-size: 0.9em; } 32 pre code { background: none; padding: 0; } 33 table { border-collapse: collapse; width: 100%; } 34 th, td { border: 1px solid #ddd; padding: 0.5em; text-align: left; } 35 th { background: #f5f5f5; } 36 hr { border: none; border-top: 1px solid #ddd; margin: 2em 0; } 37 img { max-width: 100%; } 38 ul[data-type="taskList"] { list-style: none; padding-left: 0; } 39 ul[data-type="taskList"] li { display: flex; align-items: flex-start; gap: 0.5em; } 40</style> 41</head> 42<body> 43${editorHtml} 44</body> 45</html>`; 46} 47 48/** 49 * Derive a safe filename from a document title. 50 */ 51export function pdfFilename(title: string | null | undefined): string { 52 const clean = (title || '').trim() || 'Untitled Document'; 53 return clean.replace(/[^a-zA-Z0-9_\- ]/g, '').replace(/\s+/g, '_'); 54} 55 56/** 57 * Generate and download a PDF from editor content. 58 * This is the DOM-coupled entry point — not unit-testable. 59 */ 60export async function exportPdf({ editorHtml, title }: PdfExportOptions): Promise<void> { 61 const html2pdf = (await import('html2pdf.js')).default; 62 63 const container = document.createElement('div'); 64 container.innerHTML = buildPdfHtml(editorHtml, title); 65 container.style.position = 'fixed'; 66 container.style.left = '-9999px'; 67 container.style.top = '0'; 68 container.style.width = '8.5in'; 69 container.style.background = '#fff'; 70 container.style.color = '#1a1815'; 71 document.body.appendChild(container); 72 73 try { 74 await html2pdf() 75 .set({ 76 margin: [0.5, 0.5, 0.5, 0.5], 77 filename: `${pdfFilename(title)}.pdf`, 78 image: { type: 'jpeg', quality: 0.95 }, 79 html2canvas: { 80 scale: 2, 81 useCORS: true, 82 backgroundColor: '#ffffff', 83 }, 84 jsPDF: { 85 unit: 'in', 86 format: 'letter', 87 orientation: 'portrait', 88 }, 89 }) 90 .from(container) 91 .save(); 92 } finally { 93 document.body.removeChild(container); 94 } 95}