${title} ${html}`); printWindow.document.close(); const highlightInPrint = () => { try { const hlScript = printWindow.document.createElement('script'); hlScript.src = 'https://cdn.jsdelivr.net/npm/[email protected]/highlight.min.js'; hlScript.onload = () => { printWindow.hljs.highlightAll(); setTimeout(() => printWindow.print(), 200); }; hlScript.onerror = () => { // Try cdnjs fallback const hlScript2 = printWindow.document.createElement('script'); hlScript2.src = 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js'; hlScript2.onload = () => { printWindow.hljs.highlightAll(); setTimeout(() => printWindow.print(), 200); }; hlScript2.onerror = () => setTimeout(() => printWindow.print(), 200); printWindow.document.head.appendChild(hlScript2); }; printWindow.document.head.appendChild(hlScript); } catch (e) { setTimeout(() => printWindow.print(), 200); } }; if (printWindow.document.readyState === 'complete') highlightInPrint(); else printWindow.onload = highlightInPrint; } // ── Quick Download PDF ── async function downloadPDF() { if (!markdownContent.trim()) { showError(t('toolPage.errors.inputEmpty', '请先上传Markdown文件')); return; } showLoading(t('toolPage.messages.generating', '正在生成PDF,请稍候…')); try { await ensureLibsLoaded(); if (!window.jspdf || !window.html2canvas) throw new Error('PDF generation libraries not available'); const html = renderMarkdown(markdownContent); const tc = document.createElement('div'); tc.style.cssText = 'position:absolute;left:-9999px;top:0;width:794px;padding:40px;background:#fff;font-family:Arial,sans-serif;font-size:14px;line-height:1.6;color:#333;'; tc.className = 'prose pdf-export max-w-none'; tc.innerHTML = html; document.body.appendChild(tc); tc.querySelectorAll('h1,h2,h3,h4,h5,h6,p,li,td,th,span,div').forEach(el => { el.style.color = '#1f2937'; }); tc.querySelectorAll('pre').forEach(el => { el.style.backgroundColor = '#f3f4f6'; el.style.border = '1px solid #e5e7eb'; el.style.color = '#1f2937'; }); tc.querySelectorAll('code').forEach(el => { if (!el.parentElement || el.parentElement.tagName !== 'PRE') { el.style.backgroundColor = '#f3f4f6'; el.style.border = '1px solid #e5e7eb'; el.style.color = '#1f2937'; } }); tc.querySelectorAll('pre code').forEach(block => { if (window.hljs) hljs.highlightElement(block); block.classList.add('hljs'); }); const imgs = Array.from(tc.querySelectorAll('img')); if (imgs.length) await Promise.all(imgs.map(img => new Promise(r => { if (img.complete) r(); else { img.onload = r; img.onerror = r; } }))); await new Promise(r => setTimeout(r, 300)); const canvas = await html2canvas(tc, { scale: 2, useCORS: true, allowTaint: false, backgroundColor: '#ffffff', logging: false, width: tc.scrollWidth, height: tc.scrollHeight }); document.body.removeChild(tc); const { jsPDF } = window.jspdf; const pdf = new jsPDF({ orientation: 'portrait', unit: 'px', format: 'a4', hotfixes: ['px_scaling'] }); const pw = 794, ph = 1123, m = 40, cw = pw - m * 2, ch = ph - m * 2; const sc = cw / canvas.width, tp = Math.ceil((canvas.height * sc) / ch); const sl = document.createElement('canvas'), sx = sl.getContext('2d'); sl.width = canvas.width; for (let p = 0; p < tp; p++) { if (p > 0) pdf.addPage(); const sy = (p * ch) / sc, sh = Math.min(ch / sc, canvas.height - sy); sl.height = sh; sx.drawImage(canvas, 0, sy, canvas.width, sh, 0, 0, canvas.width, sh); pdf.addImage(sl.toDataURL('image/jpeg', 0.95), 'JPEG', m, m, cw, sh * sc); } pdfBlob = pdf.output('blob'); const url = URL.createObjectURL(pdfBlob); const a = document.createElement('a'); a.href = url; a.download = (currentFileName || 'markdown-document') + '.pdf'; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); showSuccess(); } catch (error) { console.error('Convert failed:', error); if (error.message.includes('libraries not available') || libsLoadFailed) { showError(t('toolPage.errors.libsNotAvailable', 'PDF生成库加载失败')); } else { showError(t('toolPage.messages.convertFailedPrefix', '转换失败:') + (error.message || t('toolPage.messages.unknownError', '未知错误'))); } document.querySelectorAll('.prose.pdf-export[style*="position: absolute"]').forEach(el => { try { document.body.removeChild(el); } catch (e) {} }); } } // ── Reset ── function resetToUpload() { markdownContent = ''; currentFileName = 'markdown-document'; pdfBlob = null; switchTab('preview'); showUploadView(); } // ── Status helpers ── function showLoading(text) { status.classList.remove('hidden'); statusLoading.classList.remove('hidden'); statusSuccess.classList.add('hidden'); statusError.classList.add('hidden'); if (text) loadingText.textContent = text; } function showSuccess() { statusLoading.classList.add('hidden'); statusSuccess.classList.remove('hidden'); statusError.classList.add('hidden'); status.classList.remove('hidden'); } function showError(text) { status.classList.remove('hidden'); statusLoading.classList.add('hidden'); statusSuccess.classList.add('hidden'); statusError.classList.remove('hidden'); if (text) errorText.textContent = text; setTimeout(hideStatus, 5000); } function hideStatus() { status.classList.add('hidden'); } // ── Event bindings ── printPdfBtn.addEventListener('click', printAsPDF); downloadPdfBtn.addEventListener('click', downloadPDF); clearBtn.addEventListener('click', () => { if (markdownContent.trim()) { if (!confirm(t('toolPage.confirm.clearConfirm', '确定要清空内容吗?'))) return; } resetToUpload(); }); // ── Init ── (async function init() { try { await ensureLibsLoaded(); } catch (e) { console.warn(e); } showUploadView(); })();