ItextPdf를 사용한 Html To Pdf 구현

in_ho_·2023년 1월 19일
0

CommonFunc

목록 보기
2/2
post-thumbnail

0. 개요


  • HTML을 Pdf로 저장할 수 있는 요구를 한 고객의 요청사항이 있었고 해당 사항에 대한 기록을 정리한다.

  • Html Code를 보내거나, 이미지를 찍어 보내거나 등의 방법이 생각 났고 해당 방법을 수행했지만 생각보다 원활하지 않았다.

  • 자유로운 방식의 Html2Pdf를 구현하지는 못했지만 어느정도 사용이 유용한 부분이 있어 기재한다.

1. HTML


  • 해당 방식을 사용할 때의 주의점은 CSS를 따로 import할 수 없으므로 인라인 스타일로 작성을 해야 한다.

  • 하지만 프론트 단에서 인라인으로 제어하는 것은 디자인에 유용하지 않을 것으로 보여 데이터만 가져와 Server에서 양식에 기입하여 반환하는 방식을 채택하였다.

  • 각자의 방식대로 HTML을 문자화하여 서버로 요청을 보낸다.

단, jQuery 사용자 또는 itextpdf 라이브러리 상에서 tag error가 난다면 아래의 코드를 js단에서 처리하자. 아래의 방법은 태그가 닫히는 것을 인지하지 못해서 발생하는 에러를 방지하는 소스이다.

const oSerializer = new XMLSerializer();
const html = oSerializer.serializeToString(el);

2. Java


  • 우리가 구현할 Html2Pdf를 사용할 때 유의해야 할 점은 라이브러리 기본 폰트가 한글을 지원하지 않으므로 한글을 지원하는 폰트를 준비해야 한다.(bold체도 지원하지 않으므로 사용하려면 준비)
public static JSONObject converted(HttpServletRequest request, String pdfName, String savePath, JSONObject param) throws DocumentException, IOException {
  1. 프로젝트의 경로를 알기 위한 request
  2. 저장할 pdf 파일 이름
  3. 저장할 경로
  4. 양식에 기입할 데이터(HTML이 인라인으로 작성이 되어 있다면 필요 X)
// 단 IOS는 한글명의 다운로드가 바로 안되므로 URLEcoder.encode(파일명, "UTF-8")을 사용해주어야 함.
if (isIos) { // IOS일 경우
	pdfName = URLEncoder.encode(pdfName, "UTF-8");
}

File pdfFile = new File(dest + "/" + pdfName + ".pdf"); 
ConverterProperties properties = new ConverterProperties();
FontProvider fontProvider = new FontProvider();
FontProgram fontProgram = FontProgramFactory.createFont(font.getAbsolutePath());
FontProgram boldFontProgram = FontProgramFactory.createFont(boldFont.getAbsolutePath());

fontProvider.addFont(fontProgram);
fontProvider.addFont(boldFontProgram);

properties.setFontProvider(fontProvider);
properties.setBaseUri("http://localhost:7070/"); // 기본 URL 주소
  • Font를 2개 정의한 이유는 font-weight: bold 또는 <b> 태그 사용을 위해서이다. 굵은 폰트가 존재하지 않는다면 PDF로 변환했을 때 일반 폰트와 동일하게 나온다.
  • Font는 File 인스턴스이며 ttf 파일을 갖고 와 사용하면 됩니다.
  • 또한 폰트에 의한 저작권 문제가 있을 수 있으므로 확인 필요.
List<IElement> elements = HtmlConverter.convertToElements(html.toString(), properties);
com.itextpdf.kernel.pdf.PdfDocument pdfDoc = new PdfDocument(new PdfWriter(pdfFile));
document = new Document(pdfDoc);
pdfDoc.setDefaultPageSize(PageSize.A4); // 용지 크기
document.setMargins(50, 50, 50, 50); // 마진 설정
  • 여기서 사용된 html은 양식에 기입한 것이여도 되고, 화면에서 가져온 html 소스여도 괜찮습니다.
  • 용지의 크기는 A4 용지로 맞춤.
for (IElement element : elements) {
	document.add((IBlockElement) element);	
}

document.close();
  • document에 html 요소가 작성되는 과정이라고 생각하면 된다.

3. 사용한 라이브러리


  • pom.xml 또는 build.gradle에 추가하면 되고, 만약 빌드 툴을 사용하면 jar 파일을 다운로드 받아 진행하면 된다.

    ClassNotFound가 발생한다면 의존관계를 갖고 있는 라이브러리가 누락된 것으로 jar 파일을 다시 추가하자(Maven 또는 Gradle을 사용할 경우 발생하지는 않을 것으로 보임)

		<!-- itextpdf -->
		<dependency>
			<groupId>com.itextpdf</groupId>
			<artifactId>forms</artifactId>
			<version>7.1.14</version>
		</dependency>
		<dependency>
			<groupId>com.itextpdf</groupId>
			<artifactId>hyph</artifactId>
			<version>7.1.14</version>
		</dependency>
		<dependency>
			<groupId>com.itextpdf</groupId>
			<artifactId>io</artifactId>
			<version>7.1.14</version>
		</dependency>
        <dependency>
			<groupId>com.itextpdf</groupId>
			<artifactId>styled-xml-parser</artifactId>
			<version>7.1.14</version>
		</dependency>
		<dependency>
			<groupId>com.itextpdf</groupId>
			<artifactId>svg</artifactId>
			<version>7.1.14</version>
		</dependency>
		<dependency>
			<groupId>com.itextpdf</groupId>
			<artifactId>html2pdf</artifactId>
			<version>3.0.3</version>
		</dependency>
		<dependency>
			<groupId>com.itextpdf</groupId>
			<artifactId>kernel</artifactId>
			<version>7.1.14</version>
		</dependency>
		<dependency>
			<groupId>com.itextpdf</groupId>
			<artifactId>layout</artifactId>
			<version>7.1.14</version>
		</dependency>
		<dependency>
			<groupId>com.itextpdf</groupId>
			<artifactId>pdfa</artifactId>
			<version>7.1.14</version>
		</dependency>
		<dependency>
			<groupId>com.itextpdf</groupId>
			<artifactId>sign</artifactId>
			<version>7.1.14</version>
		</dependency>
        		<dependency>
			<groupId>com.itextpdf</groupId>
			<artifactId>barcodes</artifactId>
			<version>7.1.14</version>
		</dependency>
		<dependency>
			<groupId>com.itextpdf</groupId>
			<artifactId>font-asian</artifactId>
			<version>7.1.14</version>
		</dependency>

4. 동명의 라이브러리


  • 이번 작업을 진행하면서 동명의 라이브러리가 많아 애를 많이 먹었다. 내 코드에서는 에러가 나는데 google에서 본 코드에서는 동작한다는 것이 이상하다고 생각했고 여러 차례 수정에 따라 import한 라이브러리를 기재합니다.
import com.itextpdf.html2pdf.ConverterProperties;
import com.itextpdf.html2pdf.HtmlConverter;
import com.itextpdf.html2pdf.attach.impl.layout.HtmlPageBreak;
import com.itextpdf.io.font.FontProgram;
import com.itextpdf.io.font.FontProgramFactory;
import com.itextpdf.kernel.geom.PageSize;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.layout.Document;
import com.itextpdf.layout.element.AreaBreak;
import com.itextpdf.layout.element.IBlockElement;
import com.itextpdf.layout.element.IElement;
import com.itextpdf.layout.font.FontProvider;
import com.itextpdf.layout.property.AreaBreakType;
import com.itextpdf.text.DocumentException;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.IOException;
import java.util.List;

5. 소스 전체

import com.itextpdf.html2pdf.ConverterProperties;
import com.itextpdf.html2pdf.HtmlConverter;
import com.itextpdf.html2pdf.attach.impl.layout.HtmlPageBreak;
import com.itextpdf.io.font.FontProgram;
import com.itextpdf.io.font.FontProgramFactory;
import com.itextpdf.kernel.geom.PageSize;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.layout.Document;
import com.itextpdf.layout.element.AreaBreak;
import com.itextpdf.layout.element.IBlockElement;
import com.itextpdf.layout.element.IElement;
import com.itextpdf.layout.font.FontProvider;
import com.itextpdf.layout.property.AreaBreakType;
import com.itextpdf.text.DocumentException;
import org.json.simple.JSONObject;

import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.List;

public class Html2PDF {

    /**
     * HTML을 받아서 PDF로 변환해주는 메서드
     * @param request - 프로젝트 경로를 알기 위해
     * @param downloadPath - 다운로드 받기 위한 경로
     * @param baseUrl - Server의 기본 경로(루트 경로)
     * @param pdfName - 다운로드할 PdfName
     * @param html - Pdf에 그릴 HTML
     * @return outJSON - {res: boolean, pdfName: 다운로드 된 Pdf명, originPdfName: 인코딩 되기 전의 파일명}
     * @throws DocumentException
     * @throws IOException
     */
    public static JSONObject converted(HttpServletRequest request, String downloadPath,
                                       String baseUrl, String pdfName, String html
    ) throws DocumentException, IOException {
        JSONObject outJSON = new JSONObject();
        Document document = null;

        outJSON.put("res", false);

        // Path
        String staticPath = request.getSession().getServletContext().getRealPath("/static");

        // Set Font
        File normalFont = new File(staticPath + "/font" + "/malgun.ttf");
        File boldFont = new File(staticPath + "/font" + "/malgunbd.ttf");

        // Set Pdf Info
        String originPdfName = pdfName;
        pdfName = URLEncoder.encode(pdfName, "UTF-8");

        // 1. Check already Pdf File
        File pdfFile = new File(downloadPath + "/" + pdfName + ".pdf");

        if(pdfFile.exists()) pdfFile.delete();

        // 2. Set Font
        ConverterProperties properties = new ConverterProperties();
        FontProvider fontProvider = new FontProvider();
        FontProgram fontProgram = FontProgramFactory.createFont(normalFont.getAbsolutePath());
        FontProgram boldFontProgram = FontProgramFactory.createFont(boldFont.getAbsolutePath());

        fontProvider.addFont(fontProgram);
        fontProvider.addFont(boldFontProgram);
        properties.setFontProvider(fontProvider);
        properties.setBaseUri(baseUrl);

        // 3. Set Pdf Page Size
        List<IElement> elements = HtmlConverter.convertToElements(html, properties);
        com.itextpdf.kernel.pdf.PdfDocument pdfDoc = new PdfDocument(new PdfWriter(pdfFile));
        document = new Document(pdfDoc);

        pdfDoc.setDefaultPageSize(PageSize.A4);

        document.setMargins(50, 50, 50, 50);

        for (IElement element : elements) {
            if (element instanceof HtmlPageBreak) {
                document.add(new AreaBreak(AreaBreakType.LAST_PAGE));
            }
            else {
                document.add((IBlockElement) element);
            }
        }

        outJSON.put("res", true);
        outJSON.put("pdfName", pdfName);
        outJSON.put("originPdfName", originPdfName);

        document.close();
        return outJSON;
    }
}

0개의 댓글