diff --git a/pom.xml b/pom.xml index ebf41ae..d155a6c 100644 --- a/pom.xml +++ b/pom.xml @@ -49,18 +49,37 @@ + com.itextpdf kernel - 7.2.5 + 7.2.4 + + + com.itextpdf + io + 7.2.4 + + + + com.itextpdf + forms + 7.2.4 + + com.itextpdf layout - 7.2.5 + 7.2.4 + + com.itextpdf + svg + 7.2.4 + org.springframework.boot diff --git a/src/main/java/com/whdc/service/AutoCallApiService.java b/src/main/java/com/whdc/service/AutoCallApiService.java index 1289cf9..a38711d 100644 --- a/src/main/java/com/whdc/service/AutoCallApiService.java +++ b/src/main/java/com/whdc/service/AutoCallApiService.java @@ -4,35 +4,40 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.itextpdf.io.font.PdfEncodings; 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.PdfFontFactory; import com.itextpdf.kernel.geom.PageSize; +import com.itextpdf.kernel.geom.Rectangle; 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.canvas.PdfCanvas; import com.itextpdf.layout.Document; import com.itextpdf.layout.borders.Border; import com.itextpdf.layout.element.Cell; -import com.itextpdf.layout.element.Div; import com.itextpdf.layout.element.Paragraph; import com.itextpdf.layout.element.Table; import com.itextpdf.layout.properties.TextAlignment; import com.itextpdf.layout.properties.UnitValue; import com.whdc.mapper.*; import com.whdc.model.dto.AutoCallDto; -import com.whdc.model.dto.FindPageDto; import com.whdc.model.entity.AutoCall; import com.whdc.model.entity.AutoCallPerson; import com.whdc.model.entity.AutoCallTask; import com.whdc.model.entity.WarnCallMap; import com.whdc.utils.DateUtils; import lombok.extern.slf4j.Slf4j; -import org.checkerframework.checker.units.qual.A; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import javax.servlet.http.HttpServletResponse; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; +import java.io.IOException; import java.text.ParseException; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; @@ -236,6 +241,45 @@ public class AutoCallApiService { 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导出 * @param records @@ -246,65 +290,103 @@ public class AutoCallApiService { */ public ByteArrayInputStream exportPDF(List records, HttpServletResponse response, String stm, String etm) { 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); - // 设置A4横向(宽297mm,高210mm) PdfDocument pdfDoc = new PdfDocument(writer); 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); document.setFont(font); - document.add(new Paragraph("智能呼叫记录报表") - .setFontSize(16) - .setBold() - .setTextAlignment(TextAlignment.CENTER) - .setMarginBottom(20)); - // 创建头部布局(时间段和签名) - float[] columnWidths1 = {1, 1}; - Table layoutTable = new Table(columnWidths1) - .setWidth(UnitValue.createPercentValue(100)) - .setMarginBottom(15); + // 创建并注册页码事件处理器,传入总页数 + PageNumberEventHandler pageNumberEventHandler = new PageNumberEventHandler(pdfDoc, font, totalPages); + pdfDoc.addEventHandler(PdfDocumentEvent.END_PAGE, pageNumberEventHandler); - Paragraph leftPara = new Paragraph(String.format("筛选时间段:%s 至 %s", stm, etm)) - .setFontSize(12) - .setTextAlignment(TextAlignment.LEFT); - Paragraph rightPara = new Paragraph("值班人员签名:________________________") - .setFontSize(12) - .setTextAlignment(TextAlignment.RIGHT); - - layoutTable.addCell(new Cell().add(leftPara).setBorder(Border.NO_BORDER)); - layoutTable.addCell(new Cell().add(rightPara).setBorder(Border.NO_BORDER)); - document.add(layoutTable); - - // 定义表格列宽(适配横向A4,新增字段列) - float[] columnWidths = { - 30, // 序号 - 150, // 预警名称 - 120, // 智能呼叫时间 - 100, // 县(市、区) - 130, // 状态 - 59, // 通话时长(秒) - 150, // 拨打时间 - 150, // 结束时间 - 100 // 呼叫对象及职务 - }; - Table table = new Table(columnWidths); - table.setKeepTogether(true); - table.setSkipFirstHeader(false); - // 添加表头 - addTableHeader(table); - // 添加数据行 - addTableData(table, records); - - document.add(table); + // 添加内容 + 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 records, String stm, String etm, PdfFont font) throws IOException{ + document.add(new Paragraph("智能呼叫记录报表") + .setFontSize(16) + .setBold() + .setTextAlignment(TextAlignment.CENTER) + .setMarginBottom(20)); + + // 创建头部布局(时间段和签名) + float[] columnWidths1 = {1, 1}; + Table layoutTable = new Table(columnWidths1) + .setWidth(UnitValue.createPercentValue(100)) + .setMarginBottom(15); + + Paragraph leftPara = new Paragraph(String.format("筛选时间段:%s 至 %s", stm, etm)) + .setFontSize(12) + .setTextAlignment(TextAlignment.LEFT); + Paragraph rightPara = new Paragraph("值班人员签名:________________________") + .setFontSize(12) + .setTextAlignment(TextAlignment.RIGHT); + + layoutTable.addCell(new Cell().add(leftPara).setBorder(Border.NO_BORDER)); + layoutTable.addCell(new Cell().add(rightPara).setBorder(Border.NO_BORDER)); + document.add(layoutTable); + + // 定义表格列宽(适配横向A4,新增字段列) + float[] columnWidths = { + 30, // 序号 + 150, // 预警名称 + 120, // 智能呼叫时间 + 100, // 县(市、区) + 160, // 呼叫对象及职务 + 130, // 状态 + 80, // 通话时长(秒) + 130, // 拨打时间 + 130 // 结束时间 + }; + Table table = new Table(columnWidths); + table.setKeepTogether(true); + table.setSkipFirstHeader(false); + // 添加表头 + addTableHeader(table); + // 添加数据行 + addTableData(table, records,columnWidths,font); + + document.add(table); + } + // 更新表头(添加新字段) private void addTableHeader(Table table) { String[] headers = { @@ -312,11 +394,11 @@ private void addTableHeader(Table table) { "预警名称", "智能呼叫时间", "县(市、区)", + "呼叫对象及职务", "状态", "通话时长", "拨打时间", - "结束时间", - "呼叫对象及职务" + "结束时间" }; for (String header : headers) { table.addHeaderCell(new Cell() @@ -327,12 +409,11 @@ private void addTableHeader(Table table) { } // 更新数据行(适配新字段) - private void addTableData(Table table, List records) { + private void addTableData(Table table, List records, float[] columnWidths, PdfFont font) { int serialNumber = 1; for (AutoCallTask record : records) { // 拼接县(市、区) String region = record.getWarnCtnm() + record.getWarnCnnm(); - // 处理呼叫对象信息 StringBuilder callInfo = new StringBuilder(); 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(formattedCreateTime).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(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(endTime.toString()).setFontSize(10)).setKeepTogether(true)); - table.addCell(new Cell().add(new Paragraph(callInfo.toString()).setFontSize(10)).setKeepTogether(true)); } }