방명록
- [React.js] jsPDF를 이용한 웹 화면 PDF 내보내기 중 이슈: 페이지 오버플로우 이미지 잘림 문제2024년 07월 24일 11시 06분 31초에 업로드 된 글입니다.작성자: DandyNow728x90반응형
1. 화면에 렌더링 된 테이블 페이지 오버플로우 문제
jsPDF를 이용해 웹 화면을 PDF로 내보내기 기능을 구현했다. 에러 없이 작동하였으나 [그림 1]과 같이 페이지 오버플로우로 그림이 잘린 경우 앞 페이지 하단, 뒷 페이지 상단 여백이 전혀 없는 pdf 파일이 생성되었다.
2. canvas 이용하여 해결
페이지 오버플로우가 발생할 경우 이미지를 페이지 크기로 잘라 새로운 캔버스에 그리고 새로운 캔버스를 PDF에 추가하는 방식으로 해결하였다. [그림 2]는 최종 결과물이다.
작성한 전체 코드는 아래와 같다.
import jsPDF from 'jspdf'; import font from './font/NanumGothic-normal'; import { format } from 'date-fns'; const PdfDownloader = async (images, period, orientation = 'p') => { const [openDate, closeDate] = period; const startDate = format(openDate, 'yyyy.MM.dd').toString(); const endDate = format(closeDate, 'yyyy.MM.dd').toString(); const periodStr = `${startDate} ~ ${endDate}`; try { const marginLeft = 10; // 왼쪽 마진 값 (mm) const marginRight = 10; // 오른쪽 마진 값 (mm) const marginTop = 15; // 상단 마진 값 (mm) const marginBottom = 10; // 하단 마진 값 (mm) const imageMargin = 5; // 이미지 사이의 여백 (mm) // PDF 문서 준비 const doc = new jsPDF(orientation, 'mm', 'a4', true); // PDF 페이지의 가로 세로 사이즈 const pageWidth = doc.internal.pageSize.getWidth() - (marginLeft + marginRight); const pageHeight = doc.internal.pageSize.getHeight() - (marginTop + marginBottom + imageMargin); // 한글 폰트 추가 doc.addFileToVFS('NanumGothic.ttf', font); doc.addFont('NanumGothic.ttf', 'NanumGothic', 'normal'); doc.setFont('NanumGothic'); // "보고서" 문구 추가 (중앙 정렬) doc.setFontSize(24); const reportTextWidth = (doc.getStringUnitWidth('보 고 서') * doc.internal.getFontSize()) / doc.internal.scaleFactor; const reportTextX = (pageWidth - reportTextWidth) / 2 + marginLeft; doc.text('보 고 서', reportTextX, marginTop + 10); // 기간 정보 추가 (왼쪽 정렬) doc.setFontSize(10); const periodText = `◯ 기간: ${periodStr}`; doc.text(periodText, marginLeft, marginTop + 25); await images.reduce(async (promise, pdfObj, index, array) => { await promise; // Wait for the previous iteration to complete const { canvas, image } = pdfObj; console.log('canvas : ', canvas); console.log('image : ', image); // 이미지의 길이와 PDF 페이지의 가로 길이를 기준으로 비율을 구함 const widthRatio = pageWidth / canvas.width; // 비율에 따른 이미지 높이 const customHeight = canvas.height * widthRatio; // 첫 페이지에만 marginTop + 15 적용, 나머지는 marginTop만 적용 const topMargin = index === 0 ? marginTop + 30 : marginTop; // 캔버스를 사용하여 이미지를 페이지 크기로 자르기 let heightLeft = customHeight; // 남은 이미지 높이 let position = 0; // 이미지 자를 위치 while (heightLeft > 0) { const sliceHeight = Math.min(pageHeight, heightLeft); // 새로운 캔버스 생성 const newCanvas = document.createElement('canvas'); newCanvas.width = canvas.width; newCanvas.height = sliceHeight / widthRatio; // 잘라낸 이미지 부분을 새로운 캔버스에 그림 const newCtx = newCanvas.getContext('2d'); newCtx.drawImage( canvas, 0, position / widthRatio, canvas.width, sliceHeight / widthRatio, 0, 0, canvas.width, sliceHeight / widthRatio, ); // 새로운 캔버스의 이미지를 PDF에 추가 const newImage = newCanvas.toDataURL('image/jpeg'); doc.addImage( newImage, 'JPEG', marginLeft, topMargin, pageWidth, sliceHeight, ); // 남은 이미지 높이와 자를 위치 업데이트 heightLeft -= sliceHeight; position += sliceHeight; // 페이지가 남아있는 경우 새 페이지 추가 if (heightLeft > 0) { doc.addPage(); } } // 리스트의 마지막 요소가 아닌 경우에만 페이지를 추가 if (index !== array.length - 1) { doc.addPage(); } }, Promise.resolve()); // Initial value for reduce() // PDF 문서 저장 doc.save( `report_${format(openDate, 'yyyyMMdd').toString()}_${format( closeDate, 'yyyyMMdd', ).toString()}.pdf`, ); } catch (error) { console.log('error :: ', error); throw error; } }; export default PdfDownloader;
2024-12-09 추가
3. 폰트(ttf) 파일 base64 인코딩
한글 폰트의 경우 base64로 인코딩 된 폰트 파일을 적용하지 않으면 한글 깨짐 현상이 발생한다. 위 예제의 경우 ttf 폰트 파일을 인코딩한 코드를 "NanumGothic-normal.js" 모듈로 만들어 import 하였다. ttf 폰트 파일의 인코딩은 아래 웹사이트를 이용하면 [그림 3]과 같이 쉽게 가능하다.
📌 Base64 Encoder: https://www.giftofspeed.com/base64-encoder/
728x90반응형'언어·프레임워크 > React.js' 카테고리의 다른 글
[React.js] 네이버 지도 API 지도 좌표 경계 확인하여 렌더링 줄이기 (0) 2024.12.04 [React.js] 네이버 지도 API 지도 센터 위경도 값 변경 감지 성능 개선 (1) 2024.12.03 [React.js] 네이버 지도 API 마커 줌인아웃 레벨 값 이용하기 (0) 2024.03.04 [React.js] 네이버 지도 API 마커 리렌더링시 누적(쌓이는) 현상(마커 클러스터) (0) 2024.02.29 [React.js] 네이버 지도 API 마커 클러스터 튜터리얼 따라하기! (1) 2024.02.21 다음글이 없습니다.이전글이 없습니다.댓글