138 lines
3.5 KiB
TypeScript
138 lines
3.5 KiB
TypeScript
/**
|
|
* 레포트 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 = `
|
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@400;500;700&display=swap" rel="stylesheet">
|
|
`;
|
|
|
|
function escapeHtml(s: string): string {
|
|
return s
|
|
.replace(/&/g, '&')
|
|
.replace(/</g, '<')
|
|
.replace(/>/g, '>')
|
|
.replace(/"/g, '"');
|
|
}
|
|
|
|
function collectDocumentStyles(): string {
|
|
const links = Array.from(document.querySelectorAll<HTMLLinkElement>('link[rel="stylesheet"]'))
|
|
.filter(l => l.href && !l.href.includes('fonts.googleapis.com'))
|
|
.map(l => `<link rel="stylesheet" href="${l.href}">`);
|
|
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(`<!DOCTYPE html>
|
|
<html lang="ko">
|
|
<head>
|
|
<meta charset="UTF-8" />
|
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
|
<title>${escapeHtml(documentTitle)}</title>
|
|
${FONT_LINKS}
|
|
${collectDocumentStyles()}
|
|
<style>${CRITICAL_PRINT_CSS}</style>
|
|
</head>
|
|
<body class="btd-report-print-doc"></body>
|
|
</html>`);
|
|
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);
|
|
}
|