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

📅 2026/6/30 17:56:21
处方签的模板填充+PDF签名——一次医疗场景的打印设计
处方签的模板填充PDF签名——一次医疗场景的打印设计文章目录处方签的模板填充PDF签名——一次医疗场景的打印设计一、处方签不是普通的打印页面二、模板填充用$占位符替代硬编码三、合并单元格的边框陷阱四、签名的位置不是居中那么简单五、模板驱动的完整链路六、为什么不做HTML转PDF七、结语一、处方签不是普通的打印页面医疗系统里有一种特殊的东西叫处方签。它不是普通的表格打印——首先它有法律效力。一张处方签上必须有医师签名和药师签名缺一个就是废纸。其次它格式固定。药监局规定了处方签的版式和必填字段不能随便改。所以处方签的打印流程不是在屏幕上画几个框而是三步① XLS模板 → ② 填入患者药品数据 → ③ 签上医师和药师的名字 → PDF输出这个流程的每一步都有坑。我们一步步说。二、模板填充用$占位符替代硬编码处方签的格式是固定的但内容每次不同——患者姓名、药品名、用法用量。我们的做法是做一个Excel模板文件里面放占位符。比如$xm代表姓名$xb代表性别处方签模板.xls 内容 ┌──────────────────────────────┐ │ 患者姓名$xm │ │ 性别$xb │ │ 诊断$zd │ │ 药品$m1 | 用法$u1 │ │ 药品$m2 | 用法$u2 │ └──────────────────────────────┘代码里的print()方法遍历Excel的每个单元格遇到以$开头的就去Map里找对应的值if($.equals(firstChar)){colNametemp.substring(1);colValueString.valueOf(map.get(colName));// 替换单元格内容labelnewLabel(j,x,colValue,...);}这个设计的巧妙之处在于处方签格式变了改Excel模板就行一行代码不用动。药监局改了处方签版式把模板重新排版代码零改动。三、合并单元格的边框陷阱Excel模板里经常有合并单元格——比如药品那栏名称和用法两大列上面有个大标题处方明细。从Excel转到PDF时合并单元格的边框处理是最容易出错的地方。问题在哪Excel的合并单元格只有一个视觉上的大格子。实际上内部每个被合并的单元格都还有自己的格式。边框不会被自动继承——合并后的大格子下边框和右边框可能需要从被合并的最远单元格上取。for(Rangeobj:mergedCells){intrsobj.getTopLeft().getRow();intreobj.getBottomRight().getRow();intcsobj.getTopLeft().getColumn();intceobj.getBottomRight().getColumn();if(当前单元格是合并格的左上角){if(rsre){// 下边线要从最下面那行的同事格取}if(csce){// 右边线要从最右边那列的同事格取}}}然后是一个长长的 if-else 链条逐个组合判断哪条边该显示、哪条该隐藏——disableBorderSide(15)隐藏全部四边disableBorderSide(2)只隐藏下边……一共15种组合。这段代码看起来像是机器生成的。不是。它就是手工一行行写的——因为当初被合并单元格的边框问题折腾了无数次每个组合都是在实际打印中碰到的真实错误。四、签名的位置不是居中那么简单处方签上需要两个签名医师在右下角药师在左下角。这不是CSS里float:right能搞定的事。iText的PDF操作是基于坐标的——你得告诉它签名图片精确的 (x, y) 坐标。// 先加医师签名——右下角addWaterMarkIncludeWords(filename1,filename2,fileqm1,385,240);// 再加药师签名——左下角addWaterMarkIncludeWords(filename2,filename,fileqm2,110,240);addWaterMarkIncludeWords做三件事打开已有PDFPdfReader创建PdfStamper往已有PDF上盖内容把签名图片放在每一页的指定位置删掉原文件——这是关键签名后的PDF覆盖原PDF不留无签名版本PdfReaderreadernewPdfReader(inputFile);PdfStamperstampernewPdfStamper(reader,newFileOutputStream(outputFile));ImageimageImage.getInstance(imageFilePath);image.setAbsolutePosition(width,height);image.scalePercent(20f);for(inti1;itotal;i){understamper.getUnderContent(i);under.addImage(image);}stamper.close();newFile(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串起来就是一个完整的打印系统。