일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- addPostFrameCallback
- Flutter 3.0
- identifierForVender
- typescript
- redux toolkit
- Three js
- Dart 2.17.0
- androidId
- Game js
- Babel standalone
- Redux
- Image Resize typescript
- code editor
- swagger-typescript-api
- Raycasting
- REST API
- web track
- RouteObserver
- Completer
- userevent_tracker
- FirebaseAnalytics
- webrtc
- methodChannel
- react
- Three-fiber
- node
- Prism.js
- KakaoMap
- Flutter
- babel
- Today
- Total
Never give up
React - make html element to pdf(Feat. jspdf, html2canvas) 본문
이번에는 특정 html element를 pdf로 만들어서 활용하는 예제를 만들어봤습니다
먼저 사용한 라이브러리는 2개로
jspdf : pdf를 만들 수 있는 라이브러리
(링크: https://www.npmjs.com/package/jspdf)
html2canvas : html element를 canvas에 그려 이미지 파일로 만들 수 있는 라이브러리
(링크: https://www.npmjs.com/package/html2canvas)
찾다보니 html2pdf란 친구(?)도 있는데 9년전 업데이트
그리고 낮은 다운로드수로 일단 사용해보지는 않았습니다. 혹시 필요하신 분들을 위해
(링크: https://www.npmjs.com/package/html2pdf)
최근에 사용중인 라이브러리인데 페이지가 여러장인 경우, 스타일을 잘(?) 처리하고 싶은 경우 사용하시면 좋을거 같습니다
(링크: https://www.npmjs.com/package/@react-pdf/renderer)
(상대적으로 무거운 라이브러리다보니 dynamic import를 통해서 번들 크기를 나누시면 좋습니다)
먼저 출력할 화면을 만들어줍니다
app.jsx
import React from 'react'
import makePdf from 'make_pdf'
import './app.css'
function App() {
const onClick = async (e) => {
e.preventDefault()
await makePdf.viewWithPdf()
}
return (
<div className='div_container'>
<div className='div_paper'>
<div>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Eos officiis at iure sapiente maxime provident possimus dolorum eveniet illo nisi ullam, animi sunt nobis, error consequatur quos facere. Perspiciatis, harum.
</div>
</div>
<button onClick={onClick}>pdf로 보기</button>
</div>
)
}
export default App
간단한 Lorem으로 출력할 부분을 만들어주고
pdf로 보기 버튼을 만들어줬습니다
스타일을 대충(?) 잡아줍니다
app.css
body{
margin: 0;
}
.div_container {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
height: 150vh;
width: 100%;
background-color: grey;
}
.div_container > button {
position: fixed;
bottom: 30px;
right: 30px;
border: none;
border-radius: 8px;
padding: 20px;
box-shadow: 0 2px 2px #e4e4e4;
background-color: white;
cursor: pointer;
transition: 1s;
}
.div_container > button:hover {
background-color: black;
color: white;
}
.div_paper {
height: 842px;
width: 595px;
margin: 30px;
background-color: white;
text-align: center;
}
.div_paper > div {
margin: 30px;
}
paper는 a4용지 사이즈에 맞게 사이즈를 맞춰봤습니다
(해상도는 제일 낮은거로..)
make_pdf.js
import html2canvas from "html2canvas";
import jsPDF from "jspdf";
const makePdf = {
viewWithPdf: async () => {
// html to imageFile
const imageFile = await makePdf._converToImg()
// imageFile to Pdf
const pdf = makePdf._converToPdf(imageFile)
// makePdf.sendToServer(pdf)
},
_converToImg: async () => {
// html to imageFile
const paper = document.querySelector(".div_container > .div_paper");
const canvas = await html2canvas(paper);
const imageFile = canvas.toDataURL("image/png", 1.0);
return imageFile
},
_converToPdf: (imageFile) => {
// imageFile to pdf
const doc = new jsPDF("p", "mm", "a4");
const pageWidth = doc.internal.pageSize.getWidth();
const pageHeight = doc.internal.pageSize.getHeight();
doc.addImage(imageFile, "JPEG", 0, 0, pageWidth, pageHeight);
// doc.save("test.pdf")
window.open(doc.output("bloburl"))
const pdf = new File([doc.output("blob")], "test.pdf", {
type: "application/pdf",
});
return pdf
},
// _sendToServer: async (pdf) => {
// const formData = new FormData();
// formData.append("file", pdf);
// formData.append("type", "pdf");
// formData.append("name", "test");
// const res = await axios.post("/pdf/upload_file", formData, {
// headers: {
// "Content-Type": "multipart/form-data",
// },
// });
// if (res.data.code === 1) {
// window.open(`${util.mode()}${res.data.link}`);
// }
// console.log({ res });
// setTimeout(() => {
// makePdf._isLoading = false;
// }, 2000);
// }
}
export default makePdf
먼저 coverToImage부분을 보면 paper element를 cavas로 변환 후 image file로 만듭니다
이후 coverToPdf부분을 보면 doc의 사이즈를 정해주고, 캔버스의 너비와 높이를 줍니다
(안주면 align이 안맞는 문제가 발생할 수 있습니다)
doc.save("파일 이름")을 사용하면 해당이름의 pdf파일이 저장되고
window.open(doc.output("bloburl"))을 사용하면 pdf파일 미리보기가 만들어지고
서버에 보낼때는 File형태로 만들어서 서버에 보내주면 됩니다
(혹시 필요할 분들을 위해 주석부분에 간단한 예제코드를 만들어봤습니다)
뭔가 조금더 js스럽게(?) 바꿔보면
const makePdf = () => {
const converToImg = async () => {
// html to imageFile
const paper = document.querySelector(".div_container > .div_paper");
const canvas = await html2canvas(paper);
const imageFile = canvas.toDataURL("image/png", 1.0);
return imageFile
}
const converToPdf = (imageFile) => {
// imageFile to pdf
const doc = new jsPDF("p", "mm", "a4");
const pageWidth = doc.internal.pageSize.getWidth();
const pageHeight = doc.internal.pageSize.getHeight();
doc.addImage(imageFile, "JPEG", 0, 0, pageWidth, pageHeight);
// doc.save("test.pdf")
window.open(doc.output("bloburl"))
const pdf = new File([doc.output("blob")], "test.pdf", {
type: "application/pdf",
});
return pdf
}
return {
viewWithPdf: async () => {
// html to imageFile
const imageFile = await converToImg()
// imageFile to Pdf
const pdf = converToPdf(imageFile)
}
}
}
------------------------------------------------------------------
//콜하는 부분
const pdf = makePdf()
const onClick = async (e) => {
e.preventDefault()
await pdf.viewWithPdf()
}
이렇게 처리하면 될거 같습니다
결과화면을 보면
(전체 소스 코드 : https://github.com/devmemory/react_make_html_to_pdf)
추가로 해상도 문제로 인해 글자가 깔끔하지 않은 경우
html2canvas에서 scale을 더 주면 됩니다
const canvas = await html2canvas(paper, { scale: 1.5 });
용량 문제가 있는경우
compress옵션을 이용하시면 됩니다
// 마지막 인자가 compressPDF
const doc = new jsPDF("p", "mm", "a4", true);
// 마지막 인자가 compression, undefined로 선언한 부분을 공백('')으로 넣으면 여러 페이지 적용시 문제 발생
doc.addImage(e, "JPEG", 0, 0, pageWidth, pageHeight, undefined, 'FAST');
최근에 체력적인 문제로 포스트를 못했는데
작업한건 많은데 정리를 안했더니 뭔가 아쉽기도 하고
나중에 필요할 때 빠른 리마인딩을 하기 위해
다시 초심잡고 하나둘씩 포스트 해나가려고 합니다
'WEB' 카테고리의 다른 글
React - Drag & Drop file (0) | 2022.08.20 |
---|---|
React - Social login(Kakao, Naver , Facebook, Google) (18) | 2022.07.16 |
React, Node - Graphql example(feat. apollo) (0) | 2022.05.24 |
React - Without CRA (0) | 2022.05.23 |
React - redux toolkit example(createStore is deprecated) (0) | 2022.05.18 |