最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

基于Java編寫一個(gè)html轉(zhuǎn)pdf的工具類

 更新時(shí)間:2025年09月29日 09:47:09   作者:huan1993  
這篇文章主要為大家詳細(xì)介紹了如何基于Java編寫一個(gè)html轉(zhuǎn)pdf的工具類,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起了解下

1、背景

最近項(xiàng)目中需要生成日?qǐng)?bào)文件,日?qǐng)?bào)文件的格式為pdf,且日?qǐng)?bào)的樣式相對(duì)而言比較復(fù)雜,存在多段文字,存在多個(gè)表格,且存在樣式。目前想到的解決辦法是 先生成html文件,讓后將html文件轉(zhuǎn)換成pdf文件。通過網(wǎng)上搜索,發(fā)現(xiàn)openhtmltopdf可以實(shí)現(xiàn)我們的需求,此處記錄一下。

2、需求

生成的pdf需要支持中文。

生成的pdf支持簡單的樣式。(此處可以使用css樣式來解決,但不是所有的css樣式都支持)

生成的pdf存在表格,每行應(yīng)完整地出現(xiàn)在同一頁,不要一半在上一頁、一半在下一頁。

生成的pdf可以自己指定到分頁,比如某個(gè)表格的數(shù)據(jù)渲染完之后,需要單獨(dú)開啟一頁。

生成的pdf支持密碼加密。

生成的pdf可以支持紙張規(guī)格,比如是A3還是A4,并且還可設(shè)置橫向還是縱向。

3、思路

1、html的生成,我們可以通過freemarker來實(shí)現(xiàn)。

2、html轉(zhuǎn)pdf,通過openhtmltopdf來實(shí)現(xiàn)。

4、實(shí)現(xiàn)步驟

4.1 搭建一個(gè)簡單的工程

首先搭建一個(gè)簡單的可運(yùn)行的程序,可實(shí)現(xiàn)Freemarker渲染模板,然后生成pdf文件

引入依賴

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
        <version>2.6.0</version>
    </dependency>
    <!-- 模板引擎,用于渲染html -->
    <dependency>
        <groupId>org.freemarker</groupId>
        <artifactId>freemarker</artifactId>
        <version>2.3.30</version>
    </dependency>
    <!-- 用于將html轉(zhuǎn)換成pdf -->
    <dependency>
        <groupId>com.openhtmltopdf</groupId>
        <artifactId>openhtmltopdf-pdfbox</artifactId>
        <version>1.0.10</version>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.36</version>
    </dependency>
</dependencies>

編寫Freemarker工具類

加載程序中src/main/resources/templates/ftls目錄下的模板文件,然后渲染成html內(nèi)容。

package com.huan.pdf.utils;

import freemarker.cache.ClassTemplateLoader;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateExceptionHandler;
import lombok.extern.slf4j.Slf4j;

import java.io.StringWriter;
import java.util.Map;

/**
 * freemarker 工具類
 *
 * @author admin
 */
@Slf4j
public class FreemarkerUtils {
    /**
     * 模板文件夾路徑
     */
    private static final String TEMPLATE_DIR = "/templates/ftls";
    private static final Configuration CONFIGURATION;

    static {
        CONFIGURATION = new Configuration(Configuration.VERSION_2_3_30);
        CONFIGURATION.setTemplateLoader(new ClassTemplateLoader(FreemarkerUtils.class, TEMPLATE_DIR));
        CONFIGURATION.setDefaultEncoding("UTF-8");
        CONFIGURATION.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
        CONFIGURATION.setLogTemplateExceptions(false);
        CONFIGURATION.setWrapUncheckedExceptions(true);
    }

    /**
     * 根據(jù)模板名稱和數(shù)據(jù)模型生成字符串
     *
     * @param templateName 模板名稱
     * @param dataModel    數(shù)據(jù)模型
     * @return 生成的字符串
     */
    public static String processTemplate(String templateName, Map<String, Object> dataModel) {
        try {
            Template template = CONFIGURATION.getTemplate(templateName);
            StringWriter writer = new StringWriter();
            template.process(dataModel, writer);
            return writer.toString();
        } catch (Exception e) {
            log.error("解析模板出現(xiàn)問題", e);
        }
        return "";
    }
}

編寫pdf工具類

編寫pdf工具類,用于將html內(nèi)容渲染成pdf文件,此處只是簡單實(shí)現(xiàn),后期該類還需要修改

package com.huan.pdf.utils;

import com.openhtmltopdf.pdfboxout.PdfRendererBuilder;
import lombok.extern.slf4j.Slf4j;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.util.UUID;

/**
 * pdf工具類
 *
 * @author admin
 */
@Slf4j
public class PdfUtils {

    /**
     * 生成pdf文件
     *
     * @param pdfTemplate pdf模板
     * @param response    http response
     */
    public static void generatePdf(String pdfTemplate, HttpServletResponse response) {
        // 設(shè)置響應(yīng)頭
        String fileName = UUID.randomUUID() + ".pdf";
        response.setContentType("application/pdf");
        response.setHeader("Content-Disposition", "attachment; filename=" + fileName);

        try (OutputStream os = response.getOutputStream()) {
            PdfRendererBuilder builder = new PdfRendererBuilder();
            builder.withHtmlContent(pdfTemplate, null);
            builder.toStream(os);
            builder.run();
        } catch (IOException e) {
            log.error("生成pdf文件失敗", e);
            throw new RuntimeException("生成pdf文件失敗", e);
        }
    }
}

增加一個(gè)模板

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <title>生成pdf</title>
        <style>
            .main-title { text-align: center; font-size:25px; }
        </style>
    </head>
    <body>
        <div class="main-title">${mainTitle}</div>
    </body>
</html>

該模板中存在變量mainTitle,這個(gè)變量的值通過后臺(tái)來賦值

增加一個(gè)控制層

package com.huan.pdf.controller;

import com.huan.pdf.utils.FreemarkerUtils;
import com.huan.pdf.utils.PdfUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletResponse;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.Map;

/**
 * pdf控制器
 *
 * @author admin
 */
@RestController
public class PdfController {

    @GetMapping("pdf")
    public void pdf(HttpServletResponse response) {

        Map<String, Object> params = new HashMap<>(16);
        params.put("mainTitle", "這是一個(gè)標(biāo)題 - " + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));

        // 渲染模板
        String htmlContent = FreemarkerUtils.processTemplate("pdf.ftl", params);
        // 生成pdf
        PdfUtils.generatePdf(htmlContent, response);
    }
}

注意:此處的mainTitle中存在中文,生產(chǎn)的Pdf會(huì)亂碼待會(huì)兒在處理

運(yùn)行

可以看到可以正常的生成pdf了,但是中文亂碼了。 至此我們一個(gè)簡單的程序就搭建完成了,下面讓我們來完善功能。

4.2 功能完善

生成的pdf需要支持中文

默認(rèn)情況下生成的pdf,中文是亂碼的,若需要解決這個(gè)問題,就需要引入中文字體。此處我們使用宋體。

1.程序中引入宋體

在程序的src/main/resources/fonts目錄下,引入宋體(simsun.ttf)

2.pdf工具類中增加使用中文字體

builder.useFont(() -> PdfUtils.class.getClassLoader().getResourceAsStream("fonts/simsun.ttf"), "SimSun");

3.freemarker模板中使用中文字體

<style>
   body { font-family: "SimSun";  font-size: 16px; line-height: 1.5; color: #000;}
</style>

4.運(yùn)行

從上圖中可以看到,現(xiàn)在已經(jīng)可以展示中文了。

生成的pdf支持簡單的樣式

此處實(shí)現(xiàn)將生成的pdf中的 這是一個(gè)標(biāo)題-時(shí)間 這句話的字體修改成紅色。

1.freemarker模板中使用css樣式

.main-title { text-align: center; font-size:25px; color:#FF0000; }

2.運(yùn)行

通過上圖可知,樣式已經(jīng)生效了。

表格的某一行不要出現(xiàn)跨頁

1.freemarker模板中增加一個(gè)表格

<style>
     table { border-collapse: collapse; }
     td { border: 1px solid black; padding: 70px;}
</style>

<table>
    <tr><td>序號(hào)</td></tr>
    <tr><td>1</td></tr>
    <tr><td>2</td></tr>
    <tr><td>3</td></tr>
    <tr><td>4</td></tr>
    <tr><td>5</td></tr>
</table>

2.查看效果

從上圖可以看到,生成的pdf,內(nèi)容跨了2頁,那么如何解決這個(gè)問題呢?通過css樣式解決

3.css解決

 table { border-collapse: collapse; page-break-inside: auto;}
 tr { page-break-inside: avoid;}

4.查看效果

單獨(dú)開啟一頁pdf

1.freemarker模板修改

通過css樣式page-break-before:always開啟新的一頁pdf。

2.查看效果

指定pdf頁面的規(guī)格

默認(rèn)情況是A4 縱向,現(xiàn)在我想修改成A3 橫向。這個(gè)指定對(duì)所有的頁面都生效,不可只對(duì)某一個(gè)頁面生效,若想對(duì)某一個(gè)頁面生效,可以生成多個(gè)pdf文件,然后進(jìn)行pdf文件的合并操作。

css樣式指定頁面規(guī)則

@page{ size:A3 landscape; }

查看效果

從上圖中可知 正好是A3橫向

pdf 加密

實(shí)現(xiàn)思路:通過pdfbox生成加密的密碼,此處給默認(rèn)密碼a0nin13s

1.修改pdf生成的工具類

/**
 * 生成帶密碼的 PDF 文件(用戶密碼 a0min13s)
 *
 * @param pdfTemplate HTML 模板字符串
 * @param response    HTTP 響應(yīng)
 */
public static void generatePdf(String pdfTemplate, HttpServletResponse response) {
    String fileName = UUID.randomUUID() + ".pdf";
    response.setContentType("application/pdf");
    response.setHeader("Content-Disposition", "attachment; filename=" + fileName);

    // 1. 先用 openhtmltopdf 生成未加密 PDF(內(nèi)存)
    ByteArrayOutputStream temp = new ByteArrayOutputStream();
    try {
        PdfRendererBuilder builder = new PdfRendererBuilder();
        builder.useFont(() -> PdfUtils.class.getClassLoader().getResourceAsStream("fonts/simsun.ttf"), "SimSun");
        builder.withHtmlContent(pdfTemplate, null);
        builder.toStream(temp);
        // 完成渲染
        builder.run();
    } catch (IOException e) {
        log.error("生成PDF失敗", e);
        throw new RuntimeException("生成PDF失敗");
    }

    // 用 PDFBox 加載并加密
    try (PDDocument doc = PDDocument.load(temp.toByteArray());
         OutputStream os = response.getOutputStream()) {

        AccessPermission ap = new AccessPermission();
        // 可選:禁止打印、復(fù)制等
        ap.setCanPrint(false);
        ap.setCanExtractContent(false);

        // 用戶密碼,所有者密碼一樣即可(也可設(shè)不同)
        StandardProtectionPolicy policy =
                // ownerPwd  userPwd
                new StandardProtectionPolicy("a0min13s", "a0min13s", ap);
        // 128 位 AES
        policy.setEncryptionKeyLength(128);
        policy.setPermissions(ap);
        // 執(zhí)行加密
        doc.protect(policy);
        // 寫給瀏覽器
        doc.save(os);
        // 確保全部送出
        os.flush();
    } catch (IOException e) {
        log.error("PDF加密輸出失敗", e);
        throw new RuntimeException("PDF加密輸出失敗");
    }
}

2.查看效果

完整代碼:https://gitee.com/huan1993/spring-cloud-parent/tree/master/pdf/openhtmltopdf

以上就是基于Java編寫一個(gè)html轉(zhuǎn)pdf的工具類的詳細(xì)內(nèi)容,更多關(guān)于Java html轉(zhuǎn)pdf的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Java?EE實(shí)現(xiàn)用戶后臺(tái)管理系統(tǒng)

    Java?EE實(shí)現(xiàn)用戶后臺(tái)管理系統(tǒng)

    這篇文章主要為大家詳細(xì)介紹了Java?EE實(shí)現(xiàn)用戶后臺(tái)管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-05-05
  • 淺談Java中的n種隨機(jī)數(shù)產(chǎn)生辦法

    淺談Java中的n種隨機(jī)數(shù)產(chǎn)生辦法

    眾所周知,隨機(jī)數(shù)是任何一種編程語言最基本的特征之一。而生成隨機(jī)數(shù)的基本方式也是相同的:產(chǎn)生一個(gè)0到1之間的隨機(jī)數(shù)??此坪唵?,但有時(shí)我們也會(huì)忽略了一些有趣的功能。
    2015-09-09
  • SWT(JFace)體驗(yàn)之模擬BorderLayout布局

    SWT(JFace)體驗(yàn)之模擬BorderLayout布局

    SWT(JFace)體驗(yàn)之模擬BorderLayout布局代碼。
    2009-06-06
  • Spring Boot 緩存 Cache 入門詳解

    Spring Boot 緩存 Cache 入門詳解

    本文主要介紹SpringBoot緩存的入門知識(shí),包括緩存的必要性、常見的緩存策略、SpringCache的注解使用、SpringBoot與緩存的集成以及Ehcache和Redis的示例,同時(shí),還提供了緩存面試問題的思路和答案,幫助讀者更好地理解和掌握SpringBoot緩存的相關(guān)內(nèi)容,感興趣的朋友一起看看吧
    2025-03-03
  • java內(nèi)存優(yōu)化的方法總結(jié)

    java內(nèi)存優(yōu)化的方法總結(jié)

    在本篇文章里小編給大家分享的是一篇關(guān)于java內(nèi)存優(yōu)化的方法總結(jié)內(nèi)容,有需要的朋友們可以學(xué)習(xí)下。
    2021-06-06
  • IDEA中 Getter、Setter 注解不起作用的問題如何解決

    IDEA中 Getter、Setter 注解不起作用的問題如何解決

    這篇文章主要介紹了IDEA中 Getter、Setter 注解不起作用的問題如何解決,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-08-08
  • springboot項(xiàng)目中利用騰訊云上傳存儲(chǔ)文件

    springboot項(xiàng)目中利用騰訊云上傳存儲(chǔ)文件

    本文主要介紹了springboot項(xiàng)目中利用騰訊云上傳存儲(chǔ)文件,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2025-12-12
  • Java WebFlux集成DeepSeek大模型的完整步驟

    Java WebFlux集成DeepSeek大模型的完整步驟

    隨著大模型技術(shù)的普及,Java后端接入DeepSeek等大模型時(shí),傳統(tǒng)同步阻塞式調(diào)用已無法滿足高并發(fā)、低延遲的業(yè)務(wù)需求,本文基于Spring WebFlux響應(yīng)式框架,詳細(xì)講解大模型流式接入的技術(shù)方案、完整實(shí)現(xiàn)代碼、性能優(yōu)化技巧及常見問題解決方案,需要的朋友可以參考下
    2026-05-05
  • 圖解Java排序算法之希爾排序

    圖解Java排序算法之希爾排序

    這篇文章主要為大家詳細(xì)介紹了Java排序算法之希爾排序,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-11-11
  • WebSocket無法注入屬性的問題及解決方案

    WebSocket無法注入屬性的問題及解決方案

    這篇文章主要介紹了WebSocket無法注入屬性的問題及解決方法,本文通過示例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-09-09

最新評(píng)論

尼玛县| 温泉县| 那坡县| 呼玛县| 灵石县| 芷江| 泉州市| 玛纳斯县| 连云港市| 常德市| 祁门县| 桑植县| 章丘市| 习水县| 咸阳市| 隆德县| 遵化市| 白银市| 墨玉县| 崇信县| 涡阳县| 通海县| 海兴县| 赫章县| 雷山县| 永福县| 兴国县| 出国| 洛川县| 红原县| 祁门县| 阿鲁科尔沁旗| 高唐县| 吴江市| 景宁| 南平市| 论坛| 洞口县| 齐齐哈尔市| 如皋市| 惠来县|