如何用TinyButStrong生成HTML并结合Dompdf创建PDF?

来源:JS脚本作者:鱼儿头衔:草根站长
导读:本期聚焦于鱼儿创作的《如何用TinyButStrong生成HTML并结合Dompdf创建PDF?》,敬请观看详情。把数据库里的动态数据导出成PDF,常见的做法是先拼HTML再转换。TinyButStrong作为轻量PHP模板引擎,可以在不侵入业务逻辑的前提下完成数据注入,Dompdf则负责把HTML渲染为PDF。这套组合适合发票、报表、合同等需要固定版式的场景,模板语法简单,不需要学习复杂的报表引擎。本文从安装配置讲起,演示如何编写模板、合并变量、循环输出表格数据,再把生成的HTML交给Dompdf处理。过程中会重点说明中文字体注册、CSS兼容和分页控制,这些都是实际项目里容易踩坑的地方。与直接拼接字符串相比,TBS的区块合并能让模板保持清晰,后端代码也更少。Dompdf虽然不支持JavaScript,但对常规HTML和CSS的解析已经足够,读完你可以搭建一个完整的HTML转PDF流程,并理解每个环节的作用。

将数据库中的订单、报表或合同数据导出为PDF,常见的做法是先由模板引擎生成结构完整的HTML,再交给渲染库转换。TinyButStrong(简称TBS)是一款轻量级PHP模板引擎,可以用简单的标签把PHP变量、循环和条件注入HTML模板;Dompdf则是一个PHP库,不依赖浏览器就能把HTML和CSS渲染成PDF文件。两者配合后,服务端可以批量生成排版统一的报价单、对账单等文档。本文以一个产品报价单为例,展示从安装、模板编写到PDF输出的完整过程。

如何用TinyButStrong生成HTML并结合Dompdf创建PDF?

一、TinyButStrong模板引擎基础与安装

TinyButStrong的核心思路是模板与数据分离。模板文件仍然是一个普通的HTML文件,只是内部可以使用TBS定义的变量标签和区块标记。PHP代码负责加载模板、准备数据并执行合并,最终得到完整的HTML字符串。这种设计让前端开发人员可以专注页面结构,后端开发人员只处理数据填充,维护成本比较低。

安装TBS可以通过Composer执行 composer require tinybutstrong/tinybutstrong,也可以从官网下载 tbs_class.php 后手动引入。对于使用Composer的项目,加载自动加载文件后即可实例化。下面是一个最简单的示例:

<?php
require_once 'vendor/autoload.php';

$TBS = new clsTinyButStrong;
$TBS->LoadTemplate('template.html');

$name = '张三';
$TBS->MergeField('name', $name);

$TBS->Show();
?>

对应的模板文件 template.html 可以写成这样:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>报价单</title>
</head>
<body>
    <p>客户姓名:[var.name]</p>
</body>
</html>

在上面的模板中,[var.name] 是TBS默认的变量占位符。当调用 MergeField('name', $name) 时,TBS会把模板中所有 [var.name] 替换为对应的值。TBS还支持数组和对象属性的访问,例如 [var.user.name] 可以直接输出嵌套数据。如果希望修改占位符的定界符,可以在初始化后调整配置,但在大多数项目中默认语法已经够用。

二、使用区块合并生成动态HTML

报价单通常包含一个产品列表,行数会随着订单内容变化。如果只用变量替换,很难处理重复的表格行。TinyButStrong提供了区块合并机制,通过在模板中标记区块起止位置,后端传入二维数组,TBS就会自动循环输出每一行。这个机制非常适合表格、列表、发票明细等结构。

先看模板部分。下面是一个产品表格,注意 <!-- Block products --> 和 <!-- End products --> 之间的 <tr> 会被循环输出:

<table border="1" cellpadding="6">
    <thead>
        <tr>
            <th>产品名称</th>
            <th>单价</th>
            <th>数量</th>
            <th>小计</th>
        </tr>
    </thead>
    <tbody>
        <!-- Block products -->
        <tr>
            <td>[products.name]</td>
            <td>[products.price]</td>
            <td>[products.qty]</td>
            <td>[products.subtotal]</td>
        </tr>
        <!-- End products -->
        </tbody>
</table>

后端PHP代码需要准备一个二维数组,并调用 MergeBlock 方法。数组的键名要与模板中的字段名对应:

<?php
$products = array(
    array('name' => '机械键盘', 'price' => 299, 'qty' => 2, 'subtotal' => 598),
    array('name' => '无线鼠标', 'price' => 89, 'qty' => 3, 'subtotal' => 267),
    array('name' => '显示器支架', 'price' => 159, 'qty' => 1, 'subtotal' => 159),
);

$TBS->MergeBlock('products', $products);
$TBS->Show();
?>

执行后,<tbody> 中会出现三行数据。TBS区块合并不仅支持简单的二维数组,还可以处理对象数组和多级嵌套区块。例如在主产品区块内部再嵌套一个规格区块,只需在模板中再写一层 Block specs 和 End specs,后端传入对应字段的数组即可。条件显示也常用:TBS允许在变量标签上附加 onshow 参数,例如 [onshow;if [products.price] > 100;then='高价';else='平价'],这样模板自身就能表达简单逻辑,不需要在PHP里拼装太多HTML片段。

三、Dompdf渲染PDF的完整流程

Dompdf是一个纯PHP的HTML到PDF转换库,安装命令为 composer require dompdf/dompdf。它的工作流程是:加载HTML字符串,解析DOM结构和CSS样式,计算元素位置,最后通过字体和绘图引擎输出PDF。Dompdf支持CSS 2.1的大部分属性以及一些CSS 3特性,但不支持JavaScript,也不适合渲染特别复杂的网页布局。对于常规的报价单、合同、报表,它的表现足够稳定。

把TBS生成的HTML交给Dompdf时,推荐先获取HTML字符串而不是直接输出。可以使用输出缓冲来捕获 Show() 的结果,这样能避免向浏览器发送多余内容。下面是完整示例:

<?php
require_once 'vendor/autoload.php';

use Dompdf\Dompdf;
use Dompdf\Options;

// 准备数据
$products = array(
    array('name' => '机械键盘', 'price' => 299, 'qty' => 2, 'subtotal' => 598),
    array('name' => '无线鼠标', 'price' => 89, 'qty' => 3, 'subtotal' => 267),
);

// 使用TinyButStrong生成HTML
$TBS = new clsTinyButStrong;
$TBS->LoadTemplate('template.html');
$TBS->MergeBlock('products', $products);

ob_start();
$TBS->Show();
$html = ob_get_clean();

// 配置Dompdf
$options = new Options();
$options->set('isRemoteEnabled', true);
$options->set('isHtml5ParserEnabled', true);

$dompdf = new Dompdf($options);
$dompdf->loadHtml($html);
$dompdf->setPaper('A4', 'portrait');
$dompdf->render();

// 在浏览器中预览,若需要下载则把Attachment改为1
$dompdf->stream('quotation.pdf', array('Attachment' => 0));
?>

代码中的 ob_start() 和 ob_get_clean() 用于捕获TBS输出的HTML,这是比较稳妥的做法。如果不想用输出缓冲,也可以直接访问TBS的源码属性,但不同版本可能存在差异,因此缓冲方式兼容性更好。setPaper 方法可以指定纸张大小和方向,常用参数包括 A4、letter 以及 portrait、landscape。stream 方法会把PDF发送到浏览器,第一个参数是文件名,第二个参数中 Attachment 设为 0 表示在浏览器内预览,设为 1 则强制下载。如果需要把PDF保存到服务器,可以改用 $dompdf->output() 获取二进制内容,再用 file_put_contents 写入磁盘。

四、中文字体与分页控制

中文乱码是Dompdf使用中最常见的问题之一。默认情况下,Dompdf带有的是DejaVu Sans等西文字体,无法正确显示中文。解决方法是在CSS中指定一个可用的中文字体,并确保Dompdf能够找到字体文件。可以通过 @font-face 引入自定义字体,也可以使用Dompdf自带的 load_font.php 脚本注册系统字体。对于线上环境,建议将字体文件放在项目目录中,然后通过相对路径或绝对路径引用。

下面是一个简单的字体声明示例,假设字体文件放在项目的 fonts 目录下:

@font-face {
    font-family: 'NotoSansCJKsc';
    src: url('/fonts/NotoSansCJKsc-Regular.otf');
    font-weight: normal;
    font-style: normal;
}
body {
    font-family: 'NotoSansCJKsc', sans-serif;
}

如果字体文件位于本地磁盘,还可以在Dompdf的选项中设置 chroot 或开启 isRemoteEnabled 来允许加载。但要注意,开启远程资源加载可能存在安全风险,只应在可信环境下使用。另外,CSS中的分页控制也有不少细节。Dompdf支持 page-break-before、page-break-after 和 page-break-inside 属性。对于表格,建议加上 tr { page-break-inside: avoid; },这样可以防止一行数据被分页切断。如果某个章节需要另起一页,可以给标题元素设置 page-break-before: always;。合理使用这些规则,能够显著提升生成PDF的排版质量。

除了字体和分页,图片路径也需要留意。当HTML中包含本地图片时,Dompdf默认会限制文件访问范围,需要在选项中配置 chroot 或使用完整路径。对于网络图片,可以开启 isRemoteEnabled,但响应时间会变长。实际项目中更推荐先把图片转为绝对路径或使用数据URI,减少渲染阶段的网络依赖。总之,TinyButStrong负责把动态数据准确填进HTML骨架,Dompdf负责把这个骨架变成排版稳定的PDF,两者之间的接口就是HTML字符串。理解了这条链路,后续无论是增加水印、页码还是签名图片,都可以在模板和CSS层面逐步扩展。

TinyButStrongDompdfHTML转PDF修改时间:2026-09-29 01:10:36

免责声明:已尽一切努力确保本网站所含信息的准确性。网站作品多为原创整理与精心创作,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们进行处理Email:chomcom@qq.com。
引用或转载本作品时,请注明当前出处:https://www.ipipp.com/html/0929/63208.html,基于非商业用途的前提下,欢迎转载或二创本作品。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。