From 2e67e48e7109942f70db5e491c7416a9d2a4cdaf Mon Sep 17 00:00:00 2001 From: Yegor Kozlov Date: Fri, 16 Dec 2011 10:01:02 +0000 Subject: [PATCH] applied patch from Bugzilla 52314: SheetUtil.getColumnWidth could be more flexible git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1215079 13f79535-47bb-0310-9956-ffa450edef68 --- src/documentation/content/xdocs/status.xml | 1 + .../org/apache/poi/ss/util/SheetUtil.java | 248 +++++++++++------- 2 files changed, 157 insertions(+), 92 deletions(-) diff --git a/src/documentation/content/xdocs/status.xml b/src/documentation/content/xdocs/status.xml index 433cc589f..7a7a8f314 100644 --- a/src/documentation/content/xdocs/status.xml +++ b/src/documentation/content/xdocs/status.xml @@ -34,6 +34,7 @@ + 52314 - enhanced SheetUtil.getColumnWidth 52204 - Deprecated XSSFWorkbook(String path) constructor because it does not close underlying .zip file diff --git a/src/java/org/apache/poi/ss/util/SheetUtil.java b/src/java/org/apache/poi/ss/util/SheetUtil.java index 104d5c97b..a0430a76a 100644 --- a/src/java/org/apache/poi/ss/util/SheetUtil.java +++ b/src/java/org/apache/poi/ss/util/SheetUtil.java @@ -52,7 +52,7 @@ public class SheetUtil { * {@link org.apache.poi.ss.usermodel.DataFormatter#formatCellValue(org.apache.poi.ss.usermodel.Cell)} * returns formula string for formula cells. Dummy evaluator makes it to format the cached formula result. * - * See Bugzilla #50021 + * See Bugzilla #50021 */ private static final FormulaEvaluator dummyEvaluator = new FormulaEvaluator(){ public void clearAllCachedResultValues(){} @@ -74,6 +74,117 @@ public class SheetUtil { */ private static final FontRenderContext fontRenderContext = new FontRenderContext(null, true, true); + /** + * Compute width of a single cell + * + * @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 + * @return the width in pixels + */ + public static double getCellWidth(Cell cell, int defaultCharWidth, DataFormatter formatter, boolean useMergedCells) { + + Sheet sheet = cell.getSheet(); + Workbook wb = sheet.getWorkbook(); + Row row = cell.getRow(); + int column = cell.getColumnIndex(); + + int colspan = 1; + for (int i = 0 ; i < sheet.getNumMergedRegions(); i++) { + CellRangeAddress region = sheet.getMergedRegion(i); + if (containsCell(region, row.getRowNum(), column)) { + if (!useMergedCells) { + // If we're not using merged cells, skip this one and move on to the next. + return -1; + } + cell = row.getCell(region.getFirstColumn()); + colspan = 1 + region.getLastColumn() - region.getFirstColumn(); + } + } + + CellStyle style = cell.getCellStyle(); + int cellType = cell.getCellType(); + + // for formula cells we compute the cell width for the cached formula result + if(cellType == Cell.CELL_TYPE_FORMULA) cellType = cell.getCachedFormulaResultType(); + + Font font = wb.getFontAt(style.getFontIndex()); + + AttributedString str; + TextLayout layout; + + double width = -1; + if (cellType == Cell.CELL_TYPE_STRING) { + RichTextString rt = cell.getRichStringCellValue(); + String[] lines = rt.getString().split("\\n"); + for (int i = 0; i < lines.length; i++) { + String txt = lines[i] + defaultChar; + + str = new AttributedString(txt); + copyAttributes(font, str, 0, txt.length()); + + if (rt.numFormattingRuns() > 0) { + // TODO: support rich text fragments + } + + layout = new TextLayout(str.getIterator(), fontRenderContext); + if(style.getRotation() != 0){ + /* + * Transform the text using a scale so that it's height is increased by a multiple of the leading, + * and then rotate the text before computing the bounds. The scale results in some whitespace around + * the unrotated top and bottom of the text that normally wouldn't be present if unscaled, but + * is added by the standard Excel autosize. + */ + AffineTransform trans = new AffineTransform(); + trans.concatenate(AffineTransform.getRotateInstance(style.getRotation()*2.0*Math.PI/360.0)); + trans.concatenate( + AffineTransform.getScaleInstance(1, fontHeightMultiple) + ); + width = Math.max(width, ((layout.getOutline(trans).getBounds().getWidth() / colspan) / defaultCharWidth) + cell.getCellStyle().getIndention()); + } else { + width = Math.max(width, ((layout.getBounds().getWidth() / colspan) / defaultCharWidth) + cell.getCellStyle().getIndention()); + } + } + } else { + String sval = null; + if (cellType == Cell.CELL_TYPE_NUMERIC) { + // Try to get it formatted to look the same as excel + try { + sval = formatter.formatCellValue(cell, dummyEvaluator); + } catch (Exception e) { + sval = String.valueOf(cell.getNumericCellValue()); + } + } else if (cellType == Cell.CELL_TYPE_BOOLEAN) { + sval = String.valueOf(cell.getBooleanCellValue()).toUpperCase(); + } + if(sval != null) { + String txt = sval + defaultChar; + str = new AttributedString(txt); + copyAttributes(font, str, 0, txt.length()); + + layout = new TextLayout(str.getIterator(), fontRenderContext); + if(style.getRotation() != 0){ + /* + * Transform the text using a scale so that it's height is increased by a multiple of the leading, + * and then rotate the text before computing the bounds. The scale results in some whitespace around + * the unrotated top and bottom of the text that normally wouldn't be present if unscaled, but + * is added by the standard Excel autosize. + */ + AffineTransform trans = new AffineTransform(); + trans.concatenate(AffineTransform.getRotateInstance(style.getRotation()*2.0*Math.PI/360.0)); + trans.concatenate( + AffineTransform.getScaleInstance(1, fontHeightMultiple) + ); + width = Math.max(width, ((layout.getOutline(trans).getBounds().getWidth() / colspan) / defaultCharWidth) + cell.getCellStyle().getIndention()); + } else { + width = Math.max(width, ((layout.getBounds().getWidth() / colspan) / defaultCharWidth) + cell.getCellStyle().getIndention()); + } + } + } + return width; + } + /** * Compute width of a column and return the result * @@ -96,7 +207,6 @@ public class SheetUtil { int defaultCharWidth = (int)layout.getAdvance(); double width = -1; - rows: for (Row row : sheet) { Cell cell = row.getCell(column); @@ -104,97 +214,51 @@ public class SheetUtil { continue; } - int colspan = 1; - for (int i = 0 ; i < sheet.getNumMergedRegions(); i++) { - CellRangeAddress region = sheet.getMergedRegion(i); - if (containsCell(region, row.getRowNum(), column)) { - if (!useMergedCells) { - // If we're not using merged cells, skip this one and move on to the next. - continue rows; - } - cell = row.getCell(region.getFirstColumn()); - colspan = 1 + region.getLastColumn() - region.getFirstColumn(); - } - } - - CellStyle style = cell.getCellStyle(); - int cellType = cell.getCellType(); - - // for formula cells we compute the cell width for the cached formula result - if(cellType == Cell.CELL_TYPE_FORMULA) cellType = cell.getCachedFormulaResultType(); - - Font font = wb.getFontAt(style.getFontIndex()); - - if (cellType == Cell.CELL_TYPE_STRING) { - RichTextString rt = cell.getRichStringCellValue(); - String[] lines = rt.getString().split("\\n"); - for (int i = 0; i < lines.length; i++) { - String txt = lines[i] + defaultChar; - - str = new AttributedString(txt); - copyAttributes(font, str, 0, txt.length()); - - if (rt.numFormattingRuns() > 0) { - // TODO: support rich text fragments - } - - layout = new TextLayout(str.getIterator(), fontRenderContext); - if(style.getRotation() != 0){ - /* - * Transform the text using a scale so that it's height is increased by a multiple of the leading, - * and then rotate the text before computing the bounds. The scale results in some whitespace around - * the unrotated top and bottom of the text that normally wouldn't be present if unscaled, but - * is added by the standard Excel autosize. - */ - AffineTransform trans = new AffineTransform(); - trans.concatenate(AffineTransform.getRotateInstance(style.getRotation()*2.0*Math.PI/360.0)); - trans.concatenate( - AffineTransform.getScaleInstance(1, fontHeightMultiple) - ); - width = Math.max(width, ((layout.getOutline(trans).getBounds().getWidth() / colspan) / defaultCharWidth) + cell.getCellStyle().getIndention()); - } else { - width = Math.max(width, ((layout.getBounds().getWidth() / colspan) / defaultCharWidth) + cell.getCellStyle().getIndention()); - } - } - } else { - String sval = null; - if (cellType == Cell.CELL_TYPE_NUMERIC) { - // Try to get it formatted to look the same as excel - try { - sval = formatter.formatCellValue(cell, dummyEvaluator); - } catch (Exception e) { - sval = String.valueOf(cell.getNumericCellValue()); - } - } else if (cellType == Cell.CELL_TYPE_BOOLEAN) { - sval = String.valueOf(cell.getBooleanCellValue()).toUpperCase(); - } - if(sval != null) { - String txt = sval + defaultChar; - str = new AttributedString(txt); - copyAttributes(font, str, 0, txt.length()); - - layout = new TextLayout(str.getIterator(), fontRenderContext); - if(style.getRotation() != 0){ - /* - * Transform the text using a scale so that it's height is increased by a multiple of the leading, - * and then rotate the text before computing the bounds. The scale results in some whitespace around - * the unrotated top and bottom of the text that normally wouldn't be present if unscaled, but - * is added by the standard Excel autosize. - */ - AffineTransform trans = new AffineTransform(); - trans.concatenate(AffineTransform.getRotateInstance(style.getRotation()*2.0*Math.PI/360.0)); - trans.concatenate( - AffineTransform.getScaleInstance(1, fontHeightMultiple) - ); - width = Math.max(width, ((layout.getOutline(trans).getBounds().getWidth() / colspan) / defaultCharWidth) + cell.getCellStyle().getIndention()); - } else { - width = Math.max(width, ((layout.getBounds().getWidth() / colspan) / defaultCharWidth) + cell.getCellStyle().getIndention()); - } - } - } - + double cellWidth = getCellWidth(cell, defaultCharWidth, formatter, useMergedCells); + width = Math.max(width, cellWidth); } - return width; + return width; + } + + /** + * Compute width of a column based on a subset of the rows and return the result + * + * @param sheet the sheet to calculate + * @param column 0-based index of the column + * @param useMergedCells whether to use merged cells + * @param firstRow 0-based index of the first row to consider (inclusive) + * @param lastRow 0-based index of the last row to consider (inclusive) + * @return the width in pixels + */ + public static double getColumnWidth(Sheet sheet, int column, boolean useMergedCells, int firstRow, int lastRow){ + AttributedString str; + TextLayout layout; + + Workbook wb = sheet.getWorkbook(); + DataFormatter formatter = new DataFormatter(); + Font defaultFont = wb.getFontAt((short) 0); + + str = new AttributedString(String.valueOf(defaultChar)); + copyAttributes(defaultFont, str, 0, 1); + layout = new TextLayout(str.getIterator(), fontRenderContext); + int defaultCharWidth = (int)layout.getAdvance(); + + double width = -1; + for (int rowIdx = firstRow; rowIdx <= lastRow; ++rowIdx) { + Row row = sheet.getRow(rowIdx); + if( row != null ) { + + Cell cell = row.getCell(column); + + if (cell == null) { + continue; + } + + double cellWidth = getCellWidth(cell, defaultCharWidth, formatter, useMergedCells); + width = Math.max(width, cellWidth); + } + } + return width; } /**