HTML을 Pdf로 저장할 수 있는 요구를 한 고객의 요청사항이 있었고 해당 사항에 대한 기록을 정리한다.
Html Code를 보내거나, 이미지를 찍어 보내거나 등의 방법이 생각 났고 해당 방법을 수행했지만 생각보다 원활하지 않았다.
자유로운 방식의 Html2Pdf를 구현하지는 못했지만 어느정도 사용이 유용한 부분이 있어 기재한다.
해당 방식을 사용할 때의 주의점은 CSS를 따로 import할 수 없으므로 인라인 스타일로 작성을 해야 한다.
하지만 프론트 단에서 인라인으로 제어하는 것은 디자인에 유용하지 않을 것으로 보여 데이터만 가져와 Server에서 양식에 기입하여 반환하는 방식을 채택하였다.
각자의 방식대로 HTML을 문자화하여 서버로 요청을 보낸다.
단, jQuery 사용자 또는 itextpdf 라이브러리 상에서 tag error가 난다면 아래의 코드를 js단에서 처리하자. 아래의 방법은 태그가 닫히는 것을 인지하지 못해서 발생하는 에러를 방지하는 소스이다.
const oSerializer = new XMLSerializer(); const html = oSerializer.serializeToString(el);
public static JSONObject converted(HttpServletRequest request, String pdfName, String savePath, JSONObject param) throws DocumentException, IOException {
// 단 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 주소
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); // 마진 설정
for (IElement element : elements) {
document.add((IBlockElement) element);
}
document.close();
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>
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;
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;
}
}