由于现在存在需求,通过Java将数据文本生成特点格式Excel,再输出为PDF。
调研了一些方案,最终决定使用POI写入Excel,再使用Itext5生成PDF。
在网上找了一些Itext的转换工具类,进行了一些改动。
目前市面上 Excel 转 PDF 的组件较多:
- 收费:aspose、GcExcel、spire
- 开源:jacob、itextpdf
其中收费的组件封装得比较好,代码简洁,转换的效果也很好,但收费也高得离谱:
为了成本考虑,就需要考虑开源的组件了,因为它们都是免费的:
- jacob:目前没有探索出很好的导出效果。
- itextpdf:已探索出很好的导出效果,达到了与收费组件一致的效果(推荐)。
使用步骤
1.引入依赖
<dependency><groupId>org.apache.poi</groupId><artifactId>poi</artifactId><version>5.2.3</version></dependency><dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml</artifactId><version>5.2.3</version></dependency><dependency><groupId>com.itextpdf</groupId><artifactId>itextpdf</artifactId><version>5.5.13.4</version></dependency><dependency><groupId>com.itextpdf</groupId><artifactId>itext-asian</artifactId><version>5.2.0</version></dependency>
2.定义工具类
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.szis.tangram.common.utils.CollectionUtils;
import com.szis.tangram.common.utils.ObjectUtils;
import com.szis.tangram.common.utils.StringUtils;
import lombok.experimental.UtilityClass;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.hssf.usermodel.*;
import org.apache.poi.hssf.util.HSSFColor;
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.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.text.SimpleDateFormat;
import java.util.List;
import java.util.*;@UtilityClass
@Slf4j
public class ExcelToPdfUtil {/*** 单元格队列*/Set<String> cellSet = new HashSet<>();public static void main(String[] args) {String excelFilePath = "C:\\OSSPDF\\试卷导出A9aiAsTKmvOVqR4C.xlsx";String pdfFilePath = "C:\\OSSPDF\\试卷导出模板初版转换PDF预览.pdf";excelToPdf(excelFilePath, pdfFilePath, ".xlsx");System.out.println("Excel文件已成功转换为PDF!");}/*** Excel转PDF** @param excelPath Excel文件路径* @param pdfPath PDF文件路径* @param excelSuffix Excel文件后缀*/public static void excelToPdf(String excelPath, String pdfPath, String excelSuffix) {try (InputStream in = Files.newInputStream(Paths.get(excelPath));OutputStream out = Files.newOutputStream(Paths.get(pdfPath))) {ExcelToPdfUtil.excelToPdf(in, out, excelSuffix);} catch (Exception e) {throw new RuntimeException(e);}}private String getServerType() {// 这里只是示例,实际需根据你的获取方式来返回正确的服务器类型return System.getProperty("os.name").toLowerCase().contains("win") ? "windows" : "linux";}/*** Excel转PDF并写入输出流** @param inStream Excel输入流* @param outStream PDF输出流* @param excelSuffix Excel类型 .xls 和 .xlsx* @throws Exception 异常信息*/public static void excelToPdf(InputStream inStream, OutputStream outStream, String excelSuffix) {// 输入流转workbook,获取sheetSheet sheet = null;try {sheet = getPoiSheetByFileStream(inStream, 0, excelSuffix);} catch (IOException e) {throw new RuntimeException(e);}// 获取列宽度占比float[] widths = getColWidth(sheet);PdfPTable table = new PdfPTable(widths);table.setWidthPercentage(100);int colCount = widths.length;//设置基本字体BaseFont baseFont = null;try {String serverType = getServerType();if ("windows".equals(serverType)) {baseFont = BaseFont.createFont("C:\\Windows\\Fonts\\simsun.ttc,0", BaseFont.IDENTITY_H, BaseFont.EMBEDDED);} else {baseFont = BaseFont.createFont("/usr/share/fonts/simsun.ttc,0", BaseFont.IDENTITY_H, BaseFont.EMBEDDED);}
// baseFont = BaseFont.createFont("C:\\Windows\\Fonts\\simsun.ttc,0", BaseFont.IDENTITY_H, BaseFont.EMBEDDED);} catch (DocumentException e) {throw new RuntimeException(e);} catch (IOException e) {throw new RuntimeException(e);}// 遍历行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++) {com.itextpdf.text.Font pdFont = new com.itextpdf.text.Font(baseFont, 13f, 0, BaseColor.BLACK);table.addCell(createPdfPCell("", 0, 13f, pdFont, null));}} else {// 遍历单元格for (int columnIndex = 0; (columnIndex < row.getLastCellNum() || columnIndex < colCount) && columnIndex > -1; columnIndex++) {PdfPCell pCell = null;try {pCell = excelCellToPdfCell(sheet, row.getCell(columnIndex), baseFont);} catch (Exception e) {throw new RuntimeException(e);}// 是否合并单元格if (isMergedRegion(sheet, rowIndex, columnIndex)) {int[] span = getMergedSpan(sheet, rowIndex, columnIndex);//忽略合并过的单元格boolean mergedCell = span[0] == 1 && span[1] == 1;if (mergedCell) {pCell.setPhrase(null);continue;}else {pCell.setRowspan(span[0]);pCell.setColspan(span[1]);}}table.addCell(pCell);}}}// 初始化PDF文档对象createPdfTableAndWriteDocument(outStream, table);}/*** 单元格转换,poi cell 转换为 itext cell** @param sheet poi sheet页* @param excelCell poi 单元格* @param baseFont 基础字体* @return PDF单元格*/private static PdfPCell excelCellToPdfCell(Sheet sheet, Cell excelCell, BaseFont baseFont) {if (Objects.isNull(excelCell)) {com.itextpdf.text.Font pdFont = new com.itextpdf.text.Font(baseFont, 13f, 0, BaseColor.BLACK);
// table.addCell(createPdfPCell("", 0, 13f, pdFont, null));return createPdfPCell("", 0, 13f, pdFont, null);}int rowIndex = excelCell.getRowIndex();int columnIndex = excelCell.getColumnIndex();// 获取单元格背景色(更通用的获取及转换方式开始)short bgColorIndex = excelCell.getCellStyle().getFillForegroundColor();BaseColor backgroundColor = null;if (bgColorIndex != IndexedColors.WHITE.getIndex()) {if (sheet instanceof HSSFSheet) {HSSFWorkbook workbook = (HSSFWorkbook) sheet.getWorkbook();HSSFPalette palette = workbook.getCustomPalette();HSSFColor hssfColor = palette.getColor(bgColorIndex);if (hssfColor != null) {short[] argb = hssfColor.getTriplet();byte r = (byte) Math.min(255, Math.max(0, argb[0]));byte g = (byte) Math.min(255, Math.max(0, argb[1]));byte b = (byte) Math.min(255, Math.max(0, argb[2]));backgroundColor = new BaseColor(r, g, b);}} else if (sheet instanceof XSSFSheet) {XSSFColor xssfColor = ((XSSFCell) excelCell).getCellStyle().getFillForegroundXSSFColor();if (xssfColor != null) {byte[] argb = xssfColor.getARGB();if (argb != null && argb.length >= 4) {// 将有符号的byte类型转换为无符号的int类型,并确保范围在0 - 255argb[0] = (byte) (argb[0] & 0xFF);argb[1] = (byte) (argb[1] & 0xFF);argb[2] = (byte) (argb[2] & 0xFF);argb[3] = (byte) (argb[3] & 0xFF);argb[0] = (byte) Math.min(255, Math.max(0, argb[0]));argb[1] = (byte) Math.min(255, Math.max(0, argb[1]));argb[2] = (byte) Math.min(255, Math.max(0, argb[2]));argb[3] = (byte) Math.min(255, Math.max(0, argb[3]));}// backgroundColor = new BaseColor(argb[1], argb[2], argb[3], argb[0]);backgroundColor = new BaseColor(200, 200, 200); // 设置为默认的灰色}}}// 获取单元格字体颜色(新增代码部分开始)BaseColor fontColor = null;short fontColorIndex = -1;if (sheet instanceof HSSFSheet) {fontColorIndex = ((HSSFCell) excelCell).getCellStyle().getFont(sheet.getWorkbook()).getColor();} else if (sheet instanceof XSSFSheet) {fontColorIndex = ((XSSFCell) excelCell).getCellStyle().getFont().getColor();}if (fontColorIndex != IndexedColors.AUTOMATIC.getIndex()) {if (fontColorIndex == IndexedColors.BLACK.getIndex()) {fontColor = BaseColor.BLACK;} else if (fontColorIndex == IndexedColors.RED.getIndex()) {fontColor = BaseColor.RED;} else if (fontColorIndex == IndexedColors.BLUE.getIndex()) {fontColor = BaseColor.BLUE;}// 这里可以继续添加更多颜色判断,列举常见颜色,类似处理背景色时的方式,也可采用更通用的颜色转换方式(如下面注释掉的代码部分)// 以下是一种更通用的通过RGB值转换的示例(但需要进一步处理可能的异常情况等,暂注释掉先展示简单常见颜色判断方式)
// byte[] argb = getColorArgbFromIndex(fontColorIndex, sheet);
// // 假设定义了这个方法来获取RGB值,暂未实现
// if (argb == null) {
// fontColor = new BaseColor(0, 0, 0);
// } else {
// fontColor = new BaseColor(argb[1], argb[2], argb[3]);
// }}// 图片信息List<PicturesInfo> infos = null;try {infos = getAllPictureInfos(sheet, rowIndex, rowIndex, columnIndex, columnIndex, false);} catch (Exception e) {throw new RuntimeException(e);}PdfPCell pCell;if (CollectionUtils.isNotEmpty(infos)) {Image image = null;try {image = Image.getInstance(infos.get(0).getPictureData());} catch (BadElementException e) {throw new RuntimeException(e);} catch (IOException e) {throw new RuntimeException(e);}// 调整图片大小image.scaleAbsolute(527, 215);pCell = new PdfPCell(image);} else {Font excelFont = getExcelFont(sheet, excelCell);//设置单元格字体com.itextpdf.text.Font pdFont = new com.itextpdf.text.Font(baseFont, excelFont.getFontHeightInPoints(), excelFont.getBold() ? 1 : 0, fontColor != null ? fontColor : BaseColor.BLACK);// Integer border = hasBorder(excelCell) ? null : 0;Integer border = 0;if (hasTopBorder(excelCell)) {border |= com.itextpdf.text.Rectangle.TOP;}if (hasBottomBorder(excelCell)) {border |= com.itextpdf.text.Rectangle.BOTTOM;}if (hasLeftBorder(excelCell)) {border |= com.itextpdf.text.Rectangle.LEFT;}if (hasRightBorder(excelCell)) {border |= com.itextpdf.text.Rectangle.RIGHT;}String excelCellValue = getExcelCellValue(excelCell);pCell = createPdfPCell(excelCellValue, border, excelCell.getRow().getHeightInPoints(), pdFont, backgroundColor);}
// if (ObjectUtils.isEmpty(pCell) || ObjectUtils.isEmpty(pCell.getLeft())) {
// pCell = createPdfPCell(null, 0, 13f, null, null);
// }// 水平居中pCell.setHorizontalAlignment(getHorAlign(excelCell.getCellStyle().getAlignment().getCode()));// 垂直对齐pCell.setVerticalAlignment(getVerAlign(excelCell.getCellStyle().getVerticalAlignment().getCode()));return pCell;}private static byte[] getColorArgbFromIndex(short fontColorIndex, Sheet sheet) {byte[] argb = new byte[4];if (sheet instanceof HSSFSheet) {HSSFWorkbook workbook = (HSSFWorkbook) sheet.getWorkbook();HSSFPalette palette = workbook.getCustomPalette();HSSFColor hssfColor = palette.getColor(fontColorIndex);if (hssfColor != null) {short[] triplet = hssfColor.getTriplet();argb[0] = (byte) 255; // Alpha通道,暂设为不透明argb[1] = (byte) triplet[0];argb[2] = (byte) triplet[1];argb[3] = (byte) triplet[2];}} else if (sheet instanceof XSSFSheet) {org.apache.poi.xssf.usermodel.XSSFColor xssfColor = new org.apache.poi.xssf.usermodel.XSSFColor(IndexedColors.fromInt(fontColorIndex), null);argb = xssfColor.getARGB();}
// if (argb != null && argb.length >= 4) {
// // 将有符号的byte类型转换为无符号的int类型,并确保范围在0 - 255
// argb[0] = (byte) (argb[0] & 0xFF);
// argb[1] = (byte) (argb[1] & 0xFF);
// argb[2] = (byte) (argb[2] & 0xFF);
// argb[3] = (byte) (argb[3] & 0xFF);
//
// argb[0] = (byte) Math.min(255, Math.max(0, argb[0]));
// argb[1] = (byte) Math.min(255, Math.max(0, argb[1]));
// argb[2] = (byte) Math.min(255, Math.max(0, argb[2]));
// argb[3] = (byte) Math.min(255, Math.max(0, argb[3]));
// }return argb;}/*** 创建pdf文档,并添加表格** @param outStream 输出流,目标文档* @param table 表格* @throws DocumentException 异常信息*/private static void createPdfTableAndWriteDocument(OutputStream outStream, PdfPTable table) {//设置pdf纸张大小 PageSize.A4 A4横向try {Document document = new Document(PageSize.A4);PdfWriter.getInstance(document, outStream);//设置页边距 宽document.setMargins(20, 20, 40, 40);document.open();document.add(table);document.close();} catch (DocumentException e) {log.error(e.getMessage(), e);throw new RuntimeException(e);}}/*** Excel文档输入流转换为对应的workbook及获取对应的sheet** @param inputStream Excel文档输入流* @param sheetNo sheet编号,默认0 第一个sheet* @param excelSuffix 文件类型 .xls和.xlsx* @return poi sheet* @throws IOException 异常*/public static Sheet getPoiSheetByFileStream(InputStream inputStream, int sheetNo, String excelSuffix) throws IOException {Workbook workbook;if (excelSuffix.endsWith(".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 static PdfPCell createPdfPCell(String content, Integer border, Float minimumHeight, com.itextpdf.text.Font pdFont, BaseColor backgroundColor) {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);}if (backgroundColor != null) {pCell.setBackgroundColor(backgroundColor);}return pCell;}/*** excel垂直对齐方式映射到pdf对齐方式** @param align 对齐* @return 结果*/private static 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水平对齐方式** @param align 对齐* @return 结果*/private static 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 static Font getExcelFont(Sheet sheet, Cell cell) {// xlsif (sheet instanceof HSSFSheet) {Workbook workbook = sheet.getWorkbook();return ((HSSFCell) cell).getCellStyle().getFont(workbook);}// xlsxreturn ((XSSFCell) cell).getCellStyle().getFont();}/*** 判断excel单元格是否有边框** @param excelCell 单元格* @return 结果*/private static 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 static boolean hasTopBorder(Cell excelCell) {short top = excelCell.getCellStyle().getBorderTop().getCode();return top > 0;}private static boolean hasBottomBorder(Cell excelCell) {short bottom = excelCell.getCellStyle().getBorderBottom().getCode();return bottom > 0;}private static boolean hasLeftBorder(Cell excelCell) {short left = excelCell.getCellStyle().getBorderLeft().getCode();return left > 0;}private static boolean hasRightBorder(Cell excelCell) {short right = excelCell.getCellStyle().getBorderRight().getCode();return right > 0;}/*** 判断单元格是否是合并单元格** @param sheet 表* @param row 行* @param column 列* @return 结果*/private static 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;}/*** 判断指定单元格是否为合并单元格** @param sheet 当前sheet页* @param row 行号* @param column 列号* @return 是否为合并单元格*/private static boolean isMergedCell(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 && column >= firstColumn && column <= lastColumn) {return true;}}return false;}/*** 计算合并单元格合并的跨行跨列数** @param sheet 表* @param row 行* @param column 列* @return 结果*/private static 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中每列宽度的占比** @param sheet 表* @return 结果*/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中列数最多的行号** @param sheet 表* @return 结果*/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 static 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) {// 处理日期格式、时间格式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 {DataFormatter formatter = new DataFormatter();return formatter.formatCellValue(excelCell);
// 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 static 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 static 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()扩展方法!");}}/*** 获取XLS图片信息** @param sheet 表* @param minRow 最小行* @param maxRow 最大行* @param minCol 最小列* @param maxCol 最大列* @param onlyInternal 只在内部* @return 图片信息列表*/private static 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)) {String item = StringUtils.format("{},{},{},{}", anchor.getRow1(), anchor.getRow2(), anchor.getCol1(), anchor.getCol2());if (cellSet.contains(item)) {continue;}cellSet.add(item);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;}/*** 获取XLSX图片信息** @param sheet 表* @param minRow 最小行* @param maxRow 最大行* @param minCol 最小列* @param maxCol 最大列* @param onlyInternal 只在内部* @return 图片信息列表*/private static 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)) {String item = StringUtils.format("{},{},{},{}", anchor.getRow1(), anchor.getRow2(), anchor.getCol1(), anchor.getCol2());if (cellSet.contains(item)) {continue;}cellSet.add(item);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;}/*** 是内部的或相交的** @param rangeMinRow 最小行范围* @param rangeMaxRow 最大行范围* @param rangeMinCol 最小列范围* @param rangeMaxCol 最大列范围* @param pictureMinRow 图片最小行* @param pictureMaxRow 图片最大行* @param pictureMinCol 图片最小列* @param pictureMaxCol 图片最大列* @param onlyInternal 只在内部* @return 结果*/private static 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)));}}}
3.定义图片信息
import java.io.Serializable;/*** 图片信息*/
public class PicturesInfo implements Serializable {private static final long serialVersionUID = 1L;/*** 最小行*/private int minRow;/*** 最大行*/private int maxRow;/*** 最小列*/private int minCol;/*** 最大列*/private int maxCol;/*** 扩展*/private String ext;/*** 图片数据*/private byte[] pictureData;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;}public byte[] getPictureData() {return pictureData;}public PicturesInfo setPictureData(byte[] pictureData) {this.pictureData = pictureData;return this;}
}
参考文档:Java Excel转PDF(免费) - 天航星 - 博客园