diff --git a/src/java/org/apache/poi/ss/util/SheetUtil.java b/src/java/org/apache/poi/ss/util/SheetUtil.java index e5c2a31ba9..dcedf03a14 100644 --- a/src/java/org/apache/poi/ss/util/SheetUtil.java +++ b/src/java/org/apache/poi/ss/util/SheetUtil.java @@ -23,6 +23,7 @@ import java.awt.font.TextLayout; import java.awt.geom.AffineTransform; import java.awt.geom.Rectangle2D; import java.text.AttributedString; +import java.util.List; import java.util.Locale; import java.util.Map; @@ -118,6 +119,26 @@ public class SheetUtil { * @return the width in pixels or -1 if cell is empty */ public static double getCellWidth(Cell cell, int defaultCharWidth, DataFormatter formatter, boolean useMergedCells) { + List mergedRegions = cell.getSheet().getMergedRegions(); + return getCellWidth(cell, defaultCharWidth, formatter, useMergedCells, mergedRegions); + } + + /** + * Compute width of a single cell + * + * This method receives the list of merged regions as querying it from the cell/sheet + * is time-consuming and thus caching the list across cells speeds up certain operations + * considerably. + * + * @param cell the cell whose width is to be calculated + * @param defaultCharWidth the width of a single character + * @param formatter formatter used to prepare the text to be measured + * @param useMergedCells whether to use merged cells + * @param mergedRegions The list of merged regions as received via cell.getSheet().getMergedRegions() + * @return the width in pixels or -1 if cell is empty + */ + public static double getCellWidth(Cell cell, int defaultCharWidth, DataFormatter formatter, boolean useMergedCells, + List mergedRegions) { Sheet sheet = cell.getSheet(); Workbook wb = sheet.getWorkbook(); Row row = cell.getRow(); @@ -126,7 +147,7 @@ public class SheetUtil { // FIXME: this looks very similar to getCellWithMerges below. Consider consolidating. // We should only be checking merged regions if useMergedCells is true. Why are we doing this for-loop? int colspan = 1; - for (CellRangeAddress region : sheet.getMergedRegions()) { + for (CellRangeAddress region : mergedRegions) { if (region.isInRange(row.getRowNum(), column)) { if (!useMergedCells) { // If we're not using merged cells, skip this one and move on to the next. @@ -232,7 +253,7 @@ public class SheetUtil { public static double getColumnWidth(Sheet sheet, int column, boolean useMergedCells) { return getColumnWidth(sheet, column, useMergedCells, sheet.getFirstRowNum(), sheet.getLastRowNum()); } - + /** * Compute width of a column based on a subset of the rows and return the result * @@ -247,11 +268,12 @@ public class SheetUtil { DataFormatter formatter = new DataFormatter(); int defaultCharWidth = getDefaultCharWidth(sheet.getWorkbook()); + List mergedRegions = sheet.getMergedRegions(); double width = -1; for (int rowIdx = firstRow; rowIdx <= lastRow; ++rowIdx) { Row row = sheet.getRow(rowIdx); if( row != null ) { - double cellWidth = getColumnWidthForRow(row, column, defaultCharWidth, formatter, useMergedCells); + double cellWidth = getColumnWidthForRow(row, column, defaultCharWidth, formatter, useMergedCells, mergedRegions); width = Math.max(width, cellWidth); } } @@ -286,7 +308,8 @@ public class SheetUtil { * @return the width in pixels or -1 if cell is empty */ private static double getColumnWidthForRow( - Row row, int column, int defaultCharWidth, DataFormatter formatter, boolean useMergedCells) { + Row row, int column, int defaultCharWidth, DataFormatter formatter, boolean useMergedCells, + List mergedRegions) { if( row == null ) { return -1; } @@ -297,16 +320,16 @@ public class SheetUtil { return -1; } - return getCellWidth(cell, defaultCharWidth, formatter, useMergedCells); + return getCellWidth(cell, defaultCharWidth, formatter, useMergedCells, mergedRegions); } /** * Check if the Fonts are installed correctly so that Java can compute the size of - * columns. - * - * If a Cell uses a Font which is not available on the operating system then Java may + * columns. + * + * If a Cell uses a Font which is not available on the operating system then Java may * fail to return useful Font metrics and thus lead to an auto-computed size of 0. - * + * * This method allows to check if computing the sizes for a given Font will succeed or not. * * @param font The Font that is used in the Cell @@ -358,7 +381,7 @@ public class SheetUtil { /** * Return the cell, taking account of merged regions. Allows you to find the * cell who's contents are shown in a given position in the sheet. - * + * *

If the cell at the given co-ordinates is a merged cell, this will * return the primary (top-left) most cell of the merged region. *

If the cell at the given co-ordinates is not in a merged region, @@ -375,7 +398,7 @@ public class SheetUtil { public static Cell getCellWithMerges(Sheet sheet, int rowIx, int colIx) { final Cell c = getCell(sheet, rowIx, colIx); if (c != null) return c; - + for (CellRangeAddress mergedRegion : sheet.getMergedRegions()) { if (mergedRegion.isInRange(rowIx, colIx)) { // The cell wanted is in this merged range @@ -386,7 +409,7 @@ public class SheetUtil { } } } - + // If we get here, then the cell isn't defined, and doesn't // live within any merged regions return null; diff --git a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFBugs.java b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFBugs.java index 2223f5d169..86aa1ccb59 100644 --- a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFBugs.java +++ b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFBugs.java @@ -36,6 +36,8 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.charset.StandardCharsets; +import java.time.Duration; +import java.time.Instant; import java.util.Arrays; import java.util.Calendar; import java.util.HashMap; @@ -91,6 +93,8 @@ import org.apache.poi.ss.util.CellReference; import org.apache.poi.ss.util.CellUtil; import org.apache.poi.util.LocaleUtil; import org.apache.poi.util.NullOutputStream; +import org.apache.poi.util.POILogFactory; +import org.apache.poi.util.POILogger; import org.apache.poi.util.TempFile; import org.apache.poi.util.XMLHelper; import org.apache.poi.xssf.SXSSFITestDataProvider; @@ -116,6 +120,8 @@ import org.xml.sax.SAXParseException; import org.xml.sax.XMLReader; public final class TestXSSFBugs extends BaseTestBugzillaIssues { + private static POILogger LOG = POILogFactory.getLogger(TestXSSFBugs.class); + public TestXSSFBugs() { super(XSSFITestDataProvider.instance); } @@ -3465,4 +3471,27 @@ public final class TestXSSFBugs extends BaseTestBugzillaIssues { fail("Should catch exception as the file is corrupted"); } } + + @Test + public void test58896WithFile() throws IOException { + try (Workbook wb = XSSFTestDataSamples.openSampleWorkbook("58896.xlsx")) { + Sheet sheet = wb.getSheetAt(0); + Instant start = Instant.now(); + + LOG.log(POILogger.INFO, "Autosizing columns..."); + + for (int i = 0; i < 3; ++i) { + LOG.log(POILogger.INFO, "Autosize " + i + " - " + Duration.between(start, Instant.now())); + sheet.autoSizeColumn(i); + } + + for (int i = 0; i < 69 - 35 + 1; ++i) + for (int j = 0; j < 8; ++j) { + int col = 3 + 2 + i * (8 + 2) + j; + LOG.log(POILogger.INFO, "Autosize " + col + " - " + Duration.between(start, Instant.now())); + sheet.autoSizeColumn(col); + } + LOG.log(POILogger.INFO, Duration.between(start, Instant.now())); + } + } } diff --git a/test-data/spreadsheet/58896.xlsx b/test-data/spreadsheet/58896.xlsx new file mode 100644 index 0000000000..31bd6e7c97 Binary files /dev/null and b/test-data/spreadsheet/58896.xlsx differ