Freemarker+PDF修仙指南:从模板小厮到PDF生成大能的终极奥义

各位在HTML和PDF之间反复横跳的道友们!今天要解锁的是Freemarker+PDF这套组合仙术——用模板引擎生成动态HTML,再一键转化为专业PDF!从此告别手动调整页眉页脚的苦日子,让你的报表优雅如仙门典籍! 📜✨


一、筑基篇:核心法宝准备

1.1 依赖配置(Maven炼丹炉)
<!-- Freemarker模板引擎 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>

<!-- PDF转换神器(推荐openhtmltopdf) -->
<dependency>
    <groupId>com.openhtmltopdf</groupId>
    <artifactId>openhtmltopdf-core</artifactId>
    <version>1.0.10</version>
</dependency>
<dependency>
    <groupId>com.openhtmltopdf</groupId>
    <artifactId>openhtmltopdf-pdfbox</artifactId>
    <version>1.0.10</version>
</dependency>
1.2 基础目录结构
src/
├── main/
│   ├── resources/
│   │   ├── templates/
│   │   │   ├── pdf/
│   │   │   │   ├── report.ftl       # PDF模板文件
│   │   │   │   └── style.css        # PDF专用样式
│   │   └── fonts/                   # 中文字体必须!
│   │       └── simsun.ttf

二、金丹篇:模板设计心法

2.1 专用CSS(PDF炼体术)
/* style.css */
body {
    font-family: "SimSun"; /* 必须指定支持中文的字体 */
    font-size: 12pt;
    line-height: 1.5;
}

.page-break {
    page-break-after: always; /* 分页符 */
}

.table-pdf {
    width: 100%;
    border-collapse: collapse;
}

.table-pdf th {
    background-color: #f2f2f2;
    border: 1pt solid #ddd;
}
2.2 动态模板(report.ftl)
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <h1>${reportTitle}</h1>
    
    <!-- 分页示例 -->
    <div class="page-break"></div>
    
    <!-- 表格数据 -->
    <table class="table-pdf">
        <tr>
            <th>序号</th>
            <th>产品名</th>
            <th>价格</th>
        </tr>
        <#list products as item>
        <tr>
            <td>${item?index + 1}</td>
            <td>${item.name}</td>
            <td>¥${item.price?string("0.00")}</td>
        </tr>
        </#list>
    </table>
    
    <!-- 页脚 -->
    <div style="position: fixed; bottom: 0; width: 100%; text-align: center;">
        生成时间:${.now?string("yyyy-MM-dd HH:mm")}
    </div>
</body>
</html>

三、元婴篇:Java核心转换术

3.1 PDF生成器(点石成金)
import com.openhtmltopdf.pdfboxout.PdfRendererBuilder;
import org.springframework.ui.freemarker.FreeMarkerTemplateUtils;
import freemarker.template.Configuration;

public byte[] generatePdf(String templateName, Map<String, Object> data) throws Exception {
    // 1. 渲染HTML
    String html = FreeMarkerTemplateUtils.processTemplateIntoString(
        freemarkerConfig.getTemplate("pdf/" + templateName),
        data
    );

    // 2. 转换PDF
    try (ByteArrayOutputStream os = new ByteArrayOutputStream()) {
        PdfRendererBuilder builder = new PdfRendererBuilder();
        builder.useFont(new File("src/main/resources/fonts/simsun.ttf"), "SimSun");
        builder.withHtmlContent(html, "/"); // 设置基础路径以加载CSS
        builder.toStream(os);
        builder.run();
        return os.toByteArray();
    }
}
3.2 Spring控制器调用
@Controller
public class ReportController {
    
    @Autowired
    private Configuration freemarkerConfig;
    
    @GetMapping("/download-report")
    public void downloadReport(HttpServletResponse response) throws Exception {
        // 准备数据
        Map<String, Object> data = new HashMap<>();
        data.put("reportTitle", "2023年度销售报告");
        data.put("products", Arrays.asList(
            new Product("屠龙刀", 9999.99),
            new Product("倚天剑", 8888.88)
        ));
        
        // 生成PDF
        byte[] pdfBytes = generatePdf("report.ftl", data);
        
        // 输出到响应流
        response.setContentType("application/pdf");
        response.setHeader("Content-Disposition", "attachment; filename=report.pdf");
        response.getOutputStream().write(pdfBytes);
    }
}

四、化神篇:高级技巧

4.1 中文字体解决方案(必看!)
// 必须显式注册字体(否则中文显示为空白)
builder.useFontResolver(new FontResolver() {
    @Override
    public FontResource resolveFont(String family, int weight, String style) {
        try {
            File fontFile = new ClassPathResource("fonts/simsun.ttf").getFile();
            return new FontResource(fontFile.toURI().toURL(), weight, style);
        } catch (Exception e) {
            throw new RuntimeException("字体加载失败", e);
        }
    }
});
4.2 页眉页脚(宗门印记)
<!-- 在模板中添加 -->
<style>
    @page {
        @top-center {
            content: "${reportTitle} - 机密";
        }
        @bottom-right {
            content: "第 " counter(page) " 页";
        }
    }
</style>
4.3 复杂表格样式
/* 斑马线表格 */
.table-pdf tr:nth-child(even) {
    background-color: #f9f9f9;
}

/* 防止跨页断行 */
.table-pdf tr {
    page-break-inside: avoid;
}

五、大乘篇:性能优化

5.1 模板预加载(灵气压缩)
@PostConstruct
public void preloadTemplates() {
    // 启动时预加载模板
    freemarkerConfig.getTemplate("pdf/report.ftl");
}
5.2 缓存PDF(减少重复计算)
@Cacheable(value = "pdfReports", key = "#reportName")
public byte[] generateCachedPdf(String reportName, Map<String, Object> data) {
    // ...同前...
}

六、渡劫警示(常见天劫)

  1. 中文乱码

    • 确保:模板UTF-8编码 + 正确注册中文字体
    • 检查:CSS中font-family与注册字体名一致
  2. 分页异常

    • 使用page-break-before/after控制分页
    • 避免在表格/图片中间强制分页
  3. 样式失效

    • PDF引擎仅支持部分CSS属性(如不支持flex布局)
    • 推荐使用传统float+clear布局

飞升指南:最佳实践

  1. 模板设计
    • 使用pt作为单位(1pt=1/72英寸)
    • 固定尺寸使用mmcm
  2. 字体管理
    • 商业项目需购买正版字体
    • 推荐开源字体:思源宋体/黑体
  3. 版本控制
    • 将模板文件与CSS纳入Git管理
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

五行星辰

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

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

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

打赏作者

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

抵扣说明:

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

余额充值