ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Java] iText를 이용하여 HTML 텍스트를 PDF로 변환하기.
    java 2022. 7. 7. 13:20
    728x90
    반응형

    https://zero-gravity.tistory.com/251

     

    [Java] iText를 이용하여 HTML 텍스트를 PDF로 변환하기.

     iText라는 라이브러리를 사용했다.  필요한 라이브러리 파일은 2개다. - itextpdf (필자는 5.5.4 버전을 사용) http://sourceforge.net/projects/itext/ - xmlworker (5.5.4 버전 사용) http://sourceforge.n..

    zero-gravity.tistory.com

    고맙네요..

     

     iText라는 라이브러리를 사용했다.

       필요한 라이브러리 파일은 2개다.

    - itextpdf (필자는 5.5.4 버전을 사용) http://sourceforge.net/projects/itext/

    - xmlworker (5.5.4 버전 사용) http://sourceforge.net/projects/itextsharp/files/xmlworker/

     

     

       iText로 HTML->PDF 변환 기능을 구현하면서 가장 애먹었던 작업은 한글과 CSS 문제였다.

       한글 변환은 어떻게 해야 한다..라는 참고할 만한 사이트가 별로 없었고(외국 애들은 영어쓰니까 ㅠㅠ), 국내 사이트 중에는 어떤 블로거 분이 http://shonm.tistory.com/450 한글이 되는 소스를 올려주셨는데 문제는 이 소스로 CSS는 어떻게 해야 하는지 모른다는 거였다.

       텍스트 자체를 PDF로 변환하는 것은 간단한데, 한글이 온전히 보이면서 CSS까지 먹이려니 여간 짜증나는 작업이 아닐 수 없었다.

       (쉬웠던 방법은 StyleSheet로 일일히 속성을 코딩하는 것이었는데, 그짓거리는 하고 싶지 않았다)

       한글이 되니까 CSS가 안되고, CSS가 되니까 한글이 안되는 돌아버릴 것 같은 상황의 연속이었지만... 결국엔 해냈다. (self 쓰담쓰담)

       iText의 API도 뒤져보고 다른 사람들이 올려놓은 소스들도 죄다 살펴보면서 하나하나 조각을 끼워맞췄다.
        결정적으로 도움이 됐던 사이트는 http://www.wenda.io/questions/4902142/how-to-export-vietnamese-text-to-pdf-using-itext.html 여기였다.

       베트남 사람한테 도움을 받게 될 줄이야..ㄷㄷ 이 사람도 한자를 PDF로 넣어야 하는데 그 부분에서 막혔던 거다. 영어로 질답해줘서 감사 또 감사...ㅠㅠ

       자 그럼,, 아래는 소스다.

       3일 동안 개고생해서 정리한 것이니, 감사한 마음으로 가져가 쓰시기를...

    Document document = new Document(PageSize.A4, 50, 50, 50, 50); // 용지 및 여백 설정
         
    // PdfWriter 생성
    //PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream("d:/test.pdf")); // 바로 다운로드.
    PdfWriter writer = PdfWriter.getInstance(document, response.getOutputStream());
    writer.setInitialLeading(12.5f);
     
    // 파일 다운로드 설정
    response.setContentType("application/pdf");
    String fileName = URLEncoder.encode("한글파일명", "UTF-8"); // 파일명이 한글일 땐 인코딩 필요
    response.setHeader("Content-Transper-Encoding", "binary");
    response.setHeader("Content-Disposition", "inline; filename=" + fileName + ".pdf");
     
    // Document 오픈
    document.open();
    XMLWorkerHelper helper = XMLWorkerHelper.getInstance();
         
    // CSS
    CSSResolver cssResolver = new StyleAttrCSSResolver();
    CssFile cssFile = helper.getCSS(new FileInputStream("C:/eGovFrame/workspace/projectName/src/main/webapp/css/pdf.css"));
    cssResolver.addCss(cssFile);
         
    // HTML, 폰트 설정
    XMLWorkerFontProvider fontProvider = new XMLWorkerFontProvider(XMLWorkerFontProvider.DONTLOOKFORFONTS);
    fontProvider.register("C:/eGovFrame/workspace/projectName/src/main/webapp/font/MALGUN.TTF", "MalgunGothic"); // MalgunGothic은 alias,
    CssAppliers cssAppliers = new CssAppliersImpl(fontProvider);
     
    HtmlPipelineContext htmlContext = new HtmlPipelineContext(cssAppliers);
    htmlContext.setTagFactory(Tags.getHtmlTagProcessorFactory());
     
    // Pipelines
    PdfWriterPipeline pdf = new PdfWriterPipeline(document, writer);
    HtmlPipeline html = new HtmlPipeline(htmlContext, pdf);
    CssResolverPipeline css = new CssResolverPipeline(cssResolver, html);
     
    XMLWorker worker = new XMLWorker(css, true);
    XMLParser xmlParser = new XMLParser(worker, Charset.forName("UTF-8"));
     
    // 폰트 설정에서 별칭으로 줬던 "MalgunGothic"을 html 안에 폰트로 지정한다.
    String htmlStr = "<html><head><body style='font-family: MalgunGothic;'>"
                + "<p>PDF 안에 들어갈 내용입니다.</p>"
                + "<h3>한글, English, 漢字.</h3>"
            + "</body></head></html>";
     
    StringReader strReader = new StringReader(htmlStr);
    xmlParser.parse(strReader);
     
    document.close();
    writer.close();

     ↓ pdf.css

    p {font-weight: bold;}
    h3 {font-size: 15px; font-weight: bold; color: #0082E9; padding:10px 0 10px 0px; margin:0 10px;}

       폰트는 한글이 되는 폰트로 특정 경로에 해당 폰트 파일을 넣고, 위 소스에 경로를 설정해주면 된다.

       CSS는 디자이너 분 붙잡고 이것저것 다 해본 결과, 클래스나 div는 안먹고 p태그 h태그 같은 아~~주 원초적이고 순수한 것들만 적용됨을 확인할 수 있었다. ㅋㅋㅋ

       그 외... API 찾아보면 PDF 문서를 세부적으로 설정해주는 방법들도 있으니 그런 것들은 알아서....

       아래는 위 소스의 결과물,,

     

       **** 내용 추가>>>    PDF 워터마크, 쪽 번호, 우측 상단 머릿말 넣기.

       아래의 클래스 만든다.

    import com.itextpdf.text.Document;
    import com.itextpdf.text.Element;
    import com.itextpdf.text.Font;
    import com.itextpdf.text.Font.FontFamily;
    import com.itextpdf.text.Paragraph;
    import com.itextpdf.text.Phrase;
    import com.itextpdf.text.Rectangle;
    import com.itextpdf.text.pdf.CMYKColor;
    import com.itextpdf.text.pdf.ColumnText;
    import com.itextpdf.text.pdf.GrayColor;
    import com.itextpdf.text.pdf.PdfPageEventHelper;
    import com.itextpdf.text.pdf.PdfWriter;
     
    public class PdfPageEvent extends PdfPageEventHelper {
     
        // Watermark 폰트 설정
    //  Font FONT = new Font(FontFamily.HELVETICA, 52, Font.BOLD, new GrayColor(0.75f));
        Font FONT = new Font(FontFamily.HELVETICA, 52, Font.BOLD, new CMYKColor(64, 11, 0, 0));
         
        Phrase[] header = new Phrase[2];
        int pagenumber;
         
        @Override
        public void onOpenDocument(PdfWriter writer, Document document) {
            header[0] = new Phrase("2015. 01. 07"); // 헤더 머릿말에 넣을 텍스트
        }
         
        @Override
        public void onChapter(PdfWriter writer, Document document,
                float paragraphPosition, Paragraph title) {
            header[1] = new Phrase(title.getContent());
                    pagenumber = 1;
        }
     
        @Override
        public void onStartPage(PdfWriter writer, Document document) {
            pagenumber++;
        }
         
        @Override
        public void onEndPage(PdfWriter writer, Document document) {
            Rectangle rect = writer.getBoxSize("boxName"); // boxName을 PdfWriter.setBoxSize의 boxname 매개 변수와 같게 해야 함.
             
            switch (writer.getPageNumber() % 2) { // 쪽수 쪽, 홀수 쪽 구분해서 속성 적용.
            case 0: // 짝수 쪽
                ColumnText.showTextAligned(writer.getDirectContent(),
                        Element.ALIGN_RIGHT, header[0], rect.getRight(),
                        rect.getTop(), 0);
                break;
            case 1: // 홀수 쪽
                ColumnText.showTextAligned(writer.getDirectContent(),
                        Element.ALIGN_LEFT, header[1], rect.getLeft(),
                        rect.getTop(), 0);
                break;
            }
             
            // Watermark
            ColumnText.showTextAligned(writer.getDirectContentUnder(),
                    Element.ALIGN_CENTER, new Phrase("CONFIDENTIAL DOCUMENT", FONT), // 워터마크로 넣을 텍스트
                    297.5f, 421, writer.getPageNumber() % 2 == 1 ? 45 : -45);
             
            // 쪽 번호 매기기
            ColumnText.showTextAligned(writer.getDirectContent(),
                    Element.ALIGN_CENTER,
                    new Phrase(String.format("%d", pagenumber)), // %d의 앞 뒤에 "-" 추가하면 "-1-" 이런 식..
                    (rect.getLeft() + rect.getRight()) / 2, rect.getBottom() - 18, 0);
             
        }
         
    }

       그리고나서, PDF를 변환해주는 코드 부분에 아래 코드를 추가한다.

    PdfPageEvent event = new PdfPageEvent(); // Pdf 속성 설정.
    writer.setBoxSize("boxName", new Rectangle(36, 54, 559, 788));
    writer.setPageEvent(event);

       속성 적용해주는 건 여기서 ☞ http://itextpdf.com/examples/iia.php?id=103 참고했음.

    728x90
    반응형
Designed by Tistory.