프로젝트에서 특정영역을 pdf로 저장이 필요해 라이브러리를 찾아봤다.
html2pdf.js와 vue-html2pdf가 있었다.
vue용 html2pdf.js
vue-html2pdf은 html2pdf.js를 vue 컴포넌트 형식으로 제공한다.
<template>
<div>
<vue-html2pdf
:show-layout="false"
:float-layout="true"
:enable-download="true"
:preview-modal="true"
:paginate-elements-by-height="1400"
filename="hee hee"
:pdf-quality="2"
:manual-pagination="false"
pdf-format="a4"
pdf-orientation="landscape"
pdf-content-width="800px"
@progress="onProgress($event)"
@hasStartedGeneration="hasStartedGeneration()"
@hasGenerated="hasGenerated($event)"
ref="html2Pdf"
>
<section slot="pdf-content">
<!-- PDF Content Here -->
</section>
</vue-html2pdf>
</div>
</template>
기존 html2pdf.js와 다르게 미리보기 창을 띄워주는 preview-modal 옵션이 있어서 좋았다.
하지만 slot으로 pdf안으로 들어갈 내용을 넣어야 하는데 slot안에 내용이 있는 태그들을 넣게 되면 해당 태그들은 웹에서 보여지지 않기 때문에 같은 내용을 한번 더 써줘야 하는 번거로움이 생긴다.
component태그를 이용해 버튼을 누르면 vue-html2pdf로 바꿔줬었지만 ie11에서 에러가 있어 사용하지 않았다.
html2pdf.js
vue의 컴포넌트 형식의 라이브러리는 아니지만 plugin으로 만들어서 사용하니 간단했다.
ie11에서도 잘 작동돼서 선택했다.
npm을 이용해 라이브러리를 설치했다.
npm install --save html2pdf.js@0.9.3
버전은 0.9.3을 추천한다. 공식 github에서도 0.9.3버전을 추천한다.
그냥 html2pdf가 아니고 html2pdf.js이다.
html2pdf.js에서 제공되는 옵션은 html2pdf.js options (github)에 들어가면 잘 나와있다.
html2pdf.js는 html2canvas와 jspdf의 기능을 넣은 라이브러리이기 때문에 두 라이브러리의 옵션도 넣을 수 있다.
html2canvas 옵션 목록 페이지
jspdf 옵션 목록 페이지
사용한 옵션
const options = {
margin: [15, 0, 15, 0], // top, right, bottom, left 마진 여백
filename: 'document', // Pdf 파일 명
pagebreak: { mode: 'avoid-all' }, // pagebreak 옵션
image: { type: 'jpeg', quality: 1 }, // 이미지 퀄리티 (pdf 들어갈 영역을 사진을 찍어 변환 하기 때문에 이미지 퀄리티 = pdf 퀄리티
html2canvas: { // html2canvas 옵션
useCORS: true, // 영역 안에 로컬 이미지를 삽입 할 때 옵션 필요
scrollY: 0, // 스크롤 이슈 때문에 필수
scale: 1, // browsers device pixel ratio
dpi: 300,
letterRendering: true,
allowTaint: false, //useCORS를 true로 설정 시 반드시 allowTaint를 false처리 해주어야함
},
jsPDF: { orientation: 'portrait', unit: 'mm', format: 'a4' },
};
스크롤이 맨 아래에 가있는 상태에서 download버튼을 누르면, pdf의 이미지캡처가 해당스크롤을 기준으로 출력하므로 앞에 몇장의 page가 빈 상태로 나오는 문제가 생긴다.
그러므로 scrollY: 0 옵션을 넣으면 pdf는 알아서 세로 scroll위치 0부터 pdf를 잘 출력할 수 있다.
html2pdf.js를 vue plugin으로 등록해 놓으면 프로젝트 내에서 import할 필요 없어 코드를 줄일 수 있어 설정했다.
파일 이름 및 경로: src/plugins/htmlToPdf.js
htmlToPdf.js
import html2pdf from 'html2pdf.js'; //html2pdf 라이브러리 import
const methods = {
// location은 pdf영역 tag
// fileName은 PDF 파일 명
htmlToPdf: (location, fileName) => {
html2pdf()
.set({
margin: [15, 0, 15, 0],
// filename에서 IE11은 .pdf를 자동으로 넣어주지 않아 .pdf를 파일 명에 넣어줘야 한다.
filename: navigator.userAgent.indexOf('MSIE') !== -1 || navigator.appVersion.indexOf('Trident/') > -1 ? `${fileName}.pdf` : fileName,
pagebreak: { mode: 'avoid-all' },
image: { type: 'jpeg', quality: 1 },
html2canvas: {
useCORS: true,
scrollY: 0,
scale: 1,
dpi: 300,
letterRendering: true,
allowTaint: false, //useCORS를 true로 설정 시 반드시 allowTaint를 false처리 해주어야함
},
jsPDF: { orientation: 'portrait', unit: 'mm', format: 'a4' },
})
.from(location)
.save();
},
};
export default {
install(Vue) {
Vue.prototype.$htmlToPdf = methods.htmlToPdf;
},
};
main.js에 가서 plugins/htmlToPdf.js를 import 후 등록해준다.
import htmlToPdf from 'plugins/htmlToPdf';
Vue.use(htmlToPdf);
예제 - vue 파일
<template>
<div>
<a href="javascript:void(0);" @click="htmlToPDF">
<!-- key를 넣어주면 컴포넌트를 다시 load해준다. -->
<div ref="pdfArea" :key="pdfKey">
<!-- pdf에 들어갈 내용 내용 -->
</div>
</div>
</template>
<script>
export default {
data() {
return {
pdfKey: 0,
};
},
methods: {
htmlToPDF() {
const name = 'document';
this.$htmlToPdf(this.$refs.pdfArea, name);
this.pdfKey += 1;
},
},
};
</script>
pdf로 변환 후 radio box에 있던 값이 사라지는 버그가 있다.
그걸 방지 하기위해 pdf 변환 후 강제로 컴포넌트를 다시 load해줘야 한다.
간편하게 하기 위해 pdf의 영역 컴포넌트에 key로 변수를 넣고 해당 변수를 pdf변환 끝날 때 +1를 해주어 key를 변경시킨다.
key가 변경되면 해당 컴포넌트를 다시 load 시켜준다.
//useCORS를 true로 설정 시 반드시 allowTaint를 false처리 해주어야함 이부분은 왜 그런건가요?
다른 예제 보면 둘다 true로 되어있는 경우가 있던데