一、引言:为什么需要这样一个引擎?(文档暂未写完,发布先存一下)
业务背景
之前工作中,我需要为储存细胞的用户自动生成储存证书。UI只提供了原型图和图片等素材,我需要按照原型图的样式,将数据动态填充进去,并生成单页PDF。
核心挑战是:证书上的文字行数不固定(例如细胞数量可变,字段可能为空),页面高度需要自适应。
例如下面这种,模版中包含固定元素(顶部、侧面、底部图片、证书标题)和动态区域(细胞列表,数量可变)。因此,证书的高度也是不确定的。
常规方案的痛点
常规的单模板做法(硬编码坐标、逐个元素绘制)在应付一个固定模板时,确实最直接、效率最高。但它的核心问题不是“能不能实现功能”,而是:
1、模板复用成本高:当时我需要支持两套不同的证书模板。如果按单模板思路,每套模板的坐标、样式都要写一套独立的绘制代码。模板越多,代码越臃肿,维护成本成倍增长。
2、布局与业务强耦合:布局逻辑(在哪画、用什么字体)和业务代码(取哪个字段)揉在一起。调整布局可能影响业务逻辑,业务变更也可能破坏布局。
3、扩展性差:新需求(如加背景、侧边框拉伸)必须修改核心绘制代码,容易引入新Bug。
我需要一个面向多模板、可扩展的方案让布局配置和业务代码解耦,而不是每来一个新模板就复制粘贴改一遍。做到:
一套引擎,多模板复用:新增模板只加配置,不改核心代码
布局与业务分离:字段只管数据,注解管展示
扩展点机制:背景、边框等特殊需求不侵入核心流程
二、整体设计思路
不依赖现成模板引擎,而是自研一套流式布局方案
现成的模板引擎主要有这几种:HTML转PDF、PDF表单填充、报表引擎。它们都能解决部分问题,但都有各自的局限性——HTML转PDF受CSS支持限制,表单填充只能做固定布局,报表引擎配置复杂且偏向数据分析场景。
而我主要面对的是证书场景:固定框架加动态内容,文字换行后高度自适应,多个列表垂直堆叠。现有的方案要么布局不可控,要么模板维护成本太高。
所以我选择自己用Java代码实现:用注解控制布局和样式,再结合反射动态计算高度和坐标。所有布局信息都内嵌在Java类里,不依赖外部模板文件本身。换模板只需要新增模版类并添加注解,不用动引擎的核心代码。这是针对我的业务场景最合适的做法。
核心思想
先测量:先提前计算好需要填充的高度,因为PDF创建页面时需要指定高度,而我们如果提前计算出实际的高度。后续放置元素时就完全无需考虑页面能否放下,只考虑坐标位置即可。
再绘制:高度计算出来之后,如果是位置固定的元素,直接根据注解上已有的文字坐标、字体字号、颜色;图片的坐标、宽高绘制即可。如果是列表元素,就使用游标逐个列表内元素累加计算出实际位置之后绘制。
布局模型
目前处理的情况只能是块级元素 + 垂直堆叠。也就是模版部分元素,内容和位置完全固定,不会因页面的填充而产生任何变化。其余可变元素只能是列表,并且列表内部、列表之间都是垂直排布。例如证书中既有细胞列表又有底部落款信息,那我就可以定义两个列表,一个代表细胞一个代表落款,再根据初始坐标和间距等信息动态计算出位置并绘制。
整体流程图
核心流程:
1、模板定义层:普通 Java 类通过注解声明字段的位置、字体、样式,以及是否为列表。
2、引擎核心层:分两阶段执行——Measure 阶段先测量内容总高度,Draw 阶段再用游标推进绘制。
3、扩展点层:前置处理用于自定义背景等,后置处理用于二次加工。
4、输出层:生成最终 PDF 文件,支持单页长文档或自动分页。