java通過html生成pdf,支持css和圖片以及橫向打印


項目當中通常會有生成pdf的需求,pdf的排版尤為重要!通過html生成,最為方便.

1. 依賴

工具使用freemarker模板進行數據渲染

<dependency>
  <groupId>org.freemarker</groupId>
  <artifactId>freemarker</artifactId>
  <version>2.3.29</version>
</dependency>
<dependency>
  <groupId>org.xhtmlrenderer</groupId>
  <artifactId>flying-saucer-pdf</artifactId>
  <version>9.1.18</version>
</dependency>

2. 工具類

import java.io.*;
import java.util.Locale;
import java.util.Map;
import org.xhtmlrenderer.pdf.ITextRenderer;
import com.lowagie.text.pdf.BaseFont;
import freemarker.template.Configuration;
import freemarker.template.Template;

public class PdfUtil {

	/**
	 * 通過模板導出pdf文件
	 * @param data 數據
	 * @param templateFileName 模板文件名
	 * @throws Exception
	 */
    public static ByteArrayOutputStream createPDF(Map<String,Object> data, String templateFileName) throws Exception {
        // 創建一個FreeMarker實例, 負責管理FreeMarker模板的Configuration實例
        Configuration cfg = new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS);
        // 指定FreeMarker模板文件的位置 
        cfg.setClassForTemplateLoading(PdfUtil.class,"/templates");
        ITextRenderer renderer = new ITextRenderer();
        OutputStream out = new ByteArrayOutputStream();
        try {
            // 設置 css中 的字體樣式(暫時僅支持宋體和黑體) 必須,不然中文不顯示
            renderer.getFontResolver().addFont("/static/font/SIMSUN.TTC", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
            // 設置模板的編碼格式
            cfg.setEncoding(Locale.CHINA, "UTF-8");
            // 獲取模板文件 
            Template template = cfg.getTemplate(templateFileName, "UTF-8");
            StringWriter writer = new StringWriter();
            
            // 將數據輸出到html中
            template.process(data, writer);
            writer.flush();

            String html = writer.toString();
            // 把html代碼傳入渲染器中
            renderer.setDocumentFromString(html);

             // 設置模板中的圖片路徑 (這里的images在resources目錄下) 模板中img標簽src路徑需要相對路徑加圖片名 如<img src="images/xh.jpg"/>
            String url = PdfUtil.class.getClassLoader().getResource("static/images").toURI().toString();
            renderer.getSharedContext().setBaseURL(url);
            renderer.layout();
            
            renderer.createPDF(out, false);
            renderer.finishPDF();
            out.flush();
            return (ByteArrayOutputStream)out;
        } finally {
        	if(out != null){
        		 out.close();
        	}
        }
    }
}

代碼中需要注意路徑設置,否則會導致css和圖片引入無效

  • cfg.setClassForTemplateLoading(PdfUtil.class,"/templates"); 指定FreeMarker模板文件的位置

  • renderer.getFontResolver().addFont("/static/font/SIMSUN.TTC", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED); 指定字體文件,否則中文不顯示

  • PdfUtil.class.getClassLoader().getResource("static/images").toURI().toString(); 指定模板中圖片路徑

宋體字體下載: https://dl.pconline.com.cn/download/367689-1.html

靜態資源目錄結構:

3. 使用

建議使用時,先寫一個html靜態頁面,調試好了再復制到ftl文件中,保存成模板

靜態index.html

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8"/>
    <title></title>
    <style>
        .center{
            width: 380px;
            height: 538px;
            background: url("images/zs.png") center no-repeat;
            margin: 10% auto;
            font-family: SimSun;
            position: relative;
        }
        .name{
            position: absolute;
            top: 216px;
            left: 60px;
            font-size: 20px;
            width: 74px;
            text-align: center;
            display: block;
        }
    </style>
</head>
<body>
<div class="center">
    <span class="name">李逍遙</span>
</div>
</body>
</html>

模板zhengshu.ftl

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8"/>
    <title></title>
    <style>
        .center{
            width: 380px;
            height: 538px;
            background: url("images/zs.png") center no-repeat;
            margin: 15% auto;
            font-family: SimSun;
            position: relative;
        }
        .name{
            position: absolute;
            top: 216px;
            left: 60px;
            font-size: 20px;
            width: 74px;
            text-align: center;
            display: block;
        }
    </style>
</head>
<body>
<div class="center">
    <span class="name">${name}</span>
</div>
</body>
</html>

把需要設置數據的地方,用freemarker語法進行占位${}

單元測試

@Test
public void pdf() throws IOException {
    ByteArrayOutputStream baos = null;
    FileOutputStream out = null;
    try {
        Map<String,Object> data = new HashMap<>();
        data.put("name", "李逍遙");
        baos = PdfUtil.createPDF(data, "zhengshu.ftl");
        String fileName = "獲獎證書.pdf";
        File file = new File(fileName);
        out = new FileOutputStream(file);
        baos.writeTo(out);
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        if(baos!=null){
            baos.close();
        }
        if(out != null){
            out.close();
        }
    }
}

使用controller

mport java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RequestMapping("/pdf")
public class PdfController {
	
	@RequestMapping("/export")
	public void exportPdf(HttpServletResponse response) throws Exception{
		ByteArrayOutputStream baos = null;
		OutputStream out = null;
		try {
			// 模板中的數據,實際運用從數據庫中查詢
			Map<String,Object> data = new HashMap<>();
			data.put("name", "李逍遙");
			baos = PdfUtil.createPDF(data, "zhengshu.ftl");;
			// 設置響應消息頭,告訴瀏覽器當前響應是一個下載文件
			response.setContentType( "application/x-msdownload");
			// 告訴瀏覽器,當前響應數據要求用戶干預保存到文件中,以及文件名是什么 如果文件名有中文,必須URL編碼 
			String fileName = URLEncoder.encode("獲獎證書.pdf", "UTF-8");
			response.setHeader( "Content-Disposition", "attachment;filename=" + fileName);
			out = response.getOutputStream();
			baos.writeTo(out);
			baos.close();
		} catch (Exception e) {
			e.printStackTrace();
		    throw new Exception("導出失敗:" + e.getMessage());
		} finally{
			if(baos != null){
				baos.close();
			}
			if(out != null){
				out.close();
			}
		}
	}
}

4. 橫向打印

有時網頁比較寬時,生成的pdf寬度不夠,導致顯示內容不完整,可以通過在模板css設置@page控制

/*設置頁面寬高 A4大小*/
@page{size:297mm 210mm;}

參考:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM