본문 바로가기
Web Development/Spring Legacy

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

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

이 글에서는 jsPDF, jsPDF-AutoTable 라이브러리를 통해 PDF에서 테이블 생성하려고 할 때, 힘들었던 부분에 대한 모든 것을 다루고자 한다.

단순한 PDF 생성, jsPDF 사용법을 알고 싶거나, 아직 jsPDF에 대해 잘 모른다면 아래 링크 글을 참조해주시면 좋겠다.

 

PDF 다운로드 기능 구현 - 1. 텍스트, Footer 생성 (feat. jsPDF, jsPDF-autotable, error) 에 대해 다룬 글

 

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

프로젝트를 진행하면서 PDF 다운로드 기능을 구현하게 되었다. 처음에는 간단하게 jsPDF와 html2canvas 라이브러리의 조합으로 해결해보려고 했지만, 역시 간단한 것은 원하는 대로 잘 되지 않는다.

devlifetestcase.tistory.com

 

일단 이전 글에서 다루었듯, 테이블 추가 시 문제점 리스트는 다음과 같다.

[ 목 차 ]

1. 테이블 테마 + border 설정
2. 셀 색 채우기
3. 셀 병합
4. 셀 높이/너비 설정
5. 테이블 정렬
6. 전체 셀 너비 균등
7. 너비 퍼센트 지정
8. 페이지 넘어갈 시 잘림 현상
9. 한 행에 여러 개 테이블 생성
10. 한 행에 여러 개 테이블 생성 시 페이지 넘어갈 때 잘리는 현상
11. 한 행의 여러 개 테이블 중 가장 긴 테이블의 y 좌표 얻기 및 테이블 y 좌표 얻기
12. 셀에 이미지 추가

 

다 하나같이 실무에 필수적인 부분인데, 공식 깃허브의 사용법만을 가지고는 이해가 잘 되지 않고 해결하기가 쉽지 않다. 그래서 직접 사용해보고 구글링과 stackoverflow를 통해 해결한 방법들을 공유하려고 한다.

 

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

 

 

 

[ 적용 방법 ]

 

우선 시작하기 전에, 전체 코드가 아닌 해당 부분 문제에 대한 코드밖에 없어 바로 적용할 수 없음을 양해바란다.

 

▼ 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

 

 

1. 테이블 테마 + border 설정

 

테이블 생성 시 가장 기본적으로 신경쓰게되는 부분은 테이블 양식이다.

아래 이미지와 같이 공식적으로 제공되는 테마도 결코 나쁘다고 할 수는 없지만, 우리나라의 일반적인 양식과는 거리가 먼 것이 사실이다.

 

공식 테마

 

어쩔 수 없이 테마를 바꾸어야 하는데, 우리가 구현하고자 하는 양식은 대략 다음과 비슷할 것이다.

 

예시로 만든 문서

 

반응형

 

이제 어떻게 해당 양식을 구현할 지 알아보자.

 

우선, 기본 테마는 지워야한다. 테마를 지우기 위해서는 공식 문서의 plain 테마를 지정하면 된다. 대신 border가 모두 사라지므로 border만 따로 추가해주면 된다.

코드는 다음과 같다.

 

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

const MARGIN_SIZE = 10;
const PAGE_WIDTH = doc.internal.pageSize.width || doc.internal.pageSize.getWidth();

const head = [
    ['헤더1', '헤더2', '헤더3']
];

const body = [
    [
        {content: '데이터1', styles: {halign: 'center', lineColor: [0, 0, 0], lineWidth: 0.3}},
        {content: '데이터2', styles: {halign: 'center', lineColor: [0, 0, 0], lineWidth: 0.3}},
        {content: '데이터3', styles: {halign: 'center', lineColor: [0, 0, 0], lineWidth: 0.3}}
    ]
];

const width = PAGE_WIDTH - (MARGIN_SIZE * 2); //페이지 가로 너비 - 좌우 여백
           
doc.autoTable({
    theme: 'plain',
    tableLineColor: [0, 0, 0],
    tableLineWidth: 0.3,
    headStyles:  { halign: 'center', valign: 'middle', fillColor: [234, 234, 234], lineColor: [0, 0, 0], lineWidth: 0.3 },	  	//헤더 부분 옵션
    startX: 0, 													//시작점 X 좌표
    startY: 0,													//시작점 Y 좌표
    tableWidth : width,											//테이블 너비
    styles : { font : 'NanumMyeongjo', fontStyle :'normal'},  	//폰트적용(나눔명조체를 사용하였는데 이는 기본 사용법 글 참조하여 설정필요)
    head: head,
    body : body
});

 

autoTable로 테이블을 생성할 때 해당 스타일을 적어주면 된다.

theme는 'plain'으로 설정하여 plain 테마를 적용하고, tableLineColor와 tableLineWidth를 통해 기본 border 색과 굵기를 지정해주었다.

하지만 tableLine은 전체 테이블의 테두리를 뜻하기 때문에 th, td에 해당하는 head와 각 셀의 border도 따로 설정해주어야 한다.

우선 headStyle에 lineColor와 lineWidth를 통해 th의 border를 설정할 수 있고, td는 body에서 각 content의 styles에 lineColor와 lineWidth를 통해 설정할 수 있다.

 

여기서 두 가지 중요한 점을 알 수 있다.

th는 headStyles를 통해 설정하고 td는 body에 설정할 때 각 content의 styles로 설정한다는 것, 그리고 두 styles의 속성 명, 속성 값(key, value)은 동일한 양식이라는 것이다. 즉, 공식 Usage에서는 body의 styles만을 보여주고있는데 head에 styles를 적용하고 싶을때는 headStyles를 동일하게 이용하면 된다는 것이다.

(head도 셀마다 styles 속성 사용 가능 : 3번 참조)

 

▲ 목차로 돌아가기

 

 

2. 셀 색 채우기

 

이제 셀에 색을 채우는 것을 알아보자. 이미 위의 코드에 적혀있다.

fillColor: [234, 234, 234]

 

styles에 fillColor 속성을 추가하면 되며, 일반 숫자도 가능하지만 RGB로 나타내고 싶을 때는 [] 안에 RGB 값을 적어 채울 수 있다.

 

▲ 목차로 돌아가기

 

 

3. 셀 병합

 

셀 병합은 크게 어려울 것이 없다.

rowSpan과 colSpan기능이 기본적으로 제공되기 때문에, 속성값으로 추가하면 된다.

하지만, 주의해야할 점이 rowSpan과 colSpan은 styles속성에 속한 2depth 속성이 아니라 content, styles와 같은 1depth 속성이란 것이다. 해당 부분만 주의하면 된다.

 

그리고 head도 body와 같이

const head = [
    [ {content: '헤더1', colSpan: 2}, {content: '헤더3'} ] 
];

 

와 같이 나타낼 수 있다.

 

반응형

 

▲ 목차로 돌아가기

 

 

4. 셀 높이/너비 설정

 

버전이 바뀌면서 해당 부분이 바뀌었는데, 최신 버전에서는 styles의 속성으로 셀 높이/너비를 설정할 수 있다.

- 높이 : minCellHeight (default : 0) ; 숫자
- 너비 : cellWidth (default : 'auto') ; auto|wrap|숫자

 

해당 속성들의 값을 변경하여 다음과 같이 셀 높이/너비 설정이 가능하다.

const body = [
    [
      {content: '텍스트1', minCellheight: 20, cellWidth: 10},
      {content: '텍스트2', minCellheight: 0, cellWidth: 'auto'}
    ] 
];

 

▲ 목차로 돌아가기

 

 

5. 테이블 정렬

 

테이블 정렬은 autoTable로 테이블 추가 시 margin 속성을 변경하는 방식으로 구현할 수 있다.

 

const MARGIN_SIZE = 10;

// 페이지 전체 너비
function addTable(doc, head, body, {width = 0, align = 'center', x = MARGIN_SIZE, y = Y_LOC}){
    const PAGE_WIDTH = doc.internal.pageSize.width || doc.internal.pageSize.getWidth();

    //전달된 width 값이 없을 시 테이블의 너비는 페이지 너비로 함
    //(페이지 전체 너비 - 좌우 여백)
    width = (width == 0 ? PAGE_WIDTH - (MARGIN_SIZE * 2) : width);
    
    const MARGIN = (PAGE_WIDTH - width) / 2;
    
    let MARGIN_LEFT = MARGIN;
    let MARGIN_RIGHT = MARGIN;
    
    // 좌,우 정렬 시 여백 처리
    if(align == 'left') {
        MARGIN_LEFT = MARGIN_SIZE;
        MARGIN_RIGHT = MARGIN_SIZE;
    } else if(align == 'right') {
        MARGIN_LEFT = PAGE_WIDTH - MARGIN_SIZE - width;
        MARGIN_RIGHT = MARGIN_SIZE;
    }
    
    doc.autoTable({
        theme: 'plain',
        startX: x,
        startY: y,
        tableWidth : width,
        margin: {
            left: MARGIN_LEFT, 
            right: MARGIN_RIGHT
        },
        head: head,
        body : body
    });
}

 

위와 같이 css처럼 margin 속성의 왼쪽과 오른쪽을 상황에 맞게 달리 설정하여 테이블 정렬을 구현할 수 있다.

MARGIN_RIGHT는 사실 가운데 정렬을 제외하고는 큰 의미가 없어 왼쪽, 오른쪽 정렬 시에 동일하게 적용했다.

 

▲ 목차로 돌아가기

 

 

6. 전체 셀 너비 균등

 

구현하다보니, 셀 너비가 auto로 기본 값이 설정되어있어 테이블이 여러개일 때나 상황마다 셀 크기가 변해서 양식이 깨지는 현상이 발생했다. 그래서 너비를 동일하게 맞춰주는 기능이 필요해보였고 4번과 5번 문제를 해결하면서 구현할 수 있을 것 같아 해당 메소드를 작성했다.

 

function setWidthSame(doc, arr) {
    // 페이지 전체 너비
    const PAGE_WIDTH = doc.internal.pageSize.width || doc.internal.pageSize.getWidth();
    const SAME_WIDTH = (PAGE_WIDTH - (MARGIN_SIZE * 2)) / arr.length;
		
    for(let i = 0; i < arr.length; i++){
        arr[i].styles.cellWidth = SAME_WIDTH;
    }
}

 

위의 예제들에서 볼 수 있듯 head나 body의 형태가 배열에 배열을 감싸는 형식으로 구성되어있음을 알 수 있다.

 

//bodyArray
const body = [
    //tdArray(1)
    [
      {content: '텍스트1-1', minCellheight: 20, cellWidth: 10},
      {content: '텍스트2-1', minCellheight: 0, cellWidth: 'auto'}
    ],
    //tdArray(2)
    [
      {content: '텍스트1-2', minCellheight: 20, cellWidth: 10},
      {content: '텍스트2-2', minCellheight: 0, cellWidth: 'auto'}
    ]
];

 

그래서 실제 코드로 처리할 때는, bodyArray 안에 tdArray를 담는 식으로 처리하게 되는데, 이 tdArray를 setWidthSame 메소드의 파라미터로 제공하면 해당 요소마다 cellWidth를 균등하게 변경해준다.

 

반응형

 

▲ 목차로 돌아가기

 


7. 너비 퍼센트 지정

 

너비를 균등하게 하는 것도 필요하지만, 퍼센트로 나누는 것도 필요하다.

그래서 전체너비에서 num으로 퍼센트 수치를 파라미터로 제공해줄 때 계산된 값을 제공하는 메소드도 만들어보았다.

이 메소드는 테이블의 너비가 컨텐츠 전체너비(전체 너비 - 좌우 여백)일 때만 가능하다.

 

function getWidthPer(doc, num) {
    //num : width percent
    if(num > 100) return;  //전달받은 수치가 100보다 클 때 예외처리(상황에 따라 추가 예외 처리 필요)
    
    const PAGE_WIDTH = doc.internal.pageSize.width || doc.internal.pageSize.getWidth();
    return (PAGE_WIDTH - (MARGIN_SIZE * 2)) / 100 * num;
}

 

여기서 나온 값을 cellWidth 값으로 설정해주면 된다.

 

▲ 목차로 돌아가기

 

 

8. 페이지 넘어갈 시 잘림 현상

 

구글링을 해보면 페이지 넘어갈 때 잘리는 것을 막아주는 속성 값(옵션)이 있긴 하지만, 해당 옵션이 모든 사례를 다 막아주진 못한다.

 

특히, head에서 잘리는 경우 head가 앞, 뒤 페이지에 모두 출력된다. 경우에 따라 장점도 있지만, body에 한줄도 없이 head만 2번 반복되면 오류처럼 보인다. 또, 한 행에 여러 테이블을 생성한 경우에 첫 테이블은 문제 없지만, 이후에 생성된 테이블 위치가 깨진다거나 테이블이 중첩되는 경우가 발생한다.

 

이를 해결하기 위해 내가 접근한 방식은 테이블이 잘리는 경우 다음 페이지에서 그려주는 방식이다.

(단, 테이블이 매우 길어서 여러 페이지에 걸친 경우는 검증되지 않았으므로, 이 방법을 피할 것을 권장)

 

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

function addTable(doc, head, body, {width = 0, align = 'center', x = MARGIN_SIZE, y = Y_LOC}){
    const PAGE_WIDTH = doc.internal.pageSize.width || doc.internal.pageSize.getWidth();

    width = (width == 0 ? PAGE_WIDTH - (MARGIN_SIZE * 2) : width);
    const MARGIN = (PAGE_WIDTH - width) / 2;
    
    let MARGIN_LEFT = MARGIN;
    let MARGIN_RIGHT = MARGIN;
    
    if(align == 'left') {
        MARGIN_LEFT = MARGIN_SIZE;
        MARGIN_RIGHT = MARGIN_SIZE;
    } else if(align == 'right') {
        MARGIN_LEFT = PAGE_WIDTH - MARGIN_SIZE - width;
        MARGIN_RIGHT = MARGIN_SIZE;
    }
    
    /// 페이지 잘림 계산
    const temp = Y_LOC;
    Y_LOC = (Y_LOC + (CELL_HEIGHT * (head.length + body.length)) > 287 ? MARGIN_SIZE + 1000 : Y_LOC);
    if(y == temp) y = Y_LOC;
    /// end of 페이지 잘림 계산
    
    doc.autoTable({
        theme: 'plain',
        startX: x,
        startY: y,
        tableWidth : width,
        margin: {
            left: MARGIN_LEFT, 
            right: MARGIN_RIGHT
        },
        head: head,
        body : body
    });
}

 

5번에서 다룬 코드를 그대로 사용하여, 주석 부분만 추가했다.

생성된 테이블의 정보를 확인해보니 기본 셀 높이가 있어서 해당 값을 전역변수로 선언하고 계산했다.

 

const temp = Y_LOC;
Y_LOC = (Y_LOC + (CELL_HEIGHT * (head.length + body.length)) > 287 ? MARGIN_SIZE + 1000 : Y_LOC);
if(y == temp) y = Y_LOC;

 

이 부분이 계산을 해주는 부분인데, head와 body의 셀 갯수를 더한 값을 셀 높이 값을 곱해 현재 위치에 더해준다.

이렇게 하면 테이블 생성 후 Y 좌표값을 구할 수 있는데,

 

① 값이 A4의 297mm 길이에서 하단 여백 10mm를 뺀 287 보다 크다면 저장된 Y 좌표 값을 한 페이지 넘어간 값으로 저장한다.

② y 값이 따로 입력되지 않았을 시, 저장된 Y 좌표 값으로 y 값을 변경한다.

 

▲ 목차로 돌아가기

 

 

9. 한 행에 여러 개 테이블 생성(2단 테이블만 검증 완료)

 

jsPDF의 장점이 x, y 값을 직접 입력하여 그려내기 때문에 수학적인 계산만 잘 해주면 효율적인 이용이 가능하다.

한 행에 여러 개 테이블을 생성하는 방법 역시 실상은 x좌표와 y좌표를 계산해주는 것에 불과하다. 다만 AutoTable에서 이미 만들어서 지원해주는 옵션들이 있어서 섞어서 잘 쓰면 더 편하게 원하는 기능을 구현해낼 수 있다.

 

< 다중 테이블 생성 전체 코드 >

 

function addMultiTable(doc, headList, bodyList, {y = Y_LOC}) {
    // 페이지 전체 높이
    const PAGE_HEIGHT = doc.internal.pageSize.height || doc.internal.pageSize.getHeight();
    // 페이지 전체 너비
    const PAGE_WIDTH = doc.internal.pageSize.width || doc.internal.pageSize.getWidth();
		
    // 테이블 갯수(headList나 bodyList 중에 선택 또는 필요에 따라 count를 파라미터로 추가)
    const count = headList.length;
		
    //*** 테이블이 페이지 넘어갈 때 페이지 넘겨서 생성
    //최대 셀 카운트 찾기
    let maxHeadCount = 0;
    let maxBodyCount = 0;
    for(let i = 0; i < headList.length; i++){
        maxHeadCount = (headList[i].length > maxHeadCount ? headList[i].length : maxHeadCount);
    }
    for(let i = 0; i < bodyList.length; i++){
        maxBodyCount = (bodyList[i].length > maxBodyCount ? bodyList[i].length : maxBodyCount);
    }
    //end of 최대 셀 카운트 찾기
		
    const temp = Y_LOC;
    Y_LOC = (Y_LOC + (CELL_WIDTH * (maxHeadCount + maxBodyCount)) > 287 ? MARGIN_SIZE + 1000 : Y_LOC);
    if(y == temp) y = Y_LOC;
    //*** end of 테이블이 페이지 넘어갈 때 페이지 넘겨서 생성
		
    // 테이블 너비
    const width = ((PAGE_WIDTH - (MARGIN_SIZE * 2)) / count) - (MARGIN_SIZE / 2);
		
    const tableMetaList = new Array();

    let x = MARGIN_SIZE;
    // 현재 페이지 저장
    const startingPage = doc.internal.getCurrentPageInfo().pageNumber;
		
    // 테이블 생성
    for(let i = 0; i < count; i++){
        // 테이블 생성 후 메타데이터를 저장할 변수
        let tableMeta = null;
        // 반복문을 돌면서 페이지를 이전 페이지로 돌려서 자동으로 다음 페이지 넘어가는 것을 방지
        doc.setPage(startingPage);
			
        doc.autoTable({
            theme: 'plain',
            startX: x,
            startY: y,
            tableWidth : width,
            head: headList[i],
            body : bodyList[i],
            margin: {
                //표 간격
                left : (i == 0 ? MARGIN_SIZE : (width + MARGIN_SIZE) + (MARGIN_SIZE * i) )
            },
            didParseCell: (data) => {									//Hook
                if(!tableMeta) {
                    tableMeta = data.table;
                    tableMetaList.push(tableMeta);
                }
            }
        });
		
        //x 좌표값을 테이블 너비만큼 더해서 이동
        x += width;
    }
		
    // 테이블 Y 최대값
    let maxY = 0;

    if(tableMetaList.length > 0) {
        for(let i = 0; i < tableMetaList.length; i++){
            maxY = (tableMetaList[i].finalY > maxY ? tableMetaList[i].finalY : maxY);
        }
    }

    // 테이블 생성 후 최대 길이 셀의 y값에 줄 높이를 더해 다음 Y 좌표 위치를 설정
    Y_LOC = (LINE_HEIGHT + maxY);
};

 

위는 가독성을 위해 10, 11번 문제까지 해결된 전체 코드이다.

실제 해당 부분은 아래와 같다.

 

const width = ((PAGE_WIDTH - (MARGIN_SIZE * 2)) / count) - (MARGIN_SIZE / 2);
let x = MARGIN_SIZE;
y = Y_LOC;
		
// 테이블 생성
for(let i = 0; i < count; i++){
    doc.autoTable({
        theme: 'plain',
        startX: x,
        startY: y,
        tableWidth : width,
        head: headList[i],
        body : bodyList[i],
        margin: {
            left : (i == 0 ? MARGIN_SIZE : (width + MARGIN_SIZE) + (MARGIN_SIZE * i) )
        }
    }); 
    x += width;
}

 

코드에서 크게 복잡한 부분은 없고, 원리는 다음과 같다.

 

① 너비인 width 는 전체 너비에서 테이블 갯수를 나눈 값과 여백이 처리된 값을 빼서 구해준다.

② y 값은 고정으로 두고, for 문 내에서 동일한 y 값이 유지되게 한다.

③ tableWidth를 구했던 너비인 width로 설정한다.

④ x 좌표에 너비를 추가하여 반복하여 테이블을 생성하도록 한다.

 

단순하지만, 이렇게 하면 한 행에 여러 개의 테이블을 생성할 수 있다.

 

반응형

 

▲ 목차로 돌아가기

 

 

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

 

이 문제는 이미 8번에서 해결한 바 있다. 그러나 차이점이 있어서 추가로 처리해주어야 하는 부분이 있다.

다중 테이블을 그릴 때는 y 값을 고정으로 두고 그리게 되는데, AutoTable 라이브러리 자체에서 첫번째 테이블을 그리면서 다음 페이지로 넘어가게 되면 y 값이 변경된다.

그렇기 때문에 이후의 테이블을 그릴 때 다음페이지로 넘어가서 그리기 때문에 페이지를 넘나드는 테이블들이 그려지게 된다. 그러나 다행히도 라이브러리에서 관련 옵션을 제공하여 쉽게 해당 문제를 고칠 수 있다.

 

//현재 페이지 저장
const startingPage = doc.internal.getCurrentPageInfo().pageNumber;
		
for(let i = 0; i < count; i++){
    // 반복문을 돌면서 페이지를 이전 페이지로 돌려서 자동으로 다음 페이지 넘어가는 것을 방지
    doc.setPage(startingPage);
    
    ~~~ 중략 ~~~
}

 

위 코드를 설명하면, 라이브러리에서 현재 페이지 번호를 받을 수 있는 기능을 제공하기 때문에, 현재 페이지를 저장하고, 반복문 안에서는 시작하면서 테이블이 그려질 페이지를 현재 페이지로 지정한다는 의미이다.

 

이를 통해서 페이지가 넘어가도 다중 테이블이 깨지는 문제를 방지할 수 있다.

 

▲ 목차로 돌아가기

 


11. 한 행의 여러 개 테이블 중 가장 긴 테이블 y 좌표 얻기 및 테이블 y 좌표 얻기

 

앞서, 이전 글에서 텍스트 추가 시 자동으로 y 좌표를 설정해주는 부분이 있었다.

그러나 테이블 생성 시에는 그렇게 간단히 해결할 수 없고 테이블에서 데이터를 얻어서 처리해야 하는데, AutoTable이 굉장히 잘 만들어진 라이브러리라고 느낀게 생성 전에 Hook 를 할 수 있도록 기능을 제공해주고 있다.

 

doc.autotable({
    ~~~~~ 중략 ~~~~~
    didParseCell: (data) => {
        if(!tableMeta) {
            tableMeta = data.table;
            tableMetaList.push(tableMeta);
        }
    }
});
            
// 테이블 Y 최대값
let maxY = 0;

if(tableMetaList.length > 0) {
    for(let i = 0; i < tableMetaList.length; i++){
        maxY = (tableMetaList[i].finalY > maxY ? tableMetaList[i].finalY : maxY);
    }
}

 

그 덕분에 autotable 처리된 데이터를 받아올 수 있고 여러가지 처리를 해줄 수 있다.

table에서는 리스트로 담을 필요없이 바로 data를 받아오면 되지만 다중 테이블에서는 리스트로 담아서 처리해야 한다.

console.log로 데이터를 출력해보면 여러가지 값을 확인할 수 있는데, 이 문제를 해결하기 위해서 사용한 값은 finalY 라는 값이다.

finalY는 해석대로 생성 후, 마지막 Y값을 담고 있는데 이를 통해서 Y 최대값을 구해 원하는 기능을 구현할 수 있다.

 

▲ 목차로 돌아가기

 


12. 셀에 이미지 추가

 

이제 마지막이다.

이미지 추가는 앞의 모든 과정을 거쳐오면 생각보다 쉽게 구현할 수 있다.

방법은, 배열을 담을 때 속성을 추가해주고 Hook를 사용하는 것이다.

 

const IMG_SIZE_X = 5;  //이미지 가로 크기
const IMG_SIZE_Y = 5;  //이미지 세로 크기

//tdArray.push( {content: text, styles: {halign: align, fillColor}, rowSpan, colSpan, img} );
//예 : tdArray.push( {content: '테스트', img: '/img/button.jpg'} );

didDrawCell: function(data) {
    if(data.cell.raw.img != null) {
        //(parameter : img src, x, y, width, height)
        doc.addImage(data.cell.raw.img, data.cell.x + (data.cell.contentWidth / 2),  data.cell.y + (data.cell.contentHeight / 2), IMG_SIZE_X, IMG_SIZE_Y);
    }
}

 

Hook 를 사용하면서 알게된 것인데, table의 td를 배열에 담을 때 커스텀 속성을 파라미터로 추가하면 Hook에서 해당 데이터를 읽을 수 있다.

AutoTable로 변환되는 과정을 거치면서 사라지는 줄 알았는데 didDrawCell Hook에서 data를 console.log로 출력해보니 cell.raw에 그대로 들어가 있었다. 심지어 contentWidth와 contentHeight도 있어서 셀 내부 위치에 대한 계산도 가능하다.

 

이를 통해서 img 속성에 소스값을 설정한 후, jsPDF의 doc.addImage를 통해 각 파라미터 값을 채워 셀에 이미지 추가가 가능하다.

위의 코드는 셀의 중간에 5X5 사이즈로 이미지를 추가하는 코드이다.

 

▲ 목차로 돌아가기

 

 

이렇게 jsPDF와 jsPDF-AutoTable 라이브러리를 통해 테이블을 생성하는 과정을 모두 살펴보았다.

관련해서 한글 자료가 별로 없는데, 이 글이 널리 도움이 되었으면 좋겠다!

 

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

 

728x90
반응형

댓글