package com.ruoyi.service.impl; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; import com.itextpdf.text.*; import com.itextpdf.text.Font; import com.itextpdf.text.Image; import com.itextpdf.text.Rectangle; import com.itextpdf.text.pdf.*; import com.itextpdf.tool.xml.ElementList; import com.itextpdf.tool.xml.XMLWorker; import com.itextpdf.tool.xml.XMLWorkerFontProvider; import com.itextpdf.tool.xml.XMLWorkerHelper; import com.itextpdf.tool.xml.css.CssFile; import com.itextpdf.tool.xml.css.StyleAttrCSSResolver; import com.itextpdf.tool.xml.html.CssAppliers; import com.itextpdf.tool.xml.html.CssAppliersImpl; import com.itextpdf.tool.xml.html.Tags; import com.itextpdf.tool.xml.parser.XMLParser; import com.itextpdf.tool.xml.pipeline.css.CSSResolver; import com.itextpdf.tool.xml.pipeline.css.CssResolverPipeline; import com.itextpdf.tool.xml.pipeline.end.ElementHandlerPipeline; import com.itextpdf.tool.xml.pipeline.html.HtmlPipeline; import com.itextpdf.tool.xml.pipeline.html.HtmlPipelineContext; import com.ruoyi.common.core.domain.entity.SysDictData; import com.ruoyi.common.utils.DateUtils; import com.ruoyi.common.utils.PdfBaseWriter; import com.ruoyi.common.utils.StringUtils; import com.ruoyi.domain.ModelTestTask; import com.ruoyi.domain.Sample; import com.ruoyi.domain.TestRecords; import com.ruoyi.domain.vo.*; import com.ruoyi.mapper.SampleManagementMapper; import com.ruoyi.mapper.TaskMapper; import com.ruoyi.mapper.TestRecordsMapper; import com.ruoyi.service.PdfTemplateManagementService; import com.ruoyi.service.ReviewEnterpriseArchiveService; import com.ruoyi.system.mapper.SysDictDataMapper; import io.minio.MinioClient; import io.minio.ObjectWriteArgs; import io.minio.PutObjectArgs; import lombok.SneakyThrows; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.io.*; import java.lang.reflect.Field; import java.net.URL; import java.util.*; import java.util.List; import java.util.stream.Collectors; import java.util.stream.IntStream; /** * PDF模板管理Service业务层处理 * @author gxk */ @Service @Transactional public class PdfTemplateManagementServiceImpl implements PdfTemplateManagementService { @Value("${minio.bucketName}") private String bucketName; @Value("${minio.url}") private String minioEndpoint; @Value("${minio.accessKey}") private String accessKey; @Value("${minio.secretKey}") private String secretKey; @Autowired private ReviewEnterpriseArchiveService reviewEnterpriseArchiveService; @Autowired private TaskMapper taskMapper; @Autowired private SysDictDataMapper dictDataMapper; @Autowired private SampleManagementMapper sampleManagementMapper; @Autowired private TestRecordsMapper testRecordsMapper; /** * 检验报告PDF下载 * * @param taskId * @return */ @SneakyThrows @Override public String generateInspectionReport(Long taskId) { PdfReader reader = null; PdfStamper ps = null; ByteArrayOutputStream bos = null; // 模板绝对路径--服务器 String fileName = "/template/receipt_template_04_02.pdf"; // 读取现有模板内容 reader = new PdfReader(fileName); // 创建输出流 bos = new ByteArrayOutputStream(); // 实例化PdfStamper准备编辑pdf内容 ps = new PdfStamper(reader, bos); // 获取表单所有元素 AcroFields fields = ps.getAcroFields(); // 设置具体字体格式的编码, 不设置的话中文可能不会显示 BaseFont bf = BaseFont.createFont("/fonts/STSong.TTF", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED); fields.addSubstitutionFont(bf); // 获取真实的数据 InspectionReportPdfVO inspectionReportPdfVO = requireData(taskId); // 渲染数据 renderData(fields, inspectionReportPdfVO, ps); // 必须要调用这个,否则文档不会生成的 ps.setFormFlattening(true); // 关闭 ps.close(); bos.close(); reader.close(); // 把第一个模板填入数据后生成一个oss String templateName = uploadMinio(bos, "整车信息安全检验临时模板"); // 创建一个空PDF, 融合两个PDF Document document = new Document(); ByteArrayOutputStream allOut = new ByteArrayOutputStream(); PdfCopy copy = new PdfCopy(document, allOut); document.open(); // 第一个PDF PdfReader templateReader = new PdfReader(minioUrl() + templateName); int numberPage = templateReader.getNumberOfPages(); for (int i = 1;i < numberPage;i++) { PdfImportedPage importedPage = copy.getImportedPage(templateReader, i); copy.addPage(importedPage); } // 第二个PDF String codeName = getInspectionReportCode(inspectionReportPdfVO); PdfReader codeReader = new PdfReader(minioUrl() + codeName); for (int i = 1; i <= codeReader.getNumberOfPages(); i++) { copy.addPage(copy.getImportedPage(codeReader, i)); } document.close(); return uploadMinio(allOut, "整车信息安全检验报告-" + getReportName()); } /** * 检验报告 - 代码渲染部分 * @param data * @return */ @SneakyThrows private String getInspectionReportCode(InspectionReportPdfVO data) { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); PdfBaseWriter document = new PdfBaseWriter(outputStream); document.open(); // 代码渲染 document.setParagraph("报告编号:" + data.getReportNumber(), 10.5f, Element.ALIGN_RIGHT, 0,10f, 0f); document.setParagraph("检 验 报 告", 22f, Element.ALIGN_CENTER, 0,0f, 0f); PdfPTable signTable = new PdfPTable(new float[]{65, 20}); signTable.setSpacingBefore(5); signTable.setWidthPercentage(85); document.writeBottomCell("共 10 页", Element.ALIGN_RIGHT, 1, 1, signTable).setRightIndent(30f); document.writeBottomCell("第 10 页", Element.ALIGN_RIGHT, 1, 1, signTable); document.addContent(signTable); document.setParagraph("7.附录", 10.5f, Element.ALIGN_LEFT, 45,1f, 10f); // 添加内容到新页面 addSamplePhoto(document, data.getSamplePhotos()); document.setParagraph("图 1 样品照片", 10.5f, Element.ALIGN_CENTER, 0,20f, 0f); document.setParagraph("图 2 试验照片", 10.5f, Element.ALIGN_CENTER, 0,20f, 0f); // 关闭PDF document.close(); return uploadMinio(outputStream, "整车信息安全检验临时代码"); } /** * minio对象存储查看图片地址 * @return */ private String minioUrl() { return minioEndpoint + "/" + bucketName + "/"; } private String getReportName() { return DateUtils.parseDateToStr("yyyyMMddHHmm", new Date()); } /** * 企业留档文件PDF下载 * @param taskId * @return * @throws Exception */ @Override public String generateRetentionFile(Long taskId) throws Exception { // 活数据查询 EnterpriseFilePdfVO data = getEnterpriseFileData(taskId); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); // 新建 PdfBaseWriter document = new PdfBaseWriter(outputStream); document.setMargins(40, 40, 50,40); PdfWriter writer = PdfWriter.getInstance(document, outputStream); MyHeaderFooter headerFooter = new MyHeaderFooter(); headerText = data.getInspectionCenterName(); writer.setPageEvent(headerFooter); // 打开 document.open(); // 内容 // 封面 document.setParagraph(" ", 30f, Element.ALIGN_CENTER, 0,60f, 40f); document.setParagraph("企业留档文件", 30f, Element.ALIGN_CENTER, 0,60f, 180f); PdfPTable customTable = new PdfPTable(new float[]{15, 50}); customTable.setWidthPercentage(70); document.writeNoBorderCell("检验依据:", Element.ALIGN_LEFT, 1, 1, customTable); document.writeBottomCell(data.getInspectionBasis(), Element.ALIGN_LEFT, 1, 1, customTable); document.writeNoBorderCell("检验项目:", Element.ALIGN_LEFT, 1, 1, customTable); document.writeBottomCell(data.getInspectionItem(), Element.ALIGN_LEFT, 1, 1, customTable); document.writeNoBorderCell("创建时间:", Element.ALIGN_LEFT, 1, 1, customTable); document.writeBottomCell(data.getCreateTime(), Element.ALIGN_LEFT, 1, 1, customTable); document.writeNoBorderCell("委托单位名称:", Element.ALIGN_LEFT, 1, 1, customTable); document.writeBottomCell(data.getClientName(), Element.ALIGN_LEFT, 1, 1, customTable); document.addContent(customTable); document.setParagraph(data.getInspectionCenterName(), 20f, Element.ALIGN_CENTER, 0, 100f, 0f); // 下一页 document.newPage(); // 创建表格 List<String> list = Arrays.asList("文件名称", "文件目录", "标准章节", "标准要求", "审查要点"); float[] columnWidths = {30, 25, 25, 60, 60}; PdfPTable table = document.createWithHeaderTable(list, columnWidths); // 查询数据 List<ReviewEnterpriseArchiveViewVO> viewList = reviewEnterpriseArchiveService.view(taskId); for (ReviewEnterpriseArchiveViewVO view : viewList) { if (view.getItems().size() > 0) { document.writeCell(view.getFileName(), Element.ALIGN_LEFT, 1, view.getItems().size(), table); view.getItems().forEach(item -> { document.writeCell(item.getCatalogue(), Element.ALIGN_LEFT, 1, 1, table); document.writeCell(item.getChapter(), Element.ALIGN_LEFT, 1, 1, table); document.writeCell(item.getStandard(), Element.ALIGN_LEFT, 1, 1, table); document.writeCell(item.getKeyPoint(), Element.ALIGN_LEFT, 1, 1, table); }); } } document.addContent(table); // 最后结尾签字 PdfPTable signTable = new PdfPTable(new float[]{40, 15}); signTable.setWidthPercentage(100); signTable.setSpacingBefore(40); document.writeNoBorderCell("车企负责人签字:", Element.ALIGN_RIGHT, 1, 1, signTable); document.writeBottomCell("", Element.ALIGN_RIGHT, 1, 1, signTable); document.addContent(signTable); // 关闭 document.close(); // 上传到Minio return uploadMinio(outputStream, "企业留档文件-" + getReportName()); } /** * 原始记录PDF下载 * @param taskId * @return * @throws Exception */ @Override public String generateOriginalRecord(Long taskId) throws Exception{ // 样品数据 Sample sample = sampleManagementMapper.selectSampleByTaskId(taskId); // 测试用例 List<TestRecords> recordsList = testRecordsMapper.selectListByGeneralTaskId(taskId); // 查询本任务对应的所有样品编号、样品照片 InspectionReportPdfVO reportData = taskMapper.selectInspectionReportData(taskId); // 查询检验负责人 InspectionPersonnelVO inspectionPersonnel = taskMapper.selectInspectionPersonnelById(taskId); // 创建临时文档 String templateName = temporaryDocument(sample, recordsList, reportData, inspectionPersonnel); PdfReader pdfReader = new PdfReader(minioUrl() + templateName); totalNum = pdfReader.getNumberOfPages() - 1; // 真正文档 String currentName = temporaryDocument(sample, recordsList, reportData, inspectionPersonnel); // copy - 分割 Document currentDoc = new Document(); ByteArrayOutputStream arrayOutputStream = new ByteArrayOutputStream(); PdfCopy copy = new PdfCopy(currentDoc, arrayOutputStream); currentDoc.open(); PdfReader templateReader = new PdfReader(minioUrl() + currentName); int numberOfPages = templateReader.getNumberOfPages(); for (int i = 2; i <= numberOfPages; i++) { PdfImportedPage page = copy.getImportedPage(templateReader, i); copy.addPage(page); } currentDoc.close(); templateReader.close(); copy.close(); return uploadMinio(arrayOutputStream, "车型审查原始记录-" + getReportName()); } /** * 创建临时文档 * @param sample 样品信息 * @param recordsList 定时任务接收数据List * @param reportData * @param inspectionPersonnel 检验人员信息 * @return * @throws Exception */ @SneakyThrows private String temporaryDocument(Sample sample, List<TestRecords> recordsList, InspectionReportPdfVO reportData, InspectionPersonnelVO inspectionPersonnel) throws Exception { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); PdfBaseWriter document = new PdfBaseWriter(outputStream); document.setMargins(50, 50, 85, 40); PdfWriter writer = PdfWriter.getInstance(document, outputStream); OriginalRecordHeaderFooter headerFooter = new OriginalRecordHeaderFooter(); writer.setPageEvent(headerFooter); // open document.open(); document.setParagraph(" ", 10f, Element.ALIGN_LEFT, 0,2f, 10f); document.newPage(); // 内容 // 1.检验依据 inspectionBasisSection(document); // 2.样品情况 sampleConditionSection(document, sample); // 3.仪器设备信息 instrumentInformationSection(document); // 第一页下方信息 bottomOfTheFirstPage(document, writer,reportData, inspectionPersonnel); // 4.检验结果(循环遍历用例) testResultSection(document, recordsList, reportData, inspectionPersonnel); // 5.附录 document.setParagraph("5.附录", 10f, Element.ALIGN_LEFT, 0,2f, 0f); // 样品照片 addSamplePhoto(document, reportData.getSamplePhotos()); document.setParagraph("图1 样品照片", 10f, Element.ALIGN_CENTER, 0,20f, 0f); document.setParagraph("图2 试验照片", 10f, Element.ALIGN_CENTER, 0,20f, 0f); // close document.close(); writer.close(); return uploadMinio(outputStream, "原始记录临时"); } /** * 往PDF中添加图片组 * @param document * @param samplePhoto */ @SneakyThrows private void addSamplePhoto(PdfBaseWriter document, String samplePhoto) { PdfPTable pictureTable = new PdfPTable(new float[]{25, 25, 25, 25}); pictureTable.setWidthPercentage(90); pictureTable.setSpacingBefore(10); List<String> pictureList = Arrays.stream(samplePhoto.split(",")).filter(StringUtils::isNotBlank).collect(Collectors.toList()); int result = pictureList.size(); while (result % 4 != 0) { result++; } for (int i = 0; i < result; i++) { PdfPCell cell = new PdfPCell(); cell.setBorder(Rectangle.NO_BORDER); Optional<String> optionalElement = i < pictureList.size() ? Optional.of(pictureList.get(i)) : Optional.empty(); if (optionalElement.isPresent() && StrUtil.isNotEmpty(optionalElement.get())) { Image image = Image.getInstance(new URL((minioEndpoint + optionalElement.get()).replace(" ", "%20"))); image.scaleAbsolute(100, 100); cell.addElement(image); } else { Paragraph paragraph = new Paragraph(); cell.addElement(paragraph); } pictureTable.addCell(cell); } document.addContent(pictureTable); } /** * 车型试验原始记录 - 1.检验依据 * @param document */ private void inspectionBasisSection(PdfBaseWriter document) { PdfPTable reportNumberTable = new PdfPTable(new float[]{30, 15}); reportNumberTable.setWidthPercentage(100); reportNumberTable.setSpacingBefore(40); document.writeNoBorderCell("报告编号:", Element.ALIGN_RIGHT, 1, 1, reportNumberTable); document.writeBottomCell(DateUtils.dateTimeNow(), Element.ALIGN_CENTER, 1, 1, reportNumberTable); document.addContent(reportNumberTable); document.setParagraph("1.检验依据", 10f, Element.ALIGN_LEFT, 0,2f, 10f); document.setParagraph("口《汽车整车信息安全技术要求》", 10f, Element.ALIGN_LEFT, 14,0f, 0f); } /** * 车型试验原始记录 - 2.样品情况 * @param document * @param sample */ @SneakyThrows private void sampleConditionSection(PdfBaseWriter document, Sample sample) { document.setParagraph("2.样品情况", 10f, Element.ALIGN_LEFT, 0,10f, 10f); PdfPTable sampleConditionTable = new PdfPTable(new float[]{20, 30, 20, 30}); sampleConditionTable.setWidthPercentage(100); document.writeCell("样品名称", Element.ALIGN_CENTER, 1, 1, sampleConditionTable); document.writeCell(sample.getSampleName(), Element.ALIGN_CENTER, 1, 1, sampleConditionTable); document.writeCell("生产企业", Element.ALIGN_CENTER, 1, 1, sampleConditionTable); document.writeCell(sample.getManufacturingEnterprise(), Element.ALIGN_CENTER, 1, 1, sampleConditionTable); document.writeCell("样品型号", Element.ALIGN_CENTER, 1, 1, sampleConditionTable); document.writeCell("xxxxxxx", Element.ALIGN_CENTER, 1, 1, sampleConditionTable); document.writeCell("样品编号", Element.ALIGN_CENTER, 1, 1, sampleConditionTable); document.writeCell(sample.getSampleNumber(), Element.ALIGN_CENTER, 1, 1, sampleConditionTable); document.writeCell("样品数量", Element.ALIGN_CENTER, 1, 1, sampleConditionTable); document.writeCell(sample.getNumberOfSamples(), Element.ALIGN_CENTER, 1, 1, sampleConditionTable); document.writeCell("商标", Element.ALIGN_CENTER, 1, 1, sampleConditionTable); if (ObjectUtil.isNotEmpty(sample.getTrademark())) { document.newPdfPCellOfImage(1, 1, Image.getInstance(minioEndpoint + sample.getTrademark()), 30, 30, sampleConditionTable); } else { document.writeCell("---", Element.ALIGN_CENTER, 1, 1, sampleConditionTable); } document.writeCell("备注:" + sample.getRemark(), Element.ALIGN_LEFT, 4, 1, sampleConditionTable); document.addContent(sampleConditionTable); } /** * 车型试验原始记录 - 3.仪器设备信息 * @param document */ @SneakyThrows private void instrumentInformationSection(PdfBaseWriter document) { document.setParagraph("3.仪器设备信息", 10f, Element.ALIGN_LEFT, 0,10f, 0f); List<String> equipmentList = Arrays.asList("设备编号", "设备名称", "溯源有效期"); float[] columnWidths = {20, 20, 60}; PdfPTable equipmentTable = document.createWithHeaderTable(equipmentList, columnWidths); document.writeCell("xxxx", Element.ALIGN_CENTER, 1, 1, equipmentTable); document.writeCell("xxxxxxxxx", Element.ALIGN_CENTER, 1, 1, equipmentTable); document.writeCell("xxxx-xx-xx", Element.ALIGN_CENTER, 1, 1, equipmentTable); document.add(equipmentTable); } /** * 车型试验原始记录 - 第一页下方固定内容 * @param document * @param writer * @param reportData * @param inspectionPersonnel */ private void bottomOfTheFirstPage(PdfBaseWriter document, PdfWriter writer,InspectionReportPdfVO reportData, InspectionPersonnelVO inspectionPersonnel) { PdfPTable inspectorTable = new PdfPTable(new float[]{12, 28, 10, 40}); inspectorTable.setWidthPercentage(100); inspectorTable.setTotalWidth(PageSize.A4.getWidth() - 80); document.writeNoBorderCell("检验人员:", Element.ALIGN_LEFT, 1, 1, inspectorTable); document.writeNoBorderCell(inspectionPersonnel.getInspectionPersonnel(), Element.ALIGN_LEFT, 1, 1, inspectorTable); document.writeNoBorderCell("检验日期:", Element.ALIGN_LEFT, 1, 1, inspectorTable); document.writeNoBorderCell(inspectionPersonnel.getInspectionDate(), Element.ALIGN_LEFT, 1, 1, inspectorTable); document.writeBottomCell("检验负责人:", Element.ALIGN_LEFT, 1, 1, inspectorTable); document.writeBottomCell(inspectionPersonnel.getPrincipal(), Element.ALIGN_LEFT, 1, 1, inspectorTable); document.writeBottomCell("检验地点:", Element.ALIGN_LEFT, 1, 1, inspectorTable); document.writeBottomCell("口" + reportData.getInspectionCenterAddress() + " 口其他:", Element.ALIGN_LEFT, 1, 1, inspectorTable); inspectorTable.writeSelectedRows(0, -1, document.left(), document.bottom() + 80, writer.getDirectContent()); // 开启下一页 document.newPage(); } /** * 车型试验原始记录 - 4.检验结果 * @param document * @param recordsList * @param reportData * @param inspectionPersonnel */ private void testResultSection(PdfBaseWriter document, List<TestRecords> recordsList, InspectionReportPdfVO reportData, InspectionPersonnelVO inspectionPersonnel) { document.setParagraph("4.检验结果", 10f, Element.ALIGN_LEFT, 0,10f, 0f); // 获取数据 - 渲染 if (CollUtil.isNotEmpty(recordsList)) { IntStream.range(0, recordsList.size()) .forEach(index -> { TestRecords record = recordsList.get(index); document.setParagraph("4." + (index + 1) + " " + record.getUsecase(), 10f, Element.ALIGN_LEFT, 0,2f, 10f); PdfPTable useCaseTable = new PdfPTable(new float[]{15, 35, 15, 35}); useCaseTable.setWidthPercentage(100); useCaseTable.setSplitLate(false); document.writeCell("测试编号", Element.ALIGN_LEFT, 1, 1, useCaseTable); document.writeCell(record.getUsecaseNo(), Element.ALIGN_LEFT, 1, 1, useCaseTable); document.writeCell("测试时间", Element.ALIGN_LEFT, 1, 1, useCaseTable); document.writeCell(String.valueOf(record.getTestTime()), Element.ALIGN_LEFT, 1, 1, useCaseTable); document.writeCell("样品编号", Element.ALIGN_LEFT, 1, 1, useCaseTable); document.writeCell(reportData.getSampleNumberSummary(), Element.ALIGN_LEFT, 1, 1, useCaseTable); document.writeCell("测试人员", Element.ALIGN_LEFT, 1, 1, useCaseTable); document.writeCell(inspectionPersonnel.getInspectionPersonnel(), Element.ALIGN_LEFT, 1, 1, useCaseTable); document.writeCell("简述", Element.ALIGN_LEFT, 1, 1, useCaseTable); document.writeCell(record.getDescription(), Element.ALIGN_LEFT, 3, 1, useCaseTable); document.writeCell("风险等级", Element.ALIGN_LEFT, 1, 1, useCaseTable); document.writeCell(record.getRiskLevel(), Element.ALIGN_LEFT, 3, 1, useCaseTable); document.writeCell("测试方法", Element.ALIGN_LEFT, 1, 1, useCaseTable); document.writeCell(record.getTestMethod(), Element.ALIGN_LEFT, 3, 1, useCaseTable); document.writeCell("测试结果", Element.ALIGN_LEFT, 1, 1, useCaseTable); document.writeCell(record.getTestResult(), Element.ALIGN_LEFT, 3, 1, useCaseTable); document.writeCell("测试详情", Element.ALIGN_LEFT, 1, 1, useCaseTable); PdfPCell cell = new PdfPCell(); cell.setColspan(3); cell.setRowspan(1); cell.setHorizontalAlignment(Element.ALIGN_LEFT); // h5渲染到cell Paragraph context = new Paragraph(); ElementList elementList = null; try { elementList = parseToElementList(record.getTestDetails(), null); } catch (IOException e) { throw new RuntimeException(e); } context.addAll(elementList); cell.addElement(context); useCaseTable.addCell(cell); if (ObjectUtil.isNotEmpty(record.getRemediation())) { document.writeCell("修复建议", Element.ALIGN_LEFT, 1, 1, useCaseTable); document.writeCell(record.getRemediation(), Element.ALIGN_LEFT, 3, 1, useCaseTable); } try { document.add(useCaseTable); } catch (DocumentException e) { throw new RuntimeException(e); } document.setParagraph(" ", 10f, Element.ALIGN_LEFT, 0,2f, 10f); }); } } /** * html转pdf 写法 * @param html * @param css * @return * @throws IOException */ public static ElementList parseToElementList(String html, String css) throws IOException { // CSS CSSResolver cssResolver = new StyleAttrCSSResolver(); if (css != null) { CssFile cssFile = XMLWorkerHelper.getCSS(new ByteArrayInputStream(css.getBytes())); cssResolver.addCss(cssFile); } // HTML MyFontsProvider fontProvider = new MyFontsProvider(); CssAppliers cssAppliers = new CssAppliersImpl(fontProvider); HtmlPipelineContext htmlContext = new HtmlPipelineContext(cssAppliers); htmlContext.setTagFactory(Tags.getHtmlTagProcessorFactory()); htmlContext.autoBookmark(false); // Pipelines ElementList elements = new ElementList(); ElementHandlerPipeline end = new ElementHandlerPipeline(elements, null); HtmlPipeline htmlPipeline = new HtmlPipeline(htmlContext, end); CssResolverPipeline cssPipeline = new CssResolverPipeline(cssResolver, htmlPipeline); // XML Worker XMLWorker worker = new XMLWorker(cssPipeline, true); XMLParser p = new XMLParser(worker); html = html.replace("<br>", "").replace("<hr>", "").replace("<img>", "").replace("<param>", "").replace("<link>", ""); p.parse(new ByteArrayInputStream(html.getBytes())); return elements; } static class MyFontsProvider extends XMLWorkerFontProvider { public MyFontsProvider() { super(null, null); } @Override public Font getFont(final String fontname, String encoding, float size, final int style) { return getFontInf(); } } /** * 设置字体信息 * @return */ @SneakyThrows private static Font getFontInf() { // 字体路径 String fontPath = "/fonts/STSong.TTF"; BaseFont baseFont = null; Font font = null; // 设置字体路径,字体编码,是否将字体嵌入pdf(默认false) baseFont = BaseFont.createFont(fontPath, BaseFont.IDENTITY_H, BaseFont.EMBEDDED); // 设置默认字体数据 font = new Font(baseFont, 12f, Font.NORMAL,BaseColor.BLACK); return font; } private static int totalNum = 0; public static class OriginalRecordHeaderFooter extends PdfPageEventHelper { // PdfTemplate totalPage; // @Override // public void onOpenDocument (PdfWriter writer, Document document) { // PdfContentByte contentByte = writer.getDirectContent(); // totalPage = contentByte.createTemplate(30, 16); // } @Override @SneakyThrows public void onCloseDocument(PdfWriter writer, Document document) { // BaseFont font = BaseFont.createFont("/fonts/STSong.TTF", BaseFont.IDENTITY_H, BaseFont.EMBEDDED); // String text = " 共 " + (writer.getPageNumber()) + " 页"; // ColumnText.showTextAligned(totalPage, Element.ALIGN_CENTER, new Paragraph(String.valueOf(text), new Font(font, 8f)), 2, 4 ,0); totalNum = writer.getPageNumber() - 1; System.out.println(totalNum); } /** * 一页加载完成触发,写入页眉和页脚 */ @Override @SneakyThrows public void onEndPage(PdfWriter writer, Document document) { BaseFont font = BaseFont.createFont("/fonts/STSong.TTF", BaseFont.IDENTITY_H, BaseFont.EMBEDDED); PdfPTable table = new PdfPTable(2); table.setTotalWidth(PageSize.A4.getWidth() - 80); table.setWidths(new int[] { 60, 40 }); table.setLockedWidth(true); table.getDefaultCell().setFixedHeight(30); table.getDefaultCell().setBorder(Rectangle.BOX); table.getDefaultCell().setBorderWidth(0.5f); table.getDefaultCell().setVerticalAlignment(Element.ALIGN_MIDDLE); table.getDefaultCell().setHorizontalAlignment(Element.ALIGN_CENTER); table.getDefaultCell().setRowspan(1); table.getDefaultCell().setColspan(2); table.addCell(new Paragraph("中汽研软件测评有限公司", new Font(font))); table.getDefaultCell().setRowspan(1); table.getDefaultCell().setColspan(1); table.addCell(new Paragraph("检验记录", new Font(font))); table.getDefaultCell().setRowspan(1); table.getDefaultCell().setColspan(1); table.getDefaultCell().setHorizontalAlignment(Element.ALIGN_CENTER); table.getDefaultCell().setBorder(Rectangle.BOTTOM + Rectangle.TOP + Rectangle.LEFT + Rectangle.RIGHT); table.addCell(new Paragraph("第 " + (writer.getCurrentPageNumber() - 1) + " 页 / 共 "+ totalNum +" 页", new Font(font))); // 总页数 // table.getDefaultCell().setBorder(Rectangle.BOX); // table.getDefaultCell().setColspan(1); // table.getDefaultCell().setRowspan(1); // table.getDefaultCell().setHorizontalAlignment(Element.ALIGN_LEFT); // table.getDefaultCell().setBorder(Rectangle.BOTTOM + Rectangle.TOP + Rectangle.RIGHT); // table.addCell(Image.getInstance(totalPage)); // 将页眉写到document中,位置可以指定,指定到下面就是页脚 table.writeSelectedRows(0, -1, 40, PageSize.A4.getHeight() - 20, writer.getDirectContent()); // 页脚 PdfPTable bottomTable = new PdfPTable(1); bottomTable.setTotalWidth(PageSize.A4.getWidth() - 80); bottomTable.setWidths(new int[] { 50}); bottomTable.setLockedWidth(true); bottomTable.getDefaultCell().setFixedHeight(-10); bottomTable.getDefaultCell().setBorder(Rectangle.NO_BORDER); bottomTable.getDefaultCell().setBorderWidth(0.5f); bottomTable.getDefaultCell().setHorizontalAlignment(Element.ALIGN_CENTER); bottomTable.addCell(new Paragraph(String.valueOf(writer.getPageNumber() - 1), new Font(font))); bottomTable.writeSelectedRows(0, -1, 40, 40, writer.getDirectContent()); } } private static String headerText; public static class MyHeaderFooter extends PdfPageEventHelper { // 一页加载完成触发,写入页眉和页脚 @Override public void onEndPage(PdfWriter writer, Document document) { PdfPTable table = new PdfPTable(1); try { table.setTotalWidth(PageSize.A4.getWidth() - 80); table.setWidths(new int[] { 40 }); table.setLockedWidth(true); table.getDefaultCell().setFixedHeight(-10); table.getDefaultCell().setBorder(Rectangle.NO_BORDER); table.getDefaultCell().setBorderWidth(0.5f); BaseFont font = BaseFont.createFont("/fonts/STSong.TTF", BaseFont.IDENTITY_H, BaseFont.EMBEDDED); table.addCell(new Paragraph(headerText, new Font(font)));// 可以直接使用addCell(str),不过不能指定字体,中文无法显示 // 将页眉写到document中,位置可以指定,指定到下面就是页脚 table.writeSelectedRows(0, -1, 40, PageSize.A4.getHeight() - 20, writer.getDirectContent()); } catch (Exception de) { throw new ExceptionConverter(de); } } } /** * 获取企业留档文件数据 * @param taskId * @return */ private EnterpriseFilePdfVO getEnterpriseFileData(Long taskId) { EnterpriseFilePdfVO enterpriseFileData = taskMapper.selectEnterpriseFileData(taskId); enterpriseFileData.setInspectionItem(getInspectionItemByTaskItem(enterpriseFileData.getInspectionItem())); return enterpriseFileData; } /** * 获取当前项目的检验项目 * @return */ private String getInspectionItemByTaskItem(String inspectionItem) { List<SysDictData> dictDataList = dictDataMapper.selectDictDataByType("inspection_item"); List<String> list = Arrays.asList(inspectionItem.split(",")); StringBuilder taskItem = new StringBuilder(); for (String item : list) { Optional<SysDictData> first = dictDataList.stream().filter(data -> data.getDictValue().equals(item)).findFirst(); if (first.isPresent()) { SysDictData dictData = first.get(); taskItem.append(dictData.getDictLabel()).append("、"); } } return taskItem.substring(0, taskItem.length() - 1); } /** * 上传到minio * @param outputStream 输出流 * @param fileName 文件名 * @return minio存储地址 */ private String uploadMinio(ByteArrayOutputStream outputStream, String fileName) throws Exception { // 1.连接Minio MinioClient minioClient =MinioClient.builder() .endpoint(minioEndpoint) .credentials(accessKey, secretKey) .build(); // 2.上传 // 把os流转为is流 ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray()); String newFileName = assemblyPdfName(fileName); PutObjectArgs putObjectArgs = PutObjectArgs.builder() .bucket(bucketName) .object(newFileName) .contentType("application/pdf") .stream(inputStream, inputStream.available(), ObjectWriteArgs.MIN_MULTIPART_SIZE).build(); minioClient.putObject(putObjectArgs); return newFileName; } /** * 拼接pdf文件名 * @param name * @return */ public String assemblyPdfName(String name) { return StringUtils.format("{}/{}.pdf", DateUtils.datePath(), name); } /** * 模拟数据 * @return */ private InspectionReportPdfVO requireData(Long taskId) { InspectionReportPdfVO inspectionReport = taskMapper.selectInspectionReportData(taskId); inspectionReport.setInspectionTimeAndLocation(" 检验于" + inspectionReport.getTaskBeginTime() + "至" + inspectionReport.getTaskEndTime() + "在" + inspectionReport.getInspectionCenterName() + "进行"); String taskItem = getInspectionItemByTaskItem(inspectionReport.getInspectionItem()); inspectionReport.setInspectionItem(taskItem); // 检验结论 inspectionReport.setTestConclusion(getTestConclusion(taskItem, taskId, inspectionReport)); return inspectionReport; } /** * 获取检验结论 * @return */ private String getTestConclusion(String taskItem, Long taskId, InspectionReportPdfVO inspectionReport) { int num = 0; String result = ""; String issueDate = ""; String passItemName = ""; String failItemName = ""; List<String> taskItemList = Arrays.asList(taskItem.split(",")); List<InspectionItemVO> list = new ArrayList<>(); TestConclusionVO testConclusionVO = taskMapper.selectTestConclusion(taskId); if (ObjectUtil.isNotEmpty(testConclusionVO.getSystemReviewTaskResult())) { list.add(InspectionItemVO.builder() .inspectionItemName(taskItemList.get(num)) .taskResult(testConclusionVO.getSystemReviewTaskResult()).build()); issueDate = testConclusionVO.getSystemReviewTaskDate(); num++; } if (ObjectUtil.isNotEmpty(testConclusionVO.getCarReviewTaskResult())) { list.add(InspectionItemVO.builder() .inspectionItemName(taskItemList.get(num)) .taskResult(testConclusionVO.getCarReviewTaskResult()).build()); issueDate = testConclusionVO.getCarReviewTaskDate(); num++; } if (ObjectUtil.isNotEmpty(testConclusionVO.getModelTestTaskResult())) { list.add(InspectionItemVO.builder() .inspectionItemName(taskItemList.get(num)) .taskResult(testConclusionVO.getModelTestTaskResult()).build()); issueDate = testConclusionVO.getModelTestTaskDate(); } inspectionReport.setIssueDate(issueDate); for (InspectionItemVO item : list) { if (item.getTaskResult().equals(ModelTestTask.TASK_STATUS_PASS)) { passItemName += item.getInspectionItemName() + "、"; } if (item.getTaskResult().equals(ModelTestTask.TASK_STATUS_REJECT)) { failItemName += item.getInspectionItemName() + "、"; } } if (StrUtil.isNotEmpty(passItemName)) { passItemName = passItemName.substring(0, passItemName.length() - 1); result = "经检验,该样品的" + passItemName + "检验项目的检验结果符合" + inspectionReport.getInspectionBasis() + "的要求,"; } if (StrUtil.isNotEmpty(failItemName)) { failItemName = failItemName.substring(0, failItemName.length() - 1); result += "该样品的" + failItemName + "检验项目的检验结果不符合" + inspectionReport.getInspectionBasis() + "的要求。"; } if (result.endsWith(",")) { result = result.substring(0, result.length() - 1) + "。"; } return " " + result; } /** * 渲染数据 * @param fields */ private void renderData(AcroFields fields, InspectionReportPdfVO inspectionReportPdfVO, PdfStamper ps) throws Exception { // 取出当前对象所有属性,并赋值到模板里 // 文本域 Class<? extends InspectionReportPdfVO> aClass = inspectionReportPdfVO.getClass(); Field[] declaredFields = aClass.getDeclaredFields(); for (Field field : declaredFields) { String name = field.getName(); String key = name.replaceAll("([a-z])([A-Z])", "$1_$2").toLowerCase(); field.setAccessible(true); Object o = field.get(inspectionReportPdfVO); if (o != null) { String value = o.toString(); fields.setField(key, value); } } // 图片域 // 样品 // String url = inspectionReportPdfVO.getBrandImage(); // if (StrUtil.isNotBlank(url)) { // if (url.startsWith(",")) { // url = inspectionReportPdfVO.getBrandImage().substring(1); // } // addImageToPdf("brand", fields, ps, "http://49.232.167.247:22038" + url.split(",")[0]); // } } /** * 填充模板中的数据 * @param fields * @param data 是一个Map<String,String> 主要存储 key 表单模板中的单元格名 value为想要赋的值,遍历 * @param ps */ public void fillData(AcroFields fields, Map<String, String> data, PdfStamper ps) { try { for (String key : data.keySet()) { String value = data.get(key); if (key.contains("image")) { addImageToPdf(key, fields, ps, value); continue; } // 为字段赋值,注意字段名称是区分大小写的 fields.setField(key, value); } } catch (Exception e) { e.printStackTrace(); } } /** * 添加图片 * @param key 应为模板名 * @param form 动态字段 * @param stamper * @param filePath 本地图片路径 * @throws DocumentException * @throws IOException * @throws IOException */ private static void addImageToPdf(String key,AcroFields form, PdfStamper stamper, String filePath) throws DocumentException, IOException, IOException { // 通过图片域名获取所在页和坐标,左下角为起点 int pageNo = form.getFieldPositions(key).get(0).page; Rectangle signRect = form.getFieldPositions(key).get(0).position; float x = signRect.getLeft(); float y = signRect.getBottom(); // 读图片 Image image = Image.getInstance(filePath); // 获取操作的页面 PdfContentByte under = stamper.getOverContent(pageNo); // 根据域的大小缩放图片 image.scaleToFit(signRect.getWidth(), signRect.getHeight()); // 添加图片并设置位置(个人通过此设置使得图片垂直水平居中,可参考,具体情况已实际为准) image.setAbsolutePosition(x, y); under.addImage(image); } /** * 通过属性名获取属性在对象里的值 * @param obj * @param propertyName * @return * @param <T> */ public static <T> T getPropertyValue(Object obj, String propertyName) { try { // 获取对象的类 Class<?> objClass = obj.getClass(); // 获取类的指定属性 Field field = objClass.getDeclaredField(propertyName); // 设置属性为可访问 field.setAccessible(true); // 获取属性的值 @SuppressWarnings("unchecked") T value = (T) field.get(obj); return value; } catch (Exception e) { e.printStackTrace(); return null; } } }