优化了PDF导出的效果

master
yangzhe123 2025-07-25 16:49:58 +08:00
parent cca08d73e9
commit fd7fd56eb5
2 changed files with 155 additions and 55 deletions

23
pom.xml
View File

@ -49,18 +49,37 @@
<dependencies> <dependencies>
<!-- iTextPDF核心库 - 用于PDF生成 --> <!-- iTextPDF核心库 - 用于PDF生成 -->
<dependency> <dependency>
<groupId>com.itextpdf</groupId> <groupId>com.itextpdf</groupId>
<artifactId>kernel</artifactId> <artifactId>kernel</artifactId>
<version>7.2.5</version> <version>7.2.4</version>
</dependency> </dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>io</artifactId>
<version>7.2.4</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>forms</artifactId>
<version>7.2.4</version>
</dependency>
<dependency> <dependency>
<groupId>com.itextpdf</groupId> <groupId>com.itextpdf</groupId>
<artifactId>layout</artifactId> <artifactId>layout</artifactId>
<version>7.2.5</version> <version>7.2.4</version>
</dependency> </dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>svg</artifactId>
<version>7.2.4</version>
</dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>

View File

@ -4,35 +4,40 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.itextpdf.io.font.PdfEncodings; import com.itextpdf.io.font.PdfEncodings;
import com.itextpdf.kernel.colors.ColorConstants; import com.itextpdf.kernel.colors.ColorConstants;
import com.itextpdf.kernel.events.Event;
import com.itextpdf.kernel.events.IEventHandler;
import com.itextpdf.kernel.events.PdfDocumentEvent;
import com.itextpdf.kernel.font.PdfFont; import com.itextpdf.kernel.font.PdfFont;
import com.itextpdf.kernel.font.PdfFontFactory; import com.itextpdf.kernel.font.PdfFontFactory;
import com.itextpdf.kernel.geom.PageSize; import com.itextpdf.kernel.geom.PageSize;
import com.itextpdf.kernel.geom.Rectangle;
import com.itextpdf.kernel.pdf.PdfDocument; import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfPage;
import com.itextpdf.kernel.pdf.PdfReader;
import com.itextpdf.kernel.pdf.PdfWriter; import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.kernel.pdf.canvas.PdfCanvas;
import com.itextpdf.layout.Document; import com.itextpdf.layout.Document;
import com.itextpdf.layout.borders.Border; import com.itextpdf.layout.borders.Border;
import com.itextpdf.layout.element.Cell; import com.itextpdf.layout.element.Cell;
import com.itextpdf.layout.element.Div;
import com.itextpdf.layout.element.Paragraph; import com.itextpdf.layout.element.Paragraph;
import com.itextpdf.layout.element.Table; import com.itextpdf.layout.element.Table;
import com.itextpdf.layout.properties.TextAlignment; import com.itextpdf.layout.properties.TextAlignment;
import com.itextpdf.layout.properties.UnitValue; import com.itextpdf.layout.properties.UnitValue;
import com.whdc.mapper.*; import com.whdc.mapper.*;
import com.whdc.model.dto.AutoCallDto; import com.whdc.model.dto.AutoCallDto;
import com.whdc.model.dto.FindPageDto;
import com.whdc.model.entity.AutoCall; import com.whdc.model.entity.AutoCall;
import com.whdc.model.entity.AutoCallPerson; import com.whdc.model.entity.AutoCallPerson;
import com.whdc.model.entity.AutoCallTask; import com.whdc.model.entity.AutoCallTask;
import com.whdc.model.entity.WarnCallMap; import com.whdc.model.entity.WarnCallMap;
import com.whdc.utils.DateUtils; import com.whdc.utils.DateUtils;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.checkerframework.checker.units.qual.A;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.text.ParseException; import java.text.ParseException;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
@ -236,6 +241,45 @@ public class AutoCallApiService {
return pageResult; return pageResult;
} }
private static class PageNumberEventHandler implements IEventHandler {
protected PdfDocument pdfDoc;
protected PdfFont font;
protected int totalPages;
public PageNumberEventHandler(PdfDocument pdfDoc, PdfFont font, int totalPages) {
this.pdfDoc = pdfDoc;
this.font = font;
this.totalPages = totalPages;
}
@Override
public void handleEvent(Event event) {
PdfDocumentEvent docEvent = (PdfDocumentEvent) event;
PdfPage page = docEvent.getPage();
int pageNumber = pdfDoc.getPageNumber(page);
Rectangle pageSize = page.getPageSize();
PdfCanvas pdfCanvas = new PdfCanvas(page);
try {
pdfCanvas.setFontAndSize(font, 10);
} catch (Exception e) {
throw new RuntimeException("Error setting font for page number", e);
}
String text = String.format("第 %d 页/共 %d 页", pageNumber, totalPages);
float textWidth = font.getWidth(text, 10);
float x = (pageSize.getLeft() + pageSize.getRight()) / 2 - textWidth / 2;
float y = pageSize.getBottom() + 15;
pdfCanvas.beginText()
.setTextMatrix(x, y)
.showText(text)
.endText()
.release();
}
}
/** /**
* pdf * pdf
* @param records * @param records
@ -246,15 +290,57 @@ public class AutoCallApiService {
*/ */
public ByteArrayInputStream exportPDF(List<AutoCallTask> records, HttpServletResponse response, String stm, String etm) { public ByteArrayInputStream exportPDF(List<AutoCallTask> records, HttpServletResponse response, String stm, String etm) {
ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream();
// 第一遍:计算总页数
ByteArrayOutputStream tempOut = new ByteArrayOutputStream();
try (PdfWriter tempWriter = new PdfWriter(tempOut);
PdfDocument tempPdfDoc = new PdfDocument(tempWriter);
Document tempDoc = new Document(tempPdfDoc, PageSize.A4.rotate())) {
String fontPath = "/fonts/ChineseFonts.ttf";
PdfFont font = PdfFontFactory.createFont(fontPath, PdfEncodings.IDENTITY_H);
tempDoc.setFont(font);
// 添加内容但不处理页码
addPDFContent(tempDoc, records, stm, etm, font);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
// 获取总页数
int totalPages;
try (ByteArrayInputStream tempIn = new ByteArrayInputStream(tempOut.toByteArray());
PdfReader tempReader = new PdfReader(tempIn);
PdfDocument tempPdfDoc = new PdfDocument(tempReader)) {
totalPages = tempPdfDoc.getNumberOfPages();
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("Failed to calculate total pages", e);
}
// 第二遍正式生成PDF
try (PdfWriter writer = new PdfWriter(out); try (PdfWriter writer = new PdfWriter(out);
// 设置A4横向宽297mm高210mm
PdfDocument pdfDoc = new PdfDocument(writer); PdfDocument pdfDoc = new PdfDocument(writer);
Document document = new Document(pdfDoc, PageSize.A4.rotate())) { Document document = new Document(pdfDoc, PageSize.A4.rotate())) {
// 添加标题 String fontPath = "/fonts/ChineseFonts.ttf";
String fontPath = "/fonts/ChineseFonts.ttf";//设置中文字体(没有这个,中文会乱码)
PdfFont font = PdfFontFactory.createFont(fontPath, PdfEncodings.IDENTITY_H); PdfFont font = PdfFontFactory.createFont(fontPath, PdfEncodings.IDENTITY_H);
document.setFont(font); document.setFont(font);
// 创建并注册页码事件处理器,传入总页数
PageNumberEventHandler pageNumberEventHandler = new PageNumberEventHandler(pdfDoc, font, totalPages);
pdfDoc.addEventHandler(PdfDocumentEvent.END_PAGE, pageNumberEventHandler);
// 添加内容
addPDFContent(document, records, stm, etm,font);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
return new ByteArrayInputStream(out.toByteArray());
}
private void addPDFContent(Document document, List<AutoCallTask> records, String stm, String etm, PdfFont font) throws IOException{
document.add(new Paragraph("智能呼叫记录报表") document.add(new Paragraph("智能呼叫记录报表")
.setFontSize(16) .setFontSize(16)
.setBold() .setBold()
@ -284,11 +370,11 @@ public class AutoCallApiService {
150, // 预警名称 150, // 预警名称
120, // 智能呼叫时间 120, // 智能呼叫时间
100, // 县(市、区) 100, // 县(市、区)
160, // 呼叫对象及职务
130, // 状态 130, // 状态
59, // 通话时长(秒) 80, // 通话时长(秒)
150, // 拨打时间 130, // 拨打时间
150, // 结束时间 130 // 结束时间
100 // 呼叫对象及职务
}; };
Table table = new Table(columnWidths); Table table = new Table(columnWidths);
table.setKeepTogether(true); table.setKeepTogether(true);
@ -296,15 +382,11 @@ public class AutoCallApiService {
// 添加表头 // 添加表头
addTableHeader(table); addTableHeader(table);
// 添加数据行 // 添加数据行
addTableData(table, records); addTableData(table, records,columnWidths,font);
document.add(table); document.add(table);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
return new ByteArrayInputStream(out.toByteArray());
} }
// 更新表头(添加新字段) // 更新表头(添加新字段)
private void addTableHeader(Table table) { private void addTableHeader(Table table) {
String[] headers = { String[] headers = {
@ -312,11 +394,11 @@ private void addTableHeader(Table table) {
"预警名称", "预警名称",
"智能呼叫时间", "智能呼叫时间",
"县(市、区)", "县(市、区)",
"呼叫对象及职务",
"状态", "状态",
"通话时长", "通话时长",
"拨打时间", "拨打时间",
"结束时间", "结束时间"
"呼叫对象及职务"
}; };
for (String header : headers) { for (String header : headers) {
table.addHeaderCell(new Cell() table.addHeaderCell(new Cell()
@ -327,12 +409,11 @@ private void addTableHeader(Table table) {
} }
// 更新数据行(适配新字段) // 更新数据行(适配新字段)
private void addTableData(Table table, List<AutoCallTask> records) { private void addTableData(Table table, List<AutoCallTask> records, float[] columnWidths, PdfFont font) {
int serialNumber = 1; int serialNumber = 1;
for (AutoCallTask record : records) { for (AutoCallTask record : records) {
// 拼接县(市、区) // 拼接县(市、区)
String region = record.getWarnCtnm() + record.getWarnCnnm(); String region = record.getWarnCtnm() + record.getWarnCnnm();
// 处理呼叫对象信息 // 处理呼叫对象信息
StringBuilder callInfo = new StringBuilder(); StringBuilder callInfo = new StringBuilder();
for (AutoCallPerson item : record.getCallList()) { for (AutoCallPerson item : record.getCallList()) {
@ -372,11 +453,11 @@ private void addTableHeader(Table table) {
table.addCell(new Cell().add(new Paragraph(record.getWarnName() != null ? record.getWarnName() : "").setFontSize(10)).setKeepTogether(true)); table.addCell(new Cell().add(new Paragraph(record.getWarnName() != null ? record.getWarnName() : "").setFontSize(10)).setKeepTogether(true));
table.addCell(new Cell().add(new Paragraph(formattedCreateTime).setFontSize(10)).setKeepTogether(true)); table.addCell(new Cell().add(new Paragraph(formattedCreateTime).setFontSize(10)).setKeepTogether(true));
table.addCell(new Cell().add(new Paragraph(region).setFontSize(10)).setKeepTogether(true)); table.addCell(new Cell().add(new Paragraph(region).setFontSize(10)).setKeepTogether(true));
table.addCell(new Cell().add(new Paragraph(callInfo.toString()).setFontSize(10)).setKeepTogether(true));
table.addCell(new Cell().add(new Paragraph(status.toString()).setFontSize(10)).setKeepTogether(true)); table.addCell(new Cell().add(new Paragraph(status.toString()).setFontSize(10)).setKeepTogether(true));
table.addCell(new Cell().add(new Paragraph(callTime.toString()).setFontSize(10)).setKeepTogether(true)); table.addCell(new Cell().add(new Paragraph(callTime.toString()).setFontSize(10)).setKeepTogether(true));
table.addCell(new Cell().add(new Paragraph(startTime.toString()).setFontSize(10)).setKeepTogether(true)); table.addCell(new Cell().add(new Paragraph(startTime.toString()).setFontSize(10)).setKeepTogether(true));
table.addCell(new Cell().add(new Paragraph(endTime.toString()).setFontSize(10)).setKeepTogether(true)); table.addCell(new Cell().add(new Paragraph(endTime.toString()).setFontSize(10)).setKeepTogether(true));
table.addCell(new Cell().add(new Paragraph(callInfo.toString()).setFontSize(10)).setKeepTogether(true));
} }
} }