html 파일을 문자열로 변환하여 pdf 파일로 다운 받는 기능이다.
여기서 사용할 라이브러리는 spire.pdf 이다.
홈페이지에서 보면 java 용 뿐만 아니라 다양한 언어로 지원하고있어 다양한 프로젝트에 적용할 수 있다는 장점과 감사하게도 제한적으로 무료 라이센스를 제공하고있다.
빌드 툴 : maven 3.0
언어 : java 17
프레임워크 : spring boot 3.2.5
라이브러리 : spire.pdf.free 9.12.3
<repositories>
<!-- spire repository -->
<repository>
<id>com.e-iceblue</id>
<name>e-iceblue</name>
<url>https://repo.e-iceblue.com/nexus/content/groups/public/</url>
</repository>
</repositories>
<dependencies>
<!-- spire pdf -->
<dependency>
<groupId>e-iceblue</groupId>
<artifactId>spire.pdf.free</artifactId>
<version>9.12.3</version>
</dependency>
</dependencies>
라이브러리 홈페이지에서 오픈소스인 GT Web Engine 을 사용하고 있으니 다운 받으라고 안내 하고 있다.
필요한 OS 에 맞는 플러그인을 다운받아서 resources/spire/plugins/ 경로에 압축해제해 넣어주면 된다.
Download Plugin Link
html 에 임포트되는 스타일은 해당 css 파일역시 문자열로 변환하여 html 파일 안에 replace 처리하였다.
<link rel="stylesheet" href="simple.css"/>
//add css
String cssString = fileAsString(resourcePath + "simple.css");
htmlString = htmlString
.replace(
"<link rel=\"stylesheet\" type=\"text/css\" href=\"simple.css\"/>",
"<style>" + cssString + "</style>"
);
이미지 태그에서 사용한 리소스 파일은 base64 값으로 변환하여 src 속성값을 replace 처리하였다.
<img src="stamp.png" width="80" height="80"/>
//문서내 이미지 리소스
Resource resource = resourceLoader.getResource("classpath:spire/testResources/stamp.png");
String base64Img = encodeImageToBase64(resource.getFile());
htmlString = htmlString.replaceAll("<img src=\"stamp.png\" width=\"80\" height=\"80\"/>","<img src=\""+ base64Img + "\" width=\"80\" height=\"80\"/>");
여기서는 구글 폰트에서 제공하는 Dokdo 폰트와 Noto Sans KR 폰트를 적용해보았다.
<head>
<link rel="preconnect" href="https://fonts.googleapis.com"/>
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin/>
<link href="https://fonts.googleapis.com/css2?family=Dokdo&family=Noto+Sans+KR:wght@100..900&display=swap" rel="stylesheet">
</head>
package com.pdf.spire;
import com.spire.pdf.htmlconverter.LoadHtmlType;
import com.spire.pdf.htmlconverter.qt.HtmlConverter;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.Base64;
@Controller
@RequiredArgsConstructor
public class SpireController {
private static final Logger log = LoggerFactory.getLogger(SpireController.class);
private final ResourceLoader resourceLoader;
@RequestMapping("/spire/pdf/simple")
public void spirePDF2(HttpServletResponse response, HttpServletRequest request) throws IOException {
String htmlString = "";
String fileName = "simple.pdf";
String resourcePath = Paths.get("C:", "Users","liuje","IdeaProjects","SpringSampleApp","spire","src","main","resources","spire","testResources") + File.separator;
log.info("resourcePath:{}", resourcePath);
//html 파일 가져오기
htmlString = fileAsString(resourcePath + "simple.html");
//add css
String cssString = fileAsString(resourcePath + "simple.css");
htmlString = htmlString.replace("<link rel=\"stylesheet\" type=\"text/css\" href=\"simple.css\"/>", "<style>" + cssString + "</style>");
//문서내 이미지 리소스
Resource resource = resourceLoader.getResource("classpath:spire/testResources/stamp.png");
String base64Img = encodeImageToBase64(resource.getFile());
htmlString = htmlString.replaceAll("<img src=\"stamp.png\" width=\"80\" height=\"80\"/>","<img src=\""+ base64Img + "\" width=\"80\" height=\"80\"/>");
//Set the plugin path
String pluginPath = "C:\\Users\\liuje\\IdeaProjects\\SpringSampleApp\\spire\\src\\main\\resources\\spire\\plugins\\plugins-windows-x64";
HtmlConverter.setPluginPath(pluginPath);
response.setContentType("application/pdf");
response.setHeader("Content-Disposition", "attachment; filename=" + fileName);
HtmlConverter.convert(htmlString, response.getOutputStream(), LoadHtmlType.Source_Code);
//library error
// HtmlConverter.convert(htmlString, fileName, false, 1000000, new Size(2480.0F, 3508.0F) , new PdfMargins(5.0F, 5.0F), LoadHtmlType.Source_Code);
// HtmlConverter.convert(htmlString, fileName, false, 30000, new Size(612.0F, 792.0F) , new PdfMargins(90.0F, 72.0F), LoadHtmlType.Source_Code);
// HtmlConverter.convert(htmlString, fileName, true, 300000, new Size(612.0F, 792.0F) , new PdfMargins(5.0F, 5.0F), LoadHtmlType.Source_Code);
}
private static String fileAsString(String filePath) throws IOException {
File file = new File(filePath);
FileReader fileReader = new FileReader(file);
BufferedReader bufferedReader = new BufferedReader(fileReader);
StringBuilder stringBuilder = new StringBuilder();
String temp = "";
while ((temp = bufferedReader.readLine()) != null) {
stringBuilder.append(temp).append("\n");
}
bufferedReader.close();
return stringBuilder.toString();
}
private static String encodeImageToBase64(File file) throws IOException {
// 파일을 바이트 배열로 읽기
FileInputStream fileInputStream = new FileInputStream(file);
byte[] imageBytes = new byte[(int) file.length()];
fileInputStream.read(imageBytes);
// 바이트 배열을 Base64 문자열로 인코딩
String base64Image = Base64.getEncoder().encodeToString(imageBytes);
// 파일 스트림 닫기
fileInputStream.close();
// 이미지를 data URI 형식으로 반환
return "data:image/png;base64," + base64Image;
}
}
아쉬운점
변환된 페이지의 여백을 조정하려면
HtmlConverter.convert() 오버로딩 함수를 중 margin , pageSize 를 매개변수로 받는 아래 이미지에 표시된 함수를 사용하면 된다.
그러나 오류가 발생하고있다.