`;
pdfDownloadBtn.style.display = 'inline-block';
copyBtn.style.display = 'inline-block';
}
async function downloadPDF() {
// Per user request, ensuring this is robust
pdfTarget.classList.add('spg-pdf-view');
try {
// Use scale: 2 for better text quality
const canvas = await html2canvas(pdfTarget, {
scale: 2,
logging: false,
useCORS: true,
});
const imgData = canvas.toDataURL('image/png');
const pdf = new jsPDF('p', 'mm', 'a4'); // Standard A4 size
const pdfWidth = pdf.internal.pageSize.getWidth(); // 210mm
const pdfHeight = pdf.internal.pageSize.getHeight(); // 297mm
// A4 aspect ratio is approx 1 / 1.414 or 8.27 / 11.69
// Our preview 'aspect-[8.5/11]' is ~0.77
// PDF '210 / 297' is ~0.707
// The canvas aspect ratio will match our preview div.
const canvasWidth = canvas.width;
const canvasHeight = canvas.height;
const canvasAspect = canvasHeight / canvasWidth; // Should be ~11/8.5 = 1.29
const margin = 10; // 10mm margin
const contentWidth = pdfWidth - (margin * 2); // 190mm
const contentHeight = contentWidth * canvasAspect;
let yPos = margin;
// Check if it fits (190 * 1.29 = 245mm, which is < 297-20=277. It fits.)
if (contentHeight > (pdfHeight - (margin * 2))) {
console.warn("Content height exceeds page, scaling down further.");
// This case shouldn't be hit if aspect ratio is correct
}
pdf.addImage(imgData, 'PNG', margin, yPos, contentWidth, contentHeight);
pdf.save(`${coverPageData.title.replace(/\s+/g, '_') || 'Cover_Page'}.pdf`);
} catch (error) {
console.error("Error generating PDF:", error);
showMessage("Error generating PDF. Please try again.", "error");
} finally {
pdfTarget.classList.remove('spg-pdf-view');
}
}
function copyText() {
let text = `Title: ${coverPageData.title}\n`;
text += `Author: ${coverPageData.author}\n`;
text += `\n`;
text += `Subject: ${coverPageData.subject}\n`;
text += `Teacher: ${coverPageData.teacher}\n`;
text += `School: ${coverPageData.school}\n`;
text += `Date: ${coverPageData.date}\n`;
const el = document.createElement('textarea');
el.value = text;
document.body.appendChild(el);
el.select();
try {
document.execCommand('copy');
showMessage('Cover page text copied to clipboard!');
} catch (err) {
showMessage('Failed to copy text.', 'error');
}
document.body.removeChild(el);
}
// --- Utilities ---
function showMessage(message, type = "info") {
let feedbackEl = document.getElementById('feedback-message');
if (!feedbackEl) {
feedbackEl = document.createElement('div');
feedbackEl.id = 'feedback-message';
feedbackEl.style.position = 'fixed';
feedbackEl.style.bottom = '20px';
feedbackEl.style.left = '50%';
feedbackEl.style.transform = 'translateX(-50%)';
feedbackEl.style.padding = '10px 20px';
feedbackEl.style.borderRadius = '8px';
feedbackEl.style.zIndex = '1000';
feedbackEl.style.opacity = '0';
feedbackEl.style.transition = 'opacity 0.5s ease';
document.body.appendChild(feedbackEl);
}
feedbackEl.textContent = message;
feedbackEl.style.opacity = '1';
const colors = { error: { bg: '#fef2f2', text: '#991b1b' }, success: { bg: '#f0fdf4', text: '#14532d' }, info: { bg: '#eff6ff', text: '#1e3a8a' }};
feedbackEl.style.backgroundColor = colors[type].bg;
feedbackEl.style.color = colors[type].text;
setTimeout(() => { feedbackEl.style.opacity = '0'; }, 3000);
}
function escapeHTML(str) {
if (typeof str !== 'string') return '';
return str ? str.replace(/[&<>"']/g, m => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[m])) : '';
}
// --- Event Listeners ---
navTabs.forEach((tab, index) => tab.addEventListener('click', () => showTab(index)));
navPrev.addEventListener('click', () => showTab(currentTab - 1));
navNext.addEventListener('click', () => showTab(currentTab + 1));
// Tab 1
pdfDownloadBtn.addEventListener('click', downloadPDF);
copyBtn.addEventListener('click', copyText);
// Tab 2
generateBtn.addEventListener('click', handleGeneratePreview);
// Tab 3
configForm.addEventListener('submit', saveConfig);
// --- Initial Load ---
dateInput.value = new Date().toISOString().split('T')[0];
renderPreviewPage(); // Show initial placeholder
showTab(1); // Start on builder tab
Cover Page Preview
Configuration
Save your default information to auto-populate the builder next time.