学战 發表於 2025-9-26 12:54:00

Java中实现html转pdf

<p></p><div class="toc"><div class="toc-container-header">目录</div><ul><li>1、背景</li><li>2、需求</li><li>3、思路</li><li>4、实现步骤<ul><li>4.1 搭建一个简单的工程<ul><li>4.1.1 引入依赖</li><li>4.1.2 编写Freemarker工具类</li><li>4.1.3 编写pdf工具类</li><li>4.1.4 增加一个模板</li><li>4.1.5 增加一个控制层</li><li>4.1.6 运行</li></ul></li><li>4.2 功能完善<ul><li>4.2.1 生成的pdf需要支持中文<ul><li>4.2.1.1 程序中引入宋体</li><li>4.2.1.2 pdf工具类中增加使用中文字体</li><li>4.2.1.3 freemarker模板中使用中文字体</li><li>4.2.1.4 运行</li></ul></li><li>4.2.2 生成的pdf支持简单的样式<ul><li>4.2.2.1 freemarker模板中使用css样式</li><li>4.2.2.2 运行</li></ul></li><li>4.2.3 表格的某一行不要出现跨页<ul><li>4.2.3.1 freemarker模板中增加一个表格</li><li>4.2.3.2 查看效果</li><li>4.3.2.3 css解决</li><li>4.3.2.4 查看效果</li></ul></li><li>4.2.4 单独开启一页pdf<ul><li>4.2.4.1 freemarker模板修改</li><li>4.2.4.2 查看效果</li></ul></li><li>4.2.5 指定pdf页面的规格<ul><li>4.2.5.1 css样式指定页面规则</li><li>4.2.5.2 查看效果</li></ul></li><li>4.2.6 pdf 加密<ul><li>4.2.6.1 修改pdf生成的工具类</li><li>4.2.6.2 查看效果</li></ul></li></ul></li></ul></li><li>5、完整代码</li></ul></div><p></p>
<h1 id="1背景">1、背景</h1>
<p>最近项目中需要生成日报文件,日报文件的格式为<code>pdf</code>,且日报的样式相对而言比较复杂,存在多段文字,存在多个表格,且存在样式。目前想到的解决办法是<br>
<code>先生成html文件,让后将html文件转换成pdf文件</code>。通过网上搜索,发现<code>openhtmltopdf</code>可以实现我们的需求,此处记录一下。</p>
<h1 id="2需求">2、需求</h1>
<ol>
<li>生成的pdf需要支持中文。</li>
<li>生成的pdf支持简单的样式。(此处可以使用css样式来解决,但不是所有的css样式都支持)</li>
<li>生成的pdf存在表格,每行应完整地出现在同一页,不要一半在上一页、一半在下一页。</li>
<li>生成的pdf可以自己指定到分页,比如某个表格的数据渲染完之后,需要单独开启一页。</li>
<li>生成的pdf支持密码加密。</li>
<li>生成的pdf可以支持纸张规格,比如是A3还是A4,并且还可设置横向还是纵向。</li>
</ol>
<h1 id="3思路">3、思路</h1>
<p>1、html的生成,我们可以通过freemarker来实现。<br>
2、html转pdf,通过openhtmltopdf来实现。</p>
<h1 id="4实现步骤">4、实现步骤</h1>
<h2 id="41-搭建一个简单的工程">4.1 搭建一个简单的工程</h2>
<p><code>首先搭建一个简单的可运行的程序,可实现Freemarker渲染模板,然后生成pdf文件</code></p>
<h3 id="411-引入依赖">4.1.1 引入依赖</h3>
<pre><code class="language-xml">
&lt;dependencies&gt;
    &lt;dependency&gt;
      &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
      &lt;artifactId&gt;spring-boot-starter-web&lt;/artifactId&gt;
      &lt;version&gt;2.6.0&lt;/version&gt;
    &lt;/dependency&gt;
    &lt;!-- 模板引擎,用于渲染html --&gt;
    &lt;dependency&gt;
      &lt;groupId&gt;org.freemarker&lt;/groupId&gt;
      &lt;artifactId&gt;freemarker&lt;/artifactId&gt;
      &lt;version&gt;2.3.30&lt;/version&gt;
    &lt;/dependency&gt;
    &lt;!-- 用于将html转换成pdf --&gt;
    &lt;dependency&gt;
      &lt;groupId&gt;com.openhtmltopdf&lt;/groupId&gt;
      &lt;artifactId&gt;openhtmltopdf-pdfbox&lt;/artifactId&gt;
      &lt;version&gt;1.0.10&lt;/version&gt;
    &lt;/dependency&gt;
    &lt;dependency&gt;
      &lt;groupId&gt;org.projectlombok&lt;/groupId&gt;
      &lt;artifactId&gt;lombok&lt;/artifactId&gt;
      &lt;version&gt;1.18.36&lt;/version&gt;
    &lt;/dependency&gt;
&lt;/dependencies&gt;
</code></pre>
<h3 id="412-编写freemarker工具类">4.1.2 编写Freemarker工具类</h3>
<p>加载程序中<code>src/main/resources/templates/ftls</code>目录下的模板文件,然后渲染成html内容。</p>
<pre><code class="language-java">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);
    }

    /**
   * 根据模板名称和数据模型生成字符串
   *
   * @param templateName 模板名称
   * @param dataModel    数据模型
   * @return 生成的字符串
   */
    public static String processTemplate(String templateName, Map&lt;String, Object&gt; dataModel) {
      try {
            Template template = CONFIGURATION.getTemplate(templateName);
            StringWriter writer = new StringWriter();
            template.process(dataModel, writer);
            return writer.toString();
      } catch (Exception e) {
            log.error("解析模板出现问题", e);
      }
      return "";
    }
}
</code></pre>
<h3 id="413-编写pdf工具类">4.1.3 编写pdf工具类</h3>
<p><code>编写pdf工具类,用于将html内容渲染成pdf文件,此处只是简单实现,后期该类还需要修改</code></p>
<pre><code class="language-java">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) {
      // 设置响应头
      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);
      }
    }
}
</code></pre>
<h3 id="414-增加一个模板">4.1.4 增加一个模板</h3>
<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html lang="en"&gt;
    &lt;head&gt;
      &lt;meta charset="UTF-8" /&gt;
      &lt;title&gt;生成pdf&lt;/title&gt;
      &lt;style&gt;
            .main-title { text-align: center; font-size:25px; }
      &lt;/style&gt;
    &lt;/head&gt;
    &lt;body&gt;
      &lt;div class="main-title"&gt;${mainTitle}&lt;/div&gt;
    &lt;/body&gt;
&lt;/html&gt;
</code></pre>
<p>该模板中存在变量<code>mainTitle</code>,这个变量的值通过后台来赋值</p>
<h3 id="415-增加一个控制层">4.1.5 增加一个控制层</h3>
<pre><code class="language-java">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&lt;String, Object&gt; params = new HashMap&lt;&gt;(16);
      params.put("mainTitle", "这是一个标题 - " + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));

      // 渲染模板
      String htmlContent = FreemarkerUtils.processTemplate("pdf.ftl", params);
      // 生成pdf
      PdfUtils.generatePdf(htmlContent, response);
    }
}
</code></pre>
<p><code>注意:此处的mainTitle中存在中文,生产的Pdf会乱码待会儿在处理</code></p>
<h3 id="416-运行">4.1.6 运行</h3>
<p><img src="https://p0-xtjj-private.juejin.cn/tos-cn-i-73owjymdk6/8e261986014f4b4ba45ba6bf437fe4d1~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAgaHVhbjE5OTM=:q75.awebp?policy=eyJ2bSI6MywidWlkIjoiNDQ4MjU2NDc3NTAyMjk1In0%3D&amp;rk3s=e9ecf3d6&amp;x-orig-authkey=f32326d3454f2ac7e96d3d06cdbb035152127018&amp;x-orig-expires=1758946856&amp;x-orig-sign=eOy%2F%2FOSflV%2F4FItusOx3CjrhmH0%3D"><br>
可以看到可以正常的生成pdf了,但是中文乱码了。 至此我们一个简单的程序就搭建完成了,下面让我们来完善功能。</p>
<h2 id="42-功能完善">4.2 功能完善</h2>
<h3 id="421-生成的pdf需要支持中文">4.2.1 生成的pdf需要支持中文</h3>
<p>默认情况下生成的pdf,中文是乱码的,若需要解决这个问题,就需要引入中文字体。<code>此处我们使用宋体</code>。</p>
<h4 id="4211-程序中引入宋体">4.2.1.1 程序中引入宋体</h4>
<p>在程序的<code>src/main/resources/fonts</code>目录下,引入宋体(<code>simsun.ttf</code>)</p>
<p><img src="https://p0-xtjj-private.juejin.cn/tos-cn-i-73owjymdk6/f00cfac98a5b4f16b880af94d13ec719~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAgaHVhbjE5OTM=:q75.awebp?policy=eyJ2bSI6MywidWlkIjoiNDQ4MjU2NDc3NTAyMjk1In0%3D&amp;rk3s=e9ecf3d6&amp;x-orig-authkey=f32326d3454f2ac7e96d3d06cdbb035152127018&amp;x-orig-expires=1758946856&amp;x-orig-sign=FMcY9myy%2FiZ2fLOq%2B7o9%2B669A%2FY%3D"></p>
<h4 id="4212-pdf工具类中增加使用中文字体">4.2.1.2 pdf工具类中增加使用中文字体</h4>
<pre><code class="language-java">builder.useFont(() -&gt; PdfUtils.class.getClassLoader().getResourceAsStream("fonts/simsun.ttf"), "SimSun");
</code></pre>
<p><img src="https://p0-xtjj-private.juejin.cn/tos-cn-i-73owjymdk6/4b8d5aa983664df5b7fdbd8301abc7ba~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAgaHVhbjE5OTM=:q75.awebp?policy=eyJ2bSI6MywidWlkIjoiNDQ4MjU2NDc3NTAyMjk1In0%3D&amp;rk3s=e9ecf3d6&amp;x-orig-authkey=f32326d3454f2ac7e96d3d06cdbb035152127018&amp;x-orig-expires=1758946857&amp;x-orig-sign=7veme2PcV2wrWuYxEaxEzfUEsE0%3D"></p>
<h4 id="4213-freemarker模板中使用中文字体">4.2.1.3 freemarker模板中使用中文字体</h4>
<pre><code class="language-css">&lt;style&gt;
   body { font-family: "SimSun";font-size: 16px; line-height: 1.5; color: #000;}
&lt;/style&gt;
</code></pre>
<p><img src="https://p0-xtjj-private.juejin.cn/tos-cn-i-73owjymdk6/c193391475694f9698f7cc8fb2b9443b~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAgaHVhbjE5OTM=:q75.awebp?policy=eyJ2bSI6MywidWlkIjoiNDQ4MjU2NDc3NTAyMjk1In0%3D&amp;rk3s=e9ecf3d6&amp;x-orig-authkey=f32326d3454f2ac7e96d3d06cdbb035152127018&amp;x-orig-expires=1758946856&amp;x-orig-sign=aGuG3%2B10KqU0Zks7O1dUEGGkbiQ%3D"></p>
<h4 id="4214-运行">4.2.1.4 运行</h4>
<p><img src="https://p0-xtjj-private.juejin.cn/tos-cn-i-73owjymdk6/eaca0fd3755e4030b8b25184e559a569~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAgaHVhbjE5OTM=:q75.awebp?policy=eyJ2bSI6MywidWlkIjoiNDQ4MjU2NDc3NTAyMjk1In0%3D&amp;rk3s=e9ecf3d6&amp;x-orig-authkey=f32326d3454f2ac7e96d3d06cdbb035152127018&amp;x-orig-expires=1758946856&amp;x-orig-sign=E%2FrP2agyvcyC5cRWrdvWtvoJGtQ%3D"><br>
从上图中可以看到,现在已经可以展示中文了。</p>
<h3 id="422-生成的pdf支持简单的样式">4.2.2 生成的pdf支持简单的样式</h3>
<p>此处实现将生成的pdf中的 <code>这是一个标题-时间</code> 这句话的字体修改成<code>红色</code>。</p>
<h4 id="4221-freemarker模板中使用css样式">4.2.2.1 freemarker模板中使用css样式</h4>
<pre><code class="language-css">.main-title { text-align: center; font-size:25px; color:#FF0000; }
</code></pre>
<p><img src="https://p0-xtjj-private.juejin.cn/tos-cn-i-73owjymdk6/1d4029dc408b45cc9681950232028343~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAgaHVhbjE5OTM=:q75.awebp?policy=eyJ2bSI6MywidWlkIjoiNDQ4MjU2NDc3NTAyMjk1In0%3D&amp;rk3s=e9ecf3d6&amp;x-orig-authkey=f32326d3454f2ac7e96d3d06cdbb035152127018&amp;x-orig-expires=1758946856&amp;x-orig-sign=DjOFiRDASGsl%2FeZJqBPkzqEqJjw%3D"></p>
<h4 id="4222-运行">4.2.2.2 运行</h4>
<p><img src="https://p0-xtjj-private.juejin.cn/tos-cn-i-73owjymdk6/1dd1139cfffc4984869544a80fb75df8~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAgaHVhbjE5OTM=:q75.awebp?policy=eyJ2bSI6MywidWlkIjoiNDQ4MjU2NDc3NTAyMjk1In0%3D&amp;rk3s=e9ecf3d6&amp;x-orig-authkey=f32326d3454f2ac7e96d3d06cdbb035152127018&amp;x-orig-expires=1758946856&amp;x-orig-sign=YlcXAvu555Mr70udLdTU1WiCNLQ%3D"><br>
通过上图可知,样式已经生效了。</p>
<h3 id="423-表格的某一行不要出现跨页">4.2.3 表格的某一行不要出现跨页</h3>
<h4 id="4231-freemarker模板中增加一个表格">4.2.3.1 freemarker模板中增加一个表格</h4>
<pre><code class="language-html">&lt;style&gt;
   table { border-collapse: collapse; }
   td { border: 1px solid black; padding: 70px;}
&lt;/style&gt;

&lt;table&gt;
    &lt;tr&gt;&lt;td&gt;序号&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;1&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;2&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;3&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;4&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;5&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;
</code></pre>
<p><img src="https://p0-xtjj-private.juejin.cn/tos-cn-i-73owjymdk6/d96931971ba643f7b5fc452114f782b6~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAgaHVhbjE5OTM=:q75.awebp?policy=eyJ2bSI6MywidWlkIjoiNDQ4MjU2NDc3NTAyMjk1In0%3D&amp;rk3s=e9ecf3d6&amp;x-orig-authkey=f32326d3454f2ac7e96d3d06cdbb035152127018&amp;x-orig-expires=1758946856&amp;x-orig-sign=fvNHsukQZ3S1UU8x7rm%2FVe6LGxc%3D"></p>
<h4 id="4232-查看效果">4.2.3.2 查看效果</h4>
<p><img src="https://p0-xtjj-private.juejin.cn/tos-cn-i-73owjymdk6/6dd694fd895242e987483dd74c18319c~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAgaHVhbjE5OTM=:q75.awebp?policy=eyJ2bSI6MywidWlkIjoiNDQ4MjU2NDc3NTAyMjk1In0%3D&amp;rk3s=e9ecf3d6&amp;x-orig-authkey=f32326d3454f2ac7e96d3d06cdbb035152127018&amp;x-orig-expires=1758946856&amp;x-orig-sign=BazsLgcXczXZe7jIpKvBVcgbPT8%3D"><br>
从上图可以看到,生成的pdf,内容跨了2页,那么如何解决这个问题呢?<code>通过css样式解决</code></p>
<h4 id="4323-css解决">4.3.2.3 css解决</h4>
<pre><code class="language-css"> table { border-collapse: collapse; page-break-inside: auto;}
tr { page-break-inside: avoid;}
</code></pre>
<h4 id="4324-查看效果">4.3.2.4 查看效果</h4>
<p><img src="https://p0-xtjj-private.juejin.cn/tos-cn-i-73owjymdk6/11831e770c0048ee8b728b4c41554085~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAgaHVhbjE5OTM=:q75.awebp?policy=eyJ2bSI6MywidWlkIjoiNDQ4MjU2NDc3NTAyMjk1In0%3D&amp;rk3s=e9ecf3d6&amp;x-orig-authkey=f32326d3454f2ac7e96d3d06cdbb035152127018&amp;x-orig-expires=1758946856&amp;x-orig-sign=GzLHGjUhM4gxYsS9zfHbaOPvOoM%3D"></p>
<h3 id="424-单独开启一页pdf">4.2.4 单独开启一页pdf</h3>
<h4 id="4241-freemarker模板修改">4.2.4.1 freemarker模板修改</h4>
<p>通过<code>css</code>样式<code>page-break-before:always</code>开启新的一页pdf。<br>
<img src="https://p0-xtjj-private.juejin.cn/tos-cn-i-73owjymdk6/08c4add0487c412fbf58e80959c6e8fc~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAgaHVhbjE5OTM=:q75.awebp?policy=eyJ2bSI6MywidWlkIjoiNDQ4MjU2NDc3NTAyMjk1In0%3D&amp;rk3s=e9ecf3d6&amp;x-orig-authkey=f32326d3454f2ac7e96d3d06cdbb035152127018&amp;x-orig-expires=1758946856&amp;x-orig-sign=VI%2FZm0SqwtOCL4B2cCh2NfZFFz8%3D"></p>
<h4 id="4242-查看效果">4.2.4.2 查看效果</h4>
<p><img src="https://p0-xtjj-private.juejin.cn/tos-cn-i-73owjymdk6/04dcf3bfee614041b1c3bff9942ea769~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAgaHVhbjE5OTM=:q75.awebp?policy=eyJ2bSI6MywidWlkIjoiNDQ4MjU2NDc3NTAyMjk1In0%3D&amp;rk3s=e9ecf3d6&amp;x-orig-authkey=f32326d3454f2ac7e96d3d06cdbb035152127018&amp;x-orig-expires=1758946856&amp;x-orig-sign=qtI4QNLpT0mwRib%2FTr67Ym4dQJQ%3D"></p>
<h3 id="425-指定pdf页面的规格">4.2.5 指定pdf页面的规格</h3>
<p>默认情况是<code>A4 纵向</code>,现在我想修改成<code>A3 横向</code>。这个指定对所有的页面都生效,不可只对某一个页面生效,若想对某一个页面生效,可以生成多个pdf文件,然后进行pdf文件的合并操作。</p>
<h4 id="4251-css样式指定页面规则">4.2.5.1 css样式指定页面规则</h4>
<pre><code class="language-css">@page{ size:A3 landscape; }
</code></pre>
<h4 id="4252-查看效果">4.2.5.2 查看效果</h4>
<p><img src="https://p0-xtjj-private.juejin.cn/tos-cn-i-73owjymdk6/e3658f8e415f477ea3a9310b2151a4b5~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAgaHVhbjE5OTM=:q75.awebp?policy=eyJ2bSI6MywidWlkIjoiNDQ4MjU2NDc3NTAyMjk1In0%3D&amp;rk3s=e9ecf3d6&amp;x-orig-authkey=f32326d3454f2ac7e96d3d06cdbb035152127018&amp;x-orig-expires=1758946856&amp;x-orig-sign=kBwNIYdmsyfBTuAP5wA3k95XUcw%3D"><br>
从上图中可知 正好是A3横向</p>
<h3 id="426-pdf-加密">4.2.6 pdf 加密</h3>
<p>实现思路:<code>通过pdfbox生成加密的密码</code>,此处给默认密码<code>a0nin13s</code></p>
<h4 id="4261-修改pdf生成的工具类">4.2.6.1 修改pdf生成的工具类</h4>
<pre><code class="language-java">/**
* 生成带密码的 PDF 文件(用户密码 a0min13s)
*
* @param pdfTemplate HTML 模板字符串
* @param response    HTTP 响应
*/
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(内存)
    ByteArrayOutputStream temp = new ByteArrayOutputStream();
    try {
      PdfRendererBuilder builder = new PdfRendererBuilder();
      builder.useFont(() -&gt; 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();
      // 可选:禁止打印、复制等
      ap.setCanPrint(false);
      ap.setCanExtractContent(false);

      // 用户密码,所有者密码一样即可(也可设不同)
      StandardProtectionPolicy policy =
                // ownerPwduserPwd
                new StandardProtectionPolicy("a0min13s", "a0min13s", ap);
      // 128 位 AES
      policy.setEncryptionKeyLength(128);
      policy.setPermissions(ap);
      // 执行加密
      doc.protect(policy);
      // 写给浏览器
      doc.save(os);
      // 确保全部送出
      os.flush();
    } catch (IOException e) {
      log.error("PDF加密输出失败", e);
      throw new RuntimeException("PDF加密输出失败");
    }
}
</code></pre>
<h4 id="4262-查看效果">4.2.6.2 查看效果</h4>
<p><img src="https://p0-xtjj-private.juejin.cn/tos-cn-i-73owjymdk6/09028ca9a95847ffa929bc1203e0ac6c~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAgaHVhbjE5OTM=:q75.awebp?policy=eyJ2bSI6MywidWlkIjoiNDQ4MjU2NDc3NTAyMjk1In0%3D&amp;rk3s=e9ecf3d6&amp;x-orig-authkey=f32326d3454f2ac7e96d3d06cdbb035152127018&amp;x-orig-expires=1758946856&amp;x-orig-sign=miD5kuZ1QZ91MGv7iDCPJ%2BCI0cM%3D"></p>
<h1 id="5完整代码">5、完整代码</h1>
<p>https://gitee.com/huan1993/spring-cloud-parent/tree/master/pdf/openhtmltopdf</p>


</div>
<div id="MySignature" role="contentinfo">
    <p>本文来自博客园,作者:huan1993,转载请注明原文链接:https://www.cnblogs.com/huan1993/p/19113277</p><br><br>
来源:https://www.cnblogs.com/huan1993/p/19113277
頁: [1]
查看完整版本: Java中实现html转pdf