简体   繁体   English

当标题的单元格包含换行符时,Apache POI createTable 会生成损坏的文件

[英]Apache POI createTable generates corrupted file when a header's cell contains a line break

I am using Apache POI 4.1.2 to create Excel files in Java.我正在使用Apache POI 4.1.2在 Java 中创建 Excel 文件。 I have a piece of code that creates a table from existing cells and everything used to work fine, untill I had a linebreak inside a header's cell.我有一段代码可以从现有的单元格创建一个表格,并且以前一切正常,直到我在标题的单元格内有一个换行符。

I tried to change the table's column name afterward but it didn't fix anything.之后我尝试更改表的列名,但没有解决任何问题。

Below is a minimal piece of code to reproduce the problem:以下是重现问题的最小代码段:

    public void test() throws IOException {
        XSSFWorkbook wb = new XSSFWorkbook();
        XSSFSheet sheet = wb.createSheet();

        // headers
        XSSFRow headersRow = sheet.createRow(0);
        headersRow.createCell(0).setCellValue("Column1");
        headersRow.createCell(1).setCellValue("Column2");

        // a second row
        XSSFRow row = sheet.createRow(1);
        row.createCell(0).setCellValue(1);
        row.createCell(1).setCellValue(2);

        // create a table
        AreaReference area = wb.getCreationHelper().createAreaReference(
                new CellReference(sheet.getRow(0).getCell(0)),
                new CellReference(sheet.getRow(1).getCell(1))
        );
        XSSFTable table = sheet.createTable(area);

        // styling (no problem here)
        sheet.setColumnWidth(0, 5000);
        sheet.setColumnWidth(1, 5000);
        CTTable cttable = table.getCTTable();
        cttable.addNewTableStyleInfo();
        XSSFTableStyleInfo style = (XSSFTableStyleInfo) table.getStyle();
        style.setName("TableStyleMedium6");
        style.setShowColumnStripes(false);
        style.setShowRowStripes(true);
        cttable.addNewAutoFilter().setRef(area.formatAsString());
        CellStyle cellStyle = wb.createCellStyle();
        cellStyle.setWrapText(true);
        headersRow.getCell(0).setCellStyle(cellStyle);

        // this file is OK
        try (FileOutputStream outputStream = new FileOutputStream("C:\\tmp\\test.xlsx")) {
            wb.write(outputStream);
        }

        // add a line break in a header's cell
        headersRow.getCell(0).setCellValue("Column1\nwith a line break");
        // this file has a problem
        try (FileOutputStream outputStream = new FileOutputStream("C:\\tmp\\test2.xlsx")) {
            wb.write(outputStream);
        }

        // this doesn't fix anything
        table.getColumns().get(0).setName("Column1");
        try (FileOutputStream outputStream = new FileOutputStream("C:\\tmp\\test3.xlsx")) {
            wb.write(outputStream);
        }

        // neither does this
        cttable.getTableColumns().getTableColumnList().get(0).setName("Column1");
        try (FileOutputStream outputStream = new FileOutputStream("C:\\tmp\\test4.xlsx")) {
            wb.write(outputStream);
        }
    }

Excel loads text.xlsx properly, but complains about all other files: Excel 正确加载text.xlsx ,但抱怨所有其他文件:
We found a problem with some content...我们发现某些内容有问题...

After Excel fixes the files, everything is OK but I would like to get rid of the warning message. Excel 修复文件后,一切正常,但我想摆脱警告消息。

Any help will be appreciated.任何帮助将不胜感激。 Thanks谢谢

This is an inaccuracy with XSSFTable.updateHeaders .这是XSSFTable.updateHeaders的不准确之 This method gets called while the table's XML gets written.在写入表的XML时调用此方法。 This is because the table column names always must be synchronized with the cell contents.这是因为表列名称必须始终与单元格内容同步。 For example if the cell content is "Column1" and this cell is a column header of a table, then this tables column name also must be "Column1" (XML: <tableColumn id="1" name="Column1"/> ).例如,如果单元格内容是“Column1”,而这个单元格是一个表的列标题,那么这个表的列名也必须是“Column1”(XML: <tableColumn id="1" name="Column1"/> ) .

But for line feeds in column headers, there is a specialty.但是对于列标题中的换行符,有一个特点。 If the cell content is "Column1\\nwith a line break" and this cell is a column header of a table, then this tables column name must be XML as <tableColumn id="1" name="Column1_x000a_with a line break"/> .如果单元格内容是“Column1\\nwith a line break”并且这个单元格是一个表的列标题,那么这个表的列名必须是 XML as <tableColumn id="1" name="Column1_x000a_with a line break"/> . So "\\n" is replaced by " x000a ".所以“\\n”被“ x000a ”代替。 Also "\\r" would must be replaced by " x000d ".此外,“\\r”必须替换为“ x000d ”。 This is because "\\r\\n" line breaks will not have the meaning of line break in XML .这是因为 "\\r\\n" 换行符在XML没有换行符的含义。

So XSSFTable.java - updateHeaders would must be patched that way that "\\n" gets replaced by " x000a " and "\\r" gets replaced by " x000d ".因此, XSSFTable.java - updateHeaders必须以“\\n”替换为“ x000a ”和“\\r”替换为“ x000d ”的方式进行修补。

...
    public void updateHeaders() {
        XSSFSheet sheet = (XSSFSheet)getParent();
        CellReference ref = getStartCellReference();
        if (ref == null) return;

        int headerRow = ref.getRow();
        int firstHeaderColumn = ref.getCol();
        XSSFRow row = sheet.getRow(headerRow);
        DataFormatter formatter = new DataFormatter();

        if (row != null && row.getCTRow().validate()) {
            int cellnum = firstHeaderColumn;
            CTTableColumns ctTableColumns = getCTTable().getTableColumns();
            if(ctTableColumns != null) {
                for (CTTableColumn col : ctTableColumns.getTableColumnList()) {
                    XSSFCell cell = row.getCell(cellnum);
                    if (cell != null) {
                        String colName = formatter.formatCellValue(cell);
                        colName = colName.replace("\n", "_x000a_");
                        colName = colName.replace("\r", "_x000d_");
                        col.setName(colName);
                    }
                    cellnum++;
                }
            }
        }
        tableColumns = null;
        columnMap = null;
        xmlColumnPrs = null;
        commonXPath = null;
    }
...

Since XSSFTable.updateHeaders gets called while the table's XML gets written while XSSFWorkbook.write , there is no other way than patching this method.由于在XSSFWorkbook.write时写入表的XMLXSSFWorkbook.write ,因此除了修补此方法之外别无他法。 One does not have any chance to change table's XML while XSSFWorkbook.write .XSSFWorkbook.write ,没有任何机会更改表的XML

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM