본문 바로가기
Web Development/Spring Legacy

[Spring Legacy / Javascript / jsPDF] PDF 다운로드 기능 구현 - 1. 텍스트, Footer 생성 (feat. jsPDF-autotable, error)

by 감자맹고우 2022. 4. 29.
728x90
반응형

프로젝트를 진행하면서 PDF 다운로드(PDF 작성) 기능을 구현하게 되었다.

처음에는 간단하게 jsPDF와 html2canvas 라이브러리의 조합으로 해결해보려고 했지만, 역시 간단하기만 한 것은 의도대로 사용하기가 참 힘들다.

 

그래서, PDF 관련 라이브러리에 대해 조사하면서 알게 된 내용을 먼저 간략히 공유하고자 한다.

더 많은 라이브러리가 있겠지만, 내가 접했던 라이브러리 목록은 다음과 같다.

 

1. jsPDF : PDF 파일을 생성해주는 javascript 라이브러리
2. html2canvas : 현재 브라우저 화면을 캡쳐하여(canvas에 그려) 이미지로 만들어주는 javascript 라이브러리
3. PDFMake : PDF 파일을 생성해주는 javascript 라이브러리
4. iText : PDF 파일을 생성해주는 java 라이브러리
5. jsPDF-autotable : jsPDF 라이브러리와 연계하여 쓸 수 있는 테이블 자동생성 라이브러리

 

각각의 시행착오와 장단점에 대해서 말해보자면,

우선 jsPDF와 PDFMake는 PDF 양식을 작성하고 파일을 생성할 수 있게 해주는 라이브러리다.

iText는 java 라이브러리이기 때문에 서버단에서 PDF 파일을 생성하고자할 때 쓰면 좋을 것 같다.

 

하지만, 내가 구현하려는 기능은 클라이언트단에서 처리하는 게 맞는 것 같다는 느낌이 들어서 javascript 라이브러리를 사용하기로 했다.

 

(1) jsPDF + html2canvas

그래서 제일 처음에 생각한 것은 구글링에서 가장 빨리 검색된, html2canvas와 jsPDF의 조합이었다.

이 방식은 html2canvas로 캡쳐한 이미지를 jsPDF를 이용해 PDF 양식으로 생성한 후 PDF 파일로 저장하는 방식이었다.

 

그러나 이 방식은 현재 화면을 캡쳐하기 때문에 문제가 발생한다.

여러 페이지이거나 특히, overflow된 팝업에서 처리하려고 하면, 화면이 잘려서 제대로 출력되지 않는다. 여러 페이지인 경우에는 그나마 해결할 수도 있을 것 같지만 팝업의 경우에는 방법이 없어보인다.

어차피 버튼이라거나 여러가지 숨겨야할 것도 지정해서 처리해야하는데, 그냥 엑셀 다운로드처럼 직접 양식을 작성하여 처리하기로 했다. ㅠ_ㅠ

 

(2) PDFMake

1번 방식이 실패한 후, jsPDF도 실패의 원흉이라 생각하고 PDFMake로 갈아탔다.

그러나 PDFMake는 아주 치명적인 단점이 있다. 바로 한글 폰트.

PDF 라이브러리들이 대부분 그렇듯 유니코드를 지원하지 않는데, PDFMake는 더 복잡한게 vfs_fonts.js 파일을 수정해주어야 한다. 그래서 수정을 했다.

하지만 또 문제가, PDFMake의 버전이 업데이트되면서 nodeJS 모듈로 바뀌었고, 수정한 vfs_fonts.js와의 호환 문제때문에, 레거시 프로젝트에서 사용하려면 이전 버전을 사용해주어야 한다.

 

하지만 이전 버전은 링크가 끊기는 등 구하기가 힘들다. 그나마 간신히 구해서 사용했음에도, 사용방법이 다르고 이전 버전의 레퍼런스를 찾기가 힘들어서 결국 접게 되었다. 막혔던 부분은 셀의 높이를 부분적으로 조정하는 부분이었는데 margin이나 rowspan으로 해결하는 방식도 잘 되지 않아 답이 없었다.

 

(3) iText

JAVA 라이브러리라서 고민하다가 클라이언트단에서 처리하기로 함

 

(4) jsPDF + jsPDF-autotable

결국 다시 연어처럼 jsPDF로 돌아왔다.

막상 jsPDF로 돌아와보니 레퍼런스도 훨씬 많았고, autotable이라는 테이블 자동생성 라이브러리가 있어서 너무 행복했다.

 

그리고 결국 해당 조합을 통해서 구현에 성공했다.

 

 

[ 적용 방법 ]

 

- 라이브러리 사양 -
1. jsPDF : 2.5.1 (on 12 Feb)
2. jsPDF-AutoTable : 3.5.23 (on 26 Mar)

 

우선 기본적인 사용법은 아래 블로그를 참고하면 좋을 것 같다.

 

 

간단하게 정리한 JSPDF 사용법(Javascript pdf)

jspdf라는 라이브러리를 활용하면 웹 브라우저에서 pdf파일을 제작 할 수 있습니다. 이런 작업은 안해봤지만 그렇게 어렵지는 않았습니다. 가장먼저 jspdf와 관련된 라이브러리를 받아서 적용하였

lts0606.tistory.com

 

위 블로그만 참고해도 기본적인 PDF 파일은 작성해낼 수 있기에 기본적인 사용 부분은 이 글에서 다루지 않고자 한다.

 

나는 결재서류와 같이 좀 더 많은 내용을 가진 PDF 파일을 생성해내야 했고, 이후에도 PDF 관련 기능을 추가할 수 있다는 요구사항이 있어 간결하게 쓸 수 있는 모듈코드를 작성해보기로 했다.

(특히 X, Y 좌표를 지정해주어야하기에 해당 좌표를 계산해서 다음 좌표를 알아서 처리해주고 싶었다)

 

모듈 코드 전체는 공유할 수 없지만, 힘들었던 부분 각각의 해결방법을 공유하고자 한다.

 

 

1. 일단, 전역 변수이다.

 

/**
 * @ 전역 변수 
 */

//고정 값
const MARGIN_SIZE = 10;		//상하좌우 여백
const LINE_HEIGHT = 3;		//행 너비 비율

const IMG_SIZE_X = 5;
const IMG_SIZE_Y = 5;

//변동 값
let FONT_SIZE = 10;		//폰트 크기
let Y_LOC = MARGIN_SIZE;	//다음 행 시작 Y좌표(자동 계산 위함)

const CELL_HEIGHT = 11.641666666666664;	//기본 셀 높이

 

전역변수에서 알 수 있듯이,

여백, 이미지 사이즈, 행 높이, 폰트 사이즈는 기본으로 지정해놓았다.

그리고 Y_LOC라는 변수를 지정해서, 다음에 작성될 Y위치를 계산해서 저장해주기로 했다.

셀 높이는 나중에 (페이지 높이 - 여백)과 (셀 갯수 * 높이)를 계산해서 테이블이 잘리는 것을 막기 위해 선언했다.

셀 높이는 테이블 생성 시 높이값이 확인되어 해당 값을 기재하였다.

 

 

2. 텍스트 추가

 

텍스트는 큰 문제가 없지만, Y위치를 자동으로 계산해주는 것과 정렬이 조금 힘들었다.

 

< 텍스트 추가 샘플 코드 전체 >

const doc = new jsPDF('p', 'mm', 'a4');

//text 추가 메소드
function add(doc, text, {fontSize = FONT_SIZE, align = 'left', x = MARGIN_SIZE, y = Y_LOC}) {
    //fontSize, align, x, y값은 지정하지 않을 시 기본 값으로 적용
    //add 메소드를 쓰는 방식 예1 (기본) : add(doc, '테스트', {});
    //add 메소드를 쓰는 방식 예2 (파라미터 추가) : add(doc, '테스트', {align: 'center'});
    
    if(align != 'left'){
        const PAGE_HEIGHT = doc.internal.pageSize.height || doc.internal.pageSize.getHeight();
        const PAGE_WIDTH = doc.internal.pageSize.width || doc.internal.pageSize.getWidth();
	    
        //가운데 정렬 시, 기준점을 가로 중간으로
        if(align == 'center') x = PAGE_WIDTH / 2;
        //오른쪽 정렬 시,
        //가로 길이에서 좌우 여백값을 뺀 값이 되어야할 줄 알았지만,
        //아래의 계산 값이 결과가 일치하였음
        if(align == 'right') x = PAGE_WIDTH - (MARGIN_SIZE / 1.5);
    }

    //doc 에 텍스트를 추가
    doc.text(text, x, y, align);

    if(fontSize != null) FONT_SIZE = fontSize;  //새로 입력된 fontSize를 기준 값으로 지정(기준 폰트 미변경 원할 시 삭제)
    Y_LOC = y;  //텍스트가 입력된 y좌표를 저장
    Y_LOC += (LINE_HEIGHT + (FONT_SIZE / 100 * 1.2941) + (LINE_HEIGHT * 1.5)); 
    //변경된 y좌표에 폰트와 줄 높이를 추가하여 저장
    //(생각했던 계산 값이 정확하지 않아 끼워맞추기 식으로 다시 구현한 식이므로 필요에 따라 변경)
    //1.2941은 기본 폰트 세로 사이즈 비율로 구글링을 통해 알게된 값을 임의 사용
}

 

(1) Y 위치 자동 계산

 

jsPDF에서는 'doc.text(text, x, y, align)' 코드를 통해서 text를 작성한다.

그러니 다음 행 위치가 저장된 Y_LOC 값을 y에 할당하면 문제가 해결되는데 Y_LOC 계산이 쉽지 않다.

 

나는 위의 코드에서 이와 같이 해결했다.

 

Y_LOC += (LINE_HEIGHT + (FONT_SIZE / 100 * 1.2941) + (LINE_HEIGHT * 1.5));

 

로 다음줄이 계산되게끔하였고, add 메소드에 named parameter와 default parameter 방식을 적용하여 전달된 파라미터가 없을 때 기본으로 Y_LOC 값으로 y좌표를 설정하여 텍스트가 추가되도록 하였다.

 

 

(2) 정렬

 

정렬 시에는 기준점 x좌표가 문제가 된다.

x가 0으로 default인 왼쪽 정렬일 때는 문제가 없지만, 가운데나 오른쪽 정렬시에는 자동으로 x좌표를 계산해줄줄 알았는데 그렇게 처리되지 않는다. (x좌표 0에서 정렬이 되어 잘리거나 보이지 않는다)

그래서 x좌표를 직접 구해주어야 하는데 위에서 다음과 같이 처리하였다.

 

if(align == 'center') x = PAGE_WIDTH / 2;
if(align == 'right') x = PAGE_WIDTH - (MARGIN_SIZE / 1.5);

 

이런 식으로 가운데일 때는 중간으로, 오른쪽일 때는 계산식을 더해 x 좌표를 구해주었다.

 

반응형

 

3. 테이블 추가(jsPDF-AutoTable 사용)

 

▼ 하단 공식 링크 참조

 

GitHub - simonbengtsson/jsPDF-AutoTable: jsPDF plugin for generating PDF tables with javascript

jsPDF plugin for generating PDF tables with javascript - GitHub - simonbengtsson/jsPDF-AutoTable: jsPDF plugin for generating PDF tables with javascript

github.com

 

테이블 추가 시에는 여러가지 어려운 문제가 많다.

 

첫번째는, 테이블 테마 + border 설정

두번째는, 페이지 넘어갈 시 잘림 현상

세번째는, 셀 높이/너비 설정

네번째는, 테이블 정렬

다섯번째는, 셀 병합

여섯번째는, 셀 색 채우기

일곱번째는, 전체 셀 너비 균등

여덟번째는, 너비 퍼센트 지정

아홉번째는, 한 행에 여러 개 테이블 생성

열번째는, 한 행에 여러 개 테이블 생성 시 페이지 넘어갈 때 잘리는 현상

열한번째는, 한 행에 여러 개 테이블 생성 시 여러 개 테이블 중 가장 긴 테이블의 y 좌표 얻기 및 테이블 y 좌표 얻기

열두번째는, 셀에 이미지 추가

열세번째는, 적다보니까 너무 많았어서 열받는 문제

 

아무튼 이렇게 많은 문제가 있는데 글이 너무 길어질 것 같아서 이것은 다음 글에서 다루겠다.

공식 링크에서 Usage로 사용방법이 기재되어 있지만 충분한 정보는 아니어서 다른 분들은 꼭 아래 글을 확인해서 시행착오를 겪지 않으셨으면 좋겠다.

 

PDF 다운로드 기능 구현 - 2. 테이블 생성 A to Z

 

[Spring Legacy / Javascript / Library] PDF 다운로드 기능 구현 - 2. 테이블 생성 A to Z (feat. jsPDF, jsPDF-autotable

이 글은 jsPDF, jsPDF-AutoTable 라이브러리를 통해 PDF에서 테이블 생성하려고 할 때, 힘들었던 부분에 대한 모든 것을 다루고자 한다. 단순한 PDF 생성, jsPDF 사용법을 알고 싶거나, 아직 jsPDF에 대해 잘

devlifetestcase.tistory.com

 

4. Footer 추가(별도 함수 생성)

 

const addFooters = (doc) => {
    // 페이지 전체 높이
    const PAGE_HEIGHT = doc.internal.pageSize.height || doc.internal.pageSize.getHeight();
    // 페이지 전체 너비
    const PAGE_WIDTH = doc.internal.pageSize.width || doc.internal.pageSize.getWidth();
		
    const pageCount = doc.internal.getNumberOfPages();

    doc.setFont('helvetica', 'normal');
    doc.setFontSize(10);
		
    for (var i = 1; i <= pageCount; i++) {
        doc.setPage(i);
        doc.text(`- ${i} -`, PAGE_WIDTH / 2, PAGE_HEIGHT - MARGIN_SIZE, {
            align: 'center'
        });
    }
};

 

이 메소드는 구글링을 통해 그대로 찾을 수 있었다.

큰 어려움없이 위치를 구해서 for문으로 모든 페이지에 text를 추가하는 방식이다.

문제는 해당 메소드를 어떻게 쓰느냐인데, doc.save를 하기 전에 쓰면 된다.

 

//footer(페이지 번호)
addFooters(doc);
	    		
//저장
doc.save('test.pdf');

 

이렇게 코드를 작성하면 보다 편리하게 텍스트와 Footer를 생성할 수 있다.

 

============================================================================

 

- 에러 -

 

Uncaught ReferenceError: jsPDF is not defined

 

jsPDF를 쓸 때 이런 에러가 발생할 수 있다. 해당 에러가 발생하면 당연히 script가 제대로 추가되었는지 확인해볼텐데 크롬 개발자도구의 network를 통해 script가 제대로 불러와진 것이 확인되었다면 당황할 수 있다.

그러나 이것은 종종 있는 일인 모양이다.

해결방법을 쉽게 찾을 수 있었고, 다음과 같이 new jsPDF로 생성하기 전에 다음 코드를 추가하면 된다.

 

window.jsPDF = window.jspdf.jsPDF;
// ↑↑↑ 추가하는 코드

const doc = new jsPDF('p', 'mm', 'a4');

 

위와 같이 처리하면 jsPDF 초기화가 정상적으로 이루어지면서, 해당 에러가 해결되는 것을 확인할 수 있다!

 

🤞 도움이 되셨기를 바랍니다. 한 번의 클릭과 댓글은 어딘가의 누군가에게 진실로 큰 힘이 됩니다. 🐱‍🏍

 

728x90
반응형

댓글