이번 포스팅에서는 지난 포스팅에 이어서 html2pdf 라이브러리를 사용하여 html array 를 pdf 로 변환하는 기능을 개발해보려고 한다.
Vue.js 2버전의 기본 프로젝트 환경이 구성되어있음을 전제로 글을 작성하였다.
html2pdf.js 라이브러리 설치
우선 html2pdf.js 라이브러리를 프로젝트에 설치하자
npm install --save html2pdf.js
설치가 완료되니 package.json 파일의 dependencyies 에 아래와 같이 "html2pdf.js": "^0.10.1" 가 추가된다.
"dependencies": {
"core-js": "^3.8.3",
"element-ui": "^2.15.13",
"html2pdf.js": "^0.10.1",
"vue": "^2.6.11"
},
기능 개발
나는 HtmlToPdfConverter 라는 이름의 클래스를 사용자에게 제공하여, html to pdf 변환 기능을 사용할 수 있게 하려고 한다.
htmlToPdfConverter.js 라는 이름으로 빈 파일을 하나 생성하고 차례로 작성해보겠다.
html2pdf.js 임포트
우선, html2pdf.js 를 임포트 한다.
import html2pdf from 'html2pdf.js';
클래스 생성
그리고, 클래스를 생성한다.
import html2pdf from 'html2pdf.js';
export class HtmlToPdfConverter {
defaultOption = ''; // pdf 생성을 위한 option
/**
* HTML To PDF 를 위한 생성자
* @param pdfFormat pdf 포맷 (e.g. 'a3', 'a4', ...)
* @param pdfOrientation pdf 방향 (e.g. 'portrait' : 세로, 'landscape' : 가로)
*/
constructor(pdfFormat, pdfOrientation) {
this.defaultOption = {
margin: 0,
// filename: out.pdf,
image: {type: 'jpg', quality: 0.98},
html2canvas: {
useCORS: true,
allowTaint: true,
scrollX: 0,
scrollY: 0,
scale: 1,
dpi: 300,
letterRendering: true,
logging: false,
ignoreElements: function (element) {
if (element.id === 'pdf-ignore-area') {
return true;
}
},
},
jsPDF: {orientation: pdfOrientation, unit: 'mm', format: pdfFormat, compressPDF: true},
};
}
/**
* html element 단건을 pdf 로 생성하여 다운로드
* @param element html element
* @param filename pdf 파일명
* @returns {Promise<*>}
*/
async htmlToPdf(element, filename) {
// 개발 예정
}
/**
* html element 배열을 여러 페이지의 pdf 로 생성하여 다운로드
* @param elements html element array
* @param filename pdf 파일명
* @returns {Promise<*>}
*/
async htmlsToPdf(elements, filename, callback) {
// 개발 예정
}
}
HtmlToPdfConverter 인스턴스를 생성할 때, 변환할 pdf 의 용지 크기와 방향을 설정하도록 생성자를 구현할 것이다.
그리고, 사용자는 htmlToPdf(element, filename) 함수와 htmlsToPdf(elements, filename, callback) 함수를 호출하여 html element 단건을 pdf 로 변환하거나 html element 다건을 pdf 로 변환할 수 있도록 기능을 개발할 것이다.
생성자 코드 작성
우선, 생성자 내용 부터 채우자.
/**
* HTML To PDF 를 위한 생성자
* @param pdfFormat pdf 포맷 (e.g. 'a3', 'a4', ...)
* @param pdfOrientation pdf 방향 (e.g. 'portrait' : 세로, 'landscape' : 가로)
*/
constructor(pdfFormat, pdfOrientation) {
this.defaultOption = {
margin: 0,
// filename: out.pdf,
image: {type: 'jpg', quality: 0.98},
html2canvas: {
useCORS: true,
allowTaint: true,
scrollX: 0,
scrollY: 0,
scale: 1,
dpi: 300,
letterRendering: true,
logging: false,
ignoreElements: function (element) {
if (element.id === 'pdf-ignore-area') {
return true;
}
},
},
jsPDF: {orientation: pdfOrientation, unit: 'mm', format: pdfFormat, compressPDF: true},
};
}
사용자가 pdf 용지 크기와 방향을 파라미터로 전달하면, 해당 속성과 함께 나머지 기본 속성들을 채워서 html2pdf.js 라이브러리의 사용법에 맞게 option 객체를 생성한다.
html2pdf.js 라이브러리 options 관련 설명은 html2pdf.js npm readme 를 참고하면 된다.
htmlToPdf 함수 코드 작성
내용은 간단하다. 생성자에서 설정한 defaultOptions 에 함수 파라미터로 전달 받은 filename 만 추가로 정의한 옵션을 사용하여 pdf 변환하는 html2pdf().set().from().save(); 함수를 호출하면 된다.
/**
* html element 단건을 pdf 로 생성하여 다운로드
* @param element html element
* @param filename pdf 파일명
* @returns {Promise<*>}
*/
async htmlToPdf(element, filename) {
const opt = {...this.defaultOption, filename : `${filename}.pdf`};
return html2pdf().set(opt).from(element).save();
}
htmlsToPdf 함수 코드 작성
html element array 의 첫번째 요소로 pdf 첫 페이지를 생성하고, 두번째 요소부터는 기존 pdf 에 addPage() 로 이어붙이는 방식으로 pdf 를 생성해주도록 하였다.
그리고, page 가 추가될때마다 callback 함수를 호출하도록 하여, 해당 함수의 caller 가 pdf page 추가될때마다 원하는 기능을 수행하도록 하였다.
나는 해당 callback 함수로 진행율 업데이트 하는 함수를 파라미터로 전달하여 사용하였다.
그리고, 함수 초반부에 element를 clone 한 이유는 caller 쪽에서 html 을 숨긴채로 pdf 변환하고 싶어서 hidden 처리를 해두었기 때문에 pdf 변환하기 전 hidden 처리된 요소들의 style.display 속성을 변경하여 pdf 에는 빈 페이지가 나오지 않게 하기 위함이다.
필요하면, clone element 에 transform scale 값을 변경해서 이미지를 원하는 비율로 확대 or 축소 후 pdf 변환할 수도 있다.
아무래도 브라우져에서 렌더링 되는 html view 와 pdf 변환된 view 의 사이즈가 정확히 일치하지는 않을 것이기 때문에 필요할 것이다.
/**
* html element 배열을 여러 페이지의 pdf 로 생성하여 다운로드
* @param elements html element array
* @param filename pdf 파일명
* @returns {Promise<*>}
*/
async htmlsToPdf(elements, filename, callback) {
const opt = {...this.defaultOption, filename: `${filename}.pdf`};
let pdf = null;
for (let i = 0; i < elements.length; i++) {
// html 이 화면에 보이지 않는 상태로 pdf 생성하면, 빈 페이지로 생성되기 때문에 html 복사 후 style 만 변경 한 뒤 pdf 생성
const clone = elements[i].cloneNode(true);
clone.style.display = "block";
if (pdf == null) {
// pdf 첫 페이지
pdf = html2pdf().set(opt).from(clone).toPdf();
callback();
} else {
// pdf 두번째 페이지부터는 기존 pdf 에 이어붙임
pdf = pdf.get('pdf')
.then((pdf) => {
pdf.addPage();
callback();
})
.from(clone)
.toContainer()
.toCanvas()
.toPdf();
}
// 메모리 누수를 방지하기 위해
clone.remove();
}
return pdf.save().then(() => {
pdf = null;
console.log(`${filename} 생성 완료`);
});
}
전체 완성 코드
아래는 위의 과정을 거쳐 완성된 전체 코드이다.
import html2pdf from 'html2pdf.js';
export class HtmlToPdfConverter {
defaultOption = ''; // pdf 생성을 위한 option
/**
* HTML To PDF 를 위한 생성자
* @param pdfFormat pdf 포맷 (e.g. 'a3', 'a4', ...)
* @param pdfOrientation pdf 방향 (e.g. 'portrait' : 세로, 'landscape' : 가로)
*/
constructor(pdfFormat, pdfOrientation) {
this.defaultOption = {
margin: 0,
// filename: out.pdf,
image: {type: 'jpg', quality: 0.98},
html2canvas: {
useCORS: true,
allowTaint: true,
scrollX: 0,
scrollY: 0,
scale: 1,
dpi: 300,
letterRendering: true,
logging: false,
ignoreElements: function (element) {
if (element.id === 'pdf-ignore-area') {
return true;
}
},
},
jsPDF: {orientation: pdfOrientation, unit: 'mm', format: pdfFormat, compressPDF: true},
};
}
/**
* html element 단건을 pdf 로 생성하여 다운로드
* @param element html element
* @param filename pdf 파일명
* @returns {Promise<*>}
*/
async htmlToPdf(element, filename) {
const opt = {...this.defaultOption, filename : `${filename}.pdf`};
return html2pdf().set(opt).from(element).save();
}
/**
* html element 배열을 여러 페이지의 pdf 로 생성하여 다운로드
* @param elements html element array
* @param filename pdf 파일명
* @returns {Promise<*>}
*/
async htmlsToPdf(elements, filename, callback) {
const opt = {...this.defaultOption, filename: `${filename}.pdf`};
let pdf = null;
for (let i = 0; i < elements.length; i++) {
// html 이 화면에 보이지 않는 상태로 pdf 생성하면, 빈 페이지로 생성되기 때문에 html 복사 후 style 만 변경 한 뒤 pdf 생성
const clone = elements[i].cloneNode(true);
clone.style.display = "block";
if (pdf == null) {
// pdf 첫 페이지
pdf = html2pdf().set(opt).from(clone).toPdf();
callback();
} else {
// pdf 두번째 페이지부터는 기존 pdf 에 이어붙임
pdf = pdf.get('pdf')
.then((pdf) => {
pdf.addPage();
callback();
})
.from(clone)
.toContainer()
.toCanvas()
.toPdf();
}
// 메모리 누수를 방지하기 위해
elements[i].remove();
clone.remove();
}
return pdf.save().then(() => {
pdf = null;
console.log(`${filename} 생성 완료`);
});
}
}
회고
HtmlToPdfConverter 를 이용해 html element array 를 pdf 로 변환할 때, 안정적으로 브라우져의 메모리 관리를 하기 위해서는 caller 쪽 개발도 중요하다.
다음 포스팅에서는 해당 클래스를 사용하는 쪽의 개발 내용에 대해 작성해보겠다.
'Front-end' 카테고리의 다른 글
| HTML to PDF 변환기 (3) (0) | 2023.05.21 |
|---|---|
| HTML to PDF 변환기 (1) (0) | 2023.05.16 |
| Node.js와 Vue.js : 클라이언트와 서버에서 모두 사용되는 JavaScript 런타임 및 프레임워크 (0) | 2023.05.05 |
댓글