Browse Source

feat:pdf导出类

master
15881625488@163.com 4 days ago
parent
commit
cf3ba9b4fc
2 changed files with 410 additions and 0 deletions
  1. +22
    -0
      hxhq-modules/hxhq-system/pom.xml
  2. +388
    -0
      hxhq-modules/hxhq-system/src/main/java/com/hxhq/business/utils/PdfUtil.java

+ 22
- 0
hxhq-modules/hxhq-system/pom.xml View File

@ -77,6 +77,28 @@
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- pdf:start -->
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itextpdf</artifactId>
<version>5.5.11</version>
</dependency>
<dependency>
<groupId>com.itextpdf.tool</groupId>
<artifactId>xmlworker</artifactId>
<version>5.5.11</version>
</dependency>
<!-- 支持中文 -->
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itext-asian</artifactId>
<version>5.2.0</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
<build>

+ 388
- 0
hxhq-modules/hxhq-system/src/main/java/com/hxhq/business/utils/PdfUtil.java View File

@ -0,0 +1,388 @@
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 com.itextpdf.text.pdf.draw.LineSeparator;
import lombok.SneakyThrows;
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);
}
/**
* 导出form
* @param qm
* @param fileDir
* @return
*/
public static String exportForm(String qm,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();
// 4. 创建PDF文档
document = new Document(PageSize.A4);
fos = new FileOutputStream(filePath);
PdfWriter writer = PdfWriter.getInstance(document, fos);
// 使用完整的页脚处理器
writer.setPageEvent(new CompletePdfFooter());
// 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 titleFont = new Font(bfChinese, 16, Font.BOLD);
// 7. 添加签名
Paragraph title = new Paragraph(qm, titleFont);
title.setAlignment(Element.ALIGN_CENTER);
title.setSpacingAfter(20);
document.add(title);
//横线
LineSeparator line = new LineSeparator();
line.setLineWidth(0.5f);
// 设置100%宽度
line.setPercentage(100f); // 百分比宽度
// 设置对齐方式
line.setAlignment(Element.ALIGN_CENTER); // Element.ALIGN_CENTER, Element.ALIGN_LEFT等
// 设置颜色
line.setLineColor(BaseColor.BLACK);
// 添加到文档
document.add(line);
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 qm
* @param fileDir
* @return
*/
public static String exportTable( String qm,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();
// 4. 创建PDF文档
document = new Document(PageSize.A4);
fos = new FileOutputStream(filePath);
PdfWriter writer = PdfWriter.getInstance(document, fos);
// 使用完整的页脚处理器
writer.setPageEvent(new CompletePdfFooter());
// 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 titleFont = new Font(bfChinese, 18, Font.BOLD);
Font headerFont = new Font(bfChinese, 12, Font.BOLD);
Font contentFont = new Font(bfChinese, 10, Font.NORMAL);
// 7. 添加签名
Paragraph title = new Paragraph(qm, titleFont);
title.setAlignment(Element.ALIGN_CENTER);
title.setSpacingAfter(20);
document.add(title);
//横线
LineSeparator line = new LineSeparator();
line.setLineWidth(0.5f);
// 设置100%宽度
line.setPercentage(100f); // 百分比宽度
// 设置对齐方式
line.setAlignment(Element.ALIGN_CENTER); // Element.ALIGN_CENTER, Element.ALIGN_LEFT等
// 设置颜色
line.setLineColor(BaseColor.BLACK);
// 添加到文档
document.add(line);
// 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 < 3; 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));
}
/**
* 页脚
*/
public static class CompletePdfFooter extends PdfPageEventHelper {
private Font footerFont;
private PdfTemplate total;
private int totalPages = 0;
public CompletePdfFooter() {
try {
BaseFont 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);
}
@SneakyThrows
@Override
public void onEndPage(PdfWriter writer, Document document) {
PdfContentByte cb = 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);
}
@Override
public void onCloseDocument(PdfWriter writer, Document document) {
totalPages = writer.getPageNumber();
String totalPagesStr = String.valueOf(totalPages);
// 重新创建合适宽度的模板
BaseFont baseFont = footerFont.getBaseFont();
total.beginText();
total.setFontAndSize(baseFont, footerFont.getSize());
total.setTextMatrix(0, 0);
total.showText(totalPagesStr);
total.endText();
}
}
}

Loading…
Cancel
Save