<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes"> <title>Japa Mala PDF Generator | Sacred Bead Counter & Tracker</title> <!-- html2pdf.js Library (standalone) --> <script src="https://cdnjs.cloudflare.com/ajax/libs/html2pdf.js/0.10.1/html2pdf.bundle.min.js" integrity="sha512-GsLlZN/3F2ErC5ifS5QtgpiJtWd43JWSuIgh7mbzZ8zBps+dvLusV+eNQATqgA/HdeKFVgA5v3S/cIrLF7QnIg==" crossorigin="anonymous" referrerpolicy="no-referrer"></script> <style> * margin: 0; padding: 0; box-sizing: border-box;
// Render the mala beads array based on style & color function renderBeads() "Japa Mala"; previewTitleSpan.innerText = title; const dedication = dedicationInput.value.trim() // Update color from swatch selection function initColorPicker() colorSwatches.forEach(swatch => swatch.addEventListener('click', (e) => const color = swatch.getAttribute('data-color'); if (color) activeColor = color; // update active style colorSwatches.forEach(s => s.classList.remove('active')); swatch.classList.add('active'); renderBeads(); // re-render preview ); // set default active (first one) if (swatch.getAttribute('data-color') === "#aa6f3c") swatch.classList.add('active'); ); // refresh preview on all input changes function bindPreviewEvents() malaTitleInput.addEventListener('input', () => renderBeads()); dedicationInput.addEventListener('input', () => renderBeads()); extraMetaInput.addEventListener('input', () => renderBeads()); numberStyleSelect.addEventListener('change', () => renderBeads()); // also any other field // ---------- PDF Generation using html2pdf with high-quality settings ---------- async function generatePDF() // first sync the preview to ensure latest data (renderBeads already called but call again for safety) renderBeads(); // Get the container that holds the mala design (exactly the preview card) const element = document.getElementById('pdfContentContainer'); if (!element) return; // optional: clone to avoid any live DOM interaction glitch? but we keep original, html2pdf captures current state. // we want to ensure the title, footer and beads are exactly as shown. // Also add a little timestamp and styling for print. const originalOverflow = document.body.style.overflow; document.body.style.overflow = 'auto'; // create a clone to prevent hidden scrollbars? but not necessary, using html2pdf's options const opt = margin: [0.6, 0.6, 0.6, 0.6], // top, right, bottom, left in inches filename: `japamala_$.pdf`, image: type: 'jpeg', quality: 0.98 , html2canvas: scale: 2, letterRendering: true, useCORS: true, logging: false , jsPDF: unit: 'in', format: 'a4', orientation: 'portrait' ; // Show a subtle loading alert? Better add status. const btn = document.getElementById('generatePdfBtn'); const originalBtnText = btn.innerText; btn.innerText = "✨ Generating PDF..."; btn.disabled = true; try await html2pdf().set(opt).from(element).save(); catch (err) console.error("PDF Error:", err); alert("Could not generate PDF. Check console or try again."); finally btn.innerText = originalBtnText; btn.disabled = false; document.body.style.overflow = originalOverflow; // Additional: ensure the meru bead is highlighted and total counter // Add extra elegance: PDF will contain a full mala circle representation and additional metadata. // Also we can add some small extra details inside PDF container but already perfect. // Improve the preview area by adding a little extra note for the user to understand that Meru is included. function enhancePreviewNote() // we add subtle info inside bead-grid container tooltip - but fine. // Also if needed, ensure background white for print. const style = document.createElement('style'); style.textContent = ` @media print body background: white; .controls display: none; .preview-area width: 100%; padding: 0; .bead-grid gap: 8px; .btn-group, .hero display: none; .app-container box-shadow: none; `; document.head.appendChild(style); // Init interactive simulation function init() initColorPicker(); bindPreviewEvents(); renderBeads(); // initial render document.getElementById('generatePdfBtn').addEventListener('click', generatePDF); document.getElementById('refreshPreviewBtn').addEventListener('click', () => renderBeads(); ); enhancePreviewNote(); // Additional: add placeholder for extra charm: on hover beads effect not needed but we make it nice // Provide a small manual note that Meru is 109th const infoDiv = document.createElement('div'); infoDiv.style.fontSize = '0.7rem'; infoDiv.style.marginTop = '10px'; infoDiv.style.textAlign = 'center'; infoDiv.style.color = '#9b7b56'; infoDiv.innerHTML = '🕉️ Meru bead (golden) marks the summit — start and end of chanting. PDF is ready for printing or digital tracking.'; document.querySelector('.preview-area .mala-card').appendChild(infoDiv); init(); </script> </body> </html>
/* form styling */ .input-group margin-bottom: 1.5rem; label display: block; font-weight: 600; color: #5c3e1f; margin-bottom: 6px; font-size: 0.85rem; letter-spacing: 0.5px; input, textarea, select width: 100%; padding: 10px 12px; border-radius: 40px; border: 1px solid #e2cfb5; background: white; font-family: inherit; font-size: 0.9rem; transition: 0.2s; outline: none; textarea border-radius: 24px; resize: vertical; input:focus, textarea:focus, select:focus border-color: #b97f44; box-shadow: 0 0 0 2px rgba(185, 127, 68, 0.2); .color-option display: flex; gap: 12px; align-items: center; flex-wrap: wrap; .color-swatch width: 36px; height: 36px; border-radius: 50%; cursor: pointer; border: 2px solid transparent; transition: 0.1s; box-shadow: 0 1px 3px rgba(0,0,0,0.2); .color-swatch.active border-color: #2c2b28; transform: scale(1.05); box-shadow: 0 0 0 2px #f9c27e; .btn-group display: flex; gap: 14px; margin-top: 25px; flex-wrap: wrap; .btn flex: 1; background: #d9b48b; border: none; padding: 12px 10px; border-radius: 60px; font-weight: bold; font-size: 0.9rem; color: #2c241a; cursor: pointer; transition: all 0.2s; text-align: center; font-family: inherit; box-shadow: 0 2px 4px rgba(0,0,0,0.1); .btn-primary background: #9c6e3e; color: white; box-shadow: 0 4px 8px rgba(0,0,0,0.1); .btn-primary:hover background: #7e562e; transform: translateY(-2px); .btn-secondary background: #e7cfb0; color: #4b351c; .btn-secondary:hover background: #dbbc93; button:active transform: translateY(1px); japamala pdf
<!-- live preview (dynamic) --> <div class="preview-area"> <div class="mala-card" id="pdfContentContainer"> <!-- dynamic mala preview will be injected here --> <div id="liveMalaPreview"> <!-- JS will render preview here --> <div class="pdf-header"> <h2 id="previewTitle">Om Mani Padme Hum</h2> <div class="date-badge">Japa Mala · Preview</div> </div> <div id="beadsPreviewGrid" class="bead-grid" style="justify-content: center;"></div> <div class="counter-panel"> <span class="total-count" id="totalCountPreview">108 + 1 Meru</span> <span style="font-size:0.8rem;">🔁 complete round = 108 counts</span> </div> <div id="previewFooter" style="margin-top: 1rem; font-size:0.85rem; text-align:center; color:#745d41;">May all beings be happy and free. 🙏</div> <div id="previewMeta" style="margin-top: 0.5rem; font-size:0.7rem; text-align:center; color:#b68b60;">108 beads + 1 Meru (Sumeru)</div> </div> </div> </div> </div> </div>
// Helper: number to Devanagari (limited to 1-109) function toDevanagari(num) ch).join(''); // Also add a little timestamp and styling for print
/* header */ .hero background: #2c2b28; padding: 1.5rem 2rem; color: #f9e7cf; text-align: center; .hero h1 font-size: 2rem; letter-spacing: 2px; font-weight: 600; font-family: 'Times New Roman', serif; .hero p font-size: 0.9rem; opacity: 0.85; margin-top: 6px;
/* mala preview (PDF visual) */ .mala-card background: white; border-radius: 32px; box-shadow: 0 10px 20px rgba(0,0,0,0.08); padding: 1.5rem; transition: all 0.2s; border: 1px solid #eedbba; .pdf-header text-align: center; margin-bottom: 1.5rem; border-bottom: 2px dashed #f0e0ca; padding-bottom: 0.75rem; .pdf-header h2 font-size: 1.6rem; font-family: 'Times New Roman', serif; color: #3e2a1b; .pdf-header .date-badge font-size: 0.7rem; color: #b48c5c; margin-top: 5px; .bead-grid display: flex; flex-wrap: wrap; justify-content: center; gap: 12px; margin: 30px 0; max-height: 500px; overflow-y: auto; padding: 8px; .bead width: 46px; height: 46px; border-radius: 50%; display: inline-flex; align-items: center; justify-content: center; font-size: 0.8rem; font-weight: bold; font-family: monospace; box-shadow: 0 3px 6px rgba(0,0,0,0.1); transition: 0.05s linear; color: white; text-shadow: 0 0 2px black; .meru-bead background: radial-gradient(circle at 30% 30%, #f5cf87, #c9892e); border: 2px solid #ffdd99; transform: scale(1.08); box-shadow: 0 4px 12px gold; .counter-panel background: #f5ede2; border-radius: 28px; padding: 1rem; margin-top: 1rem; display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 12px; .total-count font-size: 1.4rem; font-weight: bold; background: #2c2b28; display: inline-block; padding: 6px 18px; border-radius: 40px; color: #fadfaf; .legend display: flex; gap: 20px; font-size: 0.75rem; .small-bead-demo display: inline-block; width: 18px; height: 18px; border-radius: 50%; margin-right: 5px; vertical-align: middle; hr margin: 12px 0; border-color: #eeddc8; .footer-note text-align: center; font-size: 0.7rem; color: #a68459; margin-top: 15px; font-style: italic; @media (max-width: 760px) .bead width: 38px; height: 38px; font-size: 0.7rem; .controls, .preview-area padding: 1.2rem; </style> </head> <body> <div class="app-container"> <div class="hero"> <h1>📿 JAPA MALA · SACRED TRACKER</h1> <p>Design your custom 108-bead mala | generate printable PDF with mantra counter & meru bead</p> </div> <div class="dashboard"> <!-- left controls --> <div class="controls"> <div class="input-group"> <label>📿 Mala Name / Mantra</label> <input type="text" id="malaTitle" placeholder="e.g., Om Namah Shivaya Mala" value="Om Mani Padme Hum"> </div> <div class="input-group"> <label>🎨 Bead Color Theme</label> <div class="color-option" id="colorPalette"> <div class="color-swatch" style="background: #aa6f3c;" data-color="#aa6f3c"></div> <div class="color-swatch" style="background: #2f6b47;" data-color="#2f6b47"></div> <div class="color-swatch" style="background: #8b3c5e;" data-color="#8b3c5e"></div> <div class="color-swatch" style="background: #2c5f8a;" data-color="#2c5f8a"></div> <div class="color-swatch" style="background: #c97e2c;" data-color="#c97e2c"></div> <div class="color-swatch" style="background: #6a4c2c;" data-color="#6a4c2c"></div> </div> </div> <div class="input-group"> <label>🔢 Bead Number Style</label> <select id="numberStyle"> <option value="numbers">Numbers (1..108)</option> <option value="dots">Simple Dots •</option> <option value="sanskrit_hint">Sanskrit Numerals (Devanagari)</option> </select> </div> <div class="input-group"> <label>📝 Intention / Dedication (footer)</label> <textarea id="dedicationText" rows="2" placeholder="May this japa bring peace & compassion...">May all beings be happy and free. 🙏</textarea> </div> <div class="input-group"> <label>✨ Extra Tracker Details</label> <input type="text" id="extraMeta" placeholder="e.g., 108 rounds • Guru Mantra" value="108 beads + 1 Meru (Sumeru)"> </div> <div class="btn-group"> <button class="btn btn-primary" id="generatePdfBtn">📄 Generate PDF</button> <button class="btn btn-secondary" id="refreshPreviewBtn">⟳ Refresh Preview</button> </div> <div class="legend"> <div><span class="small-bead-demo" style="background: #d4af7a;"></span> Regular Bead</div> <div><span class="small-bead-demo" style="background: radial-gradient(#f5cf87, #c9892e);"></span> Meru Bead (109th / Guru)</div> </div> <hr> <div class="footer-note"> 💡 Meru bead is the summit bead — traditionally never crossed. PDF includes visual mala + mantra counter. </div> </div> Better add status
body background: linear-gradient(145deg, #e8dfd1 0%, #d6ccbb 100%); font-family: 'Segoe UI', 'Roboto', 'Merriweather', 'Georgia', serif; padding: 2rem 1rem; display: flex; justify-content: center; align-items: center; min-height: 100vh;
/* two column layout */ .dashboard display: flex; flex-wrap: wrap; .controls flex: 1.2; min-width: 260px; background: #fffaf2; padding: 1.8rem; border-right: 1px solid #eedcc7; .preview-area flex: 2; background: #fef7ed; padding: 1.8rem; display: flex; flex-direction: column;
// preview containers const previewTitleSpan = document.getElementById('previewTitle'); const beadsGrid = document.getElementById('beadsPreviewGrid'); const previewFooterDiv = document.getElementById('previewFooter'); const previewMetaSpan = document.getElementById('previewMeta'); const totalCountSpan = document.getElementById('totalCountPreview');
<script> // ---------- DOM Elements ---------- const malaTitleInput = document.getElementById('malaTitle'); const dedicationInput = document.getElementById('dedicationText'); const extraMetaInput = document.getElementById('extraMeta'); const numberStyleSelect = document.getElementById('numberStyle'); const colorSwatches = document.querySelectorAll('.color-swatch'); let activeColor = "#aa6f3c"; // default sandalwood