[html to pdf] vue에서 html을 pdf로 다운로드 하기 (심플버전), pdf image not showing 문제해결
오늘은 목표 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