You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

基于iText实现Excel转PDF:如何自动处理合并单元格与空单元格?

Excel转PDF:用iText+POI搞定合并单元格与空单元格

嘿,你的需求完全可以通过iText搭配Apache POI(你代码里已经在用HSSF了)实现!你现在的代码只处理了最基础的字符串单元格,而且列数固定,自然搞不定复杂结构的Excel。下面我给你梳理下关键改进点,再附上完整的实现代码:

现有代码的核心问题

  • 仅处理STRING类型的单元格,忽略了数字、日期等其他类型
  • 列数硬编码为2,无法适配动态列数的Excel
  • 完全没处理合并单元格空单元格,这也是复杂Excel转PDF的核心痛点

关键解决方案

1. 动态获取列数

不要固定写死列数,通过遍历Excel的行来获取最大列数,确保PDF表格能匹配Excel的结构:

// 遍历所有行,找到最大列数
int columnCount = 0;
for (int rowNum = 0; rowNum <= my_worksheet.getLastRowNum(); rowNum++) {
    Row row = my_worksheet.getRow(rowNum);
    if (row != null && row.getLastCellNum() > columnCount) {
        columnCount = row.getLastCellNum();
    }
}
PdfPTable my_table = new PdfPTable(columnCount);

2. 处理合并单元格

POI提供了getMergedRegions()方法可以获取所有合并区域,我们需要把这些区域映射到PDF表格的PdfPCellcolspanrowspan属性:

  • 遍历所有合并区域,记录每个起始单元格的合并范围
  • 在生成单元格时,判断当前单元格是否是合并区域的起始单元格,若是则设置对应的跨行跨列,同时跳过后续被合并的单元格

3. 处理空单元格

不要只遍历行里的现有单元格,而是按列索引循环,用Row.MissingCellPolicy.CREATE_NULL_AS_BLANK自动创建空白单元格:

Cell cell = row.getCell(col, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK);

4. 支持多种单元格类型

扩展判断逻辑,覆盖数字、日期、布尔值等常见类型,避免数据丢失:

switch (cell.getCellType()) {
    case STRING:
        table_cell = new PdfPCell(new Phrase(cell.getStringCellValue()));
        break;
    case NUMERIC:
        if (DateUtil.isCellDateFormatted(cell)) {
            table_cell = new PdfPCell(new Phrase(cell.getDateCellValue().toString()));
        } else {
            table_cell = new PdfPCell(new Phrase(String.valueOf(cell.getNumericCellValue())));
        }
        break;
    case BOOLEAN:
        table_cell = new PdfPCell(new Phrase(String.valueOf(cell.getBooleanCellValue())));
        break;
    default:
        table_cell = new PdfPCell(new Phrase(""));
}

完整改进代码

import com.itextpdf.text.Document;
import com.itextpdf.text.Phrase;
import com.itextpdf.text.pdf.PdfPCell;
import com.itextpdf.text.pdf.PdfPTable;
import com.itextpdf.text.pdf.PdfWriter;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;

import java.io.FileInputStream;
import java.io.FileOutputStream;

public class ExcelToPdfEnhanced {
    public static void main(String[] args) throws Exception {
        // 读取Excel文件
        FileInputStream inputDocument = new FileInputStream(new File("C:\\excel_to_pdf.xls"));
        HSSFWorkbook workbook = new HSSFWorkbook(inputDocument);
        HSSFSheet worksheet = workbook.getSheetAt(0);

        // 获取Excel的最大列数
        int columnCount = 0;
        for (int rowNum = 0; rowNum <= worksheet.getLastRowNum(); rowNum++) {
            Row row = worksheet.getRow(rowNum);
            if (row != null && row.getLastCellNum() > columnCount) {
                columnCount = row.getLastCellNum();
            }
        }
        if (columnCount <= 0) {
            throw new IllegalArgumentException("Excel工作表无有效列!");
        }

        // 创建PDF文档
        Document pdfDocument = new Document();
        PdfWriter.getInstance(pdfDocument, new FileOutputStream("Excel2PDF_Enhanced_Output.pdf"));
        pdfDocument.open();

        // 创建PDF表格,设置宽度占满页面
        PdfPTable pdfTable = new PdfPTable(columnCount);
        pdfTable.setWidthPercentage(100);

        // 标记已处理的合并单元格,避免重复添加
        boolean[][] processedCells = new boolean[worksheet.getLastRowNum() + 1][columnCount];

        // 遍历所有行
        for (int rowNum = 0; rowNum <= worksheet.getLastRowNum(); rowNum++) {
            Row row = worksheet.getRow(rowNum);
            if (row == null) {
                // 空行,添加对应数量的空白单元格
                for (int col = 0; col < columnCount; col++) {
                    pdfTable.addCell(new PdfPCell(new Phrase("")));
                }
                continue;
            }

            // 按列索引遍历,确保每个列都被处理
            for (int col = 0; col < columnCount; col++) {
                if (processedCells[rowNum][col]) {
                    // 该单元格已被合并处理,跳过
                    continue;
                }

                Cell cell = row.getCell(col, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK);
                PdfPCell pdfCell = null;

                // 处理不同类型的单元格
                switch (cell.getCellType()) {
                    case STRING:
                        pdfCell = new PdfPCell(new Phrase(cell.getStringCellValue()));
                        break;
                    case NUMERIC:
                        if (DateUtil.isCellDateFormatted(cell)) {
                            pdfCell = new PdfPCell(new Phrase(cell.getDateCellValue().toString()));
                        } else {
                            pdfCell = new PdfPCell(new Phrase(String.valueOf(cell.getNumericCellValue())));
                        }
                        break;
                    case BOOLEAN:
                        pdfCell = new PdfPCell(new Phrase(String.valueOf(cell.getBooleanCellValue())));
                        break;
                    default:
                        pdfCell = new PdfPCell(new Phrase(""));
                }

                // 检查当前单元格是否属于合并区域
                for (CellRangeAddress region : worksheet.getMergedRegions()) {
                    if (region.isInRange(rowNum, col)) {
                        // 设置跨行跨列属性
                        pdfCell.setColspan(region.getLastColumn() - region.getFirstColumn() + 1);
                        pdfCell.setRowspan(region.getLastRow() - region.getFirstRow() + 1);
                        // 标记合并区域内的所有单元格为已处理
                        for (int r = region.getFirstRow(); r <= region.getLastRow(); r++) {
                            for (int c = region.getFirstColumn(); c <= region.getLastColumn(); c++) {
                                processedCells[r][c] = true;
                            }
                        }
                        break;
                    }
                }

                // 将单元格添加到PDF表格
                pdfTable.addCell(pdfCell);
            }
        }

        // 完成PDF文档生成
        pdfDocument.add(pdfTable);
        pdfDocument.close();
        inputDocument.close();
        workbook.close();

        System.out.println("PDF生成完成!");
    }
}

代码说明

  • 合并单元格处理:通过CellRangeAddress获取合并范围,设置PdfPCell的跨行跨列属性,并用二维数组标记已处理单元格,避免重复添加
  • 空单元格处理:使用CREATE_NULL_AS_BLANK策略自动生成空白单元格,确保表格结构完整
  • 动态列数:遍历所有行获取最大列数,适配不同结构的Excel文件
  • 多类型支持:覆盖了字符串、数字、日期、布尔值等常见单元格类型,避免数据丢失

内容的提问来源于stack exchange,提问作者manuel_k

火山引擎 最新活动