Resume & CV Formatter

Resume & CV Formatter

Build a professional resume with live preview and PDF export.

${resumeData.summary}

`; } // Experience html += `

Experience

`; resumeData.experience.forEach(exp => { if(exp.job) html += `

${exp.job} | ${exp.company}

${exp.dates}

${exp.desc}

`; }); html += `
`; // Education html += `

Education

`; resumeData.education.forEach(edu => { if(edu.degree) html += `

${edu.degree} | ${edu.school}

${edu.dates}

${edu.desc}

`; }); html += `
`; // Skills if (resumeData.skills) { html += `

Skills

${resumeData.skills}

`; } previewContent.innerHTML = html; } // --- PDF Generation --- function generatePdf() { const { jsPDF } = window.jspdf; const pdf = new jsPDF('p', 'pt', 'letter'); const margin = 40; const pageWidth = pdf.internal.pageSize.getWidth(); let y = margin; // Helper for text wrapping const addWrappedText = (text, options) => { const lines = pdf.splitTextToSize(text, pageWidth - margin * 2); pdf.text(lines, margin, y, options); y += lines.length * (options.fontSize || 10) * 1.2; }; // Header pdf.setFontSize(20).setFont('helvetica', 'bold').text(resumeData.personal.name, pageWidth / 2, y, { align: 'center' }); y += 20; const contactInfo = [resumeData.personal.email, resumeData.personal.phone, resumeData.personal.location, resumeData.personal.link].filter(Boolean).join(' | '); pdf.setFontSize(10).setFont('helvetica', 'normal').text(contactInfo, pageWidth / 2, y, { align: 'center' }); y += 20; // Sections const addSection = (title, contentFn) => { if (!contentFn()) return; // Don't add empty sections y += 10; pdf.setFontSize(12).setFont('helvetica', 'bold').text(title.toUpperCase(), margin, y); pdf.line(margin, y + 2, pageWidth - margin, y + 2); y += 15; pdf.setFontSize(10).setFont('helvetica', 'normal'); contentFn(); }; addSection('Summary', () => { if(resumeData.summary) addWrappedText(resumeData.summary, {}); return resumeData.summary; }); addSection('Experience', () => { resumeData.experience.forEach(e => { if(e.job) { pdf.setFont('helvetica', 'bold').text(e.job, margin, y); pdf.text(e.dates, pageWidth - margin, y, {align: 'right'}); y+=12; pdf.setFont('helvetica', 'italic').text(`${e.company}`, margin, y); y+=12; pdf.setFont('helvetica', 'normal'); addWrappedText(e.desc, {}); y+=5; }}); return resumeData.experience[0]?.job; }); addSection('Education', () => { resumeData.education.forEach(e => { if(e.degree) { pdf.setFont('helvetica', 'bold').text(e.degree, margin, y); pdf.text(e.dates, pageWidth - margin, y, {align: 'right'}); y+=12; pdf.setFont('helvetica', 'italic').text(e.school, margin, y); y+=12; pdf.setFont('helvetica', 'normal'); addWrappedText(e.desc, {}); y+=5; }}); return resumeData.education[0]?.degree; }); addSection('Skills', () => { if(resumeData.skills) addWrappedText(resumeData.skills, {}); return resumeData.skills; }); pdf.save(`${(resumeData.personal.name || 'resume').replace(/\s/g, '_')}.pdf`); } // --- Event Listeners --- function addAccordionListeners() { document.querySelectorAll('.accordion-header').forEach(header => { header.addEventListener('click', () => { const content = header.nextElementSibling; const icon = header.querySelector('svg'); if (content.style.maxHeight) { content.style.maxHeight = null; icon.style.transform = 'rotate(0deg)'; } else { content.style.maxHeight = content.scrollHeight + 'px'; icon.style.transform = 'rotate(180deg)'; } }); }); } accordionContainer.addEventListener('input', updateData); document.getElementById('template-select').addEventListener('change', (e) => { resumeData.template = e.target.value; renderPreview(); }); document.getElementById('download-pdf-btn').addEventListener('click', generatePdf); // --- Initial Load --- buildForm(); renderPreview(); });
Scroll to Top