itext7 +thymeleaf实现html转pdf, 模板动态填值,解决linux环境兼容问题

前言:  springboot项目实现html转pdf功能,踩过一些坑,记录遇到的问题。 附java代码,html代码,字体库下载地址,可直接运行main方法,生成pdf。

1.导入需要使用的包

<!--itext7 html转pdf用到的包-->
<dependency>
    <groupId>com.itextpdf</groupId>
    <artifactId>html2pdf</artifactId>
    <version>3.0.0</version>
</dependency>
<dependency>
    <groupId>org.thymeleaf</groupId>
    <artifactId>thymeleaf</artifactId>
    <version>3.0.11.RELEASE</version>
</dependency>

2.转行工具类

package com.example.demo.controller;
import com.itextpdf.html2pdf.ConverterProperties;
import com.itextpdf.html2pdf.HtmlConverter;
import com.itextpdf.html2pdf.resolver.font.DefaultFontProvider;
import com.itextpdf.layout.font.FontProvider;
import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.springframework.core.io.ClassPathResource;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;
import java.io.*;

/**
 * @Author: yh
 * @Date: 2023/6/5 12:05
 * @Description:
 */
public class PdfItext {

    /**
     * @param templatePath 模板全路径
     * @param path         生成pdf文件夹
     * @param fileName     生成pdf文件名
     * @param context      填充参数
     */
    public static void htmlToPdf(String templatePath, String path, String fileName, Context context) {
        String linuxPath = PdfItext.getFileAbsolutePath(templatePath, "/mnt/pdf/templates/template1.html", true);
        ConverterProperties converterProperties = new ConverterProperties();
        FontProvider dfp = new DefaultFontProvider();
//      dfp.addSystemFonts(); //添加中文字体库 方式a   -window环境有自带字体 linux环境需要安装中文字体,可参考 https://www.zhihu.com/question/423159370/answer/2706867741
        //添加中文字体库 方式b 使用项目导入的字体库 (已验证linux环境不安装中文字体,能正常显示中文)
        dfp.addFont(getFileAbsolutePath("/templates/simhei.ttf", "/mnt/pdf/templates/simhei.ttf", false));
        converterProperties.setFontProvider(dfp);
        //将本地的模板转换成字符串
        String htmlStr = toHtmlString(new File(linuxPath));
        //模板参数替换
        String replaceHtmlStr = new TemplateEngine().process(htmlStr, context);
        File destDir = new File(path);
        if (!destDir.exists()) {
            destDir.mkdirs();
        }
        try (OutputStream out = new FileOutputStream(new File(path + fileName))) {
            HtmlConverter.convertToPdf(replaceHtmlStr, out, converterProperties);
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("pdf转换异常3{}"+ e);
        }
    }

    public static void main(String[] args) throws Exception {
        //模板内容填充参数
        Context context = new Context();
        context.setVariable("name2", "晨曦话java");
        context.setVariable("name1", "晨曦话三国");
        PdfItext.htmlToPdf("/templates/template.html", "/mnt/pdf/", "temp.pdf", context);
    }

    /**
     *  读取本地html文件里的html代码
     * @param file  File file=new File("文件的绝对路径")
     * @return
     */
    public static String toHtmlString(File file) {
        // 获取HTML文件流
        StringBuffer htmlSb = new StringBuffer();
        try {
            BufferedReader br = new BufferedReader(new InputStreamReader(
                    new FileInputStream(file), "utf-8"));
            while (br.ready()) {
                htmlSb.append(br.readLine());
            }
            br.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        // HTML文件字符串
        String htmlStr = htmlSb.toString();
        // 返回经过清洁的html文本
        return htmlStr;
    }

    /**
     * 解决linux下运行jar,无法加载jar包下文件(window环境能正常加载)
     *
     * @param templatePath 模板路径 传相对路径 比如 /templates/template.htm
     * @param createPath   生成路径  比如  /mnt/pdf/templates/template.html
     * @param isConvered  是否覆盖模板,默认false.  如模板变化,需覆盖生成
     * @return 返回的路径就是放在linux服务器上的文件路径
     */
    public static String getFileAbsolutePath(String templatePath, String createPath, boolean isConvered) {
        try {
            // 创建临时文件,获取jar里面的配置文件
            File file = new File(createPath);
            if (file.exists() && !isConvered) {
                return file.getAbsolutePath();
            }
            InputStream inputStream = null;
            try {
                ClassPathResource resource = new ClassPathResource(templatePath);
                inputStream = resource.getInputStream();
                byte[] buffer = new byte[inputStream.available()];
                inputStream.read(buffer);
                OutputStream outStream = new FileOutputStream(file);
                outStream.write(buffer);
                return file.getAbsolutePath();
            } finally {
                IOUtils.closeQuietly(inputStream);
            }
        } catch (Exception e) {
            System.out.println("FileUtil getFilePath Fail cause by:"+ e);
        }
        return null;
    }
}

3.原模板放在项目资源目录下:resources/templates  linux环境不能加载jar包下模板文件/templates/simhei.ttf,/templates/template1.html,解决方法,将模板文件生成在服务器磁盘指定位置,再读取,笔者生成在\mnt\pdf\templates目录下,如果html模板发生变化,需要覆盖生成,或者先删掉原来生成的,再生成。

 4.中文字体不显示问题,方式a. dfp.addSystemFonts()可加载中文字体库,window环境有自带的字体库; linux环境需要安装中文字体库 ,可参考 https://www.zhihu.com/question/423159370/answer/2706867741

  方式b  dfp.addFont("/templates/simhei.ttf") 使用项目导入的字体库(已验证linux环境不安装中文字体库也能正常线上中文)

  simhei.ttf字体网上下载资源比较多,比如,http://xiazaiziti.com/210356.html

5.生成pdf文件位置:D:\mnt\pdf\temp.pdf,效果:

6.感谢ui和前端大佬画的html文件,效果炸裂。由于需要显示的内容比较多, pdf有默认边距,会换页。以下代码调整边距,使其在一页纸显示@page{ margin:10pt 25pt 0pt 25pt;}

html代码:

<!DOCTYPE html>
<html lang="">
<head>
  <meta charset="utf-8" />
  <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
  <meta name="viewport" content="width=device-width,initial-scale=1.0" />
  <meta name="google" content="notranslate" />
    
  <meta http-equiv="expires" content="0">
  <link rel="icon" href="<%= BASE_URL %>topLogo.png">
  <title>智能管理平台</title>
</head>
<style>
  @page{ margin:10pt 25pt 0pt 25pt;}
  body{
    margin-top: 0px;
  }
  .body {
    width: 100%;
  }

  .body h1 {
    text-align: center;
    font-size: 25px;
    margin-top: 0px;
    margin-bottom: 10px;
  }

  .body h2 {
    font-size: 16px;
    margin: 5px 0px;
  }

  .body .info {
    width: 88%;
  }

  .body .info span {
    display: inline-block;
    font-size: 14px;
    line-height: 24px;
    margin-right: 50px;
  }

  .body .info span label {
    display: inline-block;
    text-align: right;
  }

  .table th {
    font-weight: 400;
  }

  .table td {
    text-align: center;
  }

  .tableJe tr td:first-child {
    text-align: left;
    padding-left: 20px;
  }

  .tableLc tr td:first-child {
    text-align: left;
    padding-left: 20px;
  }

  .tableJe tr td:last-child {
    text-align: right;
    padding-right: 20px;
  }
</style>

<body>
  <div class="body">
    <h1>表</h1>
    <div style="position: relative;">
      <h2 th:text='${name2}'></h2>
      <h2 th:text='${name1}' style="font-weight: 400; position: absolute; width: 100%; text-align: right;top: 0px;"></h2>
    </div>
    <h2>明细:</h2>
    <table class="table" style="width: 100%; border-collapse: collapse;font-size: 16px; line-height: 24px;" border="1">
      <tr style="background-color: #E9F5FF;">
        <th>金额(元)</th>
        <th>额(元)</th>
        <th>总金额(元)</th>
      </tr>
      <tr>
        <td th:text='${name2}'></td>
        <td ></td>
        <td ></td>
      </tr>
    </table>
    <table style="margin-top:13px; width: 100%; border-collapse: collapse;">
      <tr>
        <td style="width: 60%;padding-right: 20px;">
          <table class="table tableJe" style="width: 100%; border-collapse: collapse;font-size: 16px;line-height: 24px;"
            border="1">
            <tr style="background-color: #E9F5FF;">
              <th>额</th>
              <th style="width: 30%;">张</th>
              <th>金额(元)</th>
            </tr>
            <tr>
              <td>1</td>
              <td ></td>
              <td ></td>
            </tr>
            <tr>
              <td>2</td>
              <td >0</td>
              <td >0.00</td>
            </tr>
            <tr>
              <td>3</td>
              <td x></td>
              <td ></td>
            </tr>
            <tr>
              <td>4</td>
              <td ></td>
              <td></td>
            </tr>
            <tr>
              <td>(5)</td>
              <td ></td>
              <td ></td>
            </tr>
            <tr>
              <td>6</td>
              <td ></td>
              <td ></td>
            </tr>
            <tr>
              <td>7</td>
              <td></td>
              <td ></td>
            </tr>
            <tr>
              <td>8</td>
              <td></td>
              <td ></td>
            </tr>
            <tr>
              <td>9</td>
              <td ></td>
              <td ></td>
            </tr>
            <tr>
              <td>10</td>
              <td ></td>
              <td ></td>
            </tr>
            <tr>
              <td>11</td>
              <td ></td>
              <td ></td>
            </tr>
            <tr style="background-color: #FEE4E4;">
              <td>合</td>
              <td ></td>
              <td ></td>
            </tr>
          </table>
        </td>
        <td style="vertical-align: top;">
          <table class="table tableJe"
            style="width: 100%; border-collapse: collapse;font-size: 16px; line-height: 24px;" border="1">
            <tr style="background-color: #E9F5FF;">
              <th>AA</th>
              <th>金额</th>
            </tr>
            <tr>
              <td>a</td>
              <td></td>
            </tr>
            <tr>
              <td>a</td>
              <td ></td>
            </tr>
            <tr>
              <td>a</td>
              <td ></td>
            </tr>
            <tr>
              <td>a</td>
              <td ></td>
            </tr>
            <tr>
              <td>a</td>
              <td ></td>
            </tr>
            <tr>
              <td>a</td>
              <td ></td>
            </tr>
            <tr style="background-color: #FEE4E4;">
              <td>合计</td>
              <td ></td>
            </tr>
          </table>
        </td>
      </tr>
    </table>
    <div style="margin-top: 13px;">
      <table class="table tableJe" style="width: 100%; border-collapse: collapse;font-size: 16px;line-height: 24px;"
        border="1">
        <tr style="background-color: #E9F5FF;">
          <th>ZZ</th>
          <th>ZZ</th>
        </tr>
        <tr>
          <td>ZZ</td>
          <td></td>
        </tr>
        <tr>
          <td>ZZ</td>
          <td >0.00</td>
        </tr>
        <tr>
          <td>ZZ</td>
          <td >0.00</td>
        </tr>
        <tr>
          <td>ZZ</td>
          <td >0.00</td>
        </tr>
        <tr>
          <td>ZZ</td>
          <td >0.00</td>
        </tr>
        <tr>
          <td>ZZ</td>
          <td >0.00</td>
        </tr>
        <tr>
          <td>ZZ</td>
          <td >0.00</td>
        </tr>
        <tr>
          <td>ZZ</td>
          <td >0.00</td>
        </tr>
        <tr>
          <td>v</td>
          <td >0.00</td>
        </tr>
        <tr>
          <td>ZZ</td>
          <td >0.00</td>
        </tr>
        <tr style="background-color: #FEE4E4;">
          <td>ZZ</td>
          <td >0.00</td>
        </tr>
      </table>
    </div>
    <h2>ZZ:</h2>
    <table class="table tableLc" style="width: 100%; border-collapse: collapse;font-size: 16px;line-height: 24px;"
      border="1">
      <tr style="background-color: #FFF8EE;">
        <th>ZZ</th>
        <th>ZZ</th>
        <th>ZZ</th>
      </tr>
      <tr>
        <td>ZZ</td>
        <td >2023-06-01 23:59:59</td>
        <td ></td>
      </tr>
      <tr>
        <td>ZZ</td>
        <td ></td>
        <td ></td>
      </tr>
      <tr>
        <td>ZZ</td>
        <td ></td>
        <td >ww</td>
      </tr>
      <tr>
        <td>ZZ</td>
        <td ></td>
        <td ></td>
      </tr>
      <tr>
        <td>ZZ</td>
        <td ></td>
        <td >ww</td>
      </tr>
    </table>
  </div>
</body>
</html>

  • 4
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
好的,关于使用iText7HTML换为PDF解决中文乱码问题,我可以给你一些思路和代码示例。 首先,我们需要使用iText7的Core和pdfHTML模块,你可以在pom.xml中添加以下依赖: ```xml <dependency> <groupId>com.itextpdf</groupId> <artifactId>itext7-core</artifactId> <version>7.1.16</version> </dependency> <dependency> <groupId>com.itextpdf</groupId> <artifactId>html2pdf</artifactId> <version>3.0.2</version> </dependency> ``` 然后,我们需要使用pdfHTML模块提供的`HtmlConverter`类将HTML换为PDF。在换之前,我们需要设置一些PDF文档的属性,例如页边距和字体。 下面是一个简单的代码示例,可以将HTML字符串换为PDF文件并解决中文乱码问题: ```java import com.itextpdf.html2pdf.ConverterProperties; import com.itextpdf.html2pdf.HtmlConverter; import com.itextpdf.kernel.font.PdfFontFactory; import com.itextpdf.kernel.font.PdfFont; import com.itextpdf.kernel.geom.PageSize; import com.itextpdf.kernel.pdf.PdfDocument; import com.itextpdf.kernel.pdf.PdfWriter; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; public class HtmlToPdfConverter { public static void main(String[] args) throws IOException { // HTML字符串 String html = "<html><head><meta charset='utf-8'></head><body><p>你好,世界!</p></body></html>"; // 输出文件 File outputFile = new File("output.pdf"); FileOutputStream outputStream = new FileOutputStream(outputFile); // 创建PDF文档 PdfWriter writer = new PdfWriter(outputStream); PdfDocument pdfDocument = new PdfDocument(writer); pdfDocument.setDefaultPageSize(PageSize.A4); // 设置字体 PdfFont font = PdfFontFactory.createFont("STSongStd-Light", "UniGB-UCS2-H", false); ConverterProperties converterProperties = new ConverterProperties(); converterProperties.setFontProvider((familyName, encoding, size, style, color) -> font); // 将HTML换为PDF HtmlConverter.convertToPdf(new ByteArrayInputStream(html.getBytes()), pdfDocument, converterProperties); // 关闭流 outputStream.close(); } } ``` 在上面的代码中,我们使用了`PdfFontFactory`来创建指定字体的PDF字体对象。这里使用了华文宋体字体和UniGB-UCS2-H编码,你可以根据自己的需求进行调整。 另外,需要注意的是,如果你的HTML中包含中文字符,一定要在`<head>`标签中设置`<meta charset='utf-8'>`,这样就可以正确处理中文字符了。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值