스프링

 

스프링부트 + 타임리프트 

 

 

 

1.라이브러리 등록

	 <dependency>
	        <groupId>org.xhtmlrenderer</groupId>
	        <artifactId>flying-saucer-pdf</artifactId>
	        <version>9.8.0</version>
	  </dependency>

 

 

 

 

2.컨트롤

폰트 설정을 잘못 할경우 한글처리가 안된다.

폰트 파일 위치 및  확장명이 ttf 파일명으로  폰트를  등록해 준다.

 

   @GetMapping(value = "/pdfdetail/{employeeID}/{month}")
   @ResponseBody
   public void pdfdetail(@PathVariable(value = "employeeID") String employeeID,
                         @PathVariable(value = "month") String month,
                         @RequestParam(defaultValue = "employee-details") String filename,
                         HttpServletResponse response) throws IOException, DocumentException {
       Map<String, Object> paramMap = new HashMap<>();
       paramMap.put("employeeID", employeeID);
       paramMap.put("month", month);

       DetailsVO employee = empInfoService.findDetailsByEmployeeID(paramMap);

       // Create context and add variables
       Context context = new Context();
       context.setVariable("employee", employee);

       // Render HTML
       String htmlContent = templateEngine.process("emp/details-pdf", context);

       // Set response headers
       response.setContentType("application/pdf");
       String encodedFilename = URLEncoder.encode(filename, "UTF-8");
       response.setHeader("Content-Disposition", "attachment; filename*=UTF-8''" + encodedFilename + ".pdf");

       // Generate PDF
       try (OutputStream os = response.getOutputStream()) {
           ITextRenderer renderer = new ITextRenderer();

           renderer.getFontResolver()
           .addFont(
                   new ClassPathResource("/static/font/NanumBarunGothic.ttf")
                           .getURL()
                           .toString(),
                   BaseFont.IDENTITY_H,
                   BaseFont.EMBEDDED);
          
           renderer.setDocumentFromString(htmlContent);
           renderer.layout();
           renderer.createPDF(os);
       }
   }

 

 

 

3.details.html

~

        <div class="col-1 text-left">
            <button href="/pdf/download" class="btn btn-outline-secondary"            
            	onclick="pdfDownload()"
            >PDF</button>

<input type="hidden" th:value="${#strings.substring(employee.month, 0, 4) + '년_' + #strings.substring(employee.month, 5, 7) + '월 급여명세표'}"  
id="filename"/>
<script>
    function pdfDownload() {
    	const filename=document.querySelector("#filename").value;
    	console.log("filename : ",filename);
    	
        const employeeID = '[[${employee.employeeID}]]';
        const month = '[[${employee.month}]]';
        location.href = `/pdfdetail/${employeeID}/${month}?filename=${filename}`;
    }
</script>
        </div>
~

 

 

 

 

 

4.details-pdf.html

    body {
      font-family: NanumBarunGothic,serif;
      margin: 20px;
      font-size: 12px;
    }

폰트명을

NanumBarunGothic 으로 했는데, 이것은 폰트파일을 클릭하면  NanumBarunGothic.ttf 글꼴 이름으로 나오는 이름으로 하면 된다.

 

 

 

 

 

 

 

닫는 태그 설정 주의

 

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"  >
<head> 
  <meta content="text/html; charset=UTF-8" http-equiv="Content-Type"/>
  <meta charset='UTF-8'/>
  <meta content='Content-type: text/html; charset=UTF-8' name='http-equiv'/>
  <meta content='IE=Edge,chrome=1' http-equiv='X-UA-Compatible'/>
  <meta content='width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no'
          name='viewport'/>
  <title>급여 명세서</title>
  <style>	 
    body {
      font-family: NanumBarunGothic,serif;
      margin: 20px;
      font-size: 12px;
    }
    .header p {
      font-size: 18px;
      font-weight: bold;
      text-align: center;
    }
    .container {
      width: 100%;
      margin: 0 auto;
    }
    .row {
      display: flex;
      margin-bottom: 10px;
    }
    .col {
      flex: 1;
      padding: 0 10px;
    }
    table {
      width: 100%;
      border-collapse: collapse;
      margin-bottom: 20px;
    }
    th, td {
      border: 1px solid #ddd;
      padding: 8px;
      text-align: left;
    }
    th {
      background-color: #f2f2f2;
    }
    .bg-light {
      background-color: #f2f2f2;
    }
    .text-center {
      text-align: center;
    }
    .mt-2 {
      margin-top: 20px;
    }
    .invisible {
      visibility: hidden;
    }
  </style>
</head>

<body>
<div class="container mt-4">
    <div class="row mb-2">
        <div class="col-12 header">
            <p>
            	<span th:text="${#strings.substring(employee.month, 0, 4) + '년 ' + #strings.substring(employee.month, 5, 7) + '월'}"></span>
             급여 명세서  月分給与 明細書
            </p>
        </div>
    </div>
    <div class="row mb-2" style="padding: 10px">
        <div class="col-11">
            주식회사
        </div>
    </div>
    <div class="row mb-2" style="padding: 10px">
        <div class="col-10" style="margin-bottom: 10px">
            성명: <span th:text="${employee.name}"> KIM MINSEOK</span> 님 
        </div>
        <div class="col-2 text-left">
            지급일: <span th:text="${#strings.substring(employee.month, 0, 4) + '년 ' + #strings.substring(employee.month, 5, 7) + '월'}"></span> 30일
        </div>
    </div>

    <div class="row">
        <div class="col" style="padding: 10px;margin-bottom:12px">
            <!-- 근태 테이블 -->
            <table class="table table-hover">
                <thead>
                <tr>
                    <th colspan="2" class="text-center bg-light">근태</th>
                </tr>
                <tr>
                    <th>항목</th>
                    <th>합계</th>
                </tr>
                </thead>
                <tbody class="table-group-divider">
                <tr>
                    <td>소정 근무일</td>
                    <td th:text="${employee.fixedWorkDays}"></td>
                </tr>
                <tr>
                    <td>출근일수</td>
                    <td th:text="${employee.workDays}"></td>
                </tr>
                <tr>
                    <td>결근일수</td>
                   <td th:text="${employee.absentDays}"></td>
                </tr>
                <tr>
                    <td>휴일 근무일수</td>
                    <td th:text="${employee.holidayWorkDays}"></td>
                </tr>
                <tr>
                    <td>법정 휴일 근무일수</td>
                     <td th:text="${employee.lawHolidayWorkDays}"></td>
                </tr>
                <tr>
                    <td>특별휴가일수</td>
                   <td th:text="${employee.specialLeaveDays}"></td>
                </tr>
                <tr>
                    <td>소정 근로시간</td>
                    <td th:text="${employee.fixedWorkHours}"></td>
                </tr>
                <tr>
                    <td class="invisible">공백</td>
                    <td class="invisible">공백</td>
                </tr>
                <tr>
                    <td class="invisible">공백</td>
                    <td class="invisible">공백</td>
                </tr>
                <tr>
                    <td class="invisible">공백</td>
                    <td class="invisible">공백</td>
                </tr>
                </tbody>
            </table>
        </div>

        <div class="col" style="padding: 10px;margin-bottom:12px">
            <!-- 지급 테이블 -->
            <table class="table table-hover">
                <thead>
                <tr>
                    <th colspan="2" class="text-center bg-light">지급</th>
                </tr>
                <tr>
                    <th>항목</th>
                    <th>합계</th>
                </tr>
                </thead>
                <tbody class="table-group-divider">
                <tr>
                    <td>기본급(월급)</td>
                    <td th:text="${#numbers.formatInteger(employee.basicSalary, 0, 'COMMA')}"></td>
                </tr>
                <tr>
                    <td>비과세 교통비</td>
                     <td th:text="${#numbers.formatInteger(employee.nonTaxableCommutingAllowance, 0, 'COMMA')}"></td>
                </tr>
                <tr>
                    <td>주택수당</td>
                     <td th:text="${#numbers.formatInteger(employee.housingAllowance, 0, 'COMMA')}"></td>
                </tr>
                <tr>
                    <td>보통 초과근무수당</td>
                     <td th:text="${#numbers.formatInteger(employee.normalOvertimeAllowance, 0, 'COMMA')}"></td>
                </tr>
                <tr>
                    <td>휴일근무수당</td>
                    <td th:text="${#numbers.formatInteger(employee.holidayWorkAllowance, 0, 'COMMA')}"></td>
                </tr>
                <tr>
                    <td>법정휴일근무수당</td>
                     <td th:text="${#numbers.formatInteger(employee.lawHolidayWorkAllowance, 0, 'COMMA')}"></td>
                </tr>
                <tr>
                    <td>결근 공제</td>
                     <td th:text="${#numbers.formatInteger(employee.absentDeduction, 0, 'COMMA')}"></td>
                </tr>
                <tr>
                    <td>지각 조퇴 공제</td>
                     <td th:text="${#numbers.formatInteger(employee.lateEarlyLeaveDeduction, 0, 'COMMA')}"></td>
                </tr>
                <tr>
                    <td class="invisible">공백</td>
                    <td class="invisible">공백</td>
                </tr>
                <tr>
                    <td class="invisible">공백</td>
                    <td class="invisible">공백</td>
                </tr>
                <tr>
                    <td class="invisible">공백</td>
                    <td class="invisible">공백</td>
                </tr>
                <tr>
                    <td>합계</td>
                    <td th:text="${#numbers.formatInteger(employee.totalSalary, 0, 'COMMA')}">167,256</td>
                </tr>
                </tbody>
            </table>
        </div>

        <div class="col" style="padding: 10px;">
            <!-- 공제 테이블 -->
            <table class="table table-hover">
                <thead>
                <tr>
                    <th colspan="2" class="text-center bg-light">공제</th>
                </tr>
                <tr>
                    <th>항목</th>
                    <th>합계</th>
                </tr>
                </thead>
                <tbody class="table-group-divider">
                <tr>
                    <td class="invisible">공백</td>
                    <td class="invisible">공백</td>
                </tr>
                <tr>
                    <td>소득세</td>
                    <td th:text="${#numbers.formatInteger(employee.incomeTax, 0, 'COMMA')}"></td>
                </tr>
                <tr>
                    <td>주민세</td>
                     <td th:text="${#numbers.formatInteger(employee.residentTax, 0, 'COMMA')}"></td>
                </tr>
                <tr>
                    <td class="invisible">공백</td>
                    <td class="invisible">공백</td>
                </tr>
                <tr>
                    <td class="invisible">공백</td>
                    <td class="invisible">공백</td>
                </tr>
                <tr>
                    <td class="invisible">공백</td>
                    <td class="invisible">공백</td>
                </tr>
                <tr>
                    <td class="invisible">공백</td>
                    <td class="invisible">공백</td>
                </tr>
                <tr>
                    <td>사회보험료 합계</td>
                    <td th:text="${#numbers.formatInteger(employee.totalSocialInsurance, 0, 'COMMA')}">24,872</td>
                </tr>
                <tr>
                    <td colspan="1">합계</td>
                    <td th:text="${#numbers.formatInteger(employee.deduction, 0, 'COMMA')}">24,872</td>
                </tr>
                </tbody>
            </table>
        </div>

        <div class="col" style="padding: 10px;">
            <!-- 기타 테이블 -->
            <table class="table table-hover">
                <thead>
                <tr>
                    <th colspan="2" class="text-center bg-light">기타</th>
                </tr>
                <tr>
                    <th>항목</th>
                    <th>합계</th>
                </tr>
                </thead>
                <tbody class="table-group-divider">
                <tr>
                    <td class="invisible">공백</td>
                    <td class="invisible">공백</td>
                </tr>
                <tr>
                    <td class="invisible">공백</td>
                    <td class="invisible">공백</td>
                </tr>
                <tr>
                    <td class="invisible">공백</td>
                    <td class="invisible">공백</td>
                </tr>
                <tr>
                    <td>합계</td>
                    <td>0</td>
                </tr>
                </tbody>
            </table>

            <!-- 차액 지급액 테이블 -->
            <table class="table table-hover mt-2">
                <tbody class="table-group-divider">
                <tr>
                    <td>차액 지급액</td>
                    <td th:text="${#numbers.formatInteger(employee.netPay, 0, 'COMMA')}">24,872</td>
                </tr>
                </tbody>
            </table>

		</div>
		
  		<div class="col" >
            <!-- 이체 지급액 테이블 -->
            <table class="table table-hover mt-2" >
                <thead>
                <tr>
                    <td colspan="2" class="text-center bg-light">이체 지급액</td>
                </tr>
                </thead>
                <tbody class="table-group-divider">
                <tr>
                    <td>이체 지급</td>
                     <td th:text="${#numbers.formatInteger(employee.netPay, 0, 'COMMA')}">139,644</td>
                </tr>
                <tr>
                    <td class="invisible">공백</td>
                    <td class="invisible">공백</td>
                </tr>
                <tr>
                    <td class="invisible">공백</td>
                    <td class="invisible">공백</td>
                </tr>
                <tr>
                    <td>합계</td>
                     <td th:text="${#numbers.formatInteger(employee.netPay, 0, 'COMMA')}">139,644</td>
                </tr>
                </tbody>
            </table>

            <!-- 현금 지급액 테이블 -->
            <table class="table table-hover mt-2">
                <tbody class="table-group-divider">
                <tr>
                    <td>현금 지급액</td>
                    <td>0</td>
                </tr>
                </tbody>
            </table>

            <!-- 현물 지급액 테이블 -->
            <table class="table table-hover mt-2">
                <tbody class="table-group-divider">
                <tr>
                    <td>현물 지급액</td>
                    <td>0</td>
                </tr>
                </tbody>
            </table>
        </div>
    </div>
</div>
</body>
</html>

 

 

 

 

 

 

가로로 출력을 원할경우 style 을 다음과 같이 추가해 주면 된다.

<style>
@page {
	size: A4 landscape;
	margin: 10mm;
}
</style>

 

 

 

한줄에 여러 테이블 출력을 원할경우 테이블안에 테이블 형식으로 하면 된다.

<table>

   <tr> 

   <td> 

        <table></table>

  </td>

   <td> 

        <table></table>

  </td>

  </tr>

</table>

 

 

 

오류:

flying-saucer-pdf 버전을 조절해 주면된다.

 

 

 

Flying Saucer는 잘 구성된 XML이나 XHTML을 CSS 2.1을 사용하여 레이아웃과 포맷팅하고, Swing 패널, PDF, 이미지로 출력하는 순수 자바 라이브러리입니다. 이 라이브러리는 XHTML/CSS 콘텐츠를 PDF로 변환하는 데 뛰어난 성능을 보여주며, 렌더링의 정밀도가 높습니다.

 

그러나 JSP를 PDF로 변환하는 것은 몇 가지 고려사항이 있습니다. Flying Saucer는 XHTML을 사용하므로 입력 HTML 파일이 잘 구성되어 있어야 합니다.

따라서 JSP 파일이 XHTML로 잘 구성되어 있지 않다면 변환 과정에서 문제가 발생할 수 있습니다.

 

또한, Flying Saucer는 기본적으로 iText 라이브러리를 사용하여 PDF를 생성합니다.

이는 AGPL 라이센스를 필요로 하거나 라이센스 비용을 지불해야 합니다.

이에 대한 대안으로 OpenPDF를 사용하는 Flying Saucer의 구현체가 있습니다.

 

 

따라서, JSP를 뷰로 사용하여 Flying Saucer를 사용하는 개발은 가능하지만, 위에서 언급한 몇 가지 고려사항을 염두에 두어야 합니다. 이러한 고려사항들로 인해 개발이 어려울 수 있습니다.

 

 

 

 

참고

https://zorba91.tistory.com/323

 

 

 

 

 

 

 

 

 

about author

PHRASE

Level 60  라이트

철 그른 동남풍 , 얼토당토않은 흰소리를 함을 이르는 말.

댓글 ( 0)

댓글 남기기

작성