# Export Functionality - Implementation Plan ## 📋 Overview Implement comprehensive export capabilities allowing users to download Bible passages, study notes, highlights, and annotations in multiple formats for offline study, sharing, and printing. **Status:** Planning Phase **Priority:** 🔴 High **Estimated Time:** 2-3 weeks (80-120 hours) **Target Completion:** TBD --- ## 🎯 Goals & Objectives ### Primary Goals 1. Export Bible passages in multiple formats (PDF, DOCX, Markdown, TXT) 2. Include user highlights and notes in exports 3. Provide print-optimized layouts 4. Support batch exports (multiple chapters/books) 5. Enable customization of export appearance ### User Value Proposition - **For students**: Create study materials for offline use - **For teachers**: Prepare handouts and lesson materials - **For preachers**: Print sermon references - **For small groups**: Share study guides - **For archiving**: Backup personal annotations --- ## ✨ Feature Specifications ### 1. Export Formats ```typescript type ExportFormat = 'pdf' | 'docx' | 'markdown' | 'txt' | 'epub' | 'json' interface ExportConfig { // Format format: ExportFormat // Content selection book: string startChapter: number endChapter: number startVerse?: number endVerse?: number includeHeadings: boolean includeVerseNumbers: boolean includeChapterNumbers: boolean // User content includeHighlights: boolean includeNotes: boolean includeBookmarks: boolean notesPosition: 'inline' | 'footnotes' | 'endnotes' | 'separate' // Appearance fontSize: number // 10-16pt fontFamily: string lineHeight: number // 1.0-2.0 pageSize: 'A4' | 'Letter' | 'Legal' margins: { top: number; right: number; bottom: number; left: number } columns: 1 | 2 // Header/Footer includeHeader: boolean headerText: string includeFooter: boolean footerText: string includePageNumbers: boolean // Metadata includeTableOfContents: boolean includeCoverPage: boolean coverTitle: string coverSubtitle: string author: string date: string // Advanced versionComparison: string[] // Multiple version IDs for parallel colorMode: 'color' | 'grayscale' | 'print' } ``` ### 2. Export Dialog UI ```typescript const ExportDialog: React.FC<{ open: boolean onClose: () => void defaultSelection?: { book: string chapter: number } }> = ({ open, onClose, defaultSelection }) => { const [config, setConfig] = useState(getDefaultConfig()) const [estimatedSize, setEstimatedSize] = useState('0 KB') const [exporting, setExporting] = useState(false) const [progress, setProgress] = useState(0) // Calculate estimated file size useEffect(() => { const estimate = calculateEstimatedSize(config) setEstimatedSize(estimate) }, [config]) const handleExport = async () => { setExporting(true) setProgress(0) try { const result = await exportContent(config, (percent) => { setProgress(percent) }) // Trigger download downloadFile(result.blob, result.filename) onClose() } catch (error) { console.error('Export failed:', error) // Show error to user } finally { setExporting(false) } } return ( Export Bible Content {activeTab === 0 && } {activeTab === 1 && } {activeTab === 2 && } {activeTab === 3 && } {/* Preview */} Estimated file size: {estimatedSize} {/* Progress */} {exporting && ( Generating {config.format.toUpperCase()}... {progress}% )} ) } ``` ### 3. PDF Export (using jsPDF) ```typescript import jsPDF from 'jspdf' import 'jspdf-autotable' export const generatePDF = async ( config: ExportConfig, onProgress?: (percent: number) => void ): Promise => { const doc = new jsPDF({ orientation: config.columns === 2 ? 'landscape' : 'portrait', unit: 'mm', format: config.pageSize.toLowerCase() }) // Set font doc.setFont(config.fontFamily) doc.setFontSize(config.fontSize) let currentPage = 1 // Add cover page if (config.includeCoverPage) { addCoverPage(doc, config) doc.addPage() currentPage++ } // Add table of contents if (config.includeTableOfContents) { const toc = await generateTableOfContents(config) addTableOfContents(doc, toc) doc.addPage() currentPage++ } // Fetch Bible content const verses = await fetchVerses( config.book, config.startChapter, config.endChapter, config.startVerse, config.endVerse ) const totalVerses = verses.length let processedVerses = 0 // Group by chapters const chapters = groupByChapters(verses) for (const [chapterNum, chapterVerses] of Object.entries(chapters)) { // Chapter heading if (config.includeChapterNumbers) { doc.setFontSize(config.fontSize + 4) doc.setFont(config.fontFamily, 'bold') doc.text(`Chapter ${chapterNum}`, 20, doc.internal.pageSize.height - 20) doc.setFont(config.fontFamily, 'normal') doc.setFontSize(config.fontSize) } // Add verses for (const verse of chapterVerses) { const verseText = formatVerseForPDF(verse, config) // Check if we need a new page if (doc.internal.pageSize.height - 40 < 20) { doc.addPage() currentPage++ } doc.text(verseText, 20, doc.internal.pageSize.height - 40) // Add highlights if enabled if (config.includeHighlights && verse.highlights) { addHighlightsToPDF(doc, verse.highlights) } // Add notes if (config.includeNotes && verse.notes) { if (config.notesPosition === 'inline') { addInlineNote(doc, verse.notes) } else if (config.notesPosition === 'footnotes') { addFootnote(doc, verse.notes, currentPage) } } processedVerses++ if (onProgress) { onProgress(Math.round((processedVerses / totalVerses) * 100)) } } } // Add header/footer to all pages if (config.includeHeader || config.includeFooter) { const totalPages = doc.getNumberOfPages() for (let i = 1; i <= totalPages; i++) { doc.setPage(i) if (config.includeHeader) { doc.setFontSize(10) doc.text(config.headerText, 20, 10) } if (config.includeFooter) { doc.setFontSize(10) const footerText = config.includePageNumbers ? `${config.footerText} | Page ${i} of ${totalPages}` : config.footerText doc.text(footerText, 20, doc.internal.pageSize.height - 10) } } } return doc.output('blob') } const formatVerseForPDF = (verse: BibleVerse, config: ExportConfig): string => { let text = '' if (config.includeVerseNumbers) { text += `${verse.verseNum}. ` } text += verse.text return text } const addCoverPage = (doc: jsPDF, config: ExportConfig): void => { const pageWidth = doc.internal.pageSize.width const pageHeight = doc.internal.pageSize.height // Title doc.setFontSize(24) doc.setFont(config.fontFamily, 'bold') doc.text(config.coverTitle, pageWidth / 2, pageHeight / 2 - 20, { align: 'center' }) // Subtitle doc.setFontSize(16) doc.setFont(config.fontFamily, 'normal') doc.text(config.coverSubtitle, pageWidth / 2, pageHeight / 2, { align: 'center' }) // Author & Date doc.setFontSize(12) doc.text(config.author, pageWidth / 2, pageHeight / 2 + 30, { align: 'center' }) doc.text(config.date, pageWidth / 2, pageHeight / 2 + 40, { align: 'center' }) } ``` ### 4. DOCX Export (using docx library) ```typescript import { Document, Paragraph, TextRun, AlignmentType, HeadingLevel } from 'docx' import { saveAs } from 'file-saver' import { Packer } from 'docx' export const generateDOCX = async ( config: ExportConfig, onProgress?: (percent: number) => void ): Promise => { const sections = [] // Cover page if (config.includeCoverPage) { sections.push({ children: [ new Paragraph({ text: config.coverTitle, heading: HeadingLevel.TITLE, alignment: AlignmentType.CENTER, spacing: { before: 400, after: 200 } }), new Paragraph({ text: config.coverSubtitle, alignment: AlignmentType.CENTER, spacing: { after: 200 } }), new Paragraph({ text: config.author, alignment: AlignmentType.CENTER, spacing: { after: 100 } }), new Paragraph({ text: config.date, alignment: AlignmentType.CENTER }) ] }) } // Fetch content const verses = await fetchVerses( config.book, config.startChapter, config.endChapter ) const chapters = groupByChapters(verses) for (const [chapterNum, chapterVerses] of Object.entries(chapters)) { // Chapter heading if (config.includeChapterNumbers) { sections.push( new Paragraph({ text: `Chapter ${chapterNum}`, heading: HeadingLevel.HEADING_1, spacing: { before: 400, after: 200 } }) ) } // Verses for (const verse of chapterVerses) { const paragraph = new Paragraph({ children: [] }) // Verse number if (config.includeVerseNumbers) { paragraph.addChildElement( new TextRun({ text: `${verse.verseNum} `, bold: true }) ) } // Verse text paragraph.addChildElement( new TextRun({ text: verse.text, size: config.fontSize * 2 // Convert to half-points }) ) sections.push(paragraph) // Highlights if (config.includeHighlights && verse.highlights) { for (const highlight of verse.highlights) { sections.push( new Paragraph({ children: [ new TextRun({ text: `[Highlight: ${highlight.color}] ${highlight.text}`, italics: true, color: highlight.color }) ], spacing: { before: 100 } }) ) } } // Notes if (config.includeNotes && verse.notes) { sections.push( new Paragraph({ children: [ new TextRun({ text: `Note: ${verse.notes}`, italics: true, color: '666666' }) ], spacing: { before: 100, after: 100 } }) ) } } } const doc = new Document({ sections: [{ properties: { page: { margin: { top: config.margins.top * 56.7, // Convert mm to twips right: config.margins.right * 56.7, bottom: config.margins.bottom * 56.7, left: config.margins.left * 56.7 } } }, children: sections }] }) return await Packer.toBlob(doc) } ``` ### 5. Markdown Export ```typescript export const generateMarkdown = async ( config: ExportConfig ): Promise => { let markdown = '' // Front matter if (config.includeCoverPage) { markdown += `---\n` markdown += `title: ${config.coverTitle}\n` markdown += `subtitle: ${config.coverSubtitle}\n` markdown += `author: ${config.author}\n` markdown += `date: ${config.date}\n` markdown += `---\n\n` } // Title markdown += `# ${config.coverTitle}\n\n` // Fetch content const verses = await fetchVerses( config.book, config.startChapter, config.endChapter ) const chapters = groupByChapters(verses) for (const [chapterNum, chapterVerses] of Object.entries(chapters)) { // Chapter heading if (config.includeChapterNumbers) { markdown += `## Chapter ${chapterNum}\n\n` } // Verses for (const verse of chapterVerses) { if (config.includeVerseNumbers) { markdown += `**${verse.verseNum}** ` } markdown += `${verse.text}\n\n` // Highlights if (config.includeHighlights && verse.highlights) { for (const highlight of verse.highlights) { markdown += `> 🎨 **Highlight (${highlight.color}):** ${highlight.text}\n\n` } } // Notes if (config.includeNotes && verse.notes) { markdown += `> 📝 **Note:** ${verse.notes}\n\n` } } markdown += '\n---\n\n' } return markdown } ``` ### 6. Batch Export ```typescript interface BatchExportConfig { books: string[] format: ExportFormat separate: boolean // Export each book as separate file combinedFilename?: string } export const batchExport = async ( config: BatchExportConfig, onProgress?: (current: number, total: number) => void ): Promise => { if (config.separate) { // Export each book separately const blobs: Blob[] = [] for (let i = 0; i < config.books.length; i++) { const book = config.books[i] const exportConfig: ExportConfig = { ...getDefaultConfig(), book, startChapter: 1, endChapter: await getLastChapter(book), format: config.format } const blob = await exportContent(exportConfig) blobs.push(blob) if (onProgress) { onProgress(i + 1, config.books.length) } } return blobs } else { // Export all books in one file const exportConfig: ExportConfig = { ...getDefaultConfig(), format: config.format // Will loop through all books internally } return await exportContent(exportConfig) } } ``` ### 7. Print Optimization ```typescript const PrintPreview: React.FC<{ config: ExportConfig }> = ({ config }) => { const contentRef = useRef(null) const handlePrint = () => { const printWindow = window.open('', '', 'height=800,width=600') if (!printWindow) return const printStyles = ` ` printWindow.document.write(` ${config.coverTitle} ${printStyles} ${contentRef.current?.innerHTML} `) printWindow.document.close() printWindow.focus() printWindow.print() } return ( {/* Rendered content here */} ) } ``` ### 8. Email Export ```typescript interface EmailExportConfig { to: string[] subject: string message: string exportConfig: ExportConfig } const EmailExportDialog: React.FC = () => { const [config, setConfig] = useState({ to: [], subject: '', message: '', exportConfig: getDefaultConfig() }) const handleSend = async () => { // Generate export const blob = await exportContent(config.exportConfig) // Convert to base64 const base64 = await blobToBase64(blob) // Send via API await fetch('/api/export/email', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ to: config.to, subject: config.subject, message: config.message, attachment: { filename: generateFilename(config.exportConfig), content: base64, contentType: getMimeType(config.exportConfig.format) } }) }) } return ( Email Export setConfig({ ...config, to: e.target.value.split(',').map(s => s.trim()) })} fullWidth /> setConfig({ ...config, subject: e.target.value })} fullWidth /> setConfig({ ...config, message: e.target.value })} multiline rows={4} fullWidth /> ) } ``` --- ## 📊 API Endpoints ```typescript // Generate and download export POST /api/export Body: ExportConfig Response: File (binary) // Email export POST /api/export/email Body: { to: string[] subject: string message: string attachment: { filename: string content: string (base64) contentType: string } } // Get export templates GET /api/export/templates Response: { templates: ExportTemplate[] } // Save export preset POST /api/export/presets Body: { name: string, config: ExportConfig } ``` --- ## 📅 Implementation Timeline ### Week 1: Core Export **Day 1-2: Foundation** - [ ] Create export dialog UI - [ ] Build configuration forms - [ ] Implement content fetching **Day 3-4: PDF Export** - [ ] Integrate jsPDF - [ ] Implement basic PDF generation - [ ] Add highlights/notes support - [ ] Test layouts **Day 5: DOCX & Markdown** - [ ] Implement DOCX export - [ ] Implement Markdown export - [ ] Test formatting **Deliverable:** Working PDF, DOCX, Markdown exports ### Week 2: Advanced Features **Day 1-2: Layout Customization** - [ ] Add cover page generation - [ ] Implement TOC - [ ] Add headers/footers - [ ] Build print preview **Day 3-4: Batch & Email** - [ ] Implement batch export - [ ] Build email functionality - [ ] Add progress tracking - [ ] Test large exports **Day 5: Polish** - [ ] Performance optimization - [ ] Error handling - [ ] UI refinement - [ ] Documentation **Deliverable:** Production-ready export system --- ## 🚀 Deployment Plan ### Pre-Launch - [ ] Test with various content sizes - [ ] Verify all formats generate correctly - [ ] Performance testing - [ ] Cross-browser testing - [ ] Mobile testing ### Rollout 1. **Beta**: Limited users, PDF only 2. **Staged**: 50% users, all formats 3. **Full**: 100% deployment --- **Document Version:** 1.0 **Last Updated:** 2025-10-13 **Owner:** Development Team **Status:** Ready for Implementation