动态生成HTML并转为PDF

1. 动态生成HTML并转为PDF

在Java中,利用Thymeleaf模板引擎生成动态HTML内容后,可以通过Flying Saucer库将其转换为PDF文件。这个过程将动态数据渲染到预定义模板中,适用于生成格式化良好的报告、发票等PDF文件。

实现思路:生成HTML内容 ----> 处理成String ----> 使用Flying Saucer库生成PDF。

1.1 依赖

<!-- Thymeleaf模板引擎依赖 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

<!-- Flying Saucer依赖(html -> pdf) -->
<dependency>
    <groupId>org.eclipse.birt.runtime.3_7_1</groupId>
    <artifactId>com.lowagie.text</artifactId>
    <version>2.1.7</version>
</dependency>
<dependency>
    <groupId>org.xhtmlrenderer</groupId>
    <artifactId>flying-saucer-pdf</artifactId>
    <version>9.1.18</version>
</dependency>
<dependency>
    <groupId>com.itextpdf</groupId>
    <artifactId>itextpdf</artifactId>
    <version>5.4.2</version>
</dependency>

1.2 实现步骤

1.2.1 实现参数接收类

首先,我们定义一个ParamEntity类,用来接收模板需要的数据。这个类包含了多种数据类型,并提供了格式化方法,如对金额的格式化显示。

package camellia.utilities.thymeleaf.model;

import lombok.Data;

import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;

/**
 * 参数实体类,包含多种数据类型。
 */
@Data
public class ParamEntity implements Serializable {
    private static final long serialVersionUID = 2971651722456178553L;

    // 基本类型
    private String name;
    private String value;

    // 数字类型
    private Integer intValue;
    private Long longValue;
    private BigDecimal decimalValue;

    // 布尔类型
    private Boolean isActive;

    // 日期时间类型
    private LocalDate dateValue;
    private LocalDateTime dateTimeValue;

    // 列表和数组类型
    private String[] tags;
    private List<String> interests;

    // 嵌套对象类型
    private ParamEntity relatedEntity;

    // 自定义格式化的值,例如金额格式
    public String getFormattedDecimalValue() {
        if (decimalValue != null) {
            return String.format("$%,.2f", decimalValue);
        }
        return null;
    }
}
1.2.2 设计HTML模版

然后,我们创建一个HTML模板来展示这些动态数据。利用Thymeleaf的表达式语言,可以直接在模板中插入对象的属性,生成一个格式化的HTML页面。

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8"/>
    <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
    <title>Thymeleaf 渲染测试</title>
    <style>
        body {
            font-family: 'SimSun', '宋体', sans-serif;
        }
    </style>
</head>
<body>
<h1>Thymeleaf 渲染测试示例</h1>

<h2>基本信息</h2>
<p><strong>名称:</strong> <span th:text="${paramEntity.name}">默认名称</span></p>
<p><strong>值:</strong> <span th:text="${paramEntity.value}">默认值</span></p>

<h2>数值类型</h2>
<p><strong>整数值:</strong> <span th:text="${paramEntity.intValue}">0</span></p>
<p><strong>长整型值:</strong> <span th:text="${paramEntity.longValue}">0</span></p>
<p><strong>小数值:</strong> <span th:text="${paramEntity.formattedDecimalValue}">0.00</span></p>

<h2>布尔值</h2>
<p><strong>是否启用:</strong> <span th:text="${paramEntity.isActive ? '' : ''}"></span></p>

<h2>日期与时间</h2>
<p><strong>日期:</strong> <span th:text="${paramEntity.dateValue}">2024-01-01</span></p>
<p><strong>日期时间:</strong> <span th:text="${paramEntity.dateTimeValue}">2024-01-01 12:00:00</span></p>

<h2>标签与兴趣</h2>
<p><strong>标签:</strong>
<ul>
    <li th:each="tag : ${paramEntity.tags}" th:text="${tag}">标签1</li>
</ul>
</p>
<p><strong>兴趣:</strong>
<ul>
    <li th:each="interest : ${paramEntity.interests}" th:text="${interest}">兴趣1</li>
</ul>
</p>

<h2>嵌套实体</h2>
<p><strong>相关实体名称:</strong> <span th:text="${paramEntity.relatedEntity.name}">默认名称</span></p>
<p><strong>相关实体值:</strong> <span th:text="${paramEntity.relatedEntity.value}">默认值</span></p>
<p><strong>相关实体是否启用:</strong> <span th:text="${paramEntity.relatedEntity.isActive ? '' : ''}"></span></p>
</body>
</html>

注意: 在HTML模板中,font-family: 'SimSun', '宋体', sans-serif; 用来指定字体格式。如果不明确指定字体,后续生成的PDF可能会导致中文字符无法正常渲染,显示为空。因此,确保选择一个支持中文的字体,并在HTML中正确指定它。

1.2.3 数据传递到Thymeleaf模板引擎

通过model.addAttribute("paramEntity", entity)ParamEntity对象传递给模板,然后通过Context对象传递给Thymeleaf引擎进行渲染。

model.addAttribute("paramEntity", entity);
Context context = new Context();
context.setVariables(model.asMap());

注意: 手动控制模板渲染时,灵活性更高,不必依赖Spring自动处理视图。

1.2.4 通过模板引擎渲染HTML

模板渲染后,得到的HTML是一个String类型,稍后可以转换成PDF。

String html = templateEngine.process("render", context);
1.2.5 生成PDF

我们利用Flying Saucer和iTextRenderer来将HTML转为PDF。需要注意的是,如果涉及中文字体,必须提供字体文件路径。

public static ByteArrayOutputStream htmlConvertPdf(String html, String fontPath) {
    if (fontPath == null || fontPath.isEmpty()) {
        throw new IllegalArgumentException("字体路径不能为空。");
    }
    ByteArrayOutputStream pdfStream = new ByteArrayOutputStream();
    ITextRenderer iTextRenderer = new ITextRenderer();
    try {
        iTextRenderer.setDocumentFromString(html);
        iTextRenderer.getFontResolver().addFont(fontPath, BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
        iTextRenderer.layout();
        iTextRenderer.createPDF(pdfStream);
    } catch (DocumentException | IOException e) {
        throw new RuntimeException("PDF转换过程中发生错误: " + e.getMessage(), e);
    }
    return pdfStream;
}

1.3 使用案例及效果

最后,我们编写控制器方法来接收数据并返回PDF文件。通过ResponseEntity包装PDF字节流,提供下载。

@PostMapping("/convert")
public ResponseEntity<byte[]> convert(@RequestBody ParamEntity entity, Model model) {
    model.addAttribute("paramEntity", entity);
    Context context = new Context();
    context.setVariables(model.asMap());
    String html = templateEngine.process("render", context);
    ByteArrayOutputStream pdfStream = HtmlConvertPdf.htmlConvertPdf(html , "font/simsun.ttc");
    return ResponseEntity.ok()
            .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=rendered.pdf")
            .contentType(MediaType.APPLICATION_PDF)
            .body(pdfStream.toByteArray());
}
  • 生成效果:

PDF效果

完整代码:

  1. 控制层代码(测试)
  2. 工具类
  3. HTML模版
  4. 参数接收类

代码可能有些许调整,simsun.ttc字体包自行下载


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值