欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 新闻 > 会展 > 手摸手系列之 Java 通过 PDF 模板生成 PDF 功能

手摸手系列之 Java 通过 PDF 模板生成 PDF 功能

2025/1/19 20:43:34 来源:https://blog.csdn.net/qq_26030541/article/details/145187117  浏览:    关键词:手摸手系列之 Java 通过 PDF 模板生成 PDF 功能

集团 SaaS 平台目前需要实现导出 PDF 格式的电子委托协议功能。业务方已经提供了一个现成的 PDF 文件作为参考。针对这一需求,我们有两个可行的方案:

  1. 完全代码生成:根据 PDF 文件的外观,完全通过代码动态生成 PDF 文件。
  2. 模板填充:将现有的 PDF 文件作为模板,仅需在代码中填充真实数据即可生成最终的 PDF 文件。

从实现效率和开发速度的角度来看,方案二(模板填充)无疑是更优的选择。它不仅能够大幅减少开发工作量,还能确保生成的 PDF 文件与业务方提供的模板完全一致,避免样式偏差。接下来,我们将重点探讨如何通过模板填充的方式实现这一功能。

一、PDF 模板制作

首先通过 PDF 编辑器制作 PDF 模板,这里我选用 Adobe Acrobat Pro 编辑表单来实现,这里我主要用到了表单的文本域和复选框。
工具我放云盘,需要的自取:https://caiyun.139.com/m/i?105CqcMLSgEyR 提取码:6ais
在这里插入图片描述
文本域的 name 对应 Java 中 model 类的属性。

二、前端编码

// html
<a-button v-has="'dec:down'" type="primary" icon="printer" :loading="printBatchLoading" @click="handlePrintBatch">批量打印</a-button>// JavaScript
/*** 批量打印*/
handlePrintBatch(){if (this.selectedRowKeys.length == 0) {this.$message.error('请选择至少一票数据!')return}let params = {}params.ids = this.selectedRowKeys.join(',')this.printBatchLoading = truelet fileName = ''if (this.selectedRowKeys.length > 1) {fileName = '电子委托协议批量导出.zip'} else {fileName = '电子委托协议导出' + (this.selectionRows[0].consignNo ? this.selectionRows[0].consignNo :this.selectionRows[0].id) + '.pdf'}downloadFile(this.url.exportElecProtocolBatch, fileName,params).then((res) => {if (res.success) {} else {this.$message.warn(`导出失败!${res.message}`)}}).finally(() => {this.printBatchLoading = false})
}/*** 下载文件* @param url 文件路径* @param fileName 文件名* @param parameter* @returns {*}*/
export function downloadFile(url, fileName, parameter) {return downFile(url, parameter).then((data) => {if (!data || data.size === 0) {Vue.prototype['$message'].warning('文件下载失败')return}if (typeof window.navigator.msSaveBlob !== 'undefined') {window.navigator.msSaveBlob(new Blob([data]), fileName)} else {let url = window.URL.createObjectURL(new Blob([data]))let link = document.createElement('a')link.style.display = 'none'link.href = urllink.setAttribute('download', fileName)document.body.appendChild(link)link.click()document.body.removeChild(link) //下载完成移除元素window.URL.revokeObjectURL(url) //释放掉blob对象}})
}

三、后端编码

/*** 批量打印电子委托协议** @param ids* @return org.jeecg.common.api.vo.Result<?>* @author ZHANGCHAO* @date 2025/1/16 08:54*/
@Override
public void exportElecProtocolBatch(String ids, HttpServletResponse response) {try {// 获取协议列表List<ElecProtocol> elecProtocolList = fetchProtocolsByIds(ids);if (isEmpty(elecProtocolList)) {throw new RuntimeException("未获取到电子委托协议数据");}if (elecProtocolList.size() == 1) {// 单个文件导出ElecProtocol protocol = elecProtocolList.get(0);Map<String, Object> data = prepareDataMap(protocol);byte[] pdfBytes = generatePdf(data);// 设置响应头String pdfFileName = URLEncoder.encode("电子委托协议_" + (isNotBlank(protocol.getConsignNo()) ? protocol.getConsignNo() : protocol.getId()) + ".pdf","UTF-8");response.setContentType("application/pdf");response.setHeader("Content-Disposition", "attachment; filename=" + pdfFileName);try (ServletOutputStream outputStream = response.getOutputStream()) {outputStream.write(pdfBytes);outputStream.flush();}} else {// 多个文件压缩成 ZIP 导出response.setContentType("application/zip");String zipFileName = URLEncoder.encode("电子委托协议导出.zip", "UTF-8");response.setHeader("Content-Disposition", "attachment; filename=" + zipFileName);try (ZipOutputStream zipOut = new ZipOutputStream(response.getOutputStream())) {for (ElecProtocol protocol : elecProtocolList) {Map<String, Object> data = prepareDataMap(protocol);byte[] pdfBytes = generatePdf(data);String pdfFileName = "电子委托协议_" +(isNotBlank(protocol.getConsignNo()) ? protocol.getConsignNo() : protocol.getId()) + ".pdf";ZipEntry zipEntry = new ZipEntry(pdfFileName);zipOut.putNextEntry(zipEntry);zipOut.write(pdfBytes);zipOut.closeEntry();}}}} catch (Exception e) {log.error("导出电子委托协议失败: {}", e.getMessage(), e);throw new RuntimeException("导出失败,请稍后再试");}
}/*** 获取数据** @param ids* @return java.util.List<org.jeecg.modules.business.entity.ElecProtocol>* @author ZHANGCHAO* @date 2025/1/16 16:24*/
private List<ElecProtocol> fetchProtocolsByIds(String ids) {return baseMapper.selectBatchIds(Arrays.asList(ids.split(",")));
}/*** 处理数据** @param elecProtocol* @return java.util.Map<java.lang.String, java.lang.Object>* @author ZHANGCHAO* @date 2025/1/16 16:23*/
private Map<String, Object> prepareDataMap(ElecProtocol elecProtocol) {Map<String, Object> map = new HashMap<>();map.put("consignorName", elecProtocol.getConsignorName());map.put("trusteeName", elecProtocol.getTrusteeName());map.put("gName", elecProtocol.getGName());if (isNotBlank(elecProtocol.getEntryId())) {map.put("entryId", "No." + elecProtocol.getEntryId());}map.put("codeTs", elecProtocol.getCodeTs());map.put("receiveDate", elecProtocol.getReceiveDate());map.put("ieDate", elecProtocol.getIeDate());map.put("billCode", elecProtocol.getBillCode());if (isNotBlank(elecProtocol.getTradeMode())) {List<DictModelVO> jgfs = decListMapper.getDictItemByCode("JGFS");List<DictModelVO> dictModelVO1=jgfs.stream().filter(i->i.getValue().equals(elecProtocol.getTradeMode())).collect(Collectors.toList());map.put("tradeMode", isNotEmpty(dictModelVO1) ? dictModelVO1.get(0).getText() : "");}map.put("qtyOrWeight", elecProtocol.getQtyOrWeight());map.put("packingCondition", elecProtocol.getPackingCondition());map.put("paperinfo", elecProtocol.getPaperinfo());if (isNotBlank(elecProtocol.getOriCountry())) {List<DictModel> dictModels2 = sysBaseApi.getDictItems("erp_countries,name,code");Map<String, String> dictMap2 = new HashMap<>();if (isNotEmpty(dictModels2)) {dictModels2.forEach(dictModel -> {dictMap2.put(dictModel.getValue(), dictModel.getText());});}if(dictMap2.containsKey(elecProtocol.getOriCountry())) {map.put("oriCountry", dictMap2.get(elecProtocol.getOriCountry()));}}if (isNotBlank(elecProtocol.getDeclarePrice())) {if (isNotBlank(elecProtocol.getCurr())) {// 币制List<DictModel> dictModels3 = sysBaseApi.getDictItems("erp_currencies,name,code,1=1");Map<String, String> dictMap3 = new HashMap<>();if (isNotEmpty(dictModels3)) {dictModels3.forEach(dictModel -> {dictMap3.put(dictModel.getValue(), dictModel.getText());});}if(dictMap3.containsKey(elecProtocol.getCurr())) {map.put("declarePrice", dictMap3.get(elecProtocol.getCurr()) + ": " + elecProtocol.getDeclarePrice() + "元");} else {map.put("declarePrice", elecProtocol.getDeclarePrice());}} else {map.put("declarePrice", elecProtocol.getDeclarePrice());}}map.put("otherNote", elecProtocol.getOtherNote());map.put("promiseNote", elecProtocol.getPromiseNote());String dateStr = DateUtil.format(new Date(), DatePattern.CHINESE_DATE_PATTERN);map.put("dateStr", dateStr);map.put("printTime", dateStr + " " + DateUtil.format(new Date(), DatePattern.NORM_TIME_PATTERN));// 处理选框逻辑String paperinfo = elecProtocol.getPaperinfo();if (isNotBlank(paperinfo) && paperinfo.length() == 6) {for (int i = 0; i < paperinfo.length(); i++) {if (paperinfo.charAt(i) == '1') {map.put("gou" + (i + 1), "On");}}}return map;
}/*** 生成PDF** @param data* @return byte[]* @author ZHANGCHAO* @date 2025/1/16 16:24*/
private byte[] generatePdf(Map<String, Object> data) throws Exception {try (ByteArrayOutputStream bos = new ByteArrayOutputStream()) {PdfReader reader = new PdfReader(this.getClass().getResourceAsStream("/templates/pdf/电子委托协议模板.pdf"));PdfStamper stamper = new PdfStamper(reader, bos);AcroFields form = stamper.getAcroFields();BaseFont bf = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.EMBEDDED);ArrayList<BaseFont> fontList = new ArrayList<>();fontList.add(bf);form.setSubstitutionFonts(fontList);for (Map.Entry<String, Object> entry : data.entrySet()) {if (entry.getKey().contains("gou")) {form.setField(entry.getKey(), isNotEmpty(entry.getValue()) ? entry.getValue().toString() : "", true);} else {form.setField(entry.getKey(), isNotEmpty(entry.getValue()) ? entry.getValue().toString() : "");}}stamper.setFormFlattening(true);stamper.close();return bos.toByteArray();}
}

同时支持导出单个和批量,单个是直接生成PDF文件,批量是打成压缩包。

四、效果展示

页面数据:
[图片]
生成的 PDF:
在这里插入图片描述

总结

总得来说,Java通过itext PDF模板生成PDF文件功能很简单,主要是数据的填充而已。还可以继续丰富下,比如多行文本、自动换行功能等。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com