Java根据模版导出PDF文件

问题

工作中经常有这样的需求,将一些数据,导出为下图的PDF文件,那Java怎么做呢?今天手把手教你

准备模版

模版地址:https://download.csdn.net/download/ZHUSHANGLIN/91923381

修改模版使用AcrobatProPortable工具,在"编辑PDF"按钮修改模版样式,在"准备表单"按钮中修改模版中填充内容,需要工具的留言私发你

Java代码

首先添加依赖

xml 复制代码
 <!--PDF打印-->
<dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>itext-asian</artifactId>
            <version>5.2.0</version>
        </dependency>
        <dependency>
            <groupId>com.itextpdf.tool</groupId>
            <artifactId>xmlworker</artifactId>
            <version>5.5.13</version>
        </dependency>
        <dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>itextpdf</artifactId>
            <version>5.5.13</version>
            <scope>compile</scope>
        </dependency>
java 复制代码
public R CreatePDF(Tbmmu301Entity tbmmu301Entity,List<Tbmmu301aEntity> list,String user){
		String separator  = File.separator;//Windows :"\" Linux : "/"
		FileSystemView fsv = FileSystemView.getFileSystemView();
		// 模板路径
		String templatePath = "PDF/MMU791.pdf";
		//第一次生成的新文件路径,文件名
		String newPDFPath = fsv.getDefaultDirectory().toString()+separator+tbmmu301Entity.getIssueNo()+"_temp.pdf";
		// 最终生成的新文件路径,文件名
		String finalPDFPath = fsv.getDefaultDirectory().toString()+separator+"Proof of going out_"+tbmmu301Entity.getIssueNo()+".pdf";
		logger.info("MMU791物资出门证明打印PDF路径",finalPDFPath);
		//com.itextpdf.text.pdf
		PdfReader reader;
		FileOutputStream out;
		ByteArrayOutputStream bos;
		PdfStamper stamper;
		R<String> result = new R<>();
		try{
			logger.info("开始", DateUtil.formatDateTime(new Date()));
			out = new FileOutputStream(newPDFPath);// 输出流
			reader = new PdfReader(templatePath);// 读取pdf模板
			bos = new ByteArrayOutputStream();
			stamper = new PdfStamper(reader, bos);
			AcroFields form = stamper.getAcroFields();
			Map<String, String> PDFData = new HashMap<>();//存放需要填充的模板数据
			String isBack = "";
			String issueType = iDictValue.getDictValue("leaveDoorType",tbmmu301Entity.getIssueType());
			if(tbmmu301Entity.getIsBack().equals("Y")){
				isBack = "是";
			}else if(tbmmu301Entity.getIsBack().equals("N")){
				isBack = "否";
			}
			Map tbmmi001Map=iDictValue.getTbmma001ByCodeAndKey("compid_sort",tbmmu301Entity.getCompanyId());//VALUE_B存公司简称
			if(Func.isEmpty(tbmmi001Map)){
				return R.fail("打印失败,获取TBMMA001表-compid_sort当前公司信息失败");
			}
			String compShort=tbmmi001Map.get("VALUE_B")==null?"":tbmmi001Map.get("VALUE_B").toString();
			if(compShort.equals("")){
				return R.fail("打印失败,获取当前公司:"+tbmmu301Entity.getCompanyId()+"简称失败,请联系系统管理员至采购业务字典compid_sort,设定当前公司参数2");
			}
			String approveNode = "";
			approveNode=iGetApproveNodeClient.getApproveNode(tbmmu301Entity.getIssueNo());//45751 获取OA审批流程打印
			PDFData.put("companyName",compShort+"出门证明");
			PDFData.put("issueNo",tbmmu301Entity.getIssueNo());//申请单号
			PDFData.put("issueType",issueType);//出门类型
			PDFData.put("issueUser",tbmmu301Entity.getIssueUserId());//申请人
			PDFData.put("issueDept",tbmmu301Entity.getIssueDeptName());//申请部门
			PDFData.put("driverName",tbmmu301Entity.getDriverName());//司机姓名
			PDFData.put("issueTel",tbmmu301Entity.getIssueTel());//司机电话
			PDFData.put("carNo",tbmmu301Entity.getCarNo());//车牌
			PDFData.put("isBack",isBack);//是否返厂
			PDFData.put("memo",tbmmu301Entity.getMemo());//备注
			PDFData.put("approveNode",approveNode);//审批流程
			logger.info("PDFData",PDFData.toString());

			// 设置字体,不设置很可能,中文无法显示。
			//BaseFont bf = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
			// 检查文件是否存在
			String fontPath = "/fonts/simsunb.ttf";
			InputStream fontStream = getClass().getResourceAsStream(fontPath);
			if (fontStream == null) {
				throw new RuntimeException("字体文件未找到: " + fontPath);
			}
			ByteArrayOutputStream buffer = new ByteArrayOutputStream();
			byte[] data = new byte[1024];
			int nRead;
			while ((nRead = fontStream.read(data, 0, data.length)) != -1) {
				buffer.write(data, 0, nRead);
			}
			byte[] fontBytes = buffer.toByteArray();
			BaseFont bf = BaseFont.createFont("simsunb.ttf", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED, false, fontBytes, null);
			form.addSubstitutionFont(bf);
			// 数据查询后,值的植入 强烈推荐键值方式,其他方式很容易混
			Set<String> keySet = PDFData.keySet();
			for (String key : keySet) {//填充固定表单值
				form.setField(key, PDFData.get(key)==null?" ":PDFData.get(key));
			}
			logger.info("模板填充完成",DateUtil.formatDateTime(new Date()));
			stamper.setFormFlattening(true);// 如果为false那么生成的PDF文件还能编辑,一定要设为true
			stamper.close();
			Document doc = new Document();
			PdfCopy copy = new PdfCopy(doc, out);
			doc.open();
			PdfImportedPage importPage = copy.getImportedPage(new PdfReader(bos.toByteArray()), 1);
			copy.addPage(importPage);
			doc.close();//第一次生成的pdf,生成模板数据
			logger.info("模板PDF生成完成",DateUtil.formatDateTime(new Date()));
			// 填充子档数据
			generateFinalPdf(newPDFPath, finalPDFPath, list ,user);
			logger.info("上传开始",DateUtil.formatDateTime(new Date()));
			File pdfFile = new File(finalPDFPath);
			MultipartFile MultipartFile = getMultipartFile(pdfFile);
			Map<String,String> result1 = obsUtil.saveFile(MultipartFile.getOriginalFilename(),MultipartFile);
			if("200".equals(result1.get("code"))){

				String key = result1.get("key");;
				String fileName = "";
				fileName = MultipartFile.getOriginalFilename();
				System.err.println(fileName);
				if (fileName.contains("\\")) {//去掉反斜杠
					fileName = fileName.split("\\\\")[1];
				}
				String url = obsUtil.generatePresignedUrl(key);
				logger.info("PDF文件key",key);
				logger.info("修改后PDF文件name",fileName);
				result.setSuccess(true);
				result.setData(url);
				if (pdfFile.exists()) {
					boolean isDelete2 = pdfFile.delete();
					logger.info("第二个PDF是否删除:", String.valueOf(isDelete2));
				}
				logger.info("出门证明打印:",result.toString());
			}
			copy.close();
			reader.close();
			bos.close();
		}catch (Exception e){
			e.printStackTrace();
			logger.info("MMU791物资出门证明打印PDF异常",e.getMessage());
		}finally {
			//删除本地文件
			File pdfFile1 = new File(newPDFPath);
			if (pdfFile1.exists()) {
				boolean isDelete1 = pdfFile1.delete();
				logger.info("第一个PDF是否删除:", String.valueOf(isDelete1));
			}
		}
		return result;
	}

private void generateFinalPdf(String newPath, String finalPath, List<Tbmmu301aEntity> list, String user) throws Exception{
		FileOutputStream outputStream = new FileOutputStream(finalPath);
		PdfReader reader = new PdfReader(newPath);// 读取pdf模板

		Rectangle pageSize = reader.getPageSize(1);
		Document document = new Document(pageSize);
		PdfWriter writer = PdfWriter.getInstance(document, outputStream);
		document.open();
		PdfContentByte cbUnder = writer.getDirectContentUnder();
		PdfImportedPage pageTemplate = writer.getImportedPage(reader, 1);
		cbUnder.addTemplate(pageTemplate, 1, 1); //第一次生成的PDF填充到第二次生成的 位置
		//document.newPage();//新创建一页来存放后面生成的表格
		//package com.itextpdf.text;
		Paragraph paragraph2 = new Paragraph(new Phrase(" ")); //创建一个空的段,用于调整第二个段的位置
		Paragraph paragraph = generatePdfATATable(list,user);//此处为生成的表格及内容方法

		paragraph.setSpacingBefore(115);//不同模板设置不同距离
		//paragraph.setKeepTogether(false);//表示该段落必须放在一个页面上
		//新建一行
		//document.add(Chunk.NEWLINE);
		document.add(paragraph2);//增加块到文档
		document.add(paragraph);//增加块到文档
		// document.setPageSize(new Rectangle(100, 100));
		document.close();
		reader.close();
		writer.close();
	}


public Paragraph generatePdfATATable(List<Tbmmu301aEntity> list,String user) throws Exception{
		//BaseFont bfChinese = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
		// 检查文件是否存在
		String fontPath = "/fonts/simsunb.ttf";
		InputStream fontStream = getClass().getResourceAsStream(fontPath);
		if (fontStream == null) {
			throw new RuntimeException("字体文件未找到: " + fontPath);
		}
		ByteArrayOutputStream buffer = new ByteArrayOutputStream();
		byte[] data = new byte[1024];
		int nRead;
		while ((nRead = fontStream.read(data, 0, data.length)) != -1) {
			buffer.write(data, 0, nRead);
		}
		byte[] fontBytes = buffer.toByteArray();
		BaseFont bfChinese = BaseFont.createFont("/fonts/STSong-Light.ttf",BaseFont.IDENTITY_H , BaseFont.NOT_EMBEDDED);
		Font fontChinese = new Font(bfChinese, 15F, Font.NORMAL);// 设置PDF字体 五号
		//Paragraph ret = new Paragraph("附表1: 基线按ATA章节分类情况统计表", fontChinese); //表title
		Paragraph ret = new Paragraph("",fontChinese); //表title

		PdfPTable tableBox = new PdfPTable(11); //列数
		tableBox.setWidths(new int[] {
			10,17,17,17,17,17,17,17,17,17,17   //每个单元格占多宽
		});
		tableBox.setWidthPercentage(105f); //占PDF整个宽度的百分比
		tableBox.setPaddingTop(500);
		tableBox.setSplitRows(false);

		//tableBox.addCell(getTableCell(new Phrase("标题", fontChinese), false, 3, 1));//colspan:跨横着的格数 rowspan:跨竖着的格数
		tableBox.addCell(getTableCell(new Phrase("Serial Number", fontChinese), false, 1, 1));
		tableBox.addCell(getTableCell(new Phrase("Types", fontChinese), false, 1, 1));
		tableBox.addCell(getTableCell(new Phrase("Supplier Code", fontChinese), false, 1, 1));
		tableBox.addCell(getTableCell(new Phrase("Supplier Name", fontChinese), false, 1, 1));
		tableBox.addCell(getTableCell(new Phrase("Material Code", fontChinese), false, 1, 1));
		tableBox.addCell(getTableCell(new Phrase("Material Name", fontChinese), false, 1, 1));
		tableBox.addCell(getTableCell(new Phrase("Model", fontChinese), false, 1, 1));
		tableBox.addCell(getTableCell(new Phrase("Procurement Unit", fontChinese), false, 1, 1));
		tableBox.addCell(getTableCell(new Phrase("Stock Unit", fontChinese), false, 1, 1));
		tableBox.addCell(getTableCell(new Phrase("Ex-factory quantity", fontChinese), false, 1, 1));
		tableBox.addCell(getTableCell(new Phrase("Date of Departure", fontChinese), false, 1, 1));

		String tbmmu301Id="";
		for(int i=0;i<list.size();i++){
			Tbmmu301aEntity s = list.get(i);
			tableBox.addCell(getTableCell(new Phrase(String.valueOf(i+1), fontChinese), false, 1, 1));
			String inventoryType = "";
			if(Func.isNotEmpty(s.getInventoryType())){
				inventoryType = iDictValue.getDictValue("ItemGrade",s.getInventoryType());
			}
			tableBox.addCell(getTableCell(new Phrase(inventoryType, fontChinese), false, 1, 1));
			tableBox.addCell(getTableCell(new Phrase(s.getSupplierId(), fontChinese), false, 1, 1));
			tableBox.addCell(getTableCell(new Phrase(s.getSupplierName(), fontChinese), false, 1, 1));
			tableBox.addCell(getTableCell(new Phrase(s.getItemId(), fontChinese), false, 1, 1));
			tableBox.addCell(getTableCell(new Phrase(s.getItemName(), fontChinese), false, 1, 1));
			tableBox.addCell(getTableCell(new Phrase(s.getTypeSpec(), fontChinese), false, 1, 1));
			tableBox.addCell(getTableCell(new Phrase(s.getItemUnit(), fontChinese), false, 1, 1));
			tableBox.addCell(getTableCell(new Phrase(s.getInvUnit(), fontChinese), false, 1, 1));
			tableBox.addCell(getTableCell(new Phrase(ZoaCommonUtil.parseNumber(s.getPlanQty(),ZoaCommonUtil.Decimal.THREE), fontChinese), false, 1, 1));
			tableBox.addCell(getTableCell(new Phrase(s.getBroutDate(), fontChinese), false, 1, 1));
			tbmmu301Id=s.getTbmmu301Id()==null?"":s.getTbmmu301Id();
		}
		Tbmmu301Entity tbmmu301Entity=baseMapper.selectOne(new QueryWrapper<Tbmmu301Entity>().lambda().eq(Tbmmu301Entity::getTbmmu301Id,tbmmu301Id).eq(Tbmmu301Entity::getIsDeleted,0));
		//tableBox.addCell(getTableCell(new Phrase("testtest", fontChinese), false, 1, 1));
		tableBox.addCell(getCell(new Phrase("Printed by:", fontChinese), true, 2, 1));
		tableBox.addCell(getCell(new Phrase(user, fontChinese), false, 3, 1));
		tableBox.addCell(getCell(new Phrase(iPrintNumFeign.printNum(tbmmu301Entity.getCompanyId(),tbmmu301Entity.getIssueNo(),"MMU791"), fontChinese), false, 2, 1));
		tableBox.addCell(getCell(new Phrase("Printing time:", fontChinese), true, 2, 1));
		tableBox.addCell(getCell(new Phrase(DateUtil.formatDateTime(new Date()), fontChinese), false, 3, 1));

		ret.add(tableBox);
		ret.setLeading(90f);// 主档和子档的上下距离
		//ret.setPaddingTop(1000);
		return ret;
	}
相关推荐
麦兜*2 小时前
MongoDB 与 GraphQL 结合:现代 API 开发新范式
java·数据库·spring boot·mongodb·spring·maven·graphql
橘子132 小时前
C++实战:搜索引擎项目(二)
开发语言·c++·搜索引擎
shan&cen2 小时前
Day02 集合 | 30. 串联所有单词的子串、146. LRU 缓存、811. 子域名访问计数
java·数据结构·算法·缓存
ITMan彪叔2 小时前
Java MQTT 主流开发方案对比
java·后端
召摇2 小时前
Java 21到25的核心API演进总结
java·后端
赵谨言2 小时前
基于python人物头像的卡通化算法设计与实现
开发语言·经验分享·python
应用市场3 小时前
Qt C++ 图形绘制完全指南:从基础到进阶实战
开发语言·c++·qt
知其然亦知其所以然3 小时前
SpringAI 玩转 OCI GenAI:这次我们聊聊 Cohere 聊天模型
java·后端·spring