#!/usr/bin/env node /** * PDF Generation Script * * Uses Puppeteer to render each HTML page to PDF. * Run with: node generate-pdfs.js * * Prerequisites: npm install puppeteer */ const puppeteer = require('puppeteer'); const http = require('http'); const fs = require('fs'); const path = require('path'); // Configuration const SITE_DIR = path.join(__dirname, 'resume'); const PDF_DIR = path.join(SITE_DIR, 'pdfs'); const PORT = 8765; // MIME types for static file serving const MIME_TYPES = { '.html': 'text/html', '.css': 'text/css', '.js': 'application/javascript', '.json': 'application/json', '.png': 'image/png', '.jpg': 'image/jpeg', '.jpeg': 'image/jpeg', '.gif': 'image/gif', '.svg': 'image/svg+xml', '.ico': 'image/x-icon', '.woff': 'font/woff', '.woff2': 'font/woff2', '.ttf': 'font/ttf', '.eot': 'application/vnd.ms-fontobject', }; /** * Find all HTML files in a directory recursively */ function findHtmlFiles(dir, baseDir = dir) { const files = []; const entries = fs.readdirSync(dir, { withFileTypes: true }); for (const entry of entries) { const fullPath = path.join(dir, entry.name); if (entry.isDirectory()) { // Skip pdfs directory, node_modules, and hidden directories if (entry.name === 'pdfs' || entry.name === 'node_modules' || entry.name.startsWith('.')) { continue; } files.push(...findHtmlFiles(fullPath, baseDir)); } else if (entry.isFile() && entry.name.endsWith('.html')) { // Skip template files if (entry.name.includes('template') || entry.name.includes('with-includes')) { continue; } const relativePath = path.relative(baseDir, fullPath); files.push(relativePath); } } return files; } /** * Create a simple static file server */ function createServer() { return http.createServer((req, res) => { let urlPath = req.url.split('?')[0]; if (urlPath === '/') urlPath = '/index.html'; const filePath = path.join(SITE_DIR, urlPath); const ext = path.extname(filePath); const contentType = MIME_TYPES[ext] || 'application/octet-stream'; fs.readFile(filePath, (err, data) => { if (err) { res.writeHead(404); res.end('Not found'); return; } res.writeHead(200, { 'Content-Type': contentType }); res.end(data); }); }); } /** * Ensure directory exists */ function ensureDir(dirPath) { if (!fs.existsSync(dirPath)) { fs.mkdirSync(dirPath, { recursive: true }); } } /** * Generate PDF for a single HTML file */ async function generatePdf(browser, htmlFile) { const page = await browser.newPage(); // Convert file path to URL path const urlPath = '/' + htmlFile.replace(/\\/g, '/'); const url = `http://localhost:${PORT}${urlPath}`; // Determine output PDF path const pdfRelativePath = htmlFile.replace(/\.html$/, '.pdf'); const pdfPath = path.join(PDF_DIR, pdfRelativePath); // Ensure output directory exists ensureDir(path.dirname(pdfPath)); try { // Navigate to the page and wait for content to load await page.goto(url, { waitUntil: 'networkidle0', timeout: 30000 }); // Wait a bit for any JavaScript to finish await page.waitForTimeout(1000); // Generate PDF await page.pdf({ path: pdfPath, format: 'A4', printBackground: true, margin: { top: '20mm', right: '15mm', bottom: '20mm', left: '15mm' } }); console.log(`āœ“ Generated: ${pdfRelativePath}`); } catch (error) { console.error(`āœ— Failed: ${htmlFile} - ${error.message}`); } finally { await page.close(); } } /** * Main function */ async function main() { console.log('PDF Generation Script'); console.log('=====================\n'); // Find all HTML files const htmlFiles = findHtmlFiles(SITE_DIR); console.log(`Found ${htmlFiles.length} HTML files to process\n`); if (htmlFiles.length === 0) { console.log('No HTML files found. Exiting.'); return; } // Clean and create PDF directory if (fs.existsSync(PDF_DIR)) { fs.rmSync(PDF_DIR, { recursive: true }); } ensureDir(PDF_DIR); // Start local server const server = createServer(); await new Promise(resolve => server.listen(PORT, resolve)); console.log(`Local server started on port ${PORT}\n`); // Launch browser const browser = await puppeteer.launch({ headless: 'new', args: ['--no-sandbox', '--disable-setuid-sandbox'] }); try { // Generate PDFs for each HTML file for (const htmlFile of htmlFiles) { await generatePdf(browser, htmlFile); } console.log(`\nāœ“ PDF generation complete! Files saved to: ${PDF_DIR}`); } finally { await browser.close(); server.close(); } } // Run the script main().catch(error => { console.error('Fatal error:', error); process.exit(1); });