⚠️ html to pdf
| 카테고리 | 기술 |
|---|---|
| 태그 | |
| 수정일 | Aug 24, 2025 |
| 작성일 | Apr 1, 2025 |
사용자 가이드 페이지를 따로 디자인 작업 없이 PDF를 추출하기 위해 작성.
JSON
{
"order": [ "register", "signin", ...]
"web": {
"title": "Main title",
"list": [
"regist": {
"title": "Service registration",
"list": [
{
"cont": [
"Lorem Ipsum is simply dummy text of the printing and typesetting industry.",
"It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passage"
]
}
]
},
"signin": {
"title": "Sign in",
"list": []
}
],
}
}
Markup
<article ref="$contents" class="contents">
<div class="visual">
<div class="cont">
<h2><br /></h2>
<BButton><Icons icon="download" /></BButton>
</div>
</div>
<section v-for="(key, index) in order" :key="index" class="section">
<div class="title"></div>
<div v-for="(subItem, index2) in data.list[key].list" :key="index2" class="cont-group">
<div class="img"><img :src="`~~${key}_${index2}.png`" /></div>
<div class="sub-cont">
<strong v-dompurify-html="subItem.title" />
<div class="cont">
<p v-for="(cont, index3) in subItem.cont" :key="index3"></p>
</div>
</div>
</div>
</section>
</article>
Composable
// composables/usePdfGenerator.ts
import jsPDF from 'jspdf'
import html2canvas from 'html2canvas'
export const usePdfGenerator = () => {
/**
* @param sections - PDF로 변환할 section HTMLElement 배열
* 각 section 내부의 "div.title" 및 "div.cont-group" 요소들을 순서대로 처리합니다.
* - "div.title"은 무조건 새 페이지에서 시작합니다.
* - "div.cont-group"은 페이지의 남은 사용 가능한 영역에 완전히 들어가지 않으면 새 페이지에서 시작합니다.
* @returns Promise<jsPDF> - 생성된 jsPDF 인스턴스
*/
const generatePdf = async (sections: HTMLElement[]): Promise<jsPDF> => {
const doc = new jsPDF('p', 'mm', 'a4')
const pageWidth = doc.internal.pageSize.getWidth()
const pageHeight = doc.internal.pageSize.getHeight()
const margin = 20 // 상하좌우 여백 20mm
// 여백 제외한 사용 가능한 영역 크기
const usableWidth = pageWidth - margin * 2
const usableHeight = pageHeight - margin * 2
// 현재 페이지에서 사용한 높이 (사용 가능한 영역 내, mm)
let currentPageY = 0
// 각 section별로 처리
for (const section of sections) {
// section 내부의 "div.title"과 "div.cont-group" 요소들을 문서 순서대로 선택
const items = section.querySelectorAll('div.title, div.cont-group')
for (const item of Array.from(items)) {
const el = item as HTMLElement
// "div.title"은 무조건 새 페이지에서 시작 (현재 페이지에 내용이 있다면)
if (el.classList.contains('title') && currentPageY > 0) {
doc.addPage()
currentPageY = 0
}
// html2canvas로 해당 요소 캡처 (JPEG 포맷, 품질 90%)
const canvas = await html2canvas(el, { scale: 2, useCORS: true })
const imgData = canvas.toDataURL('image/jpeg', 1)
// PDF상의 사용 가능한 영역 폭에 맞추기 위한 변환 비율 (canvas 픽셀 → mm)
const convFactor = usableWidth / canvas.width
let elementMmWidth = usableWidth
let elementMmHeight = canvas.height * convFactor
// 만약 캡처된 요소의 높이가 사용 가능한 영역보다 크다면, 비율을 유지하면서 사용 가능한 영역 높이에 맞춤
if (elementMmHeight > usableHeight) {
const fitScale = usableHeight / elementMmHeight
elementMmWidth *= fitScale
elementMmHeight = usableHeight
}
// "div.cont-group"은 현재 페이지의 남은 공간에 완전히 들어가지 않으면 새 페이지에서 시작
if (el.classList.contains('cont-group') && currentPageY + elementMmHeight > usableHeight) {
doc.addPage()
currentPageY = 0
}
// 요소 이미지 추가 (여백을 고려하여 좌측: margin, 상단: margin + 현재 사용 높이)
doc.addImage(imgData, 'JPEG', margin, margin + currentPageY, elementMmWidth, elementMmHeight)
currentPageY += elementMmHeight
}
}
return doc
}
return { generatePdf }
}
사용예시
const { generatePdf } = usePdfGenerator()
const pdfDoc = await generatePdf(sectionElementsArray)
pdfDoc.save('export.pdf')
Previous Feb 24, 2025
« TypeScript의 주요 컴파일러 옵션
« TypeScript의 주요 컴파일러 옵션
Apr 6, 2025 Next
Scaffolding 시스템 만들어보자 - 1 »
Scaffolding 시스템 만들어보자 - 1 »