Java多種方式實(shí)現(xiàn)Excel轉(zhuǎn)Pdf的保姆級(jí)教程
需求介紹
在企業(yè)級(jí)系統(tǒng)使用或日常使用中,我們經(jīng)常使用excel表格數(shù)據(jù)進(jìn)行瀏覽數(shù)據(jù),但有時(shí)候我們會(huì)想要將excel轉(zhuǎn)換成pdf進(jìn)行預(yù)覽使用,這時(shí)就需要進(jìn)行程序編寫實(shí)現(xiàn)這個(gè)需求,本篇博文以java進(jìn)行編寫多種實(shí)現(xiàn)方式實(shí)現(xiàn)多sheet頁(yè)excel轉(zhuǎn)換一個(gè)pdf的需求
itextpdf實(shí)現(xiàn)
引入maven依賴
引入所需依賴,下面五個(gè)是主要依賴
<dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>4.1.0</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>4.1.0</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml-schemas</artifactId> <version>4.1.0</version> </dependency> <dependency> <groupId>com.itextpdf</groupId> <artifactId>itextpdf</artifactId> <version>5.5.13</version> </dependency> <dependency> <groupId>com.itextpdf</groupId> <artifactId>itext-asian</artifactId> <version>5.2.0</version> </dependency>
具體哪幾個(gè)博主忘了,如果上面依賴導(dǎo)入后寫代碼還有缺失的,可以直接把下面完整的都copy下
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.10</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>
<dependency>
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>1.6.1</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>4.1.0</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>4.1.0</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml-schemas</artifactId>
<version>4.1.0</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itextpdf</artifactId>
<version>5.5.13</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itext-asian</artifactId>
<version>5.2.0</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.27</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.30</version>
<scope>provided</scope>
</dependency>代碼實(shí)現(xiàn)
由于在使用時(shí),有時(shí)候excel里會(huì)插入圖片,所以我們?cè)谵D(zhuǎn)換時(shí)候需要考慮到圖片的業(yè)務(wù)
先對(duì)圖片類型進(jìn)行建模處理
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;
/**
* 擴(kuò)展
*/
private String ext;
/**
* 圖片數(shù)據(jù)
*/
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;
}
}
書寫轉(zhuǎn)換工具類
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 org.apache.poi.hssf.usermodel.*;
import org.apache.poi.ooxml.POIXMLDocumentPart;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.usermodel.Font;
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.util.*;
import java.util.List;
/**
* Excel轉(zhuǎn)PDF
* @author 廖航
* @date 2024-08-29 10:52
*/
//@UtilityClass
//@Slf4j
public class ExcelToPdfUtil {
/**
* 日志輸出
*/
// private static final log log = log.getlog(ExcelToPdfUtil.class);
/**
* 單元格隊(duì)列
*/
static Set<String> cellSet = new HashSet<>();
/**
* Excel轉(zhuǎn)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) {
}
}
/**
* Excel轉(zhuǎn)PDF并寫入輸出流
*
* @param inStream Excel輸入流
* @param outStream PDF輸出流
* @param excelSuffix Excel類型 .xls 和 .xlsx
* @throws Exception 異常信息
*/
public static void excelToPdf(InputStream inStream, OutputStream outStream, String excelSuffix) throws Exception {
// 輸入流轉(zhuǎn)workbook,獲取sheet 讀取Excel文件
Workbook workbook = getPoiWorkbookByFileStream(inStream, excelSuffix);
//設(shè)置pdf紙張大小 PageSize.A4 A4橫向
// Document document = new Document(PageSize.B0);
Document document = new Document(PageSize.A2.rotate());
// 綁定PDF寫入器到輸出流
PdfWriter.getInstance(document, outStream);
// 設(shè)置頁(yè)面四周邊距
document.setMargins(50, 50, 30, 30);
document.open(); // 準(zhǔn)備開始寫入內(nèi)容
//設(shè)置基本字體
BaseFont baseFont = BaseFont.createFont("C:\\Windows\\Fonts\\simsun.ttc,0", BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
for (int j = 0; j < workbook.getNumberOfSheets(); j++) {
if(j != 0) {
document.newPage(); // 新的Sheet開始新的一頁(yè)
}
//獲取sheet
Sheet sheet = workbook.getSheetAt(j);
// 獲取列寬度占比
float[] widths = getColWidth(sheet);
PdfPTable table = new PdfPTable(widths);
// 設(shè)置表格寬度為頁(yè)面內(nèi)容區(qū)域的百分比
table.setWidthPercentage(100);
int colCount = widths.length;
// 遍歷行
for (int rowIndex = sheet.getFirstRowNum(); rowIndex <= sheet.getLastRowNum(); rowIndex++) {
Row row = sheet.getRow(rowIndex);
if (Objects.isNull(row)) {
// 插入空對(duì)象
for (int i = 0; i < colCount; i++) {
table.addCell(createPdfPCell(null, 0, 11f, 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);
//忽略合并過(guò)的單元格
boolean mergedCell = span[0] == 1 && span[1] == 1;
if (mergedCell) {
continue;
}
pCell.setRowspan(span[0]);
pCell.setColspan(span[1]);
}
table.addCell(pCell);
}
}
}
document.add(table);
}
document.close();
}
protected static CellRangeAddress getColspanRowspanByExcel(int rowIndex, int colIndex,Sheet sheet) {
CellRangeAddress result = null;
int num = sheet.getNumMergedRegions();
for (int i = 0; i < num; i++) {
CellRangeAddress range = sheet.getMergedRegion(i);
if (range.getFirstColumn() == colIndex && range.getFirstRow() == rowIndex) {
result = range;
}
}
return result;
}
// 獲取Workbook對(duì)象
public static Workbook getPoiWorkbookByFileStream(InputStream inputStream, String excelSuffix) throws IOException {
if (excelSuffix.endsWith(".xlsx")) {
return new XSSFWorkbook(inputStream);
} else {
return new HSSFWorkbook(inputStream);
}
}
/**
* 單元格轉(zhuǎn)換,poi cell 轉(zhuǎn)換為 itext cell
*
* @param sheet poi sheet頁(yè)
* @param excelCell poi 單元格
* @param baseFont 基礎(chǔ)字體
* @return PDF單元格
*/
private static PdfPCell excelCellToPdfCell(Sheet sheet, Cell excelCell, BaseFont baseFont) throws Exception {
if (Objects.isNull(excelCell)) {
return createPdfPCell(null, 0, 11f, null);
}
int rowIndex = excelCell.getRowIndex();
int columnIndex = excelCell.getColumnIndex();
// 圖片信息
List<PicturesInfo> infos = getAllPictureInfos(sheet, rowIndex, rowIndex, columnIndex, columnIndex, false);
PdfPCell pCell;
if (infos != null && !infos.isEmpty()) {
Image image = Image.getInstance(infos.get(0).getPictureData());
// 調(diào)整圖片大小
image.scaleAbsolute(527, 215);
pCell = new PdfPCell(image);
} else {
Font excelFont = getExcelFont(sheet, excelCell);
//設(shè)置單元格字體
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()));
// 垂直對(duì)齊
pCell.setVerticalAlignment(getVerAlign(excelCell.getCellStyle().getVerticalAlignment().getCode()));
return pCell;
}
/**
* 創(chuàng)建itext pdf 單元格
*
* @param content 單元格內(nèi)容
* @param border 邊框
* @param minimumHeight 高度
* @param pdFont 字體
* @return pdf cell
*/
private static 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)) {
//設(shè)置單元格的最小高度
pCell.setMinimumHeight(minimumHeight);
}
return pCell;
}
/**
* excel垂直對(duì)齊方式映射到pdf對(duì)齊方式
*
* @param align 對(duì)齊
* @return 結(jié)果
*/
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水平對(duì)齊方式映射到pdf水平對(duì)齊方式
*
* @param align 對(duì)齊
* @return 結(jié)果
*/
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獲取圖片及文本內(nèi)容工具方法 ==============================================*/
/**
* 獲取字體
*
* @param sheet excel 轉(zhuǎn)換的sheet頁(yè)
* @param cell 單元格
* @return 字體
*/
private static 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單元格是否有邊框
*
* @param excelCell 單元格
* @return 結(jié)果
*/
private static boolean hasBorder(Cell excelCell) {
CellStyle style = excelCell.getCellStyle();
BorderStyle top = style.getBorderTop();
BorderStyle bottom = style.getBorderBottom();
BorderStyle left = style.getBorderLeft();
BorderStyle right = style.getBorderRight();
return top != BorderStyle.NONE ||
bottom != BorderStyle.NONE ||
left != BorderStyle.NONE ||
right != BorderStyle.NONE;
}
/**
* 判斷單元格是否是合并單元格
*
* @param sheet 表
* @param row 行
* @param column 列
* @return 結(jié)果
*/
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;
}
/**
* 計(jì)算合并單元格合并的跨行跨列數(shù)
*
* @param sheet 表
* @param row 行
* @param column 列
* @return 結(jié)果
*/
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 結(jié)果
*/
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中列數(shù)最多的行號(hào)
*
* @param sheet 表
* @return 結(jié)果
*/
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 根據(jù)單元格類型獲取單元格內(nèi)容
*
* @param excelCell poi單元格
* @return 單元格內(nèi)容文本
*/
public static String getExcelCellValue(Cell excelCell) {
// if (excelCell == null) {
// return "";
// }
// // 判斷數(shù)據(jù)的類型
// 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) {
// // 處理日期格式、時(shí)間格式
// if (DateUtil.isCellDateFormatted(excelCell)) {
// SimpleDateFormat sdf;
// // 驗(yàn)證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("日期格式錯(cuò)誤!!!");
// }
// Date date = excelCell.getDateCellValue();
// return sdf.format(date);
// } else if (excelCell.getCellStyle().getDataFormat() == 0) {
// //處理數(shù)值格式
// DataFormatter formatter = new DataFormatter();
// return formatter.formatCellValue(excelCell);
// }
// }
// if (cellType == CellType.ERROR) {
// return "非法字符";
// }
// return "";
if (excelCell == null) {
return "";
}
DataFormatter formatter = new DataFormatter();
return formatter.formatCellValue(excelCell);
}
/**
* 根據(jù)sheet和單元格信息獲取圖片
*
* @param sheet sheet表
* @param minRow 最小行
* @param maxRow 最大行
* @param minCol 最小列
* @param maxCol 最大列
* @param onlyInternal 是否內(nèi)部
* @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()擴(kuò)展方法!");
}
}
/**
* 獲取XLS圖片信息
*
* @param sheet 表
* @param minRow 最小行
* @param maxRow 最大行
* @param minCol 最小列
* @param maxCol 最大列
* @param onlyInternal 只在內(nèi)部
* @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 = StrUtil.format("{},{},{},{}", anchor.getRow1(), anchor.getRow2(), anchor.getCol1(), anchor.getCol2());
String item = String.format("%d,%d,%d,%d",
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 只在內(nèi)部
* @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 = StrUtil.format("{},{},{},{}", anchor.getRow1(), anchor.getRow2(), anchor.getCol1(), anchor.getCol2());
String item = String.format("%d,%d,%d,%d",
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;
}
/**
* 是內(nèi)部的或相交的
*
* @param rangeMinRow 最小行范圍
* @param rangeMaxRow 最大行范圍
* @param rangeMinCol 最小列范圍
* @param rangeMaxCol 最大列范圍
* @param pictureMinRow 圖片最小行
* @param pictureMaxRow 圖片最大行
* @param pictureMinCol 圖片最小列
* @param pictureMaxCol 圖片最大列
* @param onlyInternal 只在內(nèi)部
* @return 結(jié)果
*/
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)));
}
}
public static void main(String[] args) {
ExcelToPdfUtil.excelToPdf("原excel文件路徑.xlsx",
"轉(zhuǎn)換后輸出pdf路徑.pdf", ".xlsx");
}
}
測(cè)試時(shí)直接創(chuàng)建一個(gè)多sheet頁(yè)的excel表格,并填入一些測(cè)試數(shù)據(jù)進(jìn)行測(cè)試,查看轉(zhuǎn)換后的結(jié)果

在主方法里更改路徑進(jìn)行測(cè)試


等待程序執(zhí)行完后在導(dǎo)出路徑查看轉(zhuǎn)換后的pdf

點(diǎn)擊查看


可以看到轉(zhuǎn)換后的效果還是不錯(cuò)的,表格內(nèi)容寬度自動(dòng)使用pdf頁(yè)面的寬度,每個(gè)sheet頁(yè)都另起新的頁(yè)面,多個(gè)sheet頁(yè)拼接在一個(gè)pdf里,后續(xù)紙張大小參數(shù)等都可以在代碼里進(jìn)行靈活調(diào)整
aspose-cell實(shí)現(xiàn)
簡(jiǎn)介
Aspose.Cells for Java 是一款功能強(qiáng)大的 Java 庫(kù),專門用于操作 Microsoft Excel 文件而無(wú)需依賴 Microsoft Office。提供了豐富的API來(lái)創(chuàng)建、修改、轉(zhuǎn)換和打印Excel文檔。
主要功能包括但不限于:
- 文件格式支持:全面支持 Excel 的各種格式,如
.xls,.xlsx,.xlsm,.xltx,.xltm等。 - 數(shù)據(jù)處理:輕松讀取、寫入及修改工作表中的數(shù)據(jù),支持復(fù)雜的數(shù)據(jù)類型與結(jié)構(gòu)。
- 格式化:靈活設(shè)置單元格、行、列的格式,包括字體、顏色、邊框、背景色等,并支持條件格式化。
- 圖表與圖形:創(chuàng)建、更新或刪除圖表,添加圖片和其他圖形元素到工作表中。
- 公式計(jì)算:支持復(fù)雜的公式計(jì)算,確保數(shù)據(jù)準(zhǔn)確性。
- PDF 轉(zhuǎn)換:高質(zhì)量地將 Excel 文檔轉(zhuǎn)換為 PDF 格式,同時(shí)支持自定義頁(yè)面大小、方向等屬性。
- 安全性和保護(hù):對(duì) Excel 文件進(jìn)行加密、解密,以及設(shè)置打開或修改密碼。
- 批量處理:高效處理大量數(shù)據(jù)集,適用于大規(guī)模數(shù)據(jù)操作場(chǎng)景。
特別亮點(diǎn)
- 無(wú)須安裝 Microsoft Office:由于其獨(dú)立性,開發(fā)者可以在沒有安裝 MS Office 的環(huán)境中使用此庫(kù)執(zhí)行所有相關(guān)任務(wù)。
- 跨平臺(tái)兼容性:完全基于Java開發(fā),可在任何支持Java的平臺(tái)上運(yùn)行。
- 性能優(yōu)越:優(yōu)化了內(nèi)存管理和處理速度,即使對(duì)于大型文件也能保持良好的性能表現(xiàn)。
注意該jar包是收費(fèi)的,收費(fèi)版本試用時(shí)候會(huì)有水印和行數(shù)多少限制,一旦達(dá)到一定數(shù)據(jù)量可能會(huì)導(dǎo)致程序報(bào)錯(cuò),可以進(jìn)行破解使用
下載jar包
該jar包在國(guó)內(nèi)鏡像源里是拉取不到的,可以在官網(wǎng)直接下載jar包,后導(dǎo)入到本地使用

在官網(wǎng)里找到對(duì)應(yīng)版本依賴,如果是國(guó)外鏡像源或者其他源庫(kù)可以嘗試?yán)≡囋?如果沒有也可以直接下載jar包到本地后安裝到本地使用

點(diǎn)擊后下載
輸入下面命令進(jìn)行安裝到本地
mvn install:install-file "-Dfile=jar包所在路徑\jar包名.jar" "-DgroupId=com.aspose" "-DartifactId=aspose-cells" "-Dversion=版本號(hào)" "-Dpackaging=jar"
修改對(duì)應(yīng)的jar包路徑和包名后在idea命令行里執(zhí)行安裝jar包

沒有報(bào)錯(cuò)說(shuō)明安裝成功,在pom文件里書寫下對(duì)應(yīng)的依賴刷新下maven即可

刷新下maven即可然后書寫測(cè)試類嘗試轉(zhuǎn)換

執(zhí)行轉(zhuǎn)換

可以看到導(dǎo)出后的效果還是不錯(cuò)的,但是有水印

crack使用
這里以aspose-cells-20.7 去除水印及次數(shù)限制破解作為示例
反編譯jar包
使用 jd-gui.exe 反編譯查看jar包,直接搜索 License


1.修改
public static boolean isLicenseSet() {
return (a != null);
}
改為
public static boolean isLicenseSet() {
return true;
}
2.修改
public void setLicense(InputStream stream) {
Document document = null;
if (stream != null)
try {
DocumentBuilder documentBuilder = zad.b();
document = documentBuilder.parse(stream);
} catch (Exception exception) {
throw new CellsException(9, zf.a(new byte[] {
-20, 93, -23, 54, -20, -49, -59, 86, -9, -86,
49, 44, -59, 71, -52, -86, -90, 6, -90, -25,
-86, 1, -1, -92, -91, -126, 7, 113, -66, -95,
-121, 16, -122, -126, 7, 104, -40, -70, -10, -37,
126, 7, -111, -121, -121 }) + exception.getMessage());
}
a(document);
}
改成
public void setLicense(InputStream paramInputStream){
a = new License();
zbiw.a();
}
3.修改
private static Date k(String paramString) {
if (paramString == null || paramString.length() < 8)
return null;
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(zf.a(new byte[] {
79, 89, 33, -52, 79, -121, -125, 33, 71, -126,
105, -121 }));
try {
return simpleDateFormat.parse(paramString);
} catch (ParseException parseException) {
throw new IllegalArgumentException(zf.a(new byte[] {
-21, -113, -77, 13, -115, 27, Byte.MIN_VALUE, 35, 103, -52,
-20, -106, 71, 95, 31, -73, -76, -38, 13, 31,
-91, -97, -102, 85, -68, -33, -19, -87, -127, -14,
78, -23, 6, -25, -94, 1, -97, -95, -57, -121 }));
}
}
改成
private static Date k(String paramString){
return new Date(Long.MAX_VALUE);
}
修改完后使用jdi保存所有的文件為另存


生成 aspose-cells-20.7-crack.jar
修改 class 這里使用 javassist:
新建一個(gè) Spring boot 項(xiàng)目:
pom 文件中引用 javassist
引入破解所需依賴
<dependency><!--aspose的jar破解工具-->
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.20.0-GA</version>
</dependency>指定目錄生成修改后的License.class文件
public static void crackAspose(String JarPath) throws Exception {
// 這個(gè)是得到反編譯的池
ClassPool pool = ClassPool.getDefault();
// 取得需要反編譯的jar文件,設(shè)定路徑
pool.insertClassPath(JarPath);
CtClass cc_License = pool.get("com.aspose.cells.License");
CtMethod method_isLicenseSet = cc_License.getDeclaredMethod("isLicenseSet");
method_isLicenseSet.setBody("return true;");
CtClass cc_License2 = pool.get("com.aspose.cells.License");
CtMethod method_setLicense = cc_License2.getDeclaredMethod("setLicense");
method_setLicense.setBody("{ a = new com.aspose.cells.License();\n" +
" com.aspose.cells.zbiw.a();}");
CtMethod method_k = cc_License.getDeclaredMethod("k");
method_k.setBody("return new java.util.Date(Long.MAX_VALUE);");
cc_License.writeFile("輸出目錄路徑");
}
執(zhí)行該方法

在指定目錄下可以看到生成后的編譯文件

復(fù)制一個(gè) aspose-cells-20.7.jar 改名為 aspose-cells-20.7-crack.zip 然后解壓

把剛才生成的 License.class 文件 替換到解壓的源代碼中。

造一個(gè) License xml 文件,這里文件名為 com.aspose.cells.lic_2999.xml, 位置直接放源代碼解壓的根目錄

文件內(nèi)容為:
<License>
<Data>
<Products>
<Product>Aspose.Cells for Java</Product>
</Products>
<EditionType>Enterprise</EditionType>
<SubscriptionExpiry>29991231</SubscriptionExpiry>
<LicenseExpiry>29991231</LicenseExpiry>
<SerialNumber>evilrule</SerialNumber>
</Data>
<Signature>evilrule</Signature>
</License>
防止文件指紋校驗(yàn),我們需要?jiǎng)h除掉源代碼解壓包中的 META_INF 文件夾。
最后的根目錄:

壓縮源代碼(注意要是 zip 格式)

然后重命名后綴為:jar

測(cè)試 aspose-cells-20.7-crack.jar 使用
封裝一個(gè) License 驗(yàn)證方法:
public static boolean authrolizeLicense() {
boolean result = false;
try {
InputStream is = com.aspose.cells.License.class.getResourceAsStream("/com.aspose.cells.lic_2999.xml");
License asposeLicense = new License();
asposeLicense.setLicense(is);
is.close();
result = true;
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
修改 main 方法測(cè)試使用:
public static void main(String[] args) {
// 測(cè)試破解后的 aspose-cells-20.7-crack.jar
boolean auth = authrolizeLicense();
if (!auth) {
System.out.println("aspose 許可無(wú)效!");
return;
}
System.out.println("aspose 已就緒!");
try{
Workbook wb = new Workbook("F:\\aaa123.xlsx");
Worksheet ws = wb.getWorksheets().get(0);
ImageOrPrintOptions imgOptions = new ImageOrPrintOptions();
imgOptions.setImageFormat(ImageFormat.getPng());
imgOptions.setCellAutoFit(true);
imgOptions.setOnePagePerSheet(true);
SheetRender render = new SheetRender(ws, imgOptions);
render.toImage(0, "F:\\aaa\\123.png");
}
catch (Exception e){
e.printStackTrace();
}
}
這里圖片和excel可以隨便搞一個(gè)嘗試測(cè)試使用
先生成圖片再轉(zhuǎn)pdf
前面的破解工作都做好后就可以嘗試測(cè)試了,aspose可以先將excel轉(zhuǎn)成圖片后再轉(zhuǎn)成pdf
public static void main(String[] args) {
// 授權(quán)檢查(假設(shè)你已經(jīng)破解)
boolean auth = authrolizeLicense();
if (!auth) {
System.out.println("aspose 許可無(wú)效!");
return;
}
System.out.println("aspose 已就緒!");
try {
Workbook wb = new Workbook("C:\\Users\\d\\Desktop\\excelToPdf\\奧特曼.xlsx");
ImageOrPrintOptions imgOptions = new ImageOrPrintOptions();
imgOptions.setImageFormat(ImageFormat.getPng());
imgOptions.setCellAutoFit(true);
imgOptions.setOnePagePerSheet(true);
String outputPdfPath = "C:\\Users\\d\\Desktop\\excelToPdf\\奧特曼Aspose導(dǎo)出圖片版.pdf";
// 創(chuàng)建一個(gè)無(wú)邊距的文檔
Document document = new Document(PageSize.A4); // 使用 A4 紙張尺寸
PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(outputPdfPath));
// 設(shè)置頁(yè)面無(wú)邊距(非常重要)
document.setMargins(0, 0, 0, 0); // 左、右、上、下全部為 0
document.open();
for (int i = 0; i < wb.getWorksheets().getCount(); i++) {
Worksheet ws = wb.getWorksheets().get(i);
SheetRender render = new SheetRender(ws, imgOptions);
String imageFilePath = "C:\\Users\\d\\Desktop\\excelToPdf\\sheet_" + i + ".png";
render.toImage(0, imageFilePath);
Image img = Image.getInstance(imageFilePath);
// 自動(dòng)縮放圖片以適配頁(yè)面寬度和高度
img.scaleToFit(document.getPageSize().getWidth(), document.getPageSize().getHeight());
// 居中顯示(iText 默認(rèn)就是居中的,但我們可以更明確地設(shè)置)
img.setAlignment(Image.ALIGN_CENTER);
// 新增一頁(yè)
document.newPage();
// 插入圖片到當(dāng)前頁(yè)面
document.add(img);
}
document.close();
writer.close();
System.out.println("? PDF 文件已生成: " + outputPdfPath);
} catch (Exception e) {
e.printStackTrace();
}
}執(zhí)行測(cè)試

查看生成的效果


可以看到原有的水印都沒了
直接轉(zhuǎn)換
aspose也可以直接進(jìn)行轉(zhuǎn)換
public static void main(String[] args) throws Exception {
// // 授權(quán)檢查(假設(shè)你已經(jīng)破解)
// boolean auth = authrolizeLicense();
// if (!auth) {
// System.out.println("aspose 許可無(wú)效!");
// return;
// }
//
// System.out.println("aspose 已就緒!");
//
// try {
// Workbook wb = new Workbook("C:\\Users\\d\\Desktop\\excelToPdf\\奧特曼.xlsx");
// ImageOrPrintOptions imgOptions = new ImageOrPrintOptions();
// imgOptions.setImageFormat(ImageFormat.getPng());
// imgOptions.setCellAutoFit(true);
// imgOptions.setOnePagePerSheet(true);
//
// String outputPdfPath = "C:\\Users\\d\\Desktop\\excelToPdf\\奧特曼aspose導(dǎo)出圖片版.pdf";
//
// // 創(chuàng)建一個(gè)無(wú)邊距的文檔
// Document document = new Document(PageSize.A4); // 使用 A4 紙張尺寸
// PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(outputPdfPath));
//
// // 設(shè)置頁(yè)面無(wú)邊距(非常重要)
// document.setMargins(0, 0, 0, 0); // 左、右、上、下全部為 0
// document.open();
//
// for (int i = 0; i < wb.getWorksheets().getCount(); i++) {
// Worksheet ws = wb.getWorksheets().get(i);
// SheetRender render = new SheetRender(ws, imgOptions);
// String imageFilePath = "C:\\Users\\d\\Desktop\\excelToPdf\\sheet_" + i + ".png";
// render.toImage(0, imageFilePath);
//
// Image img = Image.getInstance(imageFilePath);
//
// // 自動(dòng)縮放圖片以適配頁(yè)面寬度和高度
// img.scaleToFit(document.getPageSize().getWidth(), document.getPageSize().getHeight());
//
// // 居中顯示(iText 默認(rèn)就是居中的,但我們可以更明確地設(shè)置)
// img.setAlignment(Image.ALIGN_CENTER);
//
// // 新增一頁(yè)
// document.newPage();
//
// // 插入圖片到當(dāng)前頁(yè)面
// document.add(img);
// }
//
// document.close();
// writer.close();
//
// System.out.println("? PDF 文件已生成: " + outputPdfPath);
// } catch (Exception e) {
// e.printStackTrace();
// }
// 加載 Excel 文件
Workbook workbook = new Workbook("C:\\Users\\d\\Desktop\\excelToPdf\\奧特曼.xlsx");
// 設(shè)置 PDF 導(dǎo)出選項(xiàng)
PdfSaveOptions saveOpts = new PdfSaveOptions();
// 設(shè)置頁(yè)面尺寸為 A2(單位是 像素 或 毫米,這里使用像素)
// A2 尺寸:4961 x 7016 像素(分辨率為 300 DPI)
PageSetup pageSetup = workbook.getWorksheets().get(0).getPageSetup();
pageSetup.setPaperSize(PaperSizeType.PAPER_A_3); // 設(shè)置為 A2 紙張
pageSetup.setOrientation(PageOrientationType.LANDSCAPE); // 設(shè)置為橫向
// 如果你想對(duì)所有 sheet 都生效,可以循環(huán)設(shè)置每個(gè) sheet 的頁(yè)面屬性
for (int i = 0; i < workbook.getWorksheets().getCount(); i++) {
Worksheet sheet = workbook.getWorksheets().get(i);
sheet.getPageSetup().setPaperSize(PaperSizeType.PAPER_A_3);
sheet.getPageSetup().setOrientation(PageOrientationType.LANDSCAPE);
}
// 設(shè)置每張工作表輸出為一頁(yè) PDF
saveOpts.setOnePagePerSheet(true);
// 導(dǎo)出為 PDF
String outputPdfPath = "C:\\Users\\d\\Desktop\\excelToPdf\\奧特曼aspose導(dǎo)出.pdf";
workbook.save(outputPdfPath, saveOpts);
System.out.println("? 已成功導(dǎo)出 A2 橫向 PDF: " + outputPdfPath);
}查看效果

這種方式雖然可直接轉(zhuǎn)換,但是還是會(huì)有水印,所以還是推薦先轉(zhuǎn)圖片后再轉(zhuǎn)pdf的方式
以上就是Java多種方式實(shí)現(xiàn)Excel轉(zhuǎn)Pdf的保姆級(jí)教程的詳細(xì)內(nèi)容,更多關(guān)于Java Excel轉(zhuǎn)Pdf的多種方式的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
MyBatis中的collection兩種使用方法及效率比較
collection主要是應(yīng)對(duì)表關(guān)系是一對(duì)多的情況,本文主要介紹了MyBatis中的collection兩種使用方法及效率比較,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-06-06
java?-jar啟動(dòng)參數(shù)設(shè)置file.encoding編碼,解決中文亂碼的問題
這篇文章主要介紹了java?-jar啟動(dòng)參數(shù)設(shè)置file.encoding編碼,解決中文亂碼的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-07-07
Java源碼解析之HashMap的put、resize方法詳解
這篇文章主要介紹了Java源碼解析之HashMap的put、resize方法詳解,文中有非常詳細(xì)的代碼示例,對(duì)正在學(xué)習(xí)java的小伙伴們有很大的幫助,需要的朋友可以參考下2021-04-04
深入理解Java8新特性之接口中的默認(rèn)方法和靜態(tài)方法
從Java8開始,程序允許在接口中包含帶有具體實(shí)現(xiàn)的方法,使用default修飾,這類方法就是默認(rèn)方法。默認(rèn)方法在接口中可以添加多個(gè),并且Java8提供了很多對(duì)應(yīng)的接口默認(rèn)方法,接下來(lái)讓我們一起來(lái)看看吧2021-11-11
Java Mybatis框架Dao層的實(shí)現(xiàn)與映射文件以及核心配置文件詳解分析
MyBatis 是一款優(yōu)秀的持久層框架,它支持自定義 SQL、存儲(chǔ)過(guò)程以及高級(jí)映射。MyBatis 免除了幾乎所有的 JDBC 代碼以及設(shè)置參數(shù)和獲取結(jié)果集的工作。MyBatis 可以通過(guò)簡(jiǎn)單的 XML 或注解來(lái)配置和映射原始類型、接口和 Java POJO為數(shù)據(jù)庫(kù)中的記錄2021-10-10
SpringBoot中的ApplicationListener事件監(jiān)聽器使用詳解
這篇文章主要介紹了SpringBoot中的ApplicationListener事件監(jiān)聽器使用詳解,ApplicationListener是應(yīng)用程序的事件監(jiān)聽器,繼承自java.util.EventListener標(biāo)準(zhǔn)接口,采用觀察者設(shè)計(jì)模式,需要的朋友可以參考下2023-11-11
詳解SpringBoot簡(jiǎn)化配置分析總結(jié)
這篇文章主要介紹了詳解SpringBoot簡(jiǎn)化配置分析總結(jié),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-10-10

