|
|
|
@ -53,16 +53,14 @@ public class PDFGenerator {
|
|
|
|
private static final float CELL_PADDING = 5;
|
|
|
|
private static final float CELL_PADDING = 5;
|
|
|
|
private static final float[] COLUMN_WIDTHS = {LEFT_COL_WIDTH, RIGHT_COL_WIDTH};
|
|
|
|
private static final float[] COLUMN_WIDTHS = {LEFT_COL_WIDTH, RIGHT_COL_WIDTH};
|
|
|
|
private static final float MIN_IMAGE_HEIGHT = 50;
|
|
|
|
private static final float MIN_IMAGE_HEIGHT = 50;
|
|
|
|
public static void generateInstallationPDF(DeviceInstallInfo entity, OutputStream outputStream) throws IOException {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 首先生成PDF内容到字节数组
|
|
|
|
|
|
|
|
ByteArrayOutputStream pdfBaos = new ByteArrayOutputStream();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public static void generateInstallationPDFBytes(DeviceInstallInfo entity, ByteArrayOutputStream outputStream) throws IOException {
|
|
|
|
try (PDDocument document = new PDDocument()) {
|
|
|
|
try (PDDocument document = new PDDocument()) {
|
|
|
|
// 加载字体(需替换实际路径)
|
|
|
|
// 加载字体
|
|
|
|
// PDType0Font font = PDType0Font.load(document, new File("D:\\NotoSansCJK-Regular.ttf"));
|
|
|
|
// PDType0Font font = PDType0Font.load(document, new File("D:\\NotoSansCJK-Regular.ttf"));
|
|
|
|
//PDType0Font font = PDType0Font.load(document, new File("/ttf/simsun.ttf"));
|
|
|
|
PDType0Font font = PDType0Font.load(document, new File("/ttf/NotoSansCJK-Regular.ttf"));
|
|
|
|
PDType0Font font = PDType0Font.load(document, new File("/ttf/NotoSansCJK-Regular.ttf"));
|
|
|
|
|
|
|
|
// 初始化第一页
|
|
|
|
// 初始化第一页
|
|
|
|
PDPage currentPage = new PDPage(PDRectangle.A4);
|
|
|
|
PDPage currentPage = new PDPage(PDRectangle.A4);
|
|
|
|
document.addPage(currentPage);
|
|
|
|
document.addPage(currentPage);
|
|
|
|
@ -71,20 +69,18 @@ public class PDFGenerator {
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
try {
|
|
|
|
// 1. 绘制标题(居中)
|
|
|
|
// 1. 绘制标题(居中)
|
|
|
|
drawCenteredText(cs, font, FONT_SIZE+10,
|
|
|
|
drawCenteredText(cs, font, FONT_SIZE + 10,
|
|
|
|
"安装信息录入表",
|
|
|
|
"安装信息录入表",
|
|
|
|
PDRectangle.A4.getWidth()/2, currentY);
|
|
|
|
PDRectangle.A4.getWidth() / 2, currentY);
|
|
|
|
currentY -= ROW_HEIGHT * 1.5f;
|
|
|
|
currentY -= ROW_HEIGHT * 1.5f;
|
|
|
|
|
|
|
|
|
|
|
|
// 2. 绘制基本信息表格
|
|
|
|
// 2. 绘制基本信息表格
|
|
|
|
String[][] baseData = {
|
|
|
|
String[][] baseData = {
|
|
|
|
// {"公司名称", entity.getCorporateName()},
|
|
|
|
|
|
|
|
{"申请时间", entity.getProposerTime().toString()},
|
|
|
|
{"申请时间", entity.getProposerTime().toString()},
|
|
|
|
{"申请人", entity.getProposer()},
|
|
|
|
{"申请人", entity.getProposer()},
|
|
|
|
{"申请人班组", entity.getProposerTeam()},
|
|
|
|
{"申请人班组", entity.getProposerTeam()},
|
|
|
|
{"小区名字", entity.getCommunityName()},
|
|
|
|
{"小区名字", entity.getCommunityName()},
|
|
|
|
{"用户姓名", entity.getUserName()},
|
|
|
|
{"用户姓名", entity.getUserName()},
|
|
|
|
// {"用户姓名", entity.getUserName()+"-" + entity.getCommunityName()+entity.getBuildingUnit()+entity.getRoomNo()},
|
|
|
|
|
|
|
|
{"电话", entity.getUserIpone()},
|
|
|
|
{"电话", entity.getUserIpone()},
|
|
|
|
{"楼栋单元号", entity.getBuildingUnit()},
|
|
|
|
{"楼栋单元号", entity.getBuildingUnit()},
|
|
|
|
{"房间号", entity.getRoomNo()},
|
|
|
|
{"房间号", entity.getRoomNo()},
|
|
|
|
@ -92,14 +88,11 @@ public class PDFGenerator {
|
|
|
|
{"切断阀编号", entity.getShutValueNumber()},
|
|
|
|
{"切断阀编号", entity.getShutValueNumber()},
|
|
|
|
{"燃气表号", entity.getGasMeterNumber()},
|
|
|
|
{"燃气表号", entity.getGasMeterNumber()},
|
|
|
|
{"备注", entity.getBuildingUnit()}
|
|
|
|
{"备注", entity.getBuildingUnit()}
|
|
|
|
|
|
|
|
|
|
|
|
};
|
|
|
|
};
|
|
|
|
drawTable(cs, font, TABLE_MARGIN, currentY, baseData, COLUMN_WIDTHS);
|
|
|
|
drawTable(cs, font, TABLE_MARGIN, currentY, baseData, COLUMN_WIDTHS);
|
|
|
|
currentY -= (ROW_HEIGHT * baseData.length) + 20f;
|
|
|
|
currentY -= (ROW_HEIGHT * baseData.length) + 20f;
|
|
|
|
// @ApiModelProperty(value = "安装前图片")
|
|
|
|
|
|
|
|
// 创建后续页面 - 图片部分
|
|
|
|
// 3. 图片处理(带自动分页)
|
|
|
|
// 图片处理(带自动分页)
|
|
|
|
|
|
|
|
// 图片处理(带自动分页)
|
|
|
|
|
|
|
|
List<String[]> imageGroups = Arrays.asList(
|
|
|
|
List<String[]> imageGroups = Arrays.asList(
|
|
|
|
new String[]{entity.getDeviceInfoImage(), "设备信息图片"},
|
|
|
|
new String[]{entity.getDeviceInfoImage(), "设备信息图片"},
|
|
|
|
new String[]{entity.getBeforeInstallationImage(), "安装前图片"},
|
|
|
|
new String[]{entity.getBeforeInstallationImage(), "安装前图片"},
|
|
|
|
@ -184,37 +177,201 @@ public class PDFGenerator {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
} finally {
|
|
|
|
} finally {
|
|
|
|
cs.close();
|
|
|
|
cs.close();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// 将PDF保存到ByteArrayOutputStream
|
|
|
|
|
|
|
|
document.save(pdfBaos);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
|
|
|
// 将PDF保存到输出流
|
|
|
|
// document.save(baos);
|
|
|
|
document.save(outputStream);
|
|
|
|
// outputStream.flush();
|
|
|
|
}
|
|
|
|
// baos.writeTo(outputStream);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
public static void generateInstallationPDF(DeviceInstallInfo entity, OutputStream outputStream) throws IOException {
|
|
|
|
// 创建ZIP压缩包并将PDF写入输出流
|
|
|
|
ByteArrayOutputStream pdfBaos = new ByteArrayOutputStream();
|
|
|
|
|
|
|
|
generateInstallationPDFBytes(entity, pdfBaos);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 创建ZIP压缩包
|
|
|
|
try (ZipOutputStream zipOut = new ZipOutputStream(outputStream)) {
|
|
|
|
try (ZipOutputStream zipOut = new ZipOutputStream(outputStream)) {
|
|
|
|
// 设置最高压缩级别
|
|
|
|
|
|
|
|
zipOut.setLevel(Deflater.BEST_COMPRESSION);
|
|
|
|
zipOut.setLevel(Deflater.BEST_COMPRESSION);
|
|
|
|
// 创建ZIP条目
|
|
|
|
ZipEntry entry = new ZipEntry(entity.getUserName() + entity.getCommunityName() + entity.getBuildingUnit() + entity.getRoomNo() + ".pdf");
|
|
|
|
ZipEntry entry = new ZipEntry(entity.getUserName()+entity.getCommunityName()+entity.getBuildingUnit()+entity.getRoomNo()+".pdf");
|
|
|
|
|
|
|
|
zipOut.putNextEntry(entry);
|
|
|
|
zipOut.putNextEntry(entry);
|
|
|
|
|
|
|
|
|
|
|
|
// 将PDF数据写入ZIP
|
|
|
|
|
|
|
|
byte[] pdfBytes = pdfBaos.toByteArray();
|
|
|
|
byte[] pdfBytes = pdfBaos.toByteArray();
|
|
|
|
zipOut.write(pdfBytes, 0, pdfBytes.length);
|
|
|
|
zipOut.write(pdfBytes, 0, pdfBytes.length);
|
|
|
|
|
|
|
|
|
|
|
|
// 关闭当前条目
|
|
|
|
|
|
|
|
zipOut.closeEntry();
|
|
|
|
zipOut.closeEntry();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 辅助方法:生成文件名
|
|
|
|
|
|
|
|
public static String generateFileName(DeviceInstallInfo entity) {
|
|
|
|
|
|
|
|
return entity.getUserName() + entity.getCommunityName() +
|
|
|
|
|
|
|
|
entity.getBuildingUnit() + entity.getRoomNo() + ".pdf";
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// public static void generateInstallationPDF(DeviceInstallInfo entity, OutputStream outputStream) throws IOException {
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// // 首先生成PDF内容到字节数组
|
|
|
|
|
|
|
|
// ByteArrayOutputStream pdfBaos = new ByteArrayOutputStream();
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// try (PDDocument document = new PDDocument()) {
|
|
|
|
|
|
|
|
// // 加载字体(需替换实际路径)
|
|
|
|
|
|
|
|
//// PDType0Font font = PDType0Font.load(document, new File("D:\\NotoSansCJK-Regular.ttf"));
|
|
|
|
|
|
|
|
// //PDType0Font font = PDType0Font.load(document, new File("/ttf/simsun.ttf"));
|
|
|
|
|
|
|
|
// PDType0Font font = PDType0Font.load(document, new File("/ttf/NotoSansCJK-Regular.ttf"));
|
|
|
|
|
|
|
|
// // 初始化第一页
|
|
|
|
|
|
|
|
// PDPage currentPage = new PDPage(PDRectangle.A4);
|
|
|
|
|
|
|
|
// document.addPage(currentPage);
|
|
|
|
|
|
|
|
// PDPageContentStream cs = new PDPageContentStream(document, currentPage);
|
|
|
|
|
|
|
|
// float currentY = PDRectangle.A4.getHeight() - TABLE_MARGIN;
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// try {
|
|
|
|
|
|
|
|
// // 1. 绘制标题(居中)
|
|
|
|
|
|
|
|
// drawCenteredText(cs, font, FONT_SIZE+10,
|
|
|
|
|
|
|
|
// "安装信息录入表",
|
|
|
|
|
|
|
|
// PDRectangle.A4.getWidth()/2, currentY);
|
|
|
|
|
|
|
|
// currentY -= ROW_HEIGHT * 1.5f;
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// // 2. 绘制基本信息表格
|
|
|
|
|
|
|
|
// String[][] baseData = {
|
|
|
|
|
|
|
|
// // {"公司名称", entity.getCorporateName()},
|
|
|
|
|
|
|
|
// {"申请时间", entity.getProposerTime().toString()},
|
|
|
|
|
|
|
|
// {"申请人", entity.getProposer()},
|
|
|
|
|
|
|
|
// {"申请人班组", entity.getProposerTeam()},
|
|
|
|
|
|
|
|
// {"小区名字", entity.getCommunityName()},
|
|
|
|
|
|
|
|
// {"用户姓名", entity.getUserName()},
|
|
|
|
|
|
|
|
// // {"用户姓名", entity.getUserName()+"-" + entity.getCommunityName()+entity.getBuildingUnit()+entity.getRoomNo()},
|
|
|
|
|
|
|
|
// {"电话", entity.getUserIpone()},
|
|
|
|
|
|
|
|
// {"楼栋单元号", entity.getBuildingUnit()},
|
|
|
|
|
|
|
|
// {"房间号", entity.getRoomNo()},
|
|
|
|
|
|
|
|
// {"报警器编号", entity.getDeviceName()},
|
|
|
|
|
|
|
|
// {"切断阀编号", entity.getShutValueNumber()},
|
|
|
|
|
|
|
|
// {"燃气表号", entity.getGasMeterNumber()},
|
|
|
|
|
|
|
|
// {"备注", entity.getBuildingUnit()}
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// };
|
|
|
|
|
|
|
|
// drawTable(cs, font, TABLE_MARGIN, currentY, baseData, COLUMN_WIDTHS);
|
|
|
|
|
|
|
|
// currentY -= (ROW_HEIGHT * baseData.length) + 20f;
|
|
|
|
|
|
|
|
//// @ApiModelProperty(value = "安装前图片")
|
|
|
|
|
|
|
|
// // 创建后续页面 - 图片部分
|
|
|
|
|
|
|
|
// // 图片处理(带自动分页)
|
|
|
|
|
|
|
|
// // 图片处理(带自动分页)
|
|
|
|
|
|
|
|
// List<String[]> imageGroups = Arrays.asList(
|
|
|
|
|
|
|
|
// new String[]{entity.getDeviceInfoImage(), "设备信息图片"},
|
|
|
|
|
|
|
|
// new String[]{entity.getBeforeInstallationImage(), "安装前图片"},
|
|
|
|
|
|
|
|
// new String[]{entity.getWorkingOfTheDetectorImage(), "安装完成探测器工作图片"},
|
|
|
|
|
|
|
|
// new String[]{entity.getSideLeakageImage(), "测漏图片"},
|
|
|
|
|
|
|
|
// new String[]{entity.getFormSideLeakageImage(), "泡沫水测漏图片"},
|
|
|
|
|
|
|
|
// new String[]{entity.getIgnitionPictureImage(), "点火图片"},
|
|
|
|
|
|
|
|
// new String[]{entity.getInstallThePanoramicImage(), "安装完成全景图片"},
|
|
|
|
|
|
|
|
// new String[]{entity.getOfGasMeterImage(), "燃气表号图片"},
|
|
|
|
|
|
|
|
// new String[]{entity.getWorkOrderImage(), "工单图片"},
|
|
|
|
|
|
|
|
// new String[]{entity.getPunchingImage(), "打孔图片"},
|
|
|
|
|
|
|
|
// new String[]{entity.getFiexImage(), "安装电源线图片"},
|
|
|
|
|
|
|
|
// new String[]{entity.getHouseNumberImage(), "门牌号图片"}
|
|
|
|
|
|
|
|
// );
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// for (String[] group : imageGroups) {
|
|
|
|
|
|
|
|
// String urls = group[0];
|
|
|
|
|
|
|
|
// String title = group[1];
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// if (urls != null && !urls.trim().isEmpty()) {
|
|
|
|
|
|
|
|
// List<String> validUrls = Arrays.stream(urls.split(","))
|
|
|
|
|
|
|
|
// .map(String::trim)
|
|
|
|
|
|
|
|
// .filter(url -> !url.isEmpty() && url.startsWith("http"))
|
|
|
|
|
|
|
|
// .collect(Collectors.toList());
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// if (!validUrls.isEmpty()) {
|
|
|
|
|
|
|
|
// // 处理可能需要跨页的多图片情况
|
|
|
|
|
|
|
|
// if (validUrls.size() > 2) {
|
|
|
|
|
|
|
|
// // 对于多于2张的图片,逐个或分组处理以支持跨页
|
|
|
|
|
|
|
|
// for (int i = 0; i < validUrls.size(); i += 2) { // 每次处理最多2张图片
|
|
|
|
|
|
|
|
// List<String> subList = validUrls.subList(i,
|
|
|
|
|
|
|
|
// Math.min(i + 2, validUrls.size()));
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// List<String> wrappedTitleLines = wrapText(title,LEFT_COL_WIDTH - 2 * CELL_PADDING);
|
|
|
|
|
|
|
|
// float titleHeight = wrappedTitleLines.size() * LINE_HEIGHT;
|
|
|
|
|
|
|
|
// // 计算图片组的自适应高度
|
|
|
|
|
|
|
|
// float imagesHeight = calculateAdjustedImagesHeight(document, subList,
|
|
|
|
|
|
|
|
// RIGHT_COL_WIDTH - 2 * CELL_PADDING,
|
|
|
|
|
|
|
|
// PDRectangle.A4.getHeight() - 2 * TABLE_MARGIN - titleHeight);
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// float totalHeight = Math.max(imagesHeight, titleHeight);
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// if (currentY < TABLE_MARGIN + Math.max(totalHeight, titleHeight)) {
|
|
|
|
|
|
|
|
// cs.close();
|
|
|
|
|
|
|
|
// currentPage = new PDPage(PDRectangle.A4);
|
|
|
|
|
|
|
|
// document.addPage(currentPage);
|
|
|
|
|
|
|
|
// cs = new PDPageContentStream(document, currentPage);
|
|
|
|
|
|
|
|
// currentY = PDRectangle.A4.getHeight() - TABLE_MARGIN;
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// // 添加标识,表明这是多图片的一部分
|
|
|
|
|
|
|
|
// String subTitle = title + " (" + (i+1) + "-" + Math.min(i+2, validUrls.size()) + "/" + validUrls.size() + ")";
|
|
|
|
|
|
|
|
// List<String> subWrappedTitleLines = wrapText(subTitle, LEFT_COL_WIDTH - 2 * CELL_PADDING);
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// drawImageGroup(cs, document, font, subWrappedTitleLines, subList, currentY,
|
|
|
|
|
|
|
|
// Math.max(totalHeight, titleHeight));
|
|
|
|
|
|
|
|
// currentY -= Math.max(totalHeight, titleHeight);
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
// } else {
|
|
|
|
|
|
|
|
// // 原有的单/双图片处理逻辑
|
|
|
|
|
|
|
|
// List<String> wrappedTitleLines = wrapText(title,LEFT_COL_WIDTH - 2 * CELL_PADDING);
|
|
|
|
|
|
|
|
// float titleHeight = wrappedTitleLines.size() * LINE_HEIGHT;
|
|
|
|
|
|
|
|
// // 计算图片组的自适应高度
|
|
|
|
|
|
|
|
// float imagesHeight = calculateAdjustedImagesHeight(document, validUrls,
|
|
|
|
|
|
|
|
// RIGHT_COL_WIDTH - 2 * CELL_PADDING,
|
|
|
|
|
|
|
|
// PDRectangle.A4.getHeight() - 2 * TABLE_MARGIN - titleHeight);
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// float totalHeight = Math.max(imagesHeight, titleHeight);
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// if (currentY < TABLE_MARGIN + Math.max(totalHeight, titleHeight)) {
|
|
|
|
|
|
|
|
// cs.close();
|
|
|
|
|
|
|
|
// currentPage = new PDPage(PDRectangle.A4);
|
|
|
|
|
|
|
|
// document.addPage(currentPage);
|
|
|
|
|
|
|
|
// cs = new PDPageContentStream(document, currentPage);
|
|
|
|
|
|
|
|
// currentY = PDRectangle.A4.getHeight() - TABLE_MARGIN;
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// drawImageGroup(cs, document, font, wrappedTitleLines, validUrls, currentY,
|
|
|
|
|
|
|
|
// Math.max(totalHeight, titleHeight));
|
|
|
|
|
|
|
|
// currentY -= Math.max(totalHeight, titleHeight);
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
////
|
|
|
|
|
|
|
|
// } finally {
|
|
|
|
|
|
|
|
// cs.close();
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
// // 将PDF保存到ByteArrayOutputStream
|
|
|
|
|
|
|
|
// document.save(pdfBaos);
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
//// ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
|
|
|
|
|
|
|
//// document.save(baos);
|
|
|
|
|
|
|
|
//// outputStream.flush();
|
|
|
|
|
|
|
|
//// baos.writeTo(outputStream);
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
// // 创建ZIP压缩包并将PDF写入输出流
|
|
|
|
|
|
|
|
// try (ZipOutputStream zipOut = new ZipOutputStream(outputStream)) {
|
|
|
|
|
|
|
|
// // 设置最高压缩级别
|
|
|
|
|
|
|
|
// zipOut.setLevel(Deflater.BEST_COMPRESSION);
|
|
|
|
|
|
|
|
// // 创建ZIP条目
|
|
|
|
|
|
|
|
// ZipEntry entry = new ZipEntry(entity.getUserName()+entity.getCommunityName()+entity.getBuildingUnit()+entity.getRoomNo()+".pdf");
|
|
|
|
|
|
|
|
// zipOut.putNextEntry(entry);
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// // 将PDF数据写入ZIP
|
|
|
|
|
|
|
|
// byte[] pdfBytes = pdfBaos.toByteArray();
|
|
|
|
|
|
|
|
// zipOut.write(pdfBytes, 0, pdfBytes.length);
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// // 关闭当前条目
|
|
|
|
|
|
|
|
// zipOut.closeEntry();
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
// 加载字体,确保使用正确的文档实例
|
|
|
|
// 加载字体,确保使用正确的文档实例
|
|
|
|
private static PDType0Font loadFont(PDDocument document) throws IOException {
|
|
|
|
private static PDType0Font loadFont(PDDocument document) throws IOException {
|
|
|
|
// 尝试多个可能的字体路径
|
|
|
|
// 尝试多个可能的字体路径
|
|
|
|
@ -266,7 +423,7 @@ public class PDFGenerator {
|
|
|
|
if (text == null || text.isEmpty()) return lines;
|
|
|
|
if (text == null || text.isEmpty()) return lines;
|
|
|
|
|
|
|
|
|
|
|
|
// 优化的文本换行逻辑,减少计算量
|
|
|
|
// 优化的文本换行逻辑,减少计算量
|
|
|
|
int maxCharsPerLine = (int)(maxWidth / (FONT_SIZE * 0.4f)); // 调整字符估算系数
|
|
|
|
int maxCharsPerLine = (int) (maxWidth / (FONT_SIZE * 0.4f)); // 调整字符估算系数
|
|
|
|
|
|
|
|
|
|
|
|
if (text.length() <= maxCharsPerLine) {
|
|
|
|
if (text.length() <= maxCharsPerLine) {
|
|
|
|
lines.add(text);
|
|
|
|
lines.add(text);
|
|
|
|
@ -415,7 +572,7 @@ public class PDFGenerator {
|
|
|
|
// 单图片模式:一页两行,每行占半页高度
|
|
|
|
// 单图片模式:一页两行,每行占半页高度
|
|
|
|
float singleImageHeight = imageAvailableHeight;
|
|
|
|
float singleImageHeight = imageAvailableHeight;
|
|
|
|
// 绘制标题(垂直居中)
|
|
|
|
// 绘制标题(垂直居中)
|
|
|
|
float titleY = startY - totalHeight/2 + (titleLines.size() * LINE_HEIGHT)/2;
|
|
|
|
float titleY = startY - totalHeight / 2 + (titleLines.size() * LINE_HEIGHT) / 2;
|
|
|
|
// 绘制第一行标题(顶部)
|
|
|
|
// 绘制第一行标题(顶部)
|
|
|
|
// float titleY = startY - CELL_PADDING - FONT_SIZE;
|
|
|
|
// float titleY = startY - CELL_PADDING - FONT_SIZE;
|
|
|
|
cs.beginText();
|
|
|
|
cs.beginText();
|
|
|
|
@ -449,7 +606,7 @@ public class PDFGenerator {
|
|
|
|
float imageHeightPer = (imageAvailableHeight - CELL_PADDING) / 2;
|
|
|
|
float imageHeightPer = (imageAvailableHeight - CELL_PADDING) / 2;
|
|
|
|
|
|
|
|
|
|
|
|
// 绘制标题(垂直居中)
|
|
|
|
// 绘制标题(垂直居中)
|
|
|
|
float titleY = startY - totalHeight/2 + (titleLines.size() * LINE_HEIGHT)/2;
|
|
|
|
float titleY = startY - totalHeight / 2 + (titleLines.size() * LINE_HEIGHT) / 2;
|
|
|
|
cs.beginText();
|
|
|
|
cs.beginText();
|
|
|
|
cs.setFont(font, FONT_SIZE);
|
|
|
|
cs.setFont(font, FONT_SIZE);
|
|
|
|
cs.newLineAtOffset(TABLE_MARGIN + CELL_PADDING, titleY);
|
|
|
|
cs.newLineAtOffset(TABLE_MARGIN + CELL_PADDING, titleY);
|
|
|
|
@ -536,7 +693,7 @@ public class PDFGenerator {
|
|
|
|
TABLE_MARGIN + LEFT_COL_WIDTH + CELL_PADDING + imageWidthPer + CELL_PADDING,
|
|
|
|
TABLE_MARGIN + LEFT_COL_WIDTH + CELL_PADDING + imageWidthPer + CELL_PADDING,
|
|
|
|
startY - CELL_PADDING - scaledHeight2,
|
|
|
|
startY - CELL_PADDING - scaledHeight2,
|
|
|
|
scaledWidth2, scaledHeight2);*/
|
|
|
|
scaledWidth2, scaledHeight2);*/
|
|
|
|
}else {
|
|
|
|
} else {
|
|
|
|
// 多图片模式(超过2张图片):垂直堆叠显示,与双图片模式保持一致的样式
|
|
|
|
// 多图片模式(超过2张图片):垂直堆叠显示,与双图片模式保持一致的样式
|
|
|
|
float imageHeightPer = (imageAvailableHeight - CELL_PADDING * (imageUrls.size() - 1)) / imageUrls.size();
|
|
|
|
float imageHeightPer = (imageAvailableHeight - CELL_PADDING * (imageUrls.size() - 1)) / imageUrls.size();
|
|
|
|
|
|
|
|
|
|
|
|
@ -587,7 +744,7 @@ public class PDFGenerator {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// private static void drawImageGroup(PDPageContentStream cs, PDDocument doc, PDType0Font font,
|
|
|
|
// private static void drawImageGroup(PDPageContentStream cs, PDDocument doc, PDType0Font font,
|
|
|
|
// List<String> titleLines, List<String> imageUrls,
|
|
|
|
// List<String> titleLines, List<String> imageUrls,
|
|
|
|
// float startY, float totalHeight) throws IOException {
|
|
|
|
// float startY, float totalHeight) throws IOException {
|
|
|
|
// // 绘制单元格边框
|
|
|
|
// // 绘制单元格边框
|
|
|
|
@ -773,14 +930,14 @@ public class PDFGenerator {
|
|
|
|
float maxWidth, float maxHeight) throws IOException {
|
|
|
|
float maxWidth, float maxHeight) throws IOException {
|
|
|
|
if (imageUrls.isEmpty()) return 0;
|
|
|
|
if (imageUrls.isEmpty()) return 0;
|
|
|
|
|
|
|
|
|
|
|
|
// return maxHeight;
|
|
|
|
// return maxHeight;
|
|
|
|
if (imageUrls.size() == 1) {
|
|
|
|
if (imageUrls.size() == 1) {
|
|
|
|
// 单图片模式:占用半页高度
|
|
|
|
// 单图片模式:占用半页高度
|
|
|
|
return maxHeight / 2;
|
|
|
|
return maxHeight / 2;
|
|
|
|
} else if(imageUrls.size() == 2){
|
|
|
|
} else if (imageUrls.size() == 2) {
|
|
|
|
// 双图片模式:整页高度
|
|
|
|
// 双图片模式:整页高度
|
|
|
|
return maxHeight;
|
|
|
|
return maxHeight;
|
|
|
|
}else {
|
|
|
|
} else {
|
|
|
|
// 多图片模式:根据图片数量动态计算高度
|
|
|
|
// 多图片模式:根据图片数量动态计算高度
|
|
|
|
// 每张图片预留最小高度,防止页面过长
|
|
|
|
// 每张图片预留最小高度,防止页面过长
|
|
|
|
float minImageHeight = 50f; // 每张图片最小高度
|
|
|
|
float minImageHeight = 50f; // 每张图片最小高度
|
|
|
|
@ -798,7 +955,7 @@ public class PDFGenerator {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 截断超长文本
|
|
|
|
// 截断超长文本
|
|
|
|
private static String truncateText(PDType0Font font,String text, float maxWidth) throws IOException {
|
|
|
|
private static String truncateText(PDType0Font font, String text, float maxWidth) throws IOException {
|
|
|
|
// PDType0Font font = ...; // 获取字体对象
|
|
|
|
// PDType0Font font = ...; // 获取字体对象
|
|
|
|
StringBuilder sb = new StringBuilder();
|
|
|
|
StringBuilder sb = new StringBuilder();
|
|
|
|
float currentWidth = 0;
|
|
|
|
float currentWidth = 0;
|
|
|
|
@ -824,8 +981,8 @@ public class PDFGenerator {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 简单估算:假设每个中文字符宽度等于字体大小
|
|
|
|
// 简单估算:假设每个中文字符宽度等于字体大小
|
|
|
|
int charsPerLine = (int)(maxWidth / fontSize);
|
|
|
|
int charsPerLine = (int) (maxWidth / fontSize);
|
|
|
|
int lineCount = (int)Math.ceil((double)text.length() / charsPerLine);
|
|
|
|
int lineCount = (int) Math.ceil((double) text.length() / charsPerLine);
|
|
|
|
|
|
|
|
|
|
|
|
return lineCount * fontSize * 1.2f; // 行间距1.2倍
|
|
|
|
return lineCount * fontSize * 1.2f; // 行间距1.2倍
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@ -838,7 +995,7 @@ public class PDFGenerator {
|
|
|
|
cs.setLeading(fontSize * 1.2f); // 行间距
|
|
|
|
cs.setLeading(fontSize * 1.2f); // 行间距
|
|
|
|
|
|
|
|
|
|
|
|
// 简单换行实现
|
|
|
|
// 简单换行实现
|
|
|
|
int charsPerLine = (int)(maxWidth / (fontSize * 0.8)); // 中文字符估算
|
|
|
|
int charsPerLine = (int) (maxWidth / (fontSize * 0.8)); // 中文字符估算
|
|
|
|
int pos = 0;
|
|
|
|
int pos = 0;
|
|
|
|
float currentY = startY;
|
|
|
|
float currentY = startY;
|
|
|
|
|
|
|
|
|
|
|
|
@ -855,6 +1012,7 @@ public class PDFGenerator {
|
|
|
|
currentY -= fontSize * 1.2f;
|
|
|
|
currentY -= fontSize * 1.2f;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 图片高度预估方法
|
|
|
|
// 图片高度预估方法
|
|
|
|
private static float estimateImageHeight(String imageUrl, float targetWidth) throws IOException {
|
|
|
|
private static float estimateImageHeight(String imageUrl, float targetWidth) throws IOException {
|
|
|
|
try (InputStream is = new URL(imageUrl).openStream()) {
|
|
|
|
try (InputStream is = new URL(imageUrl).openStream()) {
|
|
|
|
@ -992,8 +1150,6 @@ public class PDFGenerator {
|
|
|
|
}*/
|
|
|
|
}*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private static void drawTable(PDPageContentStream cs, PDType0Font font,
|
|
|
|
private static void drawTable(PDPageContentStream cs, PDType0Font font,
|
|
|
|
float x, float y, String[][] data, float[] colWidths) throws IOException {
|
|
|
|
float x, float y, String[][] data, float[] colWidths) throws IOException {
|
|
|
|
|
|
|
|
|
|
|
|
@ -1018,7 +1174,7 @@ public class PDFGenerator {
|
|
|
|
// 绘制列分隔线
|
|
|
|
// 绘制列分隔线
|
|
|
|
float currentX = x;
|
|
|
|
float currentX = x;
|
|
|
|
for (int i = 1; i < colWidths.length; i++) {
|
|
|
|
for (int i = 1; i < colWidths.length; i++) {
|
|
|
|
currentX += colWidths[i-1];
|
|
|
|
currentX += colWidths[i - 1];
|
|
|
|
cs.moveTo(currentX, y);
|
|
|
|
cs.moveTo(currentX, y);
|
|
|
|
cs.lineTo(currentX, y - ROW_HEIGHT * data.length);
|
|
|
|
cs.lineTo(currentX, y - ROW_HEIGHT * data.length);
|
|
|
|
cs.stroke();
|
|
|
|
cs.stroke();
|
|
|
|
@ -1075,7 +1231,7 @@ public class PDFGenerator {
|
|
|
|
cs.stroke();
|
|
|
|
cs.stroke();
|
|
|
|
|
|
|
|
|
|
|
|
// 垂直居中文本(改进算法)
|
|
|
|
// 垂直居中文本(改进算法)
|
|
|
|
float textY = startY - rowHeight/2 + FONT_SIZE/2;
|
|
|
|
float textY = startY - rowHeight / 2 + FONT_SIZE / 2;
|
|
|
|
//String title = "安装完成探测器工作图片\n检测时间:2025-08-14";
|
|
|
|
//String title = "安装完成探测器工作图片\n检测时间:2025-08-14";
|
|
|
|
|
|
|
|
|
|
|
|
/* if(title.contains("\n")){
|
|
|
|
/* if(title.contains("\n")){
|
|
|
|
@ -1083,7 +1239,7 @@ public class PDFGenerator {
|
|
|
|
textY = startY - rowHeight/2 + FONT_SIZE/2 + (lines.length-1)*FONT_SIZE/2;
|
|
|
|
textY = startY - rowHeight/2 + FONT_SIZE/2 + (lines.length-1)*FONT_SIZE/2;
|
|
|
|
}*/
|
|
|
|
}*/
|
|
|
|
drawCenteredText(cs, font, FONT_SIZE, title,
|
|
|
|
drawCenteredText(cs, font, FONT_SIZE, title,
|
|
|
|
startX + LEFT_COL_WIDTH/2, textY);
|
|
|
|
startX + LEFT_COL_WIDTH / 2, textY);
|
|
|
|
|
|
|
|
|
|
|
|
// 绘制图片(带5pt边距)
|
|
|
|
// 绘制图片(带5pt边距)
|
|
|
|
cs.drawImage(image,
|
|
|
|
cs.drawImage(image,
|
|
|
|
|