Excel2PDF

excel 转 pdf

使用poiitext库实现,兼容xls,xlsx格式。

maven 依赖

itext 依赖了 poi 不用手动添加

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<dependencies>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>io</artifactId>
<version>7.2.0</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>kernel</artifactId>
<version>7.2.0</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>layout</artifactId>
<version>7.2.0</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itextpdf</artifactId>
<version>5.5.9</version>
</dependency>
</dependencies>

工具实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599

import com.itextpdf.text.*;
import com.itextpdf.text.pdf.BaseFont;
import com.itextpdf.text.pdf.PdfPCell;
import com.itextpdf.text.pdf.PdfPTable;
import com.itextpdf.text.pdf.PdfWriter;
import com.waysoft.modules.tools.FileUtil;
import org.apache.poi.hssf.usermodel.*;
import org.apache.poi.ooxml.POIXMLDocumentPart;
import org.apache.poi.ss.usermodel.Font;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.*;

import java.io.*;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.List;


public class ExcelToPdf implements Converter {

private static ExcelToPdf excelToPdf = null;
private ExcelToPdf(){}

public static ExcelToPdf create(){
if (excelToPdf == null){
excelToPdf = new ExcelToPdf();
}
return excelToPdf;
}

@Override
public byte[] convertToPdf(File file) throws Exception {
ByteArrayOutputStream outputStream = null;
try {
outputStream = new ByteArrayOutputStream();
// FileUtil.getFileExtension(file.getName()) 返回小写文件后缀 xxx.xls => xls
excelToPdf(new FileInputStream(file),outputStream,FileUtil.getFileExtension(file.getName()));

return outputStream.toByteArray();
}finally {
outputStream.close();
}
}

@Override
public byte[] convertToPdf(byte[] file) throws Exception {
return new byte[0];
}

/**
* Excel转PDF并写入输出流
*
* @param inStream Excel输入流
* @param outStream PDF输出流
* @param excelSuffix Excel类型 .xls 和 .xlsx
* @throws Exception 异常信息
*/
private void excelToPdf(InputStream inStream, OutputStream outStream, String excelSuffix) throws Exception {
Workbook workbook = getPoiWorkbookByFileStream(inStream, excelSuffix);
// 初始化PDF文档对象
//设置pdf纸张大小 PageSize.A4 A4横向
Document document = new Document(new RectangleReadOnly(842.0F, 595.0F));
PdfWriter.getInstance(document, outStream);
//设置页边距 宽
document.setMargins(10, 10, 10, 10);
document.open();
// 遍历Excel的每个工作表
for (Sheet sheet : workbook) {
// =========================
// 适用于 第一行必有内容的sheet
// 表头有内容才会读取该 sheet
// =========================
if(sheet.getRow(0) == null){
continue;
}
// 输入流转workbook,获取sheet
// Sheet sheet = getPoiSheetByFileStream(inStream, 0, excelSuffix);
// 获取列宽度占比
float[] widths = getColWidth(sheet);
PdfPTable table = new PdfPTable(widths);
table.setWidthPercentage(100);
int colCount = widths.length;
//设置基本字体
// =========================
// 加载字体文件
// 修改为自己的文件路径
// =========================
BaseFont baseFont = BaseFont.createFont(getClass().getClassLoader().getResource("static/font").getFile()+File.separator+"simsun.ttc,0", BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
// 遍历行
for (int rowIndex = sheet.getFirstRowNum(); rowIndex <= sheet.getLastRowNum(); rowIndex++) {
Row row = sheet.getRow(rowIndex);
if (Objects.isNull(row)) {
// 插入空对象
for (int i = 0; i < colCount; i++) {
table.addCell(createPdfPCell(null, 0, 13f, null));
}
} else {
// 遍历单元格
for (int columnIndex = 0; (columnIndex < row.getLastCellNum() || columnIndex < colCount) && columnIndex > -1; columnIndex++) {
PdfPCell pCell = excelCellToPdfCell(sheet, row.getCell(columnIndex), baseFont);
// 是否合并单元格
if (isMergedRegion(sheet, rowIndex, columnIndex)) {
int[] span = getMergedSpan(sheet, rowIndex, columnIndex);
//忽略合并过的单元格
boolean mergedCell = span[0] == 1 && span[1] == 1;
if (mergedCell) {
continue;
}
pCell.setRowspan(span[0]);
pCell.setColspan(span[1]);
}
table.addCell(pCell);
}
}
}
// 插入 excel 转化的 table
document.add(table);
}
document.close();

}

/**
* 单元格转换,poi cell 转换为 itext cell
*
* @param sheet poi sheet页
* @param excelCell poi 单元格
* @param baseFont 基础字体
* @return com.itextpdf.text.pdf.PdfPCell
*/
private PdfPCell excelCellToPdfCell(Sheet sheet, Cell excelCell, BaseFont baseFont) throws Exception {
if (Objects.isNull(excelCell)) {
return createPdfPCell(null, 0, 13f, null);
}
int rowIndex = excelCell.getRowIndex();
int columnIndex = excelCell.getColumnIndex();
// 图片信息
List<PicturesInfo> infos = getAllPictureInfos(sheet, rowIndex, rowIndex, columnIndex, columnIndex, false);
PdfPCell pCell;
if (isNotEmpty(infos)) {
pCell = new PdfPCell(Image.getInstance(infos.get(0).getPictureData()));
} else {
Font excelFont = getExcelFont(sheet, excelCell);
//设置单元格字体
com.itextpdf.text.Font pdFont = new com.itextpdf.text.Font(baseFont, excelFont.getFontHeightInPoints(), excelFont.getBold() ? 1 : 0, BaseColor.BLACK);
Integer border = hasBorder(excelCell) ? null : 0;
String excelCellValue = getExcelCellValue(excelCell);
pCell = createPdfPCell(excelCellValue, border, excelCell.getRow().getHeightInPoints(), pdFont);
}
// 水平居中
pCell.setHorizontalAlignment(getHorAlign(excelCell.getCellStyle().getAlignment().getCode()));
// 垂直对齐
pCell.setVerticalAlignment(getVerAlign(excelCell.getCellStyle().getVerticalAlignment().getCode()));
return pCell;
}



private Workbook getPoiWorkbookByFileStream(InputStream inputStream, String excelSuffix) throws IOException {
Workbook workbook;
if (excelSuffix.equals("xlsx")) {
workbook = new XSSFWorkbook(inputStream);
} else {
workbook = new HSSFWorkbook(inputStream);
}
return workbook;
}

/**
* Excel文档输入流转换为对应的workbook及获取对应的sheet
*
* @param inputStream Excel文档输入流
* @param sheetNo sheet编号,默认0 第一个sheet
* @param excelSuffix 文件类型 .xls和.xlsx
* @return poi sheet
* @throws IOException 异常
*/
private Sheet getPoiSheetByFileStream(InputStream inputStream, int sheetNo, String excelSuffix) throws IOException {
Workbook workbook;
if (excelSuffix.equals("xlsx")) {
workbook = new XSSFWorkbook(inputStream);
} else {
workbook = new HSSFWorkbook(inputStream);
}
return workbook.getSheetAt(sheetNo);
}

/**
* 创建itext pdf 单元格
*
* @param content 单元格内容
* @param border 边框
* @param minimumHeight 高度
* @param pdFont 字体
* @return pdf cell
*/
private PdfPCell createPdfPCell(String content, Integer border, Float minimumHeight, com.itextpdf.text.Font pdFont) {
String contentValue = content == null ? "" : content;
com.itextpdf.text.Font pdFontNew = pdFont == null ? new com.itextpdf.text.Font() : pdFont;
PdfPCell pCell = new PdfPCell(new Phrase(contentValue, pdFontNew));
if (Objects.nonNull(border)) {
pCell.setBorder(border);
}
if (Objects.nonNull(minimumHeight)) {
pCell.setMinimumHeight(minimumHeight);
}

return pCell;
}

/**
* excel垂直对齐方式映射到pdf对齐方式
*/
private int getVerAlign(int align) {
switch (align) {
case 2:
return com.itextpdf.text.Element.ALIGN_BOTTOM;
case 3:
return com.itextpdf.text.Element.ALIGN_TOP;
default:
return com.itextpdf.text.Element.ALIGN_MIDDLE;
}
}

/**
* excel水平对齐方式映射到pdf水平对齐方式
*/
private int getHorAlign(int align) {
switch (align) {
case 1:
return com.itextpdf.text.Element.ALIGN_LEFT;
case 3:
return com.itextpdf.text.Element.ALIGN_RIGHT;
default:
return com.itextpdf.text.Element.ALIGN_CENTER;
}
}

/*============================================== POI获取图片及文本内容工具方法 ==============================================*/

/**
* 获取字体
*
* @param sheet excel 转换的sheet页
* @param cell 单元格
* @return 字体
*/
private Font getExcelFont(Sheet sheet, Cell cell) {
// xls
if (sheet instanceof HSSFSheet) {
Workbook workbook = sheet.getWorkbook();
return ((HSSFCell) cell).getCellStyle().getFont(workbook);
}
// xlsx
return ((XSSFCell) cell).getCellStyle().getFont();
}

/**
* 判断excel单元格是否有边框
*/
private boolean hasBorder(Cell excelCell) {
short top = excelCell.getCellStyle().getBorderTop().getCode();
short bottom = excelCell.getCellStyle().getBorderBottom().getCode();
short left = excelCell.getCellStyle().getBorderLeft().getCode();
short right = excelCell.getCellStyle().getBorderRight().getCode();
return top + bottom + left + right > 2;
}

/**
* 判断单元格是否是合并单元格
*/
private boolean isMergedRegion(Sheet sheet, int row, int column) {
int sheetMergeCount = sheet.getNumMergedRegions();
for (int i = 0; i < sheetMergeCount; i++) {
CellRangeAddress range = sheet.getMergedRegion(i);
int firstColumn = range.getFirstColumn();
int lastColumn = range.getLastColumn();
int firstRow = range.getFirstRow();
int lastRow = range.getLastRow();
if (row >= firstRow && row <= lastRow) {
if (column >= firstColumn && column <= lastColumn) {
return true;
}
}
}
return false;
}

/**
* 计算合并单元格合并的跨行跨列数
*/
private int[] getMergedSpan(Sheet sheet, int row, int column) {
int sheetMergeCount = sheet.getNumMergedRegions();
int[] span = {1, 1};
for (int i = 0; i < sheetMergeCount; i++) {
CellRangeAddress range = sheet.getMergedRegion(i);
int firstColumn = range.getFirstColumn();
int lastColumn = range.getLastColumn();
int firstRow = range.getFirstRow();
int lastRow = range.getLastRow();
if (firstColumn == column && firstRow == row) {
span[0] = lastRow - firstRow + 1;
span[1] = lastColumn - firstColumn + 1;
break;
}
}
return span;
}

/**
* 获取excel中每列宽度的占比
*/
private static float[] getColWidth(Sheet sheet) {
int rowNum = getMaxColRowNum(sheet);
Row row = sheet.getRow(rowNum);
int cellCount = row.getPhysicalNumberOfCells();
int[] colWidths = new int[cellCount];
int sum = 0;

for (int i = row.getFirstCellNum(); i < cellCount; i++) {
Cell cell = row.getCell(i);
if (cell != null) {
colWidths[i] = sheet.getColumnWidth(i);
sum += sheet.getColumnWidth(i);
}
}

float[] colWidthPer = new float[cellCount];
for (int i = row.getFirstCellNum(); i < cellCount; i++) {
colWidthPer[i] = (float) colWidths[i] / sum * 100;
}
return colWidthPer;
}

/**
* 获取excel中列数最多的行号
*/
private static int getMaxColRowNum(Sheet sheet) {
int rowNum = 0;
int maxCol = 0;
for (int r = sheet.getFirstRowNum(); r < sheet.getPhysicalNumberOfRows(); r++) {
Row row = sheet.getRow(r);
if (row != null && maxCol < row.getPhysicalNumberOfCells()) {
maxCol = row.getPhysicalNumberOfCells();
rowNum = r;
}
}
return rowNum;
}

/**
* poi 根据单元格类型获取单元格内容
*
* @param excelCell poi单元格
* @return 单元格内容文本
*/
public String getExcelCellValue(Cell excelCell) {
if (excelCell == null) {
return "";
}
// 判断数据的类型
CellType cellType = excelCell.getCellType();

if (cellType == CellType.STRING) {
return excelCell.getStringCellValue();
}
if (cellType == CellType.BOOLEAN) {
return String.valueOf(excelCell.getBooleanCellValue());
}
if (cellType == CellType.FORMULA) {
return excelCell.getCellFormula();
}
if (cellType == CellType.NUMERIC) {
//short s = excelCell.getCellStyle().getDataFormat();
if (DateUtil.isCellDateFormatted(excelCell)) {// 处理日期格式、时间格式
SimpleDateFormat sdf;
// 验证short值
if (excelCell.getCellStyle().getDataFormat() == 14) {
sdf = new SimpleDateFormat("yyyy/MM/dd");
} else if (excelCell.getCellStyle().getDataFormat() == 21) {
sdf = new SimpleDateFormat("HH:mm:ss");
} else if (excelCell.getCellStyle().getDataFormat() == 22) {
sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
} else {
throw new RuntimeException("日期格式错误!!!");
}
Date date = excelCell.getDateCellValue();
return sdf.format(date);
} else if (excelCell.getCellStyle().getDataFormat() == 0) {
//处理数值格式
DataFormatter formatter = new DataFormatter();
return formatter.formatCellValue(excelCell);
}
}
if (cellType == CellType.ERROR) {
return "非法字符";
}
return "";
}

/**
* 获取sheet内的所有图片信息
*
* @param sheet sheet表
* @param onlyInternal 单元格内部
* @return 照片集合
* @throws Exception 异常
*/
public List<PicturesInfo> getAllPictureInfos(Sheet sheet, boolean onlyInternal) throws Exception {
return getAllPictureInfos(sheet, null, null, null, null, onlyInternal);
}

/**
* 根据sheet和单元格信息获取图片
*
* @param sheet sheet表
* @param minRow 最小行
* @param maxRow 最大行
* @param minCol 最小列
* @param maxCol 最大列
* @param onlyInternal 是否内部
* @return 图片集合
* @throws Exception 异常
*/
public List<PicturesInfo> getAllPictureInfos(Sheet sheet, Integer minRow, Integer maxRow, Integer minCol,
Integer maxCol, boolean onlyInternal) throws Exception {
if (sheet instanceof HSSFSheet) {
return getXLSAllPictureInfos((HSSFSheet) sheet, minRow, maxRow, minCol, maxCol, onlyInternal);
} else if (sheet instanceof XSSFSheet) {
return getXLSXAllPictureInfos((XSSFSheet) sheet, minRow, maxRow, minCol, maxCol, onlyInternal);
} else {
throw new Exception("未处理类型,没有为该类型添加:GetAllPicturesInfos()扩展方法!");
}
}

private List<PicturesInfo> getXLSAllPictureInfos(HSSFSheet sheet, Integer minRow, Integer maxRow,
Integer minCol, Integer maxCol, Boolean onlyInternal) {
List<PicturesInfo> picturesInfoList = new ArrayList<>();
HSSFShapeContainer shapeContainer = sheet.getDrawingPatriarch();
if (shapeContainer == null) {
return picturesInfoList;
}
List<HSSFShape> shapeList = shapeContainer.getChildren();
for (HSSFShape shape : shapeList) {
if (shape instanceof HSSFPicture && shape.getAnchor() instanceof HSSFClientAnchor) {
HSSFPicture picture = (HSSFPicture) shape;
HSSFClientAnchor anchor = (HSSFClientAnchor) shape.getAnchor();

if (isInternalOrIntersect(minRow, maxRow, minCol, maxCol, anchor.getRow1(), anchor.getRow2(),
anchor.getCol1(), anchor.getCol2(), onlyInternal)) {
HSSFPictureData pictureData = picture.getPictureData();
picturesInfoList.add(new PicturesInfo()
.setMinRow(anchor.getRow1())
.setMaxRow(anchor.getRow2())
.setMinCol(anchor.getCol1())
.setMaxCol(anchor.getCol2())
.setPictureData(pictureData.getData())
.setExt(pictureData.getMimeType()));
}
}
}
return picturesInfoList;
}

private List<PicturesInfo> getXLSXAllPictureInfos(XSSFSheet sheet, Integer minRow, Integer maxRow,
Integer minCol, Integer maxCol, Boolean onlyInternal) {
List<PicturesInfo> picturesInfoList = new ArrayList<>();

List<POIXMLDocumentPart> documentPartList = sheet.getRelations();
for (POIXMLDocumentPart documentPart : documentPartList) {
if (documentPart instanceof XSSFDrawing) {
XSSFDrawing drawing = (XSSFDrawing) documentPart;
List<XSSFShape> shapes = drawing.getShapes();
for (XSSFShape shape : shapes) {
if (shape instanceof XSSFPicture) {
XSSFPicture picture = (XSSFPicture) shape;
XSSFClientAnchor anchor = picture.getPreferredSize();

if (isInternalOrIntersect(minRow, maxRow, minCol, maxCol, anchor.getRow1(), anchor.getRow2(),
anchor.getCol1(), anchor.getCol2(), onlyInternal)) {
XSSFPictureData pictureData = picture.getPictureData();
picturesInfoList.add(new PicturesInfo()
.setMinRow(anchor.getRow1())
.setMaxRow(anchor.getRow2())
.setMinCol(anchor.getCol1())
.setMaxCol(anchor.getCol2())
.setPictureData(pictureData.getData())
.setExt(pictureData.getMimeType()));
}
}
}
}
}

return picturesInfoList;
}

private boolean isInternalOrIntersect(Integer rangeMinRow, Integer rangeMaxRow, Integer rangeMinCol,
Integer rangeMaxCol, int pictureMinRow, int pictureMaxRow, int pictureMinCol, int pictureMaxCol,
Boolean onlyInternal) {
int _rangeMinRow = rangeMinRow == null ? pictureMinRow : rangeMinRow;
int _rangeMaxRow = rangeMaxRow == null ? pictureMaxRow : rangeMaxRow;
int _rangeMinCol = rangeMinCol == null ? pictureMinCol : rangeMinCol;
int _rangeMaxCol = rangeMaxCol == null ? pictureMaxCol : rangeMaxCol;

if (onlyInternal) {
return (_rangeMinRow <= pictureMinRow && _rangeMaxRow >= pictureMaxRow && _rangeMinCol <= pictureMinCol
&& _rangeMaxCol >= pictureMaxCol);
} else {
return ((Math.abs(_rangeMaxRow - _rangeMinRow) + Math.abs(pictureMaxRow - pictureMinRow) >= Math
.abs(_rangeMaxRow + _rangeMinRow - pictureMaxRow - pictureMinRow))
&& (Math.abs(_rangeMaxCol - _rangeMinCol) + Math.abs(pictureMaxCol - pictureMinCol) >= Math
.abs(_rangeMaxCol + _rangeMinCol - pictureMaxCol - pictureMinCol)));
}
}

private <T> boolean isNotEmpty(T obj) {
if (obj == null) {
return false;
}

if (obj instanceof String) {
return !((String) obj).isEmpty();
}

if (obj instanceof Collection) {
return !((Collection<?>) obj).isEmpty();
}

if (obj instanceof Map) {
return !((Map<?, ?>) obj).isEmpty();
}

// Add more checks for other types if needed

return true;
}
/**
* 图片基本信息
*/
private class PicturesInfo {

private int minRow;
private int maxRow;
private int minCol;
private int maxCol;
private String ext;
private byte[] pictureData;

public PicturesInfo() {
}
public byte[] getPictureData() {
return pictureData;
}
public PicturesInfo setPictureData(byte[] pictureData) {
this.pictureData = pictureData;
return this;
}
public int getMinRow() {
return minRow;
}
public PicturesInfo setMinRow(int minRow) {
this.minRow = minRow;
return this;
}
public int getMaxRow() {
return maxRow;
}
public PicturesInfo setMaxRow(int maxRow) {
this.maxRow = maxRow;
return this;
}
public int getMinCol() {
return minCol;
}
public PicturesInfo setMinCol(int minCol) {
this.minCol = minCol;
return this;
}
public int getMaxCol() {
return maxCol;
}
public PicturesInfo setMaxCol(int maxCol) {
this.maxCol = maxCol;
return this;
}
public String getExt() {
return ext;
}
public PicturesInfo setExt(String ext) {
this.ext = ext;
return this;
}
}
}