Convert Html to PDF (spire.pdf.free)

유재영·2024년 4월 30일
0
post-custom-banner

html 파일을 문자열로 변환하여 pdf 파일로 다운 받는 기능이다.

여기서 사용할 라이브러리는 spire.pdf 이다.
홈페이지에서 보면 java 용 뿐만 아니라 다양한 언어로 지원하고있어 다양한 프로젝트에 적용할 수 있다는 장점과 감사하게도 제한적으로 무료 라이센스를 제공하고있다.

소스 환경

빌드 툴 : maven 3.0
언어 : java 17
프레임워크 : spring boot 3.2.5
라이브러리 : spire.pdf.free 9.12.3

pom.xml

<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;
	}
}

PDF 변환 결과

아쉬운점

변환된 페이지의 여백을 조정하려면
HtmlConverter.convert() 오버로딩 함수를 중 margin , pageSize 를 매개변수로 받는 아래 이미지에 표시된 함수를 사용하면 된다.
그러나 오류가 발생하고있다.

profile
#java #javascript #spring #jquery #mariadb #vue.js
post-custom-banner

0개의 댓글