处方签的模板填充+PDF签名——一次医疗场景的打印设计

处方签的模板填充+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 做三件事:

  1. 打开已有PDF(PdfReader
  2. 创建 PdfStamper(往已有PDF上盖内容)
  3. 把签名图片放在每一页的指定位置
  4. 删掉原文件------这是关键,签名后的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串起来,就是一个完整的打印系统。