ํ์ฌ ํ๋ก์ ํธ์์ ์น ํ์ด์ง๋ฅผ ์๋์ผ๋ก PDF๋ก ๋ณํํ์ฌ ์๋ฒ๋ก ์ ์กํด์ผ ํ๋ ์๊ตฌ์ฌํญ์ด ์์์ต๋๋ค. ์๋ฒ ์ธก์์ PDF๋ฅผ ์์ฑํ๋ ๋ฐฉ๋ฒ๋ ์์์ง๋ง, CSS ์ฌ์ฉ์ด ์ ํ์ ์ด๊ณ ์น ํ์ด์ง์ ๋ ์ด์์์ด ๋ฌ๋ผ์ง๋ ๋ฌธ์ ๊ฐ ์์์ต๋๋ค. ๊ทธ๋์ ํ๋ก ํธ์๋์์ ์ง์ ํ์ด์ง๋ฅผ PDF๋ก ๋ณํํ๋ ๋ฐฉ๋ฒ์ ์ ํํ์ต๋๋ค.
์น ํ์ด์ง๋ฅผ PDF๋ก ๋ณํํ๊ธฐ ์ํด ๋ ๊ฐ์ง ํต์ฌ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ์ต๋๋ค:
์ด ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์กฐํฉํ๋ฉด ์น ํ์ด์ง์ ๋ชจ์์ ๊ทธ๋๋ก ์ ์งํ๋ฉด์ PDF๋ก ๋ณํํ ์ ์์ต๋๋ค.
๋จผ์ PDF ์์ฑ์ ์ํ ํจ์์ ๊ธฐ๋ณธ ๊ตฌ์กฐ๋ฅผ ์ค๊ณํ์ต๋๋ค:
interface generateReportPageToPdfProps {
patrolElements?: HTMLDivElement;
alarmElements: HTMLDivElement[];
startDate: string;
}
export const generateReportPageToPdf = async ({
patrolElements,
alarmElements,
}: generateReportPageToPdfProps) => {
// PDF ์์ฑ ๋ก์ง
};
ํจ์๋ ๋ ์ข ๋ฅ์ HTML ์์๋ฅผ ๋ฐ์ต๋๋ค:
patrolElements
: ๋ณด๊ณ ์์ ์ฃผ์ ๋ด์ฉalarmElements
: ์๋ ๋ณด๊ณ ์ ๋ชฉ๋ก (์ฌ๋ฌ ํ์ด์ง์ ๊ฑธ์ณ ํ์๋ ์ ์์)์ฒซ ๋ฒ์งธ ๋จ๊ณ๋ HTML ์์๋ฅผ ์บ๋ฒ์ค๋ก ๋ณํํ ํ ์ด๋ฏธ์ง๋ก ๋ง๋๋ ๊ฒ์ ๋๋ค:
// patrolElements๋ฅผ ์บก์ฒํ์ฌ ์ด๋ฏธ์ง๋ก ๋ณํ
const PatrolDetail = await html2canvas(patrolElements, {
allowTaint: true,
useCORS: true,
logging: false,
scale: 2, // ํด์๋๋ฅผ ๋์ด๊ธฐ ์ํด scale ๊ฐ์ 2๋ก ์ค์
});
const PatrolDetailImg = PatrolDetail.toDataURL('image/png', 1.0);
์ฌ๊ธฐ์ ์ฃผ๋ชฉํ ์ต์ ๋ค:
allowTaint
: ์ธ๋ถ ๋๋ฉ์ธ์ ์ด๋ฏธ์ง ์ฌ์ฉ ํ์ฉuseCORS
: ํฌ๋ก์ค ๋๋ฉ์ธ ์ด๋ฏธ์ง ์ฌ์ฉscale: 2
: ํด์๋๋ฅผ ๋์ด๊ธฐ ์ํ ์ค์ (์ด ๋ถ๋ถ์ด ์ค์!)๋ค์์ผ๋ก jsPDF๋ฅผ ์ฌ์ฉํ์ฌ PDF ๋ฌธ์๋ฅผ ์์ฑํ๊ณ ์ด๋ฏธ์ง๋ฅผ ์ถ๊ฐํฉ๋๋ค:
const imgWidth = 210; // A4 ๊ธฐ์ค ๊ฐ๋ก ๊ธธ์ด(mm)
const imgHeight = (PatrolDetail.height * imgWidth) / PatrolDetail.width;
const padding = 5;
// PDF ๋ฌธ์ ์์ฑ
const doc = new jsPDF('p', 'mm', 'a4', true);
// ์ฒซ ํ์ด์ง์ PatrolDetail ์ด๋ฏธ์ง ์ถ๊ฐ
doc.addImage(PatrolDetailImg, 'PNG', 0, 10, imgWidth, imgHeight);
alarmElements๋ฅผ ์ํํ๋ฉด์ ๊ฐ๊ฐ์ ์์๋ฅผ ์ด๋ฏธ์ง๋ก ๋ณํํ๊ณ PDF์ ์ถ๊ฐํฉ๋๋ค. ์ด๋ ํ์ด์ง ํฌ๊ธฐ๋ฅผ ๋์ด๊ฐ๋ฉด ์ ํ์ด์ง๋ฅผ ์ถ๊ฐํ๋ ๋ก์ง์ด ํ์ํฉ๋๋ค:
let curHeight = imgHeight + padding;
// alarmElements๋ฅผ ์ํํ๋ฉฐ ๊ฐ๊ฐ์ ์ด๋ฏธ์ง๋ก ๋ณํํ์ฌ PDF์ ์ถ๊ฐ
for (let i = 0; i < alarmElements.length; i++) {
const canvas = await html2canvas(alarmElements[i], {
allowTaint: true,
useCORS: true,
logging: false,
scale: 2,
});
const img = canvas.toDataURL('image/png', 1.0);
const imageHeight = (canvas.height * imgWidth) / canvas.width;
// ํ์ฌ ํ์ด์ง์ ๋์ด์ ์ด๋ฏธ์ง์ ๋์ด๋ฅผ ๋น๊ตํ์ฌ ํ์ด์ง๋ฅผ ์ถ๊ฐ
if (curHeight + imageHeight > doc.internal.pageSize.height - padding) {
doc.addPage();
curHeight = padding;
}
doc.addImage(img, 'PNG', padding, curHeight, 200, imageHeight);
curHeight += imageHeight + padding;
}
์บก์ฒ๋ ์ด๋ฏธ์ง์ ๊ฒ์ ์ ๋ถ๋ถ์ด ๋ํ๋๋ ๋ฌธ์ ๊ฐ ์์์ต๋๋ค.
ํด๊ฒฐ๋ฐฉ๋ฒ: scale
๊ฐ์ 2๋ก ์ค์ ํ์ฌ ํด์๋๋ฅผ ๋์์ต๋๋ค. ํ๋ฉด ํฌ๊ธฐ์ ๋นํด ์์ scale ๊ฐ์ด ๋ฌธ์ ์์ต๋๋ค.
Material-UI์ Paper ์ปดํฌ๋ํธ๊ฐ ์ ๋๋ก ์บก์ฒ๋์ง ์๋ ๋ฌธ์ ๊ฐ ์์์ต๋๋ค.
ํด๊ฒฐ๋ฐฉ๋ฒ: div๋ฅผ ์ฐธ์กฐํ๋ Box ์ปดํฌ๋ํธ๋ฅผ ์ฌ์ฉํ๊ณ , ์ง์ border ์คํ์ผ์ ์ ์ฉํ์ต๋๋ค.
์ด๋ฏธ์ง๊ฐ ์ํ๋ ๋๋ก ๋ฐฐ์น๋์ง ์๊ณ ํ์ด์ง๊ฐ ์์๋ก ๋์ด๊ฐ๋ ๋ฌธ์ ๊ฐ ์์์ต๋๋ค.
ํด๊ฒฐ๋ฐฉ๋ฒ: ๊ฐ ์์์ ref๋ฅผ ๋ฐฐ์ด๋ก ์ ์ฅํ๊ณ , ๊ฐ ์์์ ๋์ด๋ฅผ ๊ณ์ฐํ์ฌ ํ์ด์ง ๋ถํ ์ ์ ํํ๊ฒ ์ ์ดํ์ต๋๋ค:
const alarmRef = useRef([]);
// ์ปดํฌ๋ํธ ๋ด๋ถ JSX์์
{AlarmReportData.map((data, index) => (
<Box ref={(el) => (alarmRef.current[index] = el)} key={index}>
<AlarmReport data={data} />
</Box>
))}
// ๊ฐ ์์๋ฅผ ์ด๋ฏธ์ง๋ก ๋ณํ
const alramlists = [];
for (let i = 0; i < alarmRef.current.length; i++) {
const canvas = await html2canvas(alarmRef.current[i], {...});
const img = canvas.toDataURL('image/png', 1.0);
const imageHeight = (canvas.height * imgWidth) / canvas.width;
alramlists.push({ image: img, height: imageHeight });
}
// ํ์ด์ง ๋ถํ ๋ก์ง
let curHeight = padding;
for (let i = 0; i < alramlists.length; i++) {
const image = alramlists[i].image;
const imgHeight = alramlists[i].height;
if (curHeight + imgHeight > 297 - padding * 2) { // A4 ์ธ๋ก ๊ธธ์ด (297mm)
doc.addPage();
curHeight = padding;
}
doc.addImage(image, 'PNG', padding, curHeight, 200, imgHeight);
curHeight += imgHeight;
}
PDF์ ํ๊ธ ํ ์คํธ๋ฅผ ์ถ๊ฐํ ๋ ํฐํธ๊ฐ ๊นจ์ง๋ ๋ฌธ์ ๊ฐ ์์์ต๋๋ค.
ํด๊ฒฐ๋ฐฉ๋ฒ: ํฐํธ ํ์ผ์ ์ง์ importํ์ฌ jsPDF์ ๋ฑ๋กํ์ต๋๋ค:
import hl from '../font/HyundaiHarmonyL.ttf';
// PDF ์์ฑ ์
const doc = new jsPDF('p', 'mm', 'a4', true);
doc.addFont(hl, 'HyundaiHarmonyL', 'normal');
doc.setFont('HyundaiHarmonyL');
doc.setFontSize(8);
๊ฐ ํ์ด์ง์ ์ผ๊ด๋ ํธํฐ๋ฅผ ์ถ๊ฐํ๋ ๊ธฐ๋ฅ์ ๊ตฌํํ์ต๋๋ค:
const addFooters = (doc) => {
const pageCount = doc.internal.getNumberOfPages();
doc.setFont('HyundaiHarmonyL');
doc.setFontSize(8);
for (let i = 1; i <= pageCount; i++) {
doc.setPage(i);
doc.text(
'Page ' + String(i) + ' of ' + String(pageCount),
doc.internal.pageSize.width / 2,
287,
{ align: 'center' }
);
doc.text(
`๋ ํฌํธ ์์ฑ ์ผ์: ${getToday()}`,
doc.internal.pageSize.width / 2 + 40,
287,
{ align: 'center' }
);
}
};
์ต์ข ์ ์ผ๋ก ์์ฑ๋ PDF ์์ฑ ํจ์๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค:
export const generateReportPageToPdf = async ({
patrolElements,
alarmElements,
}: generateReportPageToPdfProps) => {
if (!patrolElements) {
console.error('Invalid patrolElements');
return;
}
const PatrolDetail = await html2canvas(patrolElements, {
allowTaint: true,
useCORS: true,
logging: false,
scale: 2,
});
const PatrolDetailImg = PatrolDetail.toDataURL('image/png', 1.0);
const imgWidth = 210; // ์ด๋ฏธ์ง ๊ฐ๋ก ๊ธธ์ด(mm) / A4 ๊ธฐ์ค 210mm
const imgHeight = (PatrolDetail.height * imgWidth) / PatrolDetail.width;
const padding = 5;
// PDF ๋ฌธ์ ์์ฑ ๋ฐ ํฐํธ ์ค์
const doc = new jsPDF('p', 'mm', 'a4', true);
doc.addFont(hl, 'HyundaiHarmonyL', 'normal');
doc.setFont('HyundaiHarmonyL');
doc.setFontSize(8);
// ์ฒซ ํ์ด์ง์ PatrolDetail ์ด๋ฏธ์ง ์ถ๊ฐ
doc.addImage(PatrolDetailImg, 'PNG', 0, 10, imgWidth, imgHeight);
let curHeight = imgHeight + padding;
// alarmElements๋ฅผ ์ํํ๋ฉฐ ๊ฐ๊ฐ์ ์ด๋ฏธ์ง๋ก ๋ณํํ์ฌ PDF์ ์ถ๊ฐ
for (let i = 0; i < alarmElements.length; i++) {
const canvas = await html2canvas(alarmElements[i], {
allowTaint: true,
useCORS: true,
logging: false,
scale: 2,
});
const img = canvas.toDataURL('image/png', 1.0);
const imageHeight = (canvas.height * imgWidth) / canvas.width;
// ํ์ฌ ํ์ด์ง์ ๋์ด์ ์ด๋ฏธ์ง์ ๋์ด๋ฅผ ๋น๊ตํ์ฌ ํ์์ ์ ํ์ด์ง ์ถ๊ฐ
if (curHeight + imageHeight > doc.internal.pageSize.height - padding) {
doc.addPage();
curHeight = padding;
}
doc.addImage(img, 'PNG', padding, curHeight, 200, imageHeight);
curHeight += imageHeight + padding;
}
addWaterMark(doc); // ์ํฐ๋งํฌ ์ถ๊ฐ
return doc;
};
์ด ๋ฐฉ๋ฒ์ ํตํด ์น ํ์ด์ง๋ฅผ ์๋ณธ ๋ ์ด์์๊ณผ ์คํ์ผ ๊ทธ๋๋ก PDF๋ก ๋ณํํ๊ณ , ์๋ฒ๋ก ์ ์กํ๋ ๊ธฐ๋ฅ์ ์ฑ๊ณต์ ์ผ๋ก ๊ตฌํํ์ต๋๋ค. ๋ฌผ๋ก ๋ณต์กํ ํ์ด์ง๋ ๋์ ์ปจํ ์ธ ๊ฐ ๋ง์ ๊ฒฝ์ฐ ์ถ๊ฐ์ ์ธ ์ต์ ํ๊ฐ ํ์ํ ์ ์์ง๋ง, ๊ธฐ๋ณธ์ ์ธ ์๊ตฌ์ฌํญ์ ์ถฉ์กฑํ๋ ์๋ฃจ์ ์ ์ ๊ณตํฉ๋๋ค.
๋ฆฌ์กํธ ํ๋ก์ ํธ์์ PDF ๋ณํ ๊ธฐ๋ฅ์ด ํ์ํ์ ๋ถ๋ค๊ป ์ด ๊ธ์ด ๋์์ด ๋๊ธธ ๋ฐ๋๋๋ค. ๋๋ถ์ ์ ๋ ๋ง์ ๊ฒ์ ๋ฐฐ์ ๊ณ , ์ด ์ง์์ ๊ณต์ ํ ์ ์์ด ๊ธฐ์ฉ๋๋ค.