You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用Grails Rendering将HTML转换为PDF?

如何用Grails Rendering实现HTML到PDF的转换

我来帮你搞定用Grails Rendering插件把HTML转PDF的事儿,结合你提供的模板,一步步来操作:


1. 先安装Rendering插件

根据你的Grails版本选择对应的安装方式:

  • Grails 3.x+:在项目根目录的build.gradle中添加依赖:
    compile "org.grails.plugins:rendering:2.0.3"
    
  • Grails 2.x:在grails-app/conf/BuildConfig.groovy的plugins块中添加:
    plugins {
        runtime ":rendering:1.0.0"
    }
    

添加后刷新依赖,确保插件安装成功。

2. 完善你的HTML模板

把你提供的模板补全后,放在grails-app/views/[你的控制器名]/seldata.gsp路径下(比如控制器叫InvoiceController,就放在grails-app/views/invoice/seldata.gsp)。这里给你补全并优化了模板内容,方便数据渲染:

<!doctype html>
<html>
<head>
    <meta charset="utf-8">
    <title>template seldata</title>
    <style>
        /* 解决中文显示问题,需提前把中文字体文件(比如simhei.ttf)放在web-app/fonts目录 */
        @font-face {
            font-family: 'SimHei';
            src: url('${resource(dir: "fonts", file: "simhei.ttf")}');
            font-weight: normal;
            font-style: normal;
        }
        .invoice-box {
            max-width: 800px;
            margin: auto;
            padding: 30px;
            border: 1px solid #eee;
            box-shadow: 0 0 10px rgba(0, 0, 0, .15);
            font-size: 16px;
            line-height: 24px;
            font-family: 'SimHei', 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif;
            color: #555;
        }
        .invoice-box table {
            width: 100%;
            line-height: inherit;
            text-align: left;
            border-collapse: collapse;
        }
        .invoice-box table td {
            padding: 8px;
            vertical-align: top;
        }
        .invoice-box table tr.top table td.title {
            font-size: 45px;
            line-height: 45px;
            color: #333;
        }
        .invoice-box table tr.information td {
            padding-bottom: 20px;
        }
        .invoice-box table tr.item td {
            border-bottom: 1px solid #eee;
        }
        .invoice-box table tr.total td:nth-child(2) {
            border-top: 2px solid #eee;
            font-weight: bold;
        }
    </style>
</head>
<body>
    <div class="invoice-box">
        <table cellpadding="0" cellspacing="0">
            <tr class="top">
                <td colspan="2">
                    <table>
                        <tr>
                            <td class="title">
                                <!-- 可选:添加你的logo -->
                                <img src="${resource(dir: 'images', file: 'logo.png')}" style="width:100%; max-width:300px;" />
                            </td>
                            <td>
                                发票编号: ${invoiceNumber}<br>
                                生成日期: ${createDate.format("yyyy-MM-dd HH:mm")}<br>
                            </td>
                        </tr>
                    </table>
                </td>
            </tr>
            <tr class="information">
                <td colspan="2">
                    <table>
                        <tr>
                            <td>
                                客户名称: ${customer.name}<br>
                                联系地址: ${customer.address}<br>
                                联系电话: ${customer.phone}
                            </td>
                            <td>
                                开票方: 你的公司名称<br>
                                税号: 你的公司税号<br>
                                地址: 你的公司地址
                            </td>
                        </tr>
                    </table>
                </td>
            </tr>
            <tr class="heading">
                <td>商品名称</td>
                <td>金额</td>
            </tr>
            <!-- 循环渲染商品数据 -->
            <g:each in="${items}" var="item">
                <tr class="item">
                    <td>${item.name}</td>
                    <td>¥${item.amount}</td>
                </tr>
            </g:each>
            <tr class="total">
                <td></td>
                <td>总计: ¥${totalAmount}</td>
            </tr>
        </table>
    </div>
</body>
</html>

3. 编写控制器动作处理PDF生成

创建一个控制器(比如InvoiceController),注入PdfRenderingService,然后编写生成PDF的方法:

import grails.plugin.rendering.pdf.PdfRenderingService

class InvoiceController {
    // 自动注入PDF渲染服务
    PdfRenderingService pdfRenderingService

    def generatePdf() {
        // 1. 准备要传递给模板的业务数据(这里是示例,替换成你的真实数据)
        def model = [
            invoiceNumber: "INV-2024-0001",
            createDate: new Date(),
            customer: [
                name: "张三",
                address: "北京市朝阳区XX路XX号",
                phone: "138XXXXXXX"
            ],
            items: [
                [name: "商品A", amount: 100.00],
                [name: "商品B", amount: 200.00]
            ],
            totalAmount: 300.00
        ]

        // 2. 渲染指定模板为PDF字节数组
        def pdfBytes = pdfRenderingService.render(
            template: "/invoice/seldata", // 对应你的模板路径
            model: model,
            // 可选:设置PDF页面参数
            pluginContextPath: servletContext.contextPath,
            encoding: "UTF-8"
        )

        // 3. 设置响应头,让浏览器下载PDF文件
        response.contentType = "application/pdf"
        response.setHeader("Content-disposition", "attachment; filename=invoice_${invoiceNumber}.pdf")
        response.outputStream << pdfBytes
        response.outputStream.flush()
    }
}

4. 测试生成PDF

启动Grails应用,访问http://localhost:8080/invoice/generatePdf,浏览器就会自动下载生成好的PDF文件了。


常见注意事项

  • 中文显示问题:Flying Saucer(Rendering插件底层依赖)默认不支持中文字体,需要添加中文字体文件并在CSS中通过@font-face引入,模板中已经给出示例。
  • 样式兼容性:尽量使用内联样式或简单的CSS选择器,复杂CSS可能无法被Flying Saucer完全解析。
  • 图片路径:模板中的图片要使用Grails的${resource()}标签生成正确的路径,避免图片无法加载。

内容的提问来源于stack exchange,提问作者reasonzila

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 04:26:45