因为调不来合并单元格的展示形式,所以暂时不用了。其它部分可以直接使用,想修改的话需要读懂怎么拼接的。有类似生成表格的需求可以试试itext。
直接调这个方法generatePdf传参数就可以了 。其实是求dk写的,但是调不明白,丢了感觉浪费,说不定会发挥点什么作用,万一有人用到呢
1 public byte[] generatePdf(List<Map<String, String>> dataList, String instrumentName) { 2 try (PDDocument document = new PDDocument(); 3 ByteArrayOutputStream baos = new ByteArrayOutputStream()) { 4 5 // 计算总页数 6 int totalPages = (int) Math.ceil((double) dataList.size() / 10); 7 if (totalPages == 0) totalPages = 1; // 至少一页 8 PDFont chineseFont = PDType0Font.load(document, new File("C:/Windows/Fonts/simhei.ttf")); 9 // 分页处理数据 10 for (int pageNum = 0; pageNum < totalPages; pageNum++) { 11 // 计算当前页的数据范围 12 int startIndex = pageNum * 10; 13 int endIndex = Math.min(startIndex + 10, dataList.size()); 14 List<Map<String, String>> pageData = dataList.subList(startIndex, endIndex); 15 16 // 创建新页面 17 PDPage page = new PDPage(PDRectangle.A4); 18 document.addPage(page); 19 20 PDPageContentStream contentStream = new PDPageContentStream(document, page); 21 22 try { 23 // 绘制当前页的表格 24 drawTableForPage(contentStream, pageData, instrumentName, pageNum + 1, totalPages, startIndex,chineseFont); 25 } finally { 26 contentStream.close(); 27 } 28 } 29 30 document.save(baos); 31 return baos.toByteArray(); 32 33 } catch (Exception e) { 34 throw new RuntimeException("PDF生成失败", e); 35 } 36 } 37 38 // 绘制单页表格 39 private void drawTableForPage(PDPageContentStream contentStream, 40 List<Map<String, String>> dataList, 41 String instrumentName, 42 int currentPage, 43 int totalPages, 44 int startIndex,PDFont font) throws IOException { 45 try{ 46 47 // 表格参数 48 float margin = 30; //左边距 49 float yStart = 700; // 从页面顶部往下700点的位置开始绘制 50 float rowHeight = 55; 51 float tableWidth = 550; 52 float[] colWidths = {30, 80, 120, 80, 80,60,100}; // 列宽 53 54 float nextY = yStart; 55 56 // 绘制页眉信息 57 drawPageHeader(contentStream, instrumentName, currentPage, totalPages, margin, nextY, font); 58 // 表头 59 String[] headers = {"序号", " 日期", " 地址"," 项目"," 状态", "使用人", " 编号"}; 60 61 // 绘制表头背景 62 contentStream.setNonStrokingColor(200, 200, 200); // 灰色背景 63 contentStream.addRect(margin, nextY - rowHeight, tableWidth, rowHeight); 64 contentStream.fill(); 65 contentStream.setNonStrokingColor(0, 0, 0); // 恢复黑色 66 67 // 表头文字 68 float textX = margin; 69 contentStream.beginText(); 70 contentStream.setFont(font, 12); 71 contentStream.newLineAtOffset(textX + 5, nextY - rowHeight + 20); 72 73 for (int i = 0; i < headers.length; i++) { 74 contentStream.showText(headers[i]); 75 contentStream.newLineAtOffset(colWidths[i], 0); 76 } 77 contentStream.endText(); 78 79 nextY -= rowHeight; //减去这部分行高,向下定位 80 81 // 绘制当前页的数据行 82 for (int i = 0; i < dataList.size(); i++) { 83 Map<String, String> data = dataList.get(i); 84 int globalIndex = startIndex + i; // 全局序号 85 86 87 float currentY = nextY - rowHeight; 88 float textVerticalOffset = rowHeight * 0.6f; 89 90 // 绘制数据内容 - 使用全局序号 91 contentStream.beginText(); 92 contentStream.setFont(font, 9); 93 contentStream.newLineAtOffset(margin + 10, currentY + textVerticalOffset); 94 contentStream.showText(String.valueOf(globalIndex + 1)); // 全局序号 95 contentStream.endText(); 96 97 // 日期 98 String syrq = data.get("syrq"); 99 float syrqX = margin + colWidths[0] + 5; 100 float syrqWidth = colWidths[1] - 10; // 留出边距 101 drawWrappedText(contentStream, syrq, font, 9, syrqX, currentY + 35, syrqWidth, 12); 102 103 // 地址 104 String sjdw = data.get("sydz"); 105 float addressX = margin + colWidths[0] + colWidths[1] + 5; 106 float addressWidth = colWidths[2] - 10; // 留出边距 107 drawWrappedText(contentStream, sjdw, font, 9, addressX, currentY + 35, addressWidth, 12); 108 109 // 项目 110 String sbmc = data.get("syxm"); 111 float projectX = margin + colWidths[0] + colWidths[1] + colWidths[2] + 5; 112 float projectWidth = colWidths[3] - 10; 113 drawWrappedText(contentStream, sbmc, font, 9, projectX, currentY + 35, projectWidth, 12); 114 115 // 状态 116 String xnzt = data.get("xnzt"); 117 float xnztX = margin + colWidths[0] + colWidths[1] + colWidths[2] + colWidths[3] + 5; 118 float xnztWidth = colWidths[3] - 10; 119 drawWrappedText(contentStream, xnzt, font, 9, xnztX, currentY + 35, xnztWidth, 12); 120 121 // 使用人 122 String syr = data.get("syr"); 123 float syrX = margin + colWidths[0] + colWidths[1] + colWidths[2] + colWidths[3] + colWidths[4] + 5; 124 float syrWidth = colWidths[3] - 10; 125 drawWrappedText(contentStream, syr, font, 9, syrX, currentY + 35, syrWidth, 12); 126 127 // 编号 128 String bh = data.get("bh"); 129 float bhX = margin + colWidths[0] + colWidths[1] + colWidths[2] + colWidths[3] + colWidths[4] + colWidths[5] + 5; 130 float bhWidth = colWidths[3] - 10; 131 drawWrappedText(contentStream, bh, font, 9, bhX, currentY + 35, bhWidth, 12); 132 133 nextY -= rowHeight; 134 // 绘制边框 135 contentStream.setStrokingColor(0, 0, 0); // 边框颜色 136 contentStream.addRect(margin, nextY, tableWidth, yStart - nextY); 137 contentStream.stroke(); 138 drawTableInnerBorders(contentStream, margin, yStart, tableWidth, rowHeight, colWidths, dataList.size() + 1); 139 140 141 // 检查页面底部,避免内容超出 142 if (nextY < 100) { 143 break; 144 } 145 } 146 147 // 绘制表格外边框 148 contentStream.setStrokingColor(0, 0, 0); 149 contentStream.setLineWidth(1.5f); 150 float tableBottom = nextY + (dataList.size() * rowHeight); // 计算表格底部位置 151 contentStream.addRect(margin, tableBottom, tableWidth, yStart - tableBottom); 152 contentStream.stroke(); 153 154 155 }catch(Exception e){ 156 e.printStackTrace(); 157 } 158 } 159 160 161 // 绘制页眉 162 private void drawPageHeader(PDPageContentStream contentStream, 163 String instrumentName, 164 int currentPage, 165 int totalPages, 166 float margin, 167 float yStart, 168 PDFont chineseFont) throws IOException { 169 170 // 主标题 171 contentStream.beginText(); 172 contentStream.setFont(chineseFont, 16); 173 contentStream.newLineAtOffset(margin+180, yStart+50); 174 contentStream.showText("xx设备使用记录表"); 175 contentStream.endText(); 176 177 // 仪器名称 178 String generateTime = " 生成时间: " + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()); 179 contentStream.beginText(); 180 contentStream.setFont(chineseFont, 12); 181 contentStream.newLineAtOffset(margin, yStart+20 ); 182 contentStream.showText("设备名称: " + instrumentName); 183 contentStream.showText(generateTime); 184 contentStream.endText(); 185 186 // 页码信息 187 String pageInfo = "第 " + currentPage + " 页 / 共 " + totalPages + " 页"; 188 contentStream.beginText(); 189 contentStream.setFont(chineseFont, 10); 190 contentStream.newLineAtOffset(margin + 430, yStart + 20); // 右对齐 191 contentStream.showText(pageInfo); 192 contentStream.endText(); 193 194 } 195 196 197 // 文本换行辅助方法 198 private List<String> wrapText(String text, PDFont font, float fontSize, float maxWidth) throws IOException { 199 List<String> lines = new ArrayList<>(); 200 if (text == null || text.isEmpty()) { 201 lines.add(""); 202 return lines; 203 } 204 205 // 按字符分割,支持中文 206 List<String> words = new ArrayList<>(); 207 for (int i = 0; i < text.length(); i++) { 208 words.add(String.valueOf(text.charAt(i))); 209 } 210 211 StringBuilder currentLine = new StringBuilder(); 212 float currentWidth = 0; 213 214 for (String word : words) { 215 float wordWidth = font.getStringWidth(word) * fontSize / 1000; 216 217 // 如果当前行加上这个词不超过最大宽度 218 if (currentWidth + wordWidth <= maxWidth) { 219 currentLine.append(word); 220 currentWidth += wordWidth; 221 } else { 222 // 换行 223 if (currentLine.length() > 0) { 224 lines.add(currentLine.toString()); 225 } 226 currentLine = new StringBuilder(word); 227 currentWidth = wordWidth; 228 } 229 } 230 231 // 添加最后一行 232 if (currentLine.length() > 0) { 233 lines.add(currentLine.toString()); 234 } 235 236 return lines; 237 } 238 239 private void drawWrappedText(PDPageContentStream contentStream, String text, PDFont font, float fontSize, 240 float x, float y, float maxWidth, float lineHeight) throws IOException { 241 List<String> lines = wrapText(text, font, fontSize, maxWidth); 242 243 for (int i = 0; i < lines.size(); i++) { 244 contentStream.beginText(); 245 contentStream.setFont(font, fontSize); 246 contentStream.newLineAtOffset(x, y - (i * lineHeight)); 247 contentStream.showText(lines.get(i)); 248 contentStream.endText(); 249 } 250 } 251 252 // 绘制表格内边框(横线和竖线) 253 private void drawTableInnerBorders(PDPageContentStream contentStream, 254 float margin, float yStart, float tableWidth, 255 float rowHeight, float[] colWidths, int rowCount) throws IOException { 256 257 float currentY = yStart; 258 259 // 绘制所有横线 260 contentStream.setStrokingColor(150, 150, 150); 261 contentStream.setLineWidth(0.5f); 262 263 for (int i = 0; i <= rowCount; i++) { 264 contentStream.moveTo(margin, currentY); 265 contentStream.lineTo(margin + tableWidth, currentY); 266 contentStream.stroke(); 267 currentY -= rowHeight; 268 } 269 270 // 绘制所有竖线 271 float currentX = margin; 272 contentStream.setStrokingColor(150, 150, 150); 273 contentStream.setLineWidth(0.5f); 274 275 // 绘制列之间的竖线 276 for (int i = 0; i <= colWidths.length; i++) { 277 contentStream.moveTo(currentX, yStart); 278 contentStream.lineTo(currentX, yStart - (rowCount * rowHeight)); 279 contentStream.stroke(); 280 281 if (i < colWidths.length) { 282 currentX += colWidths[i]; 283 } 284 } 285 } 286 287 288 public Map<String, Object> getAllYqybUsageData(CommonReportBean commonReportBean) { 289 Map<String, Object> result = new HashMap<>(); 290 try { 291 292 // 这里替换成您的数据查询逻辑 293 List<Map<String, String>> dataList = new ArrayList<>(); 294 295 // 示例数据 296 Map<String, String> data = new HashMap<>(); 297 data.put("sbmc", "设备名称1"); 298 data.put("glbh", "管理编号2"); 299 data.put("syrq", "实验日期3"); 300 data.put("sydz", "使用地址4"); 301 data.put("syxm", "试验项目5"); 302 data.put("xnzt", "性能状态6"); 303 data.put("syr", "张三"); 304 data.put("bh", "BG20240001"); 305 dataList.add(data); 306 307 result.put("success", true); 308 result.put("data", dataList); 309 result.put("total", dataList.size()); 310 311 } catch (Exception e) { 312 result.put("success", false); 313 result.put("message", e.getMessage()); 314 } 315 return result; 316 } 317
控制层这部分代码可以不用管
1 @RequestMapping(value = "/printpdf", method = {RequestMethod.GET, RequestMethod.POST}) 2 public void printpdf( HttpServletResponse response) { 3 try { 4 // 1.省略..... 5 //dataList.... instrumentName... 6 // 2. 生成PDF 7 byte[] pdfBytes = utilService.generatePdf(dataList, instrumentName); 8 9 response.reset(); // 重要:重置响应 10 response.setContentType("application/pdf"); 11 response.setCharacterEncoding("UTF-8"); 12 response.setHeader("Content-Disposition", "attachment; filename=report.pdf"); 13 response.setHeader("Content-Length", String.valueOf(pdfBytes.length)); 14 response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate"); 15 response.setHeader("Pragma", "no-cache"); 16 response.setDateHeader("Expires", 0); 17 18 // 4. 输出PDF 19 ServletOutputStream out = response.getOutputStream(); 20 out.write(pdfBytes); 21 out.flush(); 22 out.close(); 23 24 } catch (Exception e) { 25 e.printStackTrace(); 26 try { 27 response.setContentType("text/html;charset=UTF-8"); 28 response.getWriter().write("PDF生成失败:" + e.getMessage()); 29 } catch (IOException ex) { 30 ex.printStackTrace(); 31 } 32 } 33 }
生成结果:↓
