java使用ITextRender(xhtmlrenderer)将html转为pdf下载(及遇到的一些坑)

本文记录了使用Java的ITextRenderer库将HTML转换为PDF的过程,包括在SpringBoot项目中实现该功能的步骤。内容涉及控制器、PDF模板、PDFUtil工具类的编写,以及HTML文件的注意事项。在实践中遇到的坑包括中文不显示、图片加载失败、CSS3样式失效、空值报错和PDF边框问题,并提供了相应的解决方案。文章最后分享了包含完整示例的代码下载链接。

1.最近项目中使用到了这个功能,所以就来记录一下,并且记录下使用过程中遇到的坑

2.首先需要引入依赖

        <dependency>
            <groupId>org.freemarker</groupId>
            <artifactId>freemarker</artifactId>
            <version>2.3.23</version>
        </dependency>       
        <dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>itextpdf</artifactId>
            <version>5.5.10</version>
        </dependency>
        <dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>itext-asian</artifactId>
            <version>5.2.0</version>
        </dependency>
        <dependency>
            <groupId>com.itextpdf.tool</groupId>
            <artifactId>xmlworker</artifactId>
            <version>5.5.11</version>
        </dependency>
        <dependency>
            <groupId>org.xhtmlrenderer</groupId>
            <artifactId>flying-saucer-pdf-itext5</artifactId>
            <version>9.0.6</version>
        </dependency>

3.我这里是自己简单搭了一个springboot的项目来进行示例讲解,所以首先是controller层的代码:

package com.example.demo;


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;


import java.util.HashMap;
import java.util.Map;

@RestController
@RequestMapping("/download")
public class download {
    @Autowired
    private PdfTemplateRenderImpl pdfTemplateRender;
    @RequestMapping("/d1")
    public ResponseEntity<byte[]> downloadTemplatex() throws Exception {
        HttpHeaders headers = new HttpHeaders();
        String filename = new String("ssk.pdf".getBytes("utf-8"), "iso-8859-1");
        headers.setContentDispositionFormData("attachment", filename);
        headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
        //xxxxService.queryXXXById(); 查询数据填充到html模板中,使用map存放
        Map<String,Object> dataMap=new HashMap<>();
        dataMap.put("name","张三丰");
        dataMap.put("age","18岁");
        dataMap.put("address","浙江省杭州市滨江区");
        dataMap.put("study","本科");
        dataMap.put("marry","未婚");
        byte[] bytes= pdfTemplateRender.render(dataMap,"person.html");
        ResponseEntity<byte[]> responseEntity = new ResponseEntity<>(bytes, headers, HttpStatus.CREATED);
        return responseEntity;
    }
}

4.然后是pdfTemplate

package com.example.demo;

import javax.annotation.PostConstruct;
import java.io.IOException;
import java.util.Map;
import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;

import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import org.springframework.stereotype.Service;

@Service
public class PdfTemplateRenderImpl {

    private Configuration configuration;

    @PostConstruct
    private void init() throws IOException {
        configuration = new Configuration(Configuration.VERSION_2_3_23);
        //download是存放html文件的目录,对该目录下的html文件进行加载
        configuration.setClassLoaderForTemplateLoading(this.getClass().getClassLoader(), "/download");
        configuration.setDefaultEncoding("UTF-8");
    }
    public  byte[] render(Map<String, Object> data, String templateFileName) throws Exception {

        String htmlContent = freeMarkerVar(data, templateFileName);

        return PdfUtil.htmlToPdf(htmlContent);

    }

    private String freeMarkerVar(Map<String, Object> data, String templateFileName) throws Exception {
        try {
            Template template = configuration.getTemplate(templateFileName);
            Writer writer = new StringWriter();
            template.process(data, writer);
            return writer.toString();
        } catch (IOException | TemplateException e) {
            throw new Exception("系统异常", e);
        }

    }
}

5.然后是PdfUtil相关代码

/**
 * Alipay.com Inc.
 * Copyright (c) 2004-2021 All Rights Reserved.
 */
package com.example.demo;

import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;

import com.itextpdf.text.pdf.BaseFont;
import org.xhtmlrenderer.pdf.ITextFontResolver;
import org.xhtmlrenderer.pdf.ITextRenderer;


/**
 * @author hanansheng
 * @version $Id: PdfUtil.java, v 0.1 2021年03月03日 下午4:56 hanansheng Exp $
 */
public class PdfUtil {

    /**
     * 将html 转化为 pdf
     *
     * @param htmlContent html utf-8编码
     * @return pdf文件字节流
     */
    public static byte[] htmlToPdf(String htmlContent) throws Exception {

        try {
            ITextRenderer renderer = new ITextRenderer();
            //解决中文不显示问题
            ITextFontResolver fontResolver = renderer.getFontResolver();
            fontResolver.addFont(PdfUtil.class.getClass().getResource("/test1/simhei.ttf").getPath(), BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
            renderer.setDocumentFromString(htmlContent);
            renderer.layout();
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            renderer.createPDF(outputStream);
            return outputStream.toByteArray();

        } catch (FileNotFoundException e) {
            throw new Exception("系统异常", e);
        } catch (Exception e) {
            throw new Exception("系统异常", e);
        }
    }
}

6.然后是我的html文件;注意这里html文件是解析为freemarker然后进行解析的

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8"/>
    <meta http-equiv="X-UA-Compatible" content="IE=edge"/>
    <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
    <title>个人情况</title>
    <style>
        body {
            font-family: SimHei;
        }
        .main{
            width: 960px;margin: 0 auto;
            color: rgba(0,0,0,0.85);
            position: relative;
            padding-top: 16px;
            padding-bottom: 25px;
        }
        .title {
            line-height: 40px;font-size: 28px;font-weight: bold;text-align: center;position: relative;
        }
        .table{
            width: 100%;
            border: 1px solid #696969;
            border-collapse: collapse;
            font-size: 12px;
        }
        .table td {
            height: 32px;
            border: 1px solid #ccc;
            text-align: center;
        }
        .table td:last-child{ width: 750px; text-align: left;padding-left: 16px; box-sizing: border-box;}
    </style>
</head>
<body>
<div class="main">
    <h1 class="title">
        <img src="https://xxxxxxxxx" style="width: 218px;height:30px;position: absolute;left:0;top: 0"/> 心系天下</h1>
    <table class="table">
        <tr style="border-bottom: 1px solid #696969">
            <td rowspan="6" style="max-width: 40px; background-color: #e0e0e0;color: #000">个<br/>人<br/>情<br/>况</td><td>姓名</td><td>${name!"/"}</td>
        </tr>
        <tr>
            <td>年龄</td> <td>${age!"/"}</td>
        </tr>
        <tr>
            <td>家庭住址</td><td>${address!"/"}</td>
        </tr>
        <tr>
            <td>学历</td><td>${study!"/"}</td>
        </tr>
        <tr>
            <td>婚姻情况</td><td>${marry!"/"}</td>
        </tr>
    </table>
    <image src="https:xxxxxxxxxxxxxxxxxxx"
           style="width: 75px;position: absolute;bottom: 0; right: -20px"/>
</div>

</body>
</html>

7.然后导出来的pdf如下图所示:

 容易出现的一些坑:

1.中文不显示问题,这个问题在pdfUtil类中我已经解决了,需要在html页面中设置字体,然后pdfUtil加载一个字体集xx.ttf文件

2.图片加载不出来问题,图片路径尽量使用http:xxxx连接,另外要使用img标签,不要使用image标签

3.在html中尽量不要使用css3的语法,否则下载出来的pdf可能对css3的样式会失效

4.html页面取值的时候,如果后台传过来是空,那么直接会报错,可以在页面进行一个控制,如果为空就显示斜杠/ ,如下图所示:

5.生成的pdf有白色边框的问题,有问题的形式如下:

这样有白色边框是有问题的,解决方法就是在html(ftl)文件中添加样式

@page:left{
   margin: 0cm;
} 
@page:right{
   margin: 0cm;
}

添加后完美解决边框问题,修改后展示如下:完美解决

演示的demo已经打包上传,需要的可以自行下载,地址:XItextRender将html文件转为pdf下载deno(解决中文不显示和图片等问题)javademo-Java文档类资源-CSDN下载

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

酒书

你的鼓励是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值