/** * 레포트 HTML을 별도 iframe 문서로 열어 인쇄·PDF 저장 (메인 앱 visibility 인쇄 버그 회피) */ /** App.css 전체 로드 전에도 PDF 다페이지·하단 잘림 방지 */ const CRITICAL_PRINT_CSS = ` @media print { html, body { height: auto !important; overflow: visible !important; margin: 0 !important; padding: 0 !important; } .btd-report-print-doc, .btd-report-print-area, .brd-dashboard, .brd-dashboard--report { overflow: visible !important; max-height: none !important; min-height: 0 !important; height: auto !important; font-family: 'Apple SD Gothic Neo', 'Malgun Gothic', 'Noto Sans KR', sans-serif !important; } .brd-dashboard { break-inside: auto !important; page-break-inside: auto !important; } .brd-row, .brd-row--2col, .brd-row--3col, .brd-card, .brd-card--full { break-inside: auto !important; page-break-inside: auto !important; overflow: visible !important; } .brd-kpi-grid { break-inside: auto !important; page-break-inside: auto !important; } .brd-kpi-card, .brd-dash-header { break-inside: avoid !important; page-break-inside: avoid !important; } .brd-sig-scroll { max-height: none !important; overflow: visible !important; height: auto !important; } .brd-expand-btn { display: none !important; } .btd-report-print-footer { break-inside: avoid !important; page-break-inside: avoid !important; } } @page { size: A4 portrait; margin: 10mm; } `; const FONT_LINKS = ` `; function escapeHtml(s: string): string { return s .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"'); } function collectDocumentStyles(): string { const links = Array.from(document.querySelectorAll('link[rel="stylesheet"]')) .filter(l => l.href && !l.href.includes('fonts.googleapis.com')) .map(l => ``); const inline = Array.from(document.querySelectorAll('style')) .map(s => s.outerHTML); return [...links, ...inline].join('\n'); } export function printReportDocument(contentHtml: string, documentTitle: string): void { const iframe = document.createElement('iframe'); iframe.setAttribute('title', 'print'); iframe.style.cssText = 'position:fixed;width:0;height:0;border:0;left:-9999px;top:0;'; document.body.appendChild(iframe); const win = iframe.contentWindow; const doc = win?.document; if (!doc) { iframe.remove(); return; } doc.open(); doc.write(` ${escapeHtml(documentTitle)} ${FONT_LINKS} ${collectDocumentStyles()} `); doc.close(); if (doc.body) { doc.body.insertAdjacentHTML('beforeend', contentHtml); } let printed = false; const runPrint = () => { if (printed) return; printed = true; try { win?.focus(); win?.print(); } finally { window.setTimeout(() => iframe.remove(), 2000); } }; iframe.addEventListener('load', () => { window.setTimeout(runPrint, 500); }, { once: true }); window.setTimeout(runPrint, 900); }