html2pdf.js 설치
NPM
https://www.npmjs.com/package/html2pdf.js
2024.07.16 기준 NPM 사이트에 아래와 같은 문구가 있어 0.9.3 버전을 설치했다.
설치한 라이브러리는 그냥 html2pdf 가 아니라 html2pdf.js 이다
npm install --save html2pdf.js@0.9.3
v0.10에서 몇 가지 문제가 보고되어 조사중이므로 당분간은 v0.9.3(npm에서 "^0.9.3" 또는 HTML 스크립트 태그에 cdnjs 사용)을 사용하는 것을 추천합니다.
declaration 추가
html2pdf.js는 TypeScript 타입 정의 파일을 기본적으로 제공하지 않기 때문에 declaration을 추가했다.
// declarations.d.ts
declare module 'html2pdf.js' {
interface Html2PdfOptions {
margin?: number | number[];
filename?: string;
image?: { type: string; quality: number };
html2canvas?: {};
jsPDF?: { unit: string; format: string; orientation: string };
}
interface Html2Pdf {
output(
type: "blob" | "datauristring" | "arraybuffer"
): Promise<Blob | string | ArrayBuffer>;
from(element: HTMLElement): this;
set(options: Html2PdfOptions): this;
toPdf(): this;
get(type: 'blob' | 'datauristring' | 'arraybuffer'): Promise<Blob | string | ArrayBuffer>;
save(filename?: string): void;
}
function html2pdf(): Html2Pdf;
export default html2pdf;
}
Html2PdfOptions 인터페이스
interface Html2PdfOptions {
margin?: number | number[]; // PDF의 여백 설정 (단위: mm)
filename?: string; // 생성될 PDF 파일의 이름
image?: { type: string; quality: number }; // 이미지 타입 및 품질 설정
html2canvas?: {}; // html2canvas 옵션 설정
jsPDF?: { unit: string; format: string; orientation: string }; // jsPDF 옵션 설정
}
margin
PDF 문서의 여백을 설정한다.
- 단위는 밀리미터(mm)이다.
- 하나의 숫자를 전달하면 모든 방향(상, 우, 하, 좌)에 동일한 여백이 설정된다.
- 배열 형식([상, 우, 하, 좌])을 사용하여 각 방향에 대해 다른 여백을 설정할 수도 있다.
filename
생성될 PDF 파일의 이름을 설정한다.
ex) filename: "report.pdf"
image
이미지의 타입 및 품질을 설정한다.
- type : 이미지 형식 ex) jpeg 또는 png
- quality : 이미지 품질 (0 ~ 1 사이의 값, 1이 가장 높은 품질)
html2canvas
라이브러리의 옵션을 설정한다.
- scale : 캔버스 스케일을 설정한다.
- dpi : 캔버스의 DPI(Dots Per Inch) 설정.
- scrollY : 세로 스크롤 위치를 0으로 설정한다.
- scrollX : 가로 스크롤 위치를 0으로 설정한다.
jsPDF
jsPDF 라이브러리의 옵션을 설정한다.
- unit : PDF의 단위 (예: mm, pt, cm, in)
- format : PDF의 형식 (예: a4, letter)
- orientation : PDF의 방향 (portrait(세로) 또는 landscape(가로))
참고 코드
환경
- React 18
- Typescript
- styled-components
SaveToPdf.tsx
SaveToPdf에 감싸져있는 컴포넌트들은 모두 pdf 다운로드 영역에 포함시키기 위해 props에 cilldren을 사용했다.
margin: [0, 0, 0, 0] 즉, 배열을 사용하여 margin: 10 보다 export시 용지 설정을 좀 더 섬세하게 할 수 있다.
// SaveToPdf.tsx
import { ReactNode, useRef } from "react";
import styled from "styled-components";
import html2pdf from "html2pdf.js";
import BtnWithSvg from "components/button/BtnWithSvg";
interface ISaveToPdf {
filename: string;
/**
* PDF Export시 용지 방향
* - portrait : 세로
* - landscape : 가로
*/
orientation?: "portrait" | "landscape";
isLoading?: boolean;
children: ReactNode;
}
const SaveToPdf = ({
filename,
orientation = "portrait",
isLoading,
children,
}: ISaveAndShare) => {
const contentRef = useRef<HTMLDivElement>(null);
const handleExportClick = async () => {
const contentElement = contentRef.current;
if (!contentElement) return;
const option = {
// margin: 10,
margin: [0, 0, 0, 0], // 마진을 배열로 설정하여 용지 여백없이 꽉 차게 export 할 수 있었다.
// filename: "cluster_results.pdf",
filename: filename, // 파일 이름을 props로 받는다.
image: { type: "jpeg", quality: 0.98 },
html2canvas: { scale: 2 },
jsPDF: { unit: "mm", format: "a4", orientation: orientation },
};
try {
html2pdf().from(contentElement).set(option).save();
} catch (error) {
console.error(error);
}
};
return (
<SLayout>
<SToPdfArea ref={contentRef}>{children}</SToPdfArea>
{!isLoading && (
<BtnWithSvg
text="PDF 저장"
svgName="download"
onClick={() => handleExportClick()}
/>
)}
</SLayout>
);
};
export default SaveToPdf;
const SLayout = styled.div`
height: 100%;
width: 100%;
`;
const SToPdfArea = styled.div``;
적용하기
SaveToPdf 안쪽에 있는 컴포넌트들은 모두 PDF 변환 대상이 된다. pdf 변환시 임의대로 페이지를 나누어야 했는데 <SCutPage />를 사용해 페이지를 끊을 수 있었다.
const Result = () => {
return (
<BaseDrawerRight
title="분석 결과"
open={openDrawer}
setOpen={setOpenDrawer}
>
<SaveToPdf
filename="analysis_result.pdf"
orientation="landscape"
isLoading={isLoading}
>
<STitle>{"1번 결과"}</STitle>
<SImage
src={url}
alt={"1번 이미지"}
/>
<SCutPage />
<STitle>{"2번 결과"}</STitle>
<SImage
src={url}
alt={"2번 이미지"}
/>
<SCutPage />
</SaveToPdf>
</BaseDrawerRight>
);
};
export default Result
const SCutPage = styled.div`
${cutPage}
`;
css의 break-after: page를 사용하여 한장씩 나눌 수 있었다.
import { css } from "styled-components";
/**
* PDF 다운시 화면 컷 분할
*/
export const cutPage = css`
break-after: page;
`;
'React' 카테고리의 다른 글
[Webpack] 웹팩 환경에서 favicon 추가하는 방법 (1) | 2024.11.15 |
---|---|
[React-Query] 리액트 쿼리 브라우저 복귀시 API 중복 요청 방지 (1) | 2024.10.29 |
[React] SVG 불러오기 (Webpack5 설정 + SVGR) (0) | 2024.07.02 |
[React] 글로벌스타일(GlobalStyle) 적용 with Styled-Components (0) | 2024.06.25 |
[React] SVG 파일 index.ts에서 export 하기 - SVGR (0) | 2024.05.22 |