-
[html to pdf] vue에서 html을 pdf로 다운로드 하기 (심플버전), pdf image not showing 문제해결개발자의 공부는 은퇴까지 필수다/vue 2021. 2. 23. 19:45
오늘은 목표 html to pdf in vuejs
--> pdf 다운로드 버튼 클릭 시 html에 출력되어 있는 내용을 그대로 pdf에서 출력하기
총 3단계의 시행착오를 걸쳐 완성했다. 3단계 전부 있으니 급하다면 마지막코드로..
"vue html to pdf" 라고 구글에 검색하면 가장 먼저 나오고 사용량이 가장 많은 vue-html2pdf plugin을 사용하려다가 몇시간의 시도 끝에 실패했다. demo의 component 구조가 약간 복잡하다. 뭐 쉽다면 쉽지만 단시간에 단순구조로 pdf로 다운로드 할 수 있도록 하는 것이 목표였다.
원래 vue-html2pdf도 html2canvas와 jsPDF 기반으로 만들어져있어 vue-html2pdf 말고 본래의 html2pdf을 사용하기로 결정했다.
※ 참고답변
stackoverflow.com/questions/60204249/impossible-to-convert-html-code-to-pdf-with-vue-html2pdf
※ plugin site
https://www.npmjs.com/package/html2pdf.js/v/0.9.1
※ demo site
https://codesandbox.io/s/xlnzq7y28q
본격적으로 시작 ! 사실 위에 demo와 똑같이 적용했다. 옵션만 살짝 바꿨을 뿐.
여기서 포인트는 window.scrollTo(0,0); 이다.
사실 같은 예제를 가지고 어제부터 헤맸는데 이유가 scroll 때문이었다. pdf download button이 아래쪽에 있으니 스크롤이 맨 아래에 가있는 상태에서 download를 하고, pdf의 이미지캡처가 해당스크롤을 기준으로 출력하므로 앞에 몇장의 page가 빈 상태로 나오는 문제가 있었다. 그래서 계속 다른 라이브러리를 찾다가 돌고돌아 문제를 찾았다. 해당 pdf plugin은 화면을 이미지로 변환 후에 다시 pdf로 변환하기 때문에 페이지가 여러개로 나타나는 경우에는 scroll이 맨 위에 올라가있는 상태에서 makePDF 로직을 수행해야한다.
=== 추가수정 ===
window.scrollTo를 굳이 할 필요가 없다. html2canvas안에 scrollY 옵션이 있다. 해당 옵션을 0으로 맞춰주면 pdf는 알아서 세로 scroll위치 0부터 pdf를 예쁘게 출력한다.
적용 1단계) in vue component javascript
<script> import html2pdf from 'html2pdf.js' export default { name: 'submission-detail', methods: { exportToPDF () { //window.scrollTo(0, 0); html2pdf(this.$refs.pdfarea, { margin: 0, filename: 'document.pdf', image: {type:"jpg", quality: 0.95}, html2canvas: { scrollY: 0,scale:1, dpi: 300, letterRendering: true, ignoreElements : function( element ) { //pdf에 출력하지 않아야할 dom이 있다면 해당 옵션 사용 if( element.id == "pdf-button-area" ) { return true; } } }, jsPDF: {orientation: 'portrait', unit: 'mm', format: 'a4', compressPDF: true} }) }, ... } } </script>
in vue component html
<template> <div> <div ref="pdfarea"> //기존 html code (화면에 출력되어야하는 pdf html tags) </div> <div class="col-md-8 text-left" style="float:none;margin:auto;" v-show="!loading"> <b-button class="mb-5 float-right" @click="exportToPDF">PDF 다운로드</b-button> </div> </div> </template>
이렇게 하고나니 pdf는 제대로 출력이 되는데 문제는 image가 안나온다. 프로젝트 내에 있는 image는 괜찮은데 url로 이미지를 가져오는 부분에서 정상적으로 가져오지를 않는다.
이 때도 html2canvas의 옵션에서 cross-origin의 이미지도 가져오도록 true로 설정해주는 옵션이 있다.
옵션명 : allowTaint
이 옵션을 true로 변경해주면 error가 발생한다. 에러 내용은 아래와 같다.
html2pdf.js?d67e:332 Uncaught (in promise) DOMException: Failed to execute 'toDataURL' on 'HTMLCanvasElement': Tainted canvases may not be exported.
Uncaught (in promise) DOMException: Failed to execute 'toDataURL' on 'HTMLCanvasElement': Tainted canvases may not be exported.
그래서 생각한 방법 ! 이미지를 blob으로 파일화 시켜서 src에 로드하기
fetch(url) .then(response => response.blob()) .then(blob => { // 여기서 this가 가리키는건 현재 VueComponent이다. this.testImage = URL.createObjectURL(blob); }) //위에서 blob으로 image 객체를 생성했으니 아래 태그에 이미지가 정상적으로 나타난다 <b-img :src="testImage"></b-img>
수정 후 전체소스코드
<template> <div ref="pdfarea"> //pdf 내용 ... <b-img :src="testIamge"></b-img> ... <b-button class="mb-5 float-right" @click="exportToPDF">PDF 다운로드</b-button> </div> </template> <script> import html2pdf from 'html2pdf.js' export default { name: 'submission-detail', methods: { exportToPDF () { //window.scrollTo(0, 0); html2pdf(this.$refs.pdfarea, { margin: 0, filename: 'document.pdf', image: {type:"jpg", quality: 0.95}, // allowTaint 옵션추가 html2canvas: { scrollY: 0,scale:1, dpi: 300, letterRendering: true, allowTaint: true }, jsPDF: {orientation: 'portrait', unit: 'mm', format: 'a4', compressPDF: true} }) }, mounted() { //data를 가져오는 api 호출 //가져온 data 중 image url 데이터를 blob파일로 변경하여 vue data에 넣어줌 let imageUrl = data.imageUrl; fetch( imageUrl ) .then(response => response.blob()) .then(blob => { console.log( "객체는? " ) //this.images.selfie = URL.createObjectURL(blob); this.testImage = URL.createObjectURL(blob); }) }, ... } } </script>
수정 후 최종 전체소스코드
이번에도 더 간단한 방법을 찾았다.. image를 blob file로 만들지 않아도 되고, 로직 다 제외한 후 allowTaint: true 옵션을 html2canvas 옵션에 추가한다.
<template> <div ref="pdfarea"> //pdf 내용 ... <b-img :src="이미지url"></b-img> ... <b-button class="mb-5 float-right" @click="exportToPDF">PDF 다운로드</b-button> </div> </template> <script> import html2pdf from 'html2pdf.js' export default { name: 'submission-detail', methods: { exportToPDF () { //window.scrollTo(0, 0); html2pdf(this.$refs.pdfarea, { margin: 0, filename: 'document.pdf', image: {type:"jpg", quality: 0.95}, // allowTaint 옵션추가 html2canvas: { useCORS: true, scrollY: 0, scale:1, dpi: 300, letterRendering: true, allowTaint: false //useCORS를 true로 설정 시 반드시 allowTaint를 false처리 해주어야함 }, jsPDF: {orientation: 'portrait', unit: 'mm', format: 'a4', compressPDF: true} }) }, mounted() { }, ... } } </script>
# html to pdf empty page
# html to pdf 빈화면
# html to pdf 빈페이지
# html to pdf scroll
'개발자의 공부는 은퇴까지 필수다 > vue' 카테고리의 다른 글
[vue] 특정영역 제외 버튼 클릭 시 페이지 이동 (0) 2021.03.03 [html2pdf] pdf 출력 시 쏠림현상, 한쪽 라인이 흐릿한 현상 해결하기 (3) 2021.02.26 vue multipage 개발 시 사용자 분기처리, vue url 설정 (0) 2021.02.17 b-table list 화면에서 상세화면 넘어가기 (parameter 넘기기) (0) 2021.02.16 vue에서 image load 정상여부 체크하기 (0) 2021.02.10