处方签的模板填充+PDF签名------一次医疗场景的打印设计
文章目录
- 处方签的模板填充+PDF签名------一次医疗场景的打印设计
-
- 一、处方签不是普通的打印页面
- [二、模板填充:用占位符替代硬编码](#二、模板填充:用占位符替代硬编码)
- 三、合并单元格的边框陷阱
- 四、签名的位置:不是"居中"那么简单
- 五、模板驱动的完整链路
- 六、为什么不做HTML转PDF
- 七、结语
一、处方签不是普通的打印页面
医疗系统里有一种特殊的东西叫"处方签"。它不是普通的表格打印------首先,它有法律效力。一张处方签上必须有医师签名和药师签名,缺一个就是废纸。其次,它格式固定。药监局规定了处方签的版式和必填字段,不能随便改。
所以,处方签的打印流程不是"在屏幕上画几个框",而是三步:
① XLS模板 → ② 填入患者+药品数据 → ③ 签上医师和药师的名字 → PDF输出
这个流程的每一步都有坑。我们一步步说。
二、模板填充:用$占位符替代硬编码
处方签的格式是固定的,但内容每次不同------患者姓名、药品名、用法用量。我们的做法是做一个Excel模板文件,里面放占位符。比如 $xm 代表姓名,$xb 代表性别:
处方签模板.xls 内容:
┌──────────────────────────────┐
│ 患者姓名:$xm │
│ 性别:$xb │
│ 诊断:$zd │
│ 药品:$m1 | 用法:$u1 │
│ 药品:$m2 | 用法:$u2 │
└──────────────────────────────┘
代码里的 print() 方法遍历Excel的每个单元格,遇到以 $ 开头的就去Map里找对应的值:
java
if ("$".equals(firstChar)) {
colName = temp.substring(1);
colValue = String.valueOf(map.get(colName));
// 替换单元格内容
label = new Label(j, x, colValue, ...);
}
这个设计的巧妙之处在于:处方签格式变了,改Excel模板就行,一行代码不用动。药监局改了处方签版式?把模板重新排版,代码零改动。
三、合并单元格的边框陷阱
Excel模板里经常有合并单元格------比如"药品"那栏,名称和用法两大列上面有个大标题"处方明细"。从Excel转到PDF时,合并单元格的边框处理是最容易出错的地方。
问题在哪?Excel的合并单元格只有一个"视觉上"的大格子。实际上,内部每个被合并的单元格都还有自己的格式。边框不会被自动"继承"------合并后的大格子,下边框和右边框可能需要从被合并的最远单元格上取。
java
for (Range obj : mergedCells) {
int rs = obj.getTopLeft().getRow();
int re = obj.getBottomRight().getRow();
int cs = obj.getTopLeft().getColumn();
int ce = obj.getBottomRight().getColumn();
if (当前单元格是合并格的左上角) {
if (rs < re) {
// 下边线要从最下面那行的同事格取
}
if (cs < ce) {
// 右边线要从最右边那列的同事格取
}
}
}
然后是一个长长的 if-else 链条,逐个组合判断哪条边该显示、哪条该隐藏------disableBorderSide(15) 隐藏全部四边,disableBorderSide(2) 只隐藏下边......一共15种组合。
这段代码看起来像是机器生成的。不是。它就是手工一行行写的------因为当初被合并单元格的边框问题折腾了无数次,每个组合都是在实际打印中碰到的真实错误。
四、签名的位置:不是"居中"那么简单
处方签上需要两个签名:医师在右下角 ,药师在左下角。
这不是CSS里"float:right"能搞定的事。iText的PDF操作是基于坐标的------你得告诉它签名图片精确的 (x, y) 坐标。
java
// 先加医师签名------右下角
addWaterMarkIncludeWords(filename1, filename2, fileqm1, 385, 240);
// 再加药师签名------左下角
addWaterMarkIncludeWords(filename2, filename, fileqm2, 110, 240);
addWaterMarkIncludeWords 做三件事:
- 打开已有PDF(
PdfReader) - 创建
PdfStamper(往已有PDF上盖内容) - 把签名图片放在每一页的指定位置
- 删掉原文件------这是关键,签名后的PDF覆盖原PDF,不留无签名版本
java
PdfReader reader = new PdfReader(inputFile);
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(outputFile));
Image image = Image.getInstance(imageFilePath);
image.setAbsolutePosition(width, height);
image.scalePercent(20f);
for (int i = 1; i < total; i++) {
under = stamper.getUnderContent(i);
under.addImage(image);
}
stamper.close();
new File(inputFile).delete(); // 删除未签名版本
为什么签名必须放在 UnderContent(内容之下)而不是 OverContent(内容之上)?因为处方签的文字不能被签名图片遮挡。放下面,签名像是印在纸上的底纹------能看清签名,也能看清上面的处方内容。
五、模板驱动的完整链路
一条完整的处方签打印链路是这样的:
getfile(map, 医师签名.jpg, 药师签名.jpg)
├── print(map, "处方单.xls") → 填充模板,生成ByteArrayOutputStream
├── Workbook(流) → JXL读回内存
├── xlsToPDF(workbook, "v", f1) → 转PDF(纵向)
├── addWaterMark(医师签名) → 盖医师签名
├── addWaterMark(药师签名) → 盖药师签名
└── 返回pdf文件路径
模板是Excel,数据是Map,签名是图片,输出是PDF。每层都是独立步骤,改签名位置不需要动模板,改模板格式不需要动数据,改数据字段不需要动签名。
六、为什么不做HTML转PDF
很多人会问:用HTML画处方签,然后转PDF不是更灵活吗?
两个原因。第一,在医院里,Excel就是行业语言。药房的人、医保中心的人、药监局的检查员------他们用Excel排版处方签比用HTML熟练得多。第二,Excel转PDF能保住原始格式------字体、边框、对齐、合并单元格------一行一行地从JXL的CellFormat里抠出来,再用iText还原回去。HTML转PDF看似方便,但CSS的渲染结果在不同浏览器之间天差地别,兼容性问题更多。
选择Excel作为模板格式,本质上是一个**"让业务人员自己能维护"**的决策------改处方签版式不用找开发,自己调Excel就行。
七、结语
处方签的打印功能,代码不到700行。但它是医疗信息化里极少数**"不容忍任何格式错误"**的功能------边框差一条线,被药监局退回来;签名位置歪了3毫米,处方作废;字体大小不对,药品名称看不清,发药发错。
这套模板+填充+签名的三段式设计,核心是隔离变化:版式变化改Excel,数据变化改代码,签名变化改坐标。三个维度各自独立,互不干扰。
在今天看来这套方案没什么特别------模板引擎+PDF生成,任何一个报表引擎都能做。但在没有模板引擎、没有报表工具的2010年代,自己把JXL和iText串起来,就是一个完整的打印系统。