带有合并单元格的Apache POI和自动行高 [英] Apache POI and Auto Row Height with merged cells

查看:104
本文介绍了带有合并单元格的Apache POI和自动行高的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用Apache POI,但遇到了一个奇怪的问题.我可以自动调整行的大小,但前提是该行中没有合并的单元格.这是一个示例:

I'm using Apache POI and I am running into a weird problem. I can auto-size my rows, but only if there are no merged cells in that row. Here's an example:

new FileOutputStream('test.xlsx').withStream { OutputStream os ->
    Workbook workbook = new XSSFWorkbook();
    Sheet sheet = workbook.createSheet();

    CellStyle wrapStyle = workbook.createCellStyle();
    wrapStyle.setWrapText(true);

    Row row = sheet.createRow(0); row.setRowStyle(wrapStyle);

    Cell cell = row.createCell(0); cell.setCellStyle(wrapStyle);
    cell.setCellValue("Very long text that needs to be wrapped")

    cell = row.createCell(1); cell.setCellStyle(wrapStyle);
    cell.setCellValue("Short text");

    cell = row.createCell(2); cell.setCellStyle(wrapStyle);
    cell.setCellValue("");

    // These two lines break row auto-height!
    //
    CellRangeAddress cellRangeAddress = new CellRangeAddress(0, 0, 1, 2);
    sheet.addMergedRegion(cellRangeAddress);

    workbook.write(os);
}

此代码生成以下文档:

This code generates the following document:

但是,当我注释掉合并两个单元格的行时,输出如下所示:

However, as soon as I comment out the lines that merge the two cells, the output looks like this:

这是一个错误吗?有人知道解决方法吗?

Is this a bug? Does anyone know of a workaround?

推荐答案

通过使用Val Blant的最后一部分,我做了一些更易于使用但又非常复杂的事情.请注意,出于个人原因,只有一行代码向单元格的高度添加了另一行.如果您不希望这样做,请将其删除.也可以随意将其更改为非静态类,由于我正在努力使特定类变为静态类,因此我不得不使用静态方法.

By using the last part from Val Blant I've did something that's easier to use, and yet much complex. Please be aware that there is a single line of code that adds one additional line to the cell height-wise, for personal reasons. If you do not wish that, please remove it. Also feel free to change it to a non-static, I've had to use static methods due to the company I'm working for making the specific class static.

PS:这是我关于stackoverflow的第一篇文章,请保持警惕.:)

PS: It's my first post on stackoverflow, please be gentle. :)

解决方案:

public static Boolean isCellMerged(Cell cell) {
        Sheet sheet = cell.getSheet();
        for (CellRangeAddress mergedRegionRange : sheet.getMergedRegions()) {
            Integer cellColumn = cell.getColumnIndex();
            Integer cellRow = cell.getRowIndex();
            if (mergedRegionRange.containsColumn(cellColumn) && mergedRegionRange.containsRow(cellRow)) {
                return true;
            }
        }
        return false;
    }

public static List<List<Cell>> getCellsInRowsInsideRegionRange(Cell cell) {
        Sheet sheet = cell.getSheet();
        List<List<Cell>> mergedRowList = new ArrayList<>();
        List<Cell> mergedCellsList = new ArrayList<>();

        //Nejdříve musíme zjistit sloučenou sekci dané buňky
        for (CellRangeAddress mergedRegionRange : sheet.getMergedRegions()) {
            Integer cellColumn = cell.getColumnIndex();
            Integer cellRow = cell.getRowIndex();
            if (mergedRegionRange.containsColumn(cellColumn) && mergedRegionRange.containsRow(cellRow)) {

                //Protože CellRangeAddress nemá moc metod, musíme si pomoci sami a získat z ní buňky a řádky
                for (Row row : sheet) {
                    for (Cell iteratedCell : row) {
                        Integer iteratedCellColumn = iteratedCell.getColumnIndex();
                        Integer iteratedCellRow = iteratedCell.getRowIndex();
                        if (mergedRegionRange.containsColumn(iteratedCellColumn) && mergedRegionRange.containsRow(iteratedCellRow)) {
                            //Rozdělování jednotlivých řádků
                            //Není-li řádek bez buněk...
                            if (!mergedCellsList.isEmpty()) {

                                //Tak buňku přidáme do Listu buněk...
                                mergedCellsList.add(iteratedCell);

                            } else {
                                //Pokud se jedná o první buňku prvního řádku, tak přidáme rovnou
                                mergedCellsList.add(iteratedCell);
                            }
                        }
                    }
                    //Vložíme List buněk daného řádku do Listu řádků
                    if (!mergedCellsList.isEmpty()) {
                        mergedRowList.add(mergedCellsList);
                    }

                    //A vyresetujeme list buněk (začneme tak nanovo novým řádkem)
                    mergedCellsList = null;
                    mergedCellsList = new ArrayList<>();
                }
                //Vrátíme výsledný List řádků, obsahující Listy buněk ve sloučené sekci.
                if (!mergedRowList.isEmpty()) {
                    return mergedRowList;
                } else {
                    return null;
                }
            }
        }
        return null;
    }

    public static void adjustRowHeightForRowWithNonMergedCells(Row row) {
        row.setHeight((short) -1);
    }

public static void adjustRowHeightForRowWithMergedCells(Row row) {
        Sheet sheet = row.getSheet();
        Cell longestTextCell = null;

        //Potřebujeme získat buňku s nejdelším textem
        for (Cell iteratedCell : row) {
            String iteratedTextString = iteratedCell.getStringCellValue();

            if (longestTextCell != null && StringUtils.isNotBlank(longestTextCell.getStringCellValue())) {
                if (iteratedTextString.length() > longestTextCell.getStringCellValue().length()) {
                    longestTextCell = iteratedCell;
                }
            } else {
                longestTextCell = iteratedCell;
            }

        }

        //Z textově nejobsáhlejší buňky potřebujeme dostat údaje
        String longestText = "";

        if (StringUtils.isNotBlank(longestTextCell.getStringCellValue()) && longestTextCell != null) {
            longestText = longestTextCell.getStringCellValue();

            //Protože textově nejobsáhlejší buňka nemusí nutně být sloučeného typu, je zapotřebí to všude ošetřit
            Boolean isLongestTextCellMerged = isCellMerged(longestTextCell);
            Float longestCellWidthInPixels = 0f;
            Float longestMergedCellWidthInPixels = 0f;

            //Získat šířku nesloučené nejobsáhlejší buňky je jednoduché
            if (!isLongestTextCellMerged) {
                Integer longestCellColumnIndex = longestTextCell.getColumnIndex();
                longestCellWidthInPixels = sheet.getColumnWidthInPixels(longestCellColumnIndex);

            } else {

                //Musíme přijít na šířku sloučené buňky namísto buňky uvnitř sloučené buňky
                List<List<Cell>> cellsInMergedRegion = getCellsInRowsInsideRegionRange(longestTextCell);
                longestMergedCellWidthInPixels = 0f;

                //Projdeme řádky
                for (List<Cell> iteratedCell2List : cellsInMergedRegion) {
                    Float iteratedMergedCell2WidthInPixels = 0f;

                    //Projdeme jednotlivé buňky ve sloučené buňce na řádku a sečteme jejich šířky
                    for (Cell iteratedCell2 : iteratedCell2List) {
                        Integer iteratedCell2ColumnIndex = iteratedCell2.getColumnIndex();
                        Float iteratedCell2ColumnWidthInPixels = sheet.getColumnWidthInPixels(iteratedCell2ColumnIndex);

                        iteratedMergedCell2WidthInPixels = iteratedMergedCell2WidthInPixels + iteratedCell2ColumnWidthInPixels;
                    }

                    //Získáme šířku nejširší sloučené buňky na řádku
                    if (iteratedMergedCell2WidthInPixels > longestMergedCellWidthInPixels) {
                        longestMergedCellWidthInPixels = iteratedMergedCell2WidthInPixels;
                    }

                    //Resetujeme sčítání
                    iteratedMergedCell2WidthInPixels = 0f;
                }
            }

            //Uložíme si nejširší buňku dle toho, zda je sloučená či nikoliv
            Float longestWidthInPixels;
            if (isLongestTextCellMerged) {
                longestWidthInPixels = longestMergedCellWidthInPixels;
            } else {
                longestWidthInPixels = longestCellWidthInPixels;
            }

            //Potřebujeme font
            Workbook wb = sheet.getWorkbook();
            Short fontIndex = longestTextCell.getCellStyle().getFontIndex();
            Font excelFont = wb.getFontAt(fontIndex);

            //Potřebujeme i jeho styl
            Integer excelFontStyle = java.awt.Font.PLAIN;
            if (excelFont.getBold()) excelFontStyle = java.awt.Font.BOLD;
            if (excelFont.getItalic()) excelFontStyle = java.awt.Font.ITALIC;

            //Potřebujeme získat skutečný font i s velikostí
            java.awt.Font currentFont = new java.awt.Font(excelFont.getFontName(), excelFontStyle, excelFont.getFontHeightInPoints());

            //Získáme řetězec s vlastností
            AttributedString attributedString = new AttributedString(longestText);
            attributedString.addAttribute(TextAttribute.FONT, currentFont);

            //Použijeme LineBreakMeasurer k zjištění kolik řádků bude text potřebovat
            FontRenderContext fontRenderContext = new FontRenderContext(null, true, true);
            LineBreakMeasurer measurer = new LineBreakMeasurer(attributedString.getIterator(), fontRenderContext);

            Integer nextPosition = 0;
            Integer lineCount = 0;

            while (measurer.getPosition() < longestText.length()) {
                nextPosition = measurer.nextOffset(longestWidthInPixels);

                //Také musíme ošetřit případ manuálně zadaných LineBreaků pro všechny možné techtle mechtle :-S
                String textLine = StringUtils.substring(longestText, measurer.getPosition(), nextPosition);
                Boolean containsNewLine = StringUtils.containsIgnoreCase(textLine, "\r") || StringUtils.containsIgnoreCase(textLine, "\\r") || StringUtils.containsIgnoreCase(textLine, "\n") || StringUtils.containsIgnoreCase(textLine, "\\n");

                if (containsNewLine) {

                    if (StringUtils.containsIgnoreCase(textLine, "\r\n") || StringUtils.containsIgnoreCase(textLine, "\\r\\n")) {
                        lineCount = lineCount + StringUtils.countMatches(textLine, "\n");
                    } else {

                        if (StringUtils.containsIgnoreCase(textLine, "\r") || StringUtils.containsIgnoreCase(textLine, "\\r")) {
                            lineCount = lineCount + StringUtils.countMatches(textLine, "\r");
                        }
                        if (StringUtils.containsIgnoreCase(textLine, "\n") || StringUtils.containsIgnoreCase(textLine, "\\n")) {
                            lineCount = lineCount + StringUtils.countMatches(textLine, "\n");
                        }

                    }

                    lineCount = lineCount + StringUtils.countMatches(textLine, "\\r?\\n");
                }

                lineCount++;
                measurer.setPosition(nextPosition);
            }

            //Máme počet řádků, zbývá konečný dopočet výšky řádku a jeho použití
            if (lineCount > 1) {

                Float fontHeight = currentFont.getLineMetrics(longestText, fontRenderContext).getHeight();

                //Pro jistotu přidáme jeden řádek navíc, člověk nikdy neví...
                lineCount = lineCount + 1;

                //Potřebujeme získat poslední řádek
                Row lastRow = null;

                if (isCellMerged(longestTextCell)) {
                    List<List<Cell>> mergedCellsInRows = getCellsInRowsInsideRegionRange(longestTextCell);
                    Integer lastRowInMergedSectionIndex = mergedCellsInRows.size() - 1;
                    List<Cell> lastRowInMergedSection = mergedCellsInRows.get(lastRowInMergedSectionIndex);
                    lastRow = lastRowInMergedSection.get(0).getRow();
                } else {
                    lastRow = longestTextCell.getRow();
                }

                //Je potřeba ošetřit velikosti, pokud má sloučená buňka vícero řádků
                Float cellsMergedAboveHeight = 0f;
                if (isCellMerged(longestTextCell)) {
                    if (getCellsInRowsInsideRegionRange(longestTextCell).size() > 1) {
                        List<List<Cell>> mergedCellsInRows = getCellsInRowsInsideRegionRange(longestTextCell);
                        for (List<Cell> rowsWithCells : mergedCellsInRows){
                            if (!lastRow.equals(rowsWithCells.get(0).getRow())){
                                cellsMergedAboveHeight = cellsMergedAboveHeight + rowsWithCells.get(0).getRow().getHeight();
                            }
                        }
                    }
                }
                //Vzorec je ((Velikost fontu krát počet řádků plus (počet řádků krát volný prostor mezi řádky)) krát přepočet Excelu) mínus výška sloučených buněk nad posledním řádkem.
                Short finalRowHeight = (short) (((fontHeight * lineCount + (lineCount * 15))* 10) - cellsMergedAboveHeight);

                //A výsledek nastavíme na poslední řádek, protože jinak to przní sloupce vlevo a vpravo od vyšších řádků
                lastRow.setHeight(finalRowHeight);

            }
        }
    }

这篇关于带有合并单元格的Apache POI和自动行高的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆