Academic Poster Layout Generator

Poster Layout Preview

Configure Your Poster

General Information

Poster Sections

Add and arrange the sections for each column. The order here will be the order on the poster.

Add sections in the 'Data Configuration' tab.

`; } else { sections.forEach(sectionTitle => { const section = document.createElement('div'); section.className = 'apg-poster-section'; section.innerHTML = `

${apg_escapeHTML(sectionTitle) || 'Untitled Section'}

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam...

`; column.appendChild(section); }); } body.appendChild(column); } targetDiv.appendChild(body); } /** * Reads all values from the config tab and updates the apg_data object */ function apg_updateDataFromConfig() { if (!apg_inputTitle) return; apg_data.title = apg_inputTitle.value; apg_data.authors = apg_inputAuthors.value; apg_data.columnCount = parseInt(apg_inputColumns.value, 10); apg_data.columns = []; const columnBuckets = apg_columnConfigContainer.querySelectorAll('.apg-sections-list'); columnBuckets.forEach(bucket => { const colIndex = parseInt(bucket.getAttribute('data-col-index'), 10); apg_data.columns[colIndex] = []; const inputs = bucket.querySelectorAll('.apg-section-input'); inputs.forEach(input => { if (input.value) { apg_data.columns[colIndex].push(input.value); } }); }); } /** * Generates and downloads a PDF of the dashboard */ async function apg_downloadPDF() { // Check for required libraries if (typeof jspdf === 'undefined' || typeof html2canvas === 'undefined') { console.error("APG Tool Error: jsPDF or html2canvas library not loaded."); alert("Error: PDF libraries failed to load. Please check console."); return; } // 1. Create a high-res, off-screen clone const pdfClone = document.createElement('div'); document.body.appendChild(pdfClone); // 2. Render the full-size poster into the clone apg_renderDashboard(pdfClone, true); const { jsPDF } = window.jspdf; // Poster dimensions (16:9) const posterWidth = 1600; const posterHeight = 900; try { // 3. Render canvas from the clone const canvas = await html2canvas(pdfClone, { scale: 2, // Use 2x scale on 1600px for 3200px image useCORS: true, width: posterWidth, height: posterHeight, windowWidth: posterWidth, windowHeight: posterHeight }); const imgData = canvas.toDataURL('image/png'); // 4. Create a landscape PDF // Use 'px' units, and match the dimensions. // A4 landscape is approx 842 x 595 pt. // Let's use a standard 16:9 ratio in 'pt' const pdfWidth = 842; const pdfHeight = (842 * 9) / 16; // 473.625 const pdf = new jsPDF({ orientation: 'l', // landscape unit: 'pt', format: [pdfWidth, pdfHeight] }); // 5. Add image to PDF, covering the whole page pdf.addImage(imgData, 'PNG', 0, 0, pdfWidth, pdfHeight); const safeName = (apg_data.title || 'poster_layout').replace(/[^a-z0-9]/gi, '_').toLowerCase(); pdf.save(`${safeName}.pdf`); } catch (error) { console.error("APG Tool Error: PDF generation failed.", error); alert("An error occurred while generating the PDF. Please try again."); } finally { // 6. Remove the clone document.body.removeChild(pdfClone); } } /** * Utility to escape HTML for display */ function apg_escapeHTML(str) { if (typeof str !== 'string') return ''; return str.replace(/[&<>"']/g, function(m) { return { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[m]; }); } // --- EVENT LISTENERS --- // Tab link clicks apg_tabLinks.forEach((link, index) => { link.addEventListener('click', () => apg_switchTab(index)); }); // Next/Prev button clicks if (apg_prevButton) { apg_prevButton.addEventListener('click', () => { if (apg_currentTab > 0) apg_switchTab(apg_currentTab - 1); }); } if (apg_nextButton) { apg_nextButton.addEventListener('click', () => { if (apg_currentTab < apg_tabLinks.length - 1) apg_switchTab(apg_currentTab + 1); }); } // PDF download if (apg_downloadPdfButton) { apg_downloadPdfButton.addEventListener('click', apg_downloadPDF); } // Column count change if (apg_inputColumns) { apg_inputColumns.addEventListener('change', () => { apg_updateDataFromConfig(); // Save what we have apg_data.columnCount = parseInt(apg_inputColumns.value, 10); // Update count apg_renderConfig(); // Re-render config buckets }); } // Auto-update dashboard on config changes if (apg_configTab) { apg_configTab.addEventListener('change', () => { apg_updateDataFromConfig(); if (apg_currentTab === 0) { apg_renderDashboard(); } }); // Also listen for 'keyup' for faster feedback on text inputs apg_configTab.addEventListener('keyup', (e) => { if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') { apg_updateDataFromConfig(); if (apg_currentTab === 0) { apg_renderDashboard(); } } }); } // --- INITIALIZATION --- apg_initSampleData(); apg_renderConfig(); apg_renderDashboard(); });
Scroll to Top