| @ -0,0 +1,404 @@ | |||
| package com.hxhq.business.utils.Pdf; | |||
| import com.hxhq.common.core.exception.ServiceException; | |||
| import com.hxhq.common.core.text.Convert; | |||
| import com.hxhq.common.core.utils.DateUtils; | |||
| import com.itextpdf.text.*; | |||
| import com.itextpdf.text.pdf.*; | |||
| import com.itextpdf.text.pdf.draw.LineSeparator; | |||
| import org.slf4j.Logger; | |||
| import org.slf4j.LoggerFactory; | |||
| import org.springframework.stereotype.Component; | |||
| import java.io.File; | |||
| import java.io.FileOutputStream; | |||
| import java.io.IOException; | |||
| import java.nio.file.Paths; | |||
| import java.text.SimpleDateFormat; | |||
| import java.util.List; | |||
| import java.util.*; | |||
| /** | |||
| * pdf导出基类 | |||
| * @author tanfei | |||
| */ | |||
| @Component | |||
| public class PdfBaseUtil { | |||
| private static final Logger logger = LoggerFactory.getLogger(PdfBaseUtil.class); | |||
| private static final String fileDir="D:/hxhq/uploadPath"; | |||
| /** | |||
| * 生成PDF文件完整路径(按年月日创建文件夹) | |||
| * @return 完整文件路径,如:/data/files/2024/01/17/document_20240117123045.pdf | |||
| */ | |||
| public static String getFilePath() { | |||
| // 1. 获取当前年月日并创建文件夹 | |||
| Date now = new Date(); | |||
| SimpleDateFormat yearFormat = new SimpleDateFormat("yyyy"); | |||
| SimpleDateFormat monthFormat = new SimpleDateFormat("MM"); | |||
| SimpleDateFormat dayFormat = new SimpleDateFormat("dd"); | |||
| SimpleDateFormat timestampFormat = new SimpleDateFormat("yyyyMMddHHmmss"); | |||
| String year = yearFormat.format(now); | |||
| String month = monthFormat.format(now); | |||
| String day = dayFormat.format(now); | |||
| String timestamp = timestampFormat.format(now)+UUID.randomUUID(); | |||
| // 2. 构建文件夹路径 | |||
| String folderPath = fileDir + File.separator + year + File.separator + month + File.separator + day; | |||
| // 3. 确保目录存在 | |||
| File dir = new File(folderPath); | |||
| if (!dir.exists()) { | |||
| dir.mkdirs(); | |||
| } | |||
| // 4. 生成文件名 | |||
| String fileName = "document_" + timestamp + ".pdf"; | |||
| // 5. 完整文件路径 | |||
| return Paths.get(folderPath, fileName).toString(); | |||
| } | |||
| /** | |||
| * 添加带下划线的标题 | |||
| * @param document PDF文档对象 | |||
| * @param titleText 标题文本 | |||
| * @param fontSize 字体大小 | |||
| */ | |||
| public static void addUnderlinedTitle(Document document, String titleText, float fontSize) | |||
| throws DocumentException, IOException { | |||
| // 创建中文字体 | |||
| BaseFont baseFont = BaseFont.createFont( | |||
| "STSong-Light", | |||
| "UniGB-UCS2-H", | |||
| BaseFont.EMBEDDED | |||
| ); | |||
| Font titleFont = new Font(baseFont, fontSize, Font.NORMAL, BaseColor.BLACK); | |||
| Font linFont = new Font(baseFont, fontSize, Font.NORMAL, BaseColor.BLACK); | |||
| // 创建标题段落 | |||
| Paragraph titleParagraph = new Paragraph(titleText, titleFont); | |||
| titleParagraph.setAlignment(Element.ALIGN_LEFT); | |||
| // 添加下划线 | |||
| LineSeparator underline = new LineSeparator(linFont); | |||
| underline.setPercentage(100); // 宽度占50% | |||
| underline.setLineWidth(1); // 线粗 | |||
| underline.setAlignment(Element.ALIGN_LEFT); | |||
| // 添加到文档 | |||
| document.add(titleParagraph); | |||
| document.add(underline); | |||
| // 添加一些间距 | |||
| document.add(new Paragraph(titleText)); // 空行 | |||
| } | |||
| /** | |||
| * 初始化 | |||
| * | |||
| * @param document | |||
| * @param fos | |||
| * @param filePath | |||
| * @param signText | |||
| */ | |||
| public static Document init(Document document, FileOutputStream fos, String filePath, String signText, String headerText) { | |||
| try { | |||
| // 创建PDF文档 设置文档边距,避免内容遮挡页眉页脚 | |||
| float topMargin = 70; // 顶部边距(为页眉留出空间) | |||
| float bottomMargin = 60; // 底部边距(为页脚留出空间) | |||
| float leftMargin = 50; // 左边距 | |||
| float rightMargin = 50; // 右边距 | |||
| document = new Document(PageSize.A4, | |||
| leftMargin, rightMargin, topMargin, bottomMargin); | |||
| fos = new FileOutputStream(filePath); | |||
| PdfWriter writer = PdfWriter.getInstance(document, fos); | |||
| // 设置页面事件,每页添加文字页眉 | |||
| writer.setPageEvent(new PdfBaseUtil.TextHeaderEvent(signText,headerText)); | |||
| // 5. 设置PDF属性 | |||
| document.addTitle("华西海圻"); | |||
| document.addAuthor("华西海圻"); | |||
| document.addCreator("华西海圻"); | |||
| document.addCreationDate(); | |||
| document.open(); | |||
| return document; | |||
| } catch (Exception e) { | |||
| logger.error("生成失败", e); | |||
| throw new ServiceException("生成失败: " + e.getMessage()); | |||
| } | |||
| } | |||
| /**创建表单 | |||
| * @param document | |||
| * @param data | |||
| * @param count | |||
| * @throws DocumentException | |||
| * @throws IOException | |||
| */ | |||
| public static void addFormTableColumns(Document document, Map<String, String> data, Integer count) | |||
| throws DocumentException, IOException { | |||
| // 创建3列表格 | |||
| PdfPTable table = new PdfPTable(count); | |||
| table.setWidthPercentage(100); | |||
| table.setSpacingBefore(10); | |||
| table.setSpacingAfter(10); | |||
| // 设置等宽列 | |||
| float[] widths = new float[count]; | |||
| for (int i = 0; i < count; i++) { | |||
| widths[i] = 1.0f; // 等宽 | |||
| } | |||
| table.setWidths(widths); | |||
| // 创建字体 | |||
| BaseFont baseFont = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED); | |||
| Font labelFont = new Font(baseFont, 10, Font.NORMAL, BaseColor.BLACK); | |||
| Font valueFont = new Font(baseFont, 10, Font.NORMAL, BaseColor.BLACK); | |||
| // 将数据转换为List | |||
| List<Map.Entry<String, String>> entries = new ArrayList<>(data.entrySet()); | |||
| // 每3个字段一组,每组一行 | |||
| for (int i = 0; i < entries.size(); i += count) { | |||
| // 创建一行 | |||
| for (int j = 0; j < count; j++) { | |||
| int index = i + j; | |||
| if (index < entries.size()) { | |||
| Map.Entry<String, String> entry = entries.get(index); | |||
| // 创建单元格内容 | |||
| Phrase cellContent = new Phrase(); | |||
| cellContent.add(new Chunk(entry.getKey() + ":", labelFont)); | |||
| cellContent.add(new Chunk(com.hxhq.common.core.utils.StringUtils.isBlank(entry.getValue()) ? "" : entry.getValue(), valueFont)); | |||
| PdfPCell cell = new PdfPCell(cellContent); | |||
| cell.setBorder(Rectangle.NO_BORDER); // 无边框 | |||
| cell.setPadding(4); | |||
| cell.setHorizontalAlignment(Element.ALIGN_LEFT); | |||
| cell.setVerticalAlignment(Element.ALIGN_MIDDLE); | |||
| cell.setMinimumHeight(20); // 最小高度 | |||
| table.addCell(cell); | |||
| } else { | |||
| // 添加空单元格保持布局 | |||
| PdfPCell emptyCell = new PdfPCell(); | |||
| emptyCell.setBorder(Rectangle.NO_BORDER); | |||
| emptyCell.setMinimumHeight(20); | |||
| table.addCell(emptyCell); | |||
| } | |||
| } | |||
| } | |||
| document.add(table); | |||
| } | |||
| /**创建表格 | |||
| * @param content | |||
| * @param font | |||
| * @return | |||
| */ | |||
| public static PdfPCell createCell(String content, Font font) { | |||
| PdfPCell cell = new PdfPCell(new Phrase(content, font)); | |||
| cell.setHorizontalAlignment(Element.ALIGN_CENTER); | |||
| cell.setVerticalAlignment(Element.ALIGN_MIDDLE); | |||
| cell.setPadding(6); | |||
| cell.setMinimumHeight(25); | |||
| return cell; | |||
| } | |||
| /** | |||
| * 生成文件名 | |||
| */ | |||
| public static String generateFileName() { | |||
| Random random = new Random(); | |||
| String timestamp = DateUtils.dateTimeNow("yyyyMMddHHmmss"); | |||
| return String.format("%s_%s.pdf", timestamp, random.nextInt(1000)); | |||
| } | |||
| /** | |||
| * 页面事件处理类 - 每页添加文字页眉 | |||
| */ | |||
| static class TextHeaderEvent extends PdfPageEventHelper { | |||
| private Font footerFont; | |||
| private String signText; | |||
| private String headerText; | |||
| private BaseFont baseFont; | |||
| private int totalPages = 0; | |||
| private PdfTemplate total; | |||
| public TextHeaderEvent(String signText,String headerText) throws Exception { | |||
| try { | |||
| this.signText = signText; | |||
| this.headerText = headerText; | |||
| // 创建字体(支持中文) | |||
| this.baseFont = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED); | |||
| footerFont = new Font(baseFont, 10, Font.NORMAL, BaseColor.GRAY); | |||
| } catch (Exception e) { | |||
| footerFont = new Font(Font.FontFamily.HELVETICA, 10, Font.NORMAL, BaseColor.GRAY); | |||
| } | |||
| } | |||
| @Override | |||
| public void onOpenDocument(PdfWriter writer, Document document) { | |||
| total = writer.getDirectContent().createTemplate(100, 16); | |||
| } | |||
| @Override | |||
| public void onCloseDocument(PdfWriter writer, Document document) { | |||
| totalPages = writer.getPageNumber(); | |||
| String totalPagesStr = String.valueOf(totalPages); | |||
| // 重新创建合适宽度的模板 | |||
| BaseFont baseFont = footerFont.getBaseFont(); | |||
| float actualWidth = baseFont.getWidthPoint(totalPagesStr, footerFont.getSize()); | |||
| total.beginText(); | |||
| total.setFontAndSize(baseFont, footerFont.getSize()); | |||
| total.setTextMatrix(0, 0); | |||
| total.showText(totalPagesStr); | |||
| total.endText(); | |||
| } | |||
| @Override | |||
| public void onEndPage(PdfWriter writer, Document document) { | |||
| try { | |||
| PdfContentByte cb = writer.getDirectContent(); | |||
| // 获取页面尺寸 | |||
| Rectangle pageSize = document.getPageSize(); | |||
| float pageWidth = pageSize.getWidth(); | |||
| float pageHeight = pageSize.getHeight(); | |||
| // 设置页眉参数 | |||
| float topMargin = 30; // 顶部边距 | |||
| float fontSize = 12; // 字体大小 | |||
| // 计算文字宽度(用于居中) | |||
| float textWidth = baseFont.getWidthPoint(signText, fontSize); | |||
| // 计算居中位置 | |||
| float textX = (pageWidth - textWidth) / 2; | |||
| float textY = pageHeight - topMargin; | |||
| // 获取画布 | |||
| PdfContentByte canvas = writer.getDirectContent(); | |||
| int currentPage = writer.getPageNumber(); | |||
| float y = document.bottom() - 20; | |||
| // 在每一页都重新计算,确保位置准确 | |||
| String pageText = "第 " + currentPage + " 页 / 共 "; | |||
| BaseFont baseFont = footerFont.getBaseFont(); | |||
| // 计算页面文本宽度 | |||
| float pageTextWidth = baseFont.getWidthPoint(pageText, footerFont.getSize()); | |||
| // 临时用估计的总页数计算位置(等文档关闭后会被替换) | |||
| String tempTotal = String.valueOf(writer.getPageNumber() + 5); // 估计值 | |||
| float totalWidth = baseFont.getWidthPoint(tempTotal, footerFont.getSize()); | |||
| // 计算起始位置 | |||
| float totalTextWidth = pageTextWidth + totalWidth + | |||
| baseFont.getWidthPoint(" 页", footerFont.getSize()); | |||
| float startX = (document.getPageSize().getWidth() - totalTextWidth) / 2; | |||
| // 写入当前页信息 | |||
| ColumnText.showTextAligned(cb, Element.ALIGN_LEFT, | |||
| new Phrase(pageText, footerFont), startX, y, 0); | |||
| // 添加总页数模板 | |||
| cb.addTemplate(total, startX + pageTextWidth, y); | |||
| // 写入"页"字 | |||
| ColumnText.showTextAligned(cb, Element.ALIGN_LEFT, | |||
| new Phrase(" 页", footerFont), startX + pageTextWidth + totalWidth, y, 0); | |||
| PdfContentByte content = writer.getDirectContentUnder(); // 在水印层添加 | |||
| // 设置透明度 | |||
| PdfGState gs = new PdfGState(); | |||
| float opacity = Convert.toFloat("0.3"); | |||
| gs.setFillOpacity(opacity); | |||
| content.setGState(gs); | |||
| // 设置字体和颜色 | |||
| content.setColorFill(BaseColor.RED); | |||
| content.setFontAndSize(baseFont, fontSize); | |||
| float width = pageSize.getWidth(); | |||
| float height = pageSize.getHeight(); | |||
| // 重复铺满水印 | |||
| addRepeatedWatermark(content, width, height, signText); | |||
| // 添加页眉文字 | |||
| canvas.beginText(); | |||
| canvas.setFontAndSize(baseFont, fontSize); | |||
| canvas.setTextMatrix(textX, textY); | |||
| canvas.showText(headerText); | |||
| canvas.endText(); | |||
| // 添加页眉分隔线(可选) | |||
| addHeaderLine(canvas, pageWidth, textY - 10); | |||
| } catch (Exception e) { | |||
| e.printStackTrace(); | |||
| } | |||
| } | |||
| /** | |||
| * 添加水印 | |||
| * @param content | |||
| * @param width | |||
| * @param height | |||
| * @param signText | |||
| */ | |||
| private void addRepeatedWatermark(PdfContentByte content, float width, float height, String signText) { | |||
| // 计算文字尺寸 | |||
| float fontSize = Convert.toFloat("8"); | |||
| float textWidth = baseFont.getWidthPoint(signText, fontSize); | |||
| float textHeight = baseFont.getAscentPoint(signText, fontSize) - | |||
| baseFont.getDescentPoint(signText, fontSize); | |||
| // 计算间距 | |||
| float stepX = textWidth + 200; | |||
| float stepY = textHeight + 200; | |||
| // 添加重复水印 | |||
| for (float x = textWidth/2 ; x < width; x += stepX) { | |||
| for (float y = textHeight/2 ; y < height; y += stepY) { | |||
| content.beginText(); | |||
| content.showTextAligned( | |||
| Element.ALIGN_CENTER, | |||
| signText, | |||
| x, | |||
| y, | |||
| fontSize | |||
| ); | |||
| content.endText(); | |||
| } | |||
| } | |||
| } | |||
| /** | |||
| * 添加页眉线 | |||
| * @param canvas | |||
| * @param pageWidth | |||
| * @param yPos | |||
| */ | |||
| private void addHeaderLine(PdfContentByte canvas, float pageWidth, float yPos) { | |||
| canvas.setLineWidth(0.5f); | |||
| canvas.moveTo(50, yPos); | |||
| canvas.lineTo(pageWidth - 50, yPos); | |||
| canvas.stroke(); | |||
| } | |||
| } | |||
| } | |||
| @ -0,0 +1,65 @@ | |||
| package com.hxhq.business.utils.Pdf; | |||
| import com.hxhq.common.core.exception.ServiceException; | |||
| import org.slf4j.Logger; | |||
| import org.slf4j.LoggerFactory; | |||
| import org.springframework.stereotype.Component; | |||
| import java.lang.reflect.Method; | |||
| /** | |||
| * pdf导出 | |||
| */ | |||
| @Component | |||
| public class PdfExportUtil { | |||
| private static final Logger logger = LoggerFactory.getLogger(PdfExportUtil.class); | |||
| public static Object invokeMethod(String className, String methodName, Object... params) | |||
| throws Exception { | |||
| // 加载类 | |||
| Class<?> clazz = Class.forName(className); | |||
| // 创建实例 | |||
| Object instance = clazz.getDeclaredConstructor().newInstance(); | |||
| // 改为不预先获取参数类型,而是在查找方法时动态匹配 | |||
| Method method = null; | |||
| for (Method m : clazz.getMethods()) { | |||
| if (m.getName().equals(methodName) && | |||
| m.getParameterCount() == params.length) { | |||
| method = m; | |||
| } | |||
| } | |||
| if (method == null) { | |||
| throw new NoSuchMethodException("找不到匹配的方法: " + methodName); | |||
| } | |||
| // 调用方法 | |||
| return method.invoke(instance, params); | |||
| } | |||
| public static void main(String[] args) throws Exception { | |||
| String result = export( | |||
| "com.hxhq.business.utils.Pdf.Template.Sp.Sp001", | |||
| "exportDetail", | |||
| "麻精药导出", | |||
| "张三 2025-12-12 14:52:52" | |||
| ); | |||
| } | |||
| /** | |||
| * 导出 | |||
| * | |||
| * @param className | |||
| * @param methodName | |||
| * @param params | |||
| * @return | |||
| */ | |||
| public static String export(String className, String methodName, Object... params) { | |||
| try { | |||
| return (String) invokeMethod(className, methodName, params); | |||
| } catch (Exception ex) { | |||
| logger.error(ex.getMessage()); | |||
| throw new ServiceException(ex.getMessage()); | |||
| } | |||
| } | |||
| } | |||
| @ -0,0 +1,79 @@ | |||
| package com.hxhq.business.utils.Pdf.Resource; | |||
| import com.alibaba.fastjson2.JSONObject; | |||
| import com.hxhq.business.domain.Mjy; | |||
| import com.hxhq.business.utils.Pdf.PdfBaseUtil; | |||
| import com.hxhq.common.security.utils.SecurityUtils; | |||
| import com.itextpdf.text.BaseColor; | |||
| import com.itextpdf.text.Document; | |||
| import com.itextpdf.text.Element; | |||
| import com.itextpdf.text.pdf.draw.LineSeparator; | |||
| import org.apache.http.client.utils.DateUtils; | |||
| import org.slf4j.Logger; | |||
| import org.slf4j.LoggerFactory; | |||
| import java.io.FileOutputStream; | |||
| import java.io.IOException; | |||
| import java.util.LinkedHashMap; | |||
| import java.util.Map; | |||
| /** | |||
| * 麻精药详情 | |||
| * | |||
| * @author tanfei | |||
| */ | |||
| public class Drug { | |||
| private static final Logger logger = LoggerFactory.getLogger(Drug.class.getName()); | |||
| /** | |||
| * 导出 | |||
| * | |||
| * @param mjy | |||
| * @return | |||
| */ | |||
| public static String exportDetail(Mjy mjy) { | |||
| Document document = null; | |||
| FileOutputStream fos = null; | |||
| String filePath = PdfBaseUtil.getFilePath(); | |||
| try { | |||
| document = PdfBaseUtil.init(document, fos, filePath, SecurityUtils.getNickName(), mjy.getMc()); | |||
| // 基本信息 | |||
| PdfBaseUtil.addUnderlinedTitle(document, "基本信息", 10); | |||
| Map<String, String> formData1 = new LinkedHashMap<>(); | |||
| formData1.put("名称", mjy.getMc()); | |||
| formData1.put("编号", mjy.getBh()); | |||
| formData1.put("浓度", mjy.getNd() + mjy.getNddw()); | |||
| formData1.put("库存量", mjy.getKc() + mjy.getKcdw()); | |||
| formData1.put("失效日期", DateUtils.formatDate(mjy.getSxrq())); | |||
| formData1.put("存储条件", mjy.getCctj()); | |||
| formData1.put("存储位置", mjy.getCcwz()); | |||
| PdfBaseUtil.addFormTableColumns(document, formData1, 2); | |||
| // 表单信息 | |||
| PdfBaseUtil.addUnderlinedTitle(document, "表单信息", 10); | |||
| Map<String, String> formData3 = new LinkedHashMap<>(); | |||
| formData3.put("所属表单", ""); | |||
| formData3.put("所属试验信息", mjy.getMdIds()); | |||
| formData3.put("表单所属人", mjy.getBdId().toString()); | |||
| PdfBaseUtil.addFormTableColumns(document, formData3, 2); | |||
| logger.info("生成成功:{}", filePath); | |||
| } catch (Exception e) { | |||
| logger.error("生成失败", e); | |||
| throw new RuntimeException("生成失败: " + e.getMessage()); | |||
| } finally { | |||
| if (document != null) { | |||
| document.close(); | |||
| } | |||
| if (fos != null) { | |||
| try { | |||
| fos.close(); | |||
| } catch (IOException e) { | |||
| logger.error("关闭文件流失败", e); | |||
| } | |||
| } | |||
| } | |||
| return filePath; | |||
| } | |||
| } | |||
| @ -0,0 +1,55 @@ | |||
| package com.hxhq.business.utils.Pdf.Template.Sp; | |||
| import com.hxhq.business.utils.Pdf.PdfBaseUtil; | |||
| import com.itextpdf.text.Document; | |||
| import org.slf4j.Logger; | |||
| import org.slf4j.LoggerFactory; | |||
| import java.io.FileOutputStream; | |||
| import java.io.IOException; | |||
| import java.util.LinkedHashMap; | |||
| import java.util.Map; | |||
| /** | |||
| * 色谱001 | |||
| * @author tanfei | |||
| */ | |||
| public class Sp001 { | |||
| private static final Logger logger = LoggerFactory.getLogger(Sp001.class.getName()); | |||
| /**导出 | |||
| * @param headerText | |||
| * @param signText | |||
| * @return | |||
| */ | |||
| public static String exportDetail( String headerText,String signText) { | |||
| Document document = null; | |||
| FileOutputStream fos = null; | |||
| String filePath = PdfBaseUtil.getFilePath(); | |||
| try { | |||
| document=PdfBaseUtil.init(document,fos,filePath,signText,headerText); | |||
| Map<String, String> formData = new LinkedHashMap<>(); | |||
| formData.put("姓名","张三"); | |||
| formData.put("性别", "男"); | |||
| formData.put("手机","15882062878"); | |||
| // 生成table | |||
| PdfBaseUtil.addFormTableColumns(document, formData,3); | |||
| logger.info("生成成功:{}", filePath); | |||
| } catch (Exception e) { | |||
| logger.error("生成失败", e); | |||
| throw new RuntimeException("生成失败: " + e.getMessage()); | |||
| } finally { | |||
| if (document != null) { | |||
| document.close(); | |||
| } | |||
| if (fos != null) { | |||
| try { | |||
| fos.close(); | |||
| } catch (IOException e) { | |||
| logger.error("关闭文件流失败", e); | |||
| } | |||
| } | |||
| } | |||
| return filePath; | |||
| } | |||
| } | |||
| @ -1,372 +0,0 @@ | |||
| package com.hxhq.business.utils; | |||
| import com.hxhq.common.core.utils.DateUtils; | |||
| import com.itextpdf.text.*; | |||
| import com.itextpdf.text.Font; | |||
| import com.itextpdf.text.pdf.*; | |||
| import org.slf4j.Logger; | |||
| import org.slf4j.LoggerFactory; | |||
| import org.springframework.stereotype.Component; | |||
| import java.io.File; | |||
| import java.io.FileOutputStream; | |||
| import java.io.IOException; | |||
| import java.nio.file.Paths; | |||
| import java.util.*; | |||
| import java.util.List; | |||
| /** | |||
| * OpenPDF工具类 - 支持保存到本地文件 | |||
| */ | |||
| @Component | |||
| public class PdfUtil { | |||
| private static final Logger log = LoggerFactory.getLogger(PdfUtil.class); | |||
| public static void main(String[] args) { | |||
| /* String fileDir="d:/"; | |||
| exportForm("张三 2025-12-12 14:52:52",fileDir); | |||
| exportTable("张三 2025-12-12 14:52:52",fileDir);*/ | |||
| /*try { | |||
| appendWithPdfStamper("D:\\hxhq\\uploadPath\\2026\\01\\08\\20260108135814A001.pdf", "D:\\hxhq\\uploadPath\\2026\\01\\08\\output.pdf", | |||
| "这是使用iText 5追加的内容。"); | |||
| } catch (Exception e) { | |||
| e.printStackTrace(); | |||
| }*/ | |||
| exportTable("张三 2025-12-12 14:52:52","D:\\hxhq\\uploadPath\\2026\\01\\08"); | |||
| } | |||
| /** | |||
| * 复制原有PDF的所有页面到新Document中 | |||
| */ | |||
| private static void copyOriginalPagesToDocument(Document doc, PdfWriter writer, PdfReader reader) throws DocumentException { | |||
| int pageCount = reader.getNumberOfPages(); | |||
| PdfContentByte canvas = writer.getDirectContent(); // 获取直接内容画布 | |||
| for (int i = 1; i <= pageCount; i++) { | |||
| doc.newPage(); // 新建一页(与原PDF页面一一对应) | |||
| // 5.5.11中通过PdfImportedPage导入原有页面 | |||
| PdfImportedPage importedPage = writer.getImportedPage(reader, i); | |||
| // 将原有页面绘制到新文档的当前页 | |||
| canvas.addTemplate(importedPage, 0, 0); | |||
| } | |||
| } | |||
| /** | |||
| * 导出form | |||
| * @param headerText | |||
| * @param fileDir | |||
| * @return | |||
| */ | |||
| public static String exportForm(String headerText,String fileDir) { | |||
| Document document = null; | |||
| FileOutputStream fos = null; | |||
| String filePath = ""; | |||
| try { | |||
| // 1. 生成文件名 | |||
| String fileName = generateFileName(); | |||
| // 2. 确保目录存在 | |||
| File dir = new File(fileDir); | |||
| if (!dir.exists()) { | |||
| dir.mkdirs(); | |||
| } | |||
| // 3. 完整文件路径 | |||
| filePath = Paths.get(fileDir, fileName).toString(); | |||
| // 创建PDF文档 设置文档边距,避免内容遮挡页眉页脚 | |||
| float topMargin = 70; // 顶部边距(为页眉留出空间) | |||
| float bottomMargin = 60; // 底部边距(为页脚留出空间) | |||
| float leftMargin = 50; // 左边距 | |||
| float rightMargin = 50; // 右边距 | |||
| document = new Document(PageSize.A4, | |||
| leftMargin, rightMargin, topMargin, bottomMargin); | |||
| fos = new FileOutputStream(filePath); | |||
| PdfWriter writer = PdfWriter.getInstance(document, fos); | |||
| // 设置页面事件,每页添加文字页眉 | |||
| writer.setPageEvent(new TextHeaderEvent(headerText)); | |||
| // 5. 设置PDF属性 | |||
| document.addTitle("华西海圻"); | |||
| document.addAuthor("华西海圻"); | |||
| document.addCreator("华西海圻"); | |||
| document.addCreationDate(); | |||
| document.open(); | |||
| Map<String, String> formData = new LinkedHashMap<>(); | |||
| formData.put("姓名","张三"); | |||
| formData.put("性别", "男"); | |||
| formData.put("手机","15882062878"); | |||
| // 生成table | |||
| addFormTableColumns(document, formData,3); | |||
| log.info("生成成功:{}", filePath); | |||
| } catch (Exception e) { | |||
| log.error("生成失败", e); | |||
| throw new RuntimeException("生成失败: " + e.getMessage()); | |||
| } finally { | |||
| if (document != null) { | |||
| document.close(); | |||
| } | |||
| if (fos != null) { | |||
| try { | |||
| fos.close(); | |||
| } catch (IOException e) { | |||
| log.error("关闭文件流失败", e); | |||
| } | |||
| } | |||
| } | |||
| return filePath; | |||
| } | |||
| /** | |||
| * 导出table | |||
| * @param headerText | |||
| * @param fileDir | |||
| * @return | |||
| */ | |||
| public static String exportTable( String headerText,String fileDir) { | |||
| Document document = null; | |||
| FileOutputStream fos = null; | |||
| String filePath = ""; | |||
| try { | |||
| // 1. 生成文件名 | |||
| String fileName = generateFileName(); | |||
| // 2. 确保目录存在 | |||
| File dir = new File(fileDir); | |||
| if (!dir.exists()) { | |||
| dir.mkdirs(); | |||
| } | |||
| // 3. 完整文件路径 | |||
| filePath = Paths.get(fileDir, fileName).toString(); | |||
| // 创建PDF文档 设置文档边距,避免内容遮挡页眉页脚 | |||
| float topMargin = 70; // 顶部边距(为页眉留出空间) | |||
| float bottomMargin = 60; // 底部边距(为页脚留出空间) | |||
| float leftMargin = 50; // 左边距 | |||
| float rightMargin = 50; // 右边距 | |||
| document = new Document(PageSize.A4, | |||
| leftMargin, rightMargin, topMargin, bottomMargin); | |||
| fos = new FileOutputStream(filePath); | |||
| PdfWriter writer = PdfWriter.getInstance(document, fos); | |||
| // 设置页面事件,每页添加文字页眉 | |||
| writer.setPageEvent(new TextHeaderEvent(headerText)); | |||
| // 5. 设置PDF属性 | |||
| document.addTitle("华西海圻"); | |||
| document.addAuthor("华西海圻"); | |||
| document.addCreator("华西海圻"); | |||
| document.addCreationDate(); | |||
| document.open(); | |||
| // 6. 设置中文字体 | |||
| BaseFont bfChinese = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED); | |||
| Font headerFont = new Font(bfChinese, 12, Font.BOLD); | |||
| Font contentFont = new Font(bfChinese, 10, Font.NORMAL); | |||
| // 9. 创建表格 | |||
| PdfPTable table = new PdfPTable(3); | |||
| table.setWidthPercentage(100); | |||
| table.setSpacingBefore(10); | |||
| // 10. 表头 | |||
| String[] headers = {"签名人", "签名意义", "签名时间"}; | |||
| for (String header : headers) { | |||
| PdfPCell cell = new PdfPCell(new Phrase(header, headerFont)); | |||
| cell.setHorizontalAlignment(Element.ALIGN_CENTER); | |||
| cell.setBackgroundColor(BaseColor.WHITE); | |||
| cell.setPadding(8); | |||
| cell.setBorderWidth(1); | |||
| table.addCell(cell); | |||
| } | |||
| // 11. 表格数据 | |||
| int rowNum = 0; | |||
| for (int i = 0; i < 300; i ++) { | |||
| // 交替行颜色 | |||
| if (rowNum % 2 == 0) { | |||
| table.getDefaultCell().setBackgroundColor(BaseColor.WHITE); | |||
| } else { | |||
| table.getDefaultCell().setBackgroundColor(BaseColor.WHITE); | |||
| } | |||
| table.addCell(createCell("1", contentFont)); | |||
| table.addCell(createCell("2", contentFont)); | |||
| table.addCell(createCell("3", contentFont)); | |||
| rowNum++; | |||
| } | |||
| document.add(table); | |||
| log.info("生成成功:{}", filePath); | |||
| } catch (Exception e) { | |||
| log.error("生成失败", e); | |||
| throw new RuntimeException("生成失败: " + e.getMessage()); | |||
| } finally { | |||
| if (document != null) { | |||
| document.close(); | |||
| } | |||
| if (fos != null) { | |||
| try { | |||
| fos.close(); | |||
| } catch (IOException e) { | |||
| log.error("关闭文件流失败", e); | |||
| } | |||
| } | |||
| } | |||
| return filePath; | |||
| } | |||
| /** | |||
| * 创建表单 | |||
| */ | |||
| public static void addFormTableColumns(Document document, Map<String, String> data, Integer count) | |||
| throws DocumentException, IOException { | |||
| // 创建3列表格 | |||
| PdfPTable table = new PdfPTable(count); | |||
| table.setWidthPercentage(100); | |||
| table.setSpacingBefore(10); | |||
| table.setSpacingAfter(10); | |||
| // 设置等宽列 | |||
| table.setWidths(new float[]{1, 1, 1}); | |||
| // 创建字体 | |||
| BaseFont baseFont = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED); | |||
| Font labelFont = new Font(baseFont, 10, Font.NORMAL, BaseColor.BLACK); | |||
| Font valueFont = new Font(baseFont, 10, Font.NORMAL, BaseColor.BLACK); | |||
| // 将数据转换为List | |||
| List<Map.Entry<String, String>> entries = new ArrayList<>(data.entrySet()); | |||
| // 每3个字段一组,每组一行 | |||
| for (int i = 0; i < entries.size(); i += count) { | |||
| // 创建一行 | |||
| for (int j = 0; j < count; j++) { | |||
| int index = i + j; | |||
| if (index < entries.size()) { | |||
| Map.Entry<String, String> entry = entries.get(index); | |||
| // 创建单元格内容 | |||
| Phrase cellContent = new Phrase(); | |||
| cellContent.add(new Chunk(entry.getKey() + ":", labelFont)); | |||
| cellContent.add(new Chunk(com.hxhq.common.core.utils.StringUtils.isBlank(entry.getValue()) ? "" : entry.getValue(), valueFont)); | |||
| PdfPCell cell = new PdfPCell(cellContent); | |||
| cell.setBorder(Rectangle.NO_BORDER); // 无边框 | |||
| cell.setPadding(4); | |||
| cell.setHorizontalAlignment(Element.ALIGN_LEFT); | |||
| cell.setVerticalAlignment(Element.ALIGN_MIDDLE); | |||
| cell.setMinimumHeight(20); // 最小高度 | |||
| table.addCell(cell); | |||
| } else { | |||
| // 添加空单元格保持布局 | |||
| PdfPCell emptyCell = new PdfPCell(); | |||
| emptyCell.setBorder(Rectangle.NO_BORDER); | |||
| emptyCell.setMinimumHeight(20); | |||
| table.addCell(emptyCell); | |||
| } | |||
| } | |||
| } | |||
| document.add(table); | |||
| } | |||
| /** | |||
| * 创建表格 | |||
| */ | |||
| private static PdfPCell createCell(String content, Font font) { | |||
| PdfPCell cell = new PdfPCell(new Phrase(content, font)); | |||
| cell.setHorizontalAlignment(Element.ALIGN_CENTER); | |||
| cell.setVerticalAlignment(Element.ALIGN_MIDDLE); | |||
| cell.setPadding(6); | |||
| cell.setMinimumHeight(25); | |||
| return cell; | |||
| } | |||
| /** | |||
| * 生成文件名 | |||
| */ | |||
| private static String generateFileName() { | |||
| Random random= new Random(); | |||
| String timestamp = DateUtils.dateTimeNow("yyyyMMddHHmmss"); | |||
| return String.format("%s_%s.pdf", timestamp,random.nextInt(1000)); | |||
| } | |||
| /** | |||
| * 页面事件处理类 - 每页添加文字页眉 | |||
| */ | |||
| static class TextHeaderEvent extends PdfPageEventHelper { | |||
| private String headerText; | |||
| private BaseFont baseFont; | |||
| public TextHeaderEvent(String headerText) throws Exception { | |||
| this.headerText = headerText; | |||
| // 创建字体(支持中文) | |||
| this.baseFont = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED); | |||
| } | |||
| @Override | |||
| public void onEndPage(PdfWriter writer, Document document) { | |||
| try { | |||
| // 获取页面尺寸 | |||
| Rectangle pageSize = document.getPageSize(); | |||
| float pageWidth = pageSize.getWidth(); | |||
| float pageHeight = pageSize.getHeight(); | |||
| // 设置页眉参数 | |||
| float topMargin = 30; // 顶部边距 | |||
| float fontSize = 12; // 字体大小 | |||
| // 计算文字宽度(用于居中) | |||
| float textWidth = baseFont.getWidthPoint(headerText, fontSize); | |||
| // 计算居中位置 | |||
| float textX = (pageWidth - textWidth) / 2; | |||
| float textY = pageHeight - topMargin; | |||
| // 获取画布 | |||
| PdfContentByte canvas = writer.getDirectContent(); | |||
| // 添加页眉文字 | |||
| canvas.beginText(); | |||
| canvas.setFontAndSize(baseFont, fontSize); | |||
| canvas.setTextMatrix(textX, textY); | |||
| canvas.showText(headerText); | |||
| canvas.endText(); | |||
| // 添加页眉分隔线(可选) | |||
| addHeaderLine(canvas, pageWidth, textY - 10); | |||
| } catch (Exception e) { | |||
| e.printStackTrace(); | |||
| } | |||
| } | |||
| private void addHeaderLine(PdfContentByte canvas, float pageWidth, float yPos) { | |||
| canvas.setLineWidth(0.5f); | |||
| canvas.moveTo(50, yPos); | |||
| canvas.lineTo(pageWidth - 50, yPos); | |||
| canvas.stroke(); | |||
| } | |||
| } | |||
| } | |||