Browse Source

use cached formula result when autosizing sheet columns, see Bugzilla 50211

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1033005 13f79535-47bb-0310-9956-ffa450edef68
tags/REL_3_8_BETA1
Yegor Kozlov 13 years ago
parent
commit
3aef93a73b

+ 7
- 2
src/documentation/content/xdocs/spreadsheet/quick-guide.xml View File

@@ -1371,9 +1371,14 @@ Examples:
<section><title>Adjust column width to fit the contents</title>
<source>
Sheet sheet = workbook.getSheetAt(0);
sheet.autoSizeColumn((short)0); //adjust width of the first column
sheet.autoSizeColumn((short)1); //adjust width of the second column
sheet.autoSizeColumn(0); //adjust width of the first column
sheet.autoSizeColumn(1); //adjust width of the second column
</source>
<p>
Note, that Sheet#autoSizeColumn() does not evaluate formula cells,
the width of formula cells is calculated based on the cached formula result.
If your workbook has many formulas then it is a good idea to evaluate them before auto-sizing.
</p>
<warning>
To calculate column width HSSFSheet.autoSizeColumn uses Java2D classes
that throw exception if graphical environment is not available. In case if graphical environment

+ 2
- 0
src/documentation/content/xdocs/status.xml View File

@@ -34,6 +34,8 @@

<changes>
<release version="3.8-beta1" date="2010-??-??">
<action dev="poi-developers" type="fix">49761 - Tolerate Double.NaN when reading .xls files</action>
<action dev="poi-developers" type="fix">50211 - Use cached formula result when auto-sizing formula cells</action>
<action dev="poi-developers" type="fix">50118 - OLE2 does allow a directory with an empty name, so support this in POIFS</action>
<action dev="poi-developers" type="fix">50119 - avoid NPE when XSSFReader comes across chart sheets</action>
</release>

+ 5
- 156
src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java View File

@@ -54,6 +54,7 @@ import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.ss.util.CellReference;
import org.apache.poi.ss.util.SSCellRange;
import org.apache.poi.ss.util.SheetUtil;
import org.apache.poi.util.POILogFactory;
import org.apache.poi.util.POILogger;

@@ -1146,7 +1147,8 @@ public final class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet {
}

//only shift if the region outside the shifted rows is not merged too
if (!containsCell(merged, startRow-1, 0) && !containsCell(merged, endRow+1, 0)){
if (!SheetUtil.containsCell(merged, startRow-1, 0) &&
!SheetUtil.containsCell(merged, endRow+1, 0)){
merged.setFirstRow(merged.getFirstRow()+n);
merged.setLastRow(merged.getLastRow()+n);
//have to remove/add it back
@@ -1164,14 +1166,6 @@ public final class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet {
this.addMergedRegion(region);
}
}
private static boolean containsCell(CellRangeAddress cr, int rowIx, int colIx) {
if (cr.getFirstRow() <= rowIx && cr.getLastRow() >= rowIx
&& cr.getFirstColumn() <= colIx && cr.getLastColumn() >= colIx)
{
return true;
}
return false;
}

/**
* Shifts rows between startRow and endRow n number of rows.
@@ -1741,153 +1735,8 @@ public final class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet {
* @param useMergedCells whether to use the contents of merged cells when calculating the width of the column
*/
public void autoSizeColumn(int column, boolean useMergedCells) {
AttributedString str;
TextLayout layout;
/**
* Excel measures columns in units of 1/256th of a character width
* but the docs say nothing about what particular character is used.
* '0' looks to be a good choice.
*/
char defaultChar = '0';

/**
* This is the multiple that the font height is scaled by when determining the
* boundary of rotated text.
*/
double fontHeightMultiple = 2.0;

FontRenderContext frc = new FontRenderContext(null, true, true);

HSSFWorkbook wb = HSSFWorkbook.create(_book); // TODO - is it important to not use _workbook?
HSSFDataFormatter formatter = new HSSFDataFormatter();
HSSFFont defaultFont = wb.getFontAt((short) 0);

str = new AttributedString("" + defaultChar);
copyAttributes(defaultFont, str, 0, 1);
layout = new TextLayout(str.getIterator(), frc);
int defaultCharWidth = (int)layout.getAdvance();

double width = -1;
rows:
for (Iterator<Row> it = rowIterator(); it.hasNext();) {
HSSFRow row = (HSSFRow) it.next();
HSSFCell cell = row.getCell(column);

if (cell == null) {
continue;
}

int colspan = 1;
for (int i = 0 ; i < getNumMergedRegions(); i++) {
CellRangeAddress region = 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();
}
}

HSSFCellStyle style = cell.getCellStyle();
int cellType = cell.getCellType();
if(cellType == HSSFCell.CELL_TYPE_FORMULA) cellType = cell.getCachedFormulaResultType();

HSSFFont font = wb.getFontAt(style.getFontIndex());

if (cellType == HSSFCell.CELL_TYPE_STRING) {
HSSFRichTextString 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) {
for (int j = 0; j < lines[i].length(); j++) {
int idx = rt.getFontAtIndex(j);
if (idx != 0) {
HSSFFont fnt = wb.getFontAt((short) idx);
copyAttributes(fnt, str, j, j + 1);
}
}
}

layout = new TextLayout(str.getIterator(), frc);
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 == HSSFCell.CELL_TYPE_NUMERIC) {
// Try to get it formatted to look the same as excel
try {
sval = formatter.formatCellValue(cell);
} catch (Exception e) {
sval = "" + cell.getNumericCellValue();
}
} else if (cellType == HSSFCell.CELL_TYPE_BOOLEAN) {
sval = String.valueOf(cell.getBooleanCellValue());
}
if(sval != null) {
String txt = sval + defaultChar;
str = new AttributedString(txt);
copyAttributes(font, str, 0, txt.length());

layout = new TextLayout(str.getIterator(), frc);
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());
}
}
}

}
if (width != -1) {
width *= 256;
if (width > Short.MAX_VALUE) { //width can be bigger that Short.MAX_VALUE!
width = Short.MAX_VALUE;
}
_sheet.setColumnWidth(column, (short) (width));
}
}

/**
* Copy text attributes from the supplied HSSFFont to Java2D AttributedString
*/
private void copyAttributes(HSSFFont font, AttributedString str, int startIdx, int endIdx) {
str.addAttribute(TextAttribute.FAMILY, font.getFontName(), startIdx, endIdx);
str.addAttribute(TextAttribute.SIZE, new Float(font.getFontHeightInPoints()));
if (font.getBoldweight() == HSSFFont.BOLDWEIGHT_BOLD) str.addAttribute(TextAttribute.WEIGHT, TextAttribute.WEIGHT_BOLD, startIdx, endIdx);
if (font.getItalic() ) str.addAttribute(TextAttribute.POSTURE, TextAttribute.POSTURE_OBLIQUE, startIdx, endIdx);
if (font.getUnderline() == HSSFFont.U_SINGLE ) str.addAttribute(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_ON, startIdx, endIdx);
double width = SheetUtil.getColumnWidth(this, column, useMergedCells);
if(width != -1) setColumnWidth(column, (int) (256*width));
}

/**

+ 219
- 0
src/java/org/apache/poi/ss/util/SheetUtil.java View File

@@ -0,0 +1,219 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */

package org.apache.poi.ss.util;

import org.apache.poi.ss.usermodel.*;

import java.text.AttributedString;
import java.awt.font.TextLayout;
import java.awt.font.FontRenderContext;
import java.awt.font.TextAttribute;
import java.awt.geom.AffineTransform;


/**
* Helper methods for when working with Usermodel sheets
*
* @author Yegor Kozlov
*/
public class SheetUtil {

/**
* Excel measures columns in units of 1/256th of a character width
* but the docs say nothing about what particular character is used.
* '0' looks to be a good choice.
*/
private static final char defaultChar = '0';

/**
* This is the multiple that the font height is scaled by when determining the
* boundary of rotated text.
*/
private static final double fontHeightMultiple = 2.0;

/**
* Dummy formula evaluator that does nothing.
* YK: The only reason of having this class is that
* {@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
*/
private static final FormulaEvaluator dummyEvaluator = new FormulaEvaluator(){
public void clearAllCachedResultValues(){}
public void notifySetFormula(Cell cell) {}
public void notifyDeleteCell(Cell cell) {}
public void notifyUpdateCell(Cell cell) {}
public CellValue evaluate(Cell cell) {return null; }
public Cell evaluateInCell(Cell cell) { return null; }

public int evaluateFormulaCell(Cell cell) {
return cell.getCachedFormulaResultType();
}

};

/**
* drawing context to measure text
*/
private static final FontRenderContext fontRenderContext = new FontRenderContext(null, true, true);

/**
* Compute width of a column 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
* @return the width in pixels
*/
public static double getColumnWidth(Sheet sheet, int column, boolean useMergedCells){
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;
rows:
for (Row row : sheet) {
Cell cell = row.getCell(column);

if (cell == null) {
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());
}
}
}

}
return width;
}

/**
* Copy text attributes from the supplied Font to Java2D AttributedString
*/
private static void copyAttributes(Font font, AttributedString str, int startIdx, int endIdx) {
str.addAttribute(TextAttribute.FAMILY, font.getFontName(), startIdx, endIdx);
str.addAttribute(TextAttribute.SIZE, (float)font.getFontHeightInPoints());
if (font.getBoldweight() == Font.BOLDWEIGHT_BOLD) str.addAttribute(TextAttribute.WEIGHT, TextAttribute.WEIGHT_BOLD, startIdx, endIdx);
if (font.getItalic() ) str.addAttribute(TextAttribute.POSTURE, TextAttribute.POSTURE_OBLIQUE, startIdx, endIdx);
if (font.getUnderline() == Font.U_SINGLE ) str.addAttribute(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_ON, startIdx, endIdx);
}

public static boolean containsCell(CellRangeAddress cr, int rowIx, int colIx) {
if (cr.getFirstRow() <= rowIx && cr.getLastRow() >= rowIx
&& cr.getFirstColumn() <= colIx && cr.getLastColumn() >= colIx)
{
return true;
}
return false;
}

}

+ 2
- 5
src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFSheet.java View File

@@ -49,10 +49,7 @@ import org.apache.poi.ss.usermodel.Footer;
import org.apache.poi.ss.usermodel.Header;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.ss.util.CellRangeAddressList;
import org.apache.poi.ss.util.CellReference;
import org.apache.poi.ss.util.SSCellRange;
import org.apache.poi.ss.util.*;
import org.apache.poi.util.HexDump;
import org.apache.poi.util.Internal;
import org.apache.poi.util.POILogFactory;
@@ -334,7 +331,7 @@ public class XSSFSheet extends POIXMLDocumentPart implements Sheet {
* @param useMergedCells whether to use the contents of merged cells when calculating the width of the column
*/
public void autoSizeColumn(int column, boolean useMergedCells) {
double width = ColumnHelper.getColumnWidth(this, column, useMergedCells);
double width = SheetUtil.getColumnWidth(this, column, useMergedCells);
if(width != -1){
columnHelper.setColBestFit(column, true);
columnHelper.setCustomWidth(column, true);

+ 0
- 165
src/ooxml/java/org/apache/poi/xssf/usermodel/helpers/ColumnHelper.java View File

@@ -299,169 +299,4 @@ public class ColumnHelper {
}
return -1;
}

public static double getColumnWidth(XSSFSheet sheet, int column, boolean useMergedCells){
AttributedString str;
TextLayout layout;
/**
* Excel measures columns in units of 1/256th of a character width
* but the docs say nothing about what particular character is used.
* '0' looks to be a good choice.
*/
char defaultChar = '0';

/**
* This is the multiple that the font height is scaled by when determining the
* boundary of rotated text.
*/
double fontHeightMultiple = 2.0;

FontRenderContext frc = new FontRenderContext(null, true, true);

XSSFWorkbook wb = sheet.getWorkbook();
XSSFFont defaultFont = wb.getFontAt((short) 0);

str = new AttributedString("" + defaultChar);
copyAttributes(defaultFont, str, 0, 1);
layout = new TextLayout(str.getIterator(), frc);
int defaultCharWidth = (int)layout.getAdvance();

double width = -1;
rows:
for (Iterator it = sheet.rowIterator(); it.hasNext();) {
XSSFRow row = (XSSFRow) it.next();
XSSFCell cell = row.getCell(column);

if (cell == null) {
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();
}
}

XSSFCellStyle style = cell.getCellStyle();
int cellType = cell.getCellType();
if(cellType == XSSFCell.CELL_TYPE_FORMULA) cellType = cell.getCachedFormulaResultType();
XSSFFont font = wb.getFontAt(style.getFontIndex());

if (cellType == XSSFCell.CELL_TYPE_STRING) {
XSSFRichTextString 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) {
int pos = 0;
for (int j = 0; j < rt.numFormattingRuns(); j++) {
XSSFFont fnt = rt.getFontOfFormattingRun(j);
if (fnt != null) {
int len = rt.getLengthOfFormattingRun(j);
if(len > 0) { //ignore degenerate zero-length runs
copyAttributes(fnt, str, pos, pos + len);
pos += len;
}
}
}
}

layout = new TextLayout(str.getIterator(), frc);
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 == XSSFCell.CELL_TYPE_NUMERIC) {
String dfmt = style.getDataFormatString();
String format = dfmt == null ? null : dfmt.replaceAll("\"", "");
double value = cell.getNumericCellValue();
try {
NumberFormat fmt;
if ("General".equals(format))
sval = "" + value;
else
{
fmt = new DecimalFormat(format);
sval = fmt.format(value);
}
} catch (Exception e) {
sval = "" + value;
}
} else if (cellType == XSSFCell.CELL_TYPE_BOOLEAN) {
sval = String.valueOf(cell.getBooleanCellValue());
}
if(sval != null) {
String txt = sval + defaultChar;
str = new AttributedString(txt);
copyAttributes(font, str, 0, txt.length());

layout = new TextLayout(str.getIterator(), frc);
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;
}

/**
* Copy text attributes from the supplied HSSFFont to Java2D AttributedString
*/
private static void copyAttributes(XSSFFont font, AttributedString str, int startIdx, int endIdx) {
str.addAttribute(TextAttribute.FAMILY, font.getFontName(), startIdx, endIdx);
str.addAttribute(TextAttribute.SIZE, new Float(font.getFontHeightInPoints()));
if (font.getBoldweight() == XSSFFont.BOLDWEIGHT_BOLD) str.addAttribute(TextAttribute.WEIGHT, TextAttribute.WEIGHT_BOLD, startIdx, endIdx);
if (font.getItalic() ) str.addAttribute(TextAttribute.POSTURE, TextAttribute.POSTURE_OBLIQUE, startIdx, endIdx);
if (font.getUnderline() == XSSFFont.U_SINGLE ) str.addAttribute(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_ON, startIdx, endIdx);
}

private static boolean containsCell(CellRangeAddress cr, int rowIx, int colIx) {
if (cr.getFirstRow() <= rowIx && cr.getLastRow() >= rowIx
&& cr.getFirstColumn() <= colIx && cr.getLastColumn() >= colIx)
{
return true;
}
return false;
}

}

+ 31
- 0
src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFSheetAutosizeColumn.java View File

@@ -0,0 +1,31 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */

package org.apache.poi.xssf.usermodel;

import org.apache.poi.ss.usermodel.BaseTestSheetAutosizeColumn;
import org.apache.poi.xssf.XSSFITestDataProvider;

/**
* @author Yegor Kozlov
*/
public final class TestXSSFSheetAutosizeColumn extends BaseTestSheetAutosizeColumn {

public TestXSSFSheetAutosizeColumn(){
super(XSSFITestDataProvider.instance);
}
}

+ 53
- 0
src/testcases/org/apache/poi/hssf/usermodel/TestHSSFSheetAutosizeColumn.java View File

@@ -0,0 +1,53 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */

package org.apache.poi.hssf.usermodel;

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

import junit.framework.AssertionFailedError;

import org.apache.poi.ddf.EscherDgRecord;
import org.apache.poi.hssf.HSSFITestDataProvider;
import org.apache.poi.hssf.HSSFTestDataSamples;
import org.apache.poi.hssf.model.DrawingManager2;
import org.apache.poi.hssf.model.InternalWorkbook;
import org.apache.poi.hssf.model.InternalSheet;
import org.apache.poi.hssf.record.*;
import org.apache.poi.hssf.record.formula.Ptg;
import org.apache.poi.hssf.record.formula.Area3DPtg;
import org.apache.poi.hssf.record.aggregates.WorksheetProtectionBlock;
import org.apache.poi.hssf.usermodel.RecordInspector.RecordCollector;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.ss.util.CellRangeAddressList;
import org.apache.poi.util.TempFile;

/**
* Test auto-sizing columns in HSSF
*
* @author Yegor Kozlov
*/
public final class TestHSSFSheetAutosizeColumn extends BaseTestSheetAutosizeColumn {

public TestHSSFSheetAutosizeColumn() {
super(HSSFITestDataProvider.instance);
}

}

+ 255
- 0
src/testcases/org/apache/poi/ss/usermodel/BaseTestSheetAutosizeColumn.java View File

@@ -0,0 +1,255 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */

package org.apache.poi.ss.usermodel;

import junit.framework.TestCase;
import org.apache.poi.ss.ITestDataProvider;
import org.apache.poi.ss.util.CellRangeAddress;

import java.util.Calendar;

/**
* Common superclass for testing automatic sizing of sheet columns
*
* @author Yegor Kozlov
*/
public abstract class BaseTestSheetAutosizeColumn extends TestCase {

private final ITestDataProvider _testDataProvider;

protected BaseTestSheetAutosizeColumn(ITestDataProvider testDataProvider) {
_testDataProvider = testDataProvider;
}

// TODO should we have this stuff in the FormulaEvaluator?
private void evaluateWorkbook(Workbook workbook){
FormulaEvaluator eval = workbook.getCreationHelper().createFormulaEvaluator();
for(int i=0; i < workbook.getNumberOfSheets(); i++) {
Sheet sheet = workbook.getSheetAt(i);
for (Row r : sheet) {
for (Cell c : r) {
if (c.getCellType() == Cell.CELL_TYPE_FORMULA){
eval.evaluateFormulaCell(c);
}
}
}
}
}

public void testNumericCells(){
Workbook workbook = _testDataProvider.createWorkbook();
DataFormat df = workbook.getCreationHelper().createDataFormat();
Sheet sheet = workbook.createSheet();

Row row = sheet.createRow(0);
row.createCell(0).setCellValue(0); // getCachedFormulaResult() returns 0 for not evaluated formula cells
row.createCell(1).setCellValue(10);
row.createCell(2).setCellValue("10");
row.createCell(3).setCellFormula("(A1+B1)*1.0"); // a formula that returns '10'

Cell cell4 = row.createCell(4); // numeric cell with a custom style
CellStyle style4 = workbook.createCellStyle();
style4.setDataFormat(df.getFormat("0.0000"));
cell4.setCellStyle(style4);
cell4.setCellValue(10); // formatted as '10.0000'

row.createCell(5).setCellValue("10.0000");

// autosize not-evaluated cells, formula cells are sized as if the result is 0
for (int i = 0; i < 6; i++) sheet.autoSizeColumn(i);

assertTrue(sheet.getColumnWidth(0) < sheet.getColumnWidth(1)); // width of '0' is less then width of '10'
assertEquals(sheet.getColumnWidth(1), sheet.getColumnWidth(2)); // 10 and '10' should be sized equally
assertEquals(sheet.getColumnWidth(3), sheet.getColumnWidth(0)); // formula result is unknown, the width is calculated for '0'
assertEquals(sheet.getColumnWidth(4), sheet.getColumnWidth(5)); // 10.0000 and '10.0000'

// evaluate formulas and re-autosize
evaluateWorkbook(workbook);

for (int i = 0; i < 6; i++) sheet.autoSizeColumn(i);

assertTrue(sheet.getColumnWidth(0) < sheet.getColumnWidth(1)); // width of '0' is less then width of '10'
assertEquals(sheet.getColumnWidth(1), sheet.getColumnWidth(2)); // columns 1, 2 and 3 should have the same width
assertEquals(sheet.getColumnWidth(2), sheet.getColumnWidth(3)); // columns 1, 2 and 3 should have the same width
assertEquals(sheet.getColumnWidth(4), sheet.getColumnWidth(5)); // 10.0000 and '10.0000'
}

public void testBooleanCells(){
Workbook workbook = _testDataProvider.createWorkbook();
Sheet sheet = workbook.createSheet();

Row row = sheet.createRow(0);
row.createCell(0).setCellValue(0); // getCachedFormulaResult() returns 0 for not evaluated formula cells
row.createCell(1).setCellValue(true);
row.createCell(2).setCellValue("TRUE");
row.createCell(3).setCellFormula("1 > 0"); // a formula that returns true

// autosize not-evaluated cells, formula cells are sized as if the result is 0
for (int i = 0; i < 4; i++) sheet.autoSizeColumn(i);

assertTrue(sheet.getColumnWidth(1) > sheet.getColumnWidth(0)); // 'true' is wider than '0'
assertEquals(sheet.getColumnWidth(1), sheet.getColumnWidth(2)); // 10 and '10' should be sized equally
assertEquals(sheet.getColumnWidth(3), sheet.getColumnWidth(0)); // formula result is unknown, the width is calculated for '0'

// evaluate formulas and re-autosize
evaluateWorkbook(workbook);

for (int i = 0; i < 4; i++) sheet.autoSizeColumn(i);

assertTrue(sheet.getColumnWidth(1) > sheet.getColumnWidth(0)); // 'true' is wider than '0'
assertEquals(sheet.getColumnWidth(1), sheet.getColumnWidth(2)); // columns 1, 2 and 3 should have the same width
assertEquals(sheet.getColumnWidth(2), sheet.getColumnWidth(3)); // columns 1, 2 and 3 should have the same width
}

public void testDateCells(){
Workbook workbook = _testDataProvider.createWorkbook();
Sheet sheet = workbook.createSheet();
DataFormat df = workbook.getCreationHelper().createDataFormat();

CellStyle style1 = workbook.createCellStyle();
style1.setDataFormat(df.getFormat("m"));

CellStyle style3 = workbook.createCellStyle();
style3.setDataFormat(df.getFormat("mmm"));

CellStyle style5 = workbook.createCellStyle(); //rotated text
style5.setDataFormat(df.getFormat("mmm/dd/yyyy"));

Calendar calendar = Calendar.getInstance();
calendar.set(2010, 0, 1); // Jan 1 2010

Row row = sheet.createRow(0);
row.createCell(0).setCellValue(DateUtil.getJavaDate(0)); //default date

Cell cell1 = row.createCell(1);
cell1.setCellValue(calendar);
cell1.setCellStyle(style1);
row.createCell(2).setCellValue("1"); // column 1 should be sized as '1'

Cell cell3 = row.createCell(3);
cell3.setCellValue(calendar);
cell3.setCellStyle(style3);
row.createCell(4).setCellValue("Jan");

Cell cell5 = row.createCell(5);
cell5.setCellValue(calendar);
cell5.setCellStyle(style5);
row.createCell(6).setCellValue("Jan/01/2010");

Cell cell7 = row.createCell(7);
cell7.setCellFormula("DATE(2010,1,1)");
cell7.setCellStyle(style3); // should be sized as 'Jan'

// autosize not-evaluated cells, formula cells are sized as if the result is 0
for (int i = 0; i < 8; i++) sheet.autoSizeColumn(i);

assertEquals(sheet.getColumnWidth(2), sheet.getColumnWidth(1)); // date formatted as 'm'
assertTrue(sheet.getColumnWidth(3) > sheet.getColumnWidth(1)); // 'mmm' is wider than 'm'
assertEquals(sheet.getColumnWidth(4), sheet.getColumnWidth(3)); // date formatted as 'mmm'
assertTrue(sheet.getColumnWidth(5) > sheet.getColumnWidth(3)); // 'mmm/dd/yyyy' is wider than 'mmm'
assertEquals(sheet.getColumnWidth(6), sheet.getColumnWidth(5)); // date formatted as 'mmm/dd/yyyy'

// YK: width of not-evaluated formulas that return data is not determined
// POI seems to conevert '0' to Excel date which is the beginng of the Excel's date system

// evaluate formulas and re-autosize
evaluateWorkbook(workbook);

for (int i = 0; i < 8; i++) sheet.autoSizeColumn(i);

assertEquals(sheet.getColumnWidth(2), sheet.getColumnWidth(1)); // date formatted as 'm'
assertTrue(sheet.getColumnWidth(3) > sheet.getColumnWidth(1)); // 'mmm' is wider than 'm'
assertEquals(sheet.getColumnWidth(4), sheet.getColumnWidth(3)); // date formatted as 'mmm'
assertTrue(sheet.getColumnWidth(5) > sheet.getColumnWidth(3)); // 'mmm/dd/yyyy' is wider than 'mmm'
assertEquals(sheet.getColumnWidth(6), sheet.getColumnWidth(5)); // date formatted as 'mmm/dd/yyyy'
assertEquals(sheet.getColumnWidth(4), sheet.getColumnWidth(7)); // date formula formatted as 'mmm'
}

public void testStringCells(){
Workbook workbook = _testDataProvider.createWorkbook();
Sheet sheet = workbook.createSheet();
Row row = sheet.createRow(0);

Font defaultFont = workbook.getFontAt((short)0);

CellStyle style1 = workbook.createCellStyle();
Font font1 = workbook.createFont();
font1.setFontHeight((short)(2*defaultFont.getFontHeight()));
style1.setFont(font1);

row.createCell(0).setCellValue("x");
row.createCell(1).setCellValue("xxxx");
row.createCell(2).setCellValue("xxxxxxxxxxxx");
row.createCell(3).setCellValue("Apache\nSoftware Foundation"); // the text is splitted into two lines
row.createCell(4).setCellValue("Software Foundation");

Cell cell5 = row.createCell(5);
cell5.setCellValue("Software Foundation");
cell5.setCellStyle(style1); // same as in column 4 but the font is twice larger than the default font

for (int i = 0; i < 10; i++) sheet.autoSizeColumn(i);

assertTrue(2*sheet.getColumnWidth(0) < sheet.getColumnWidth(1)); // width is roughly proportional to the number of characters
assertTrue(2*sheet.getColumnWidth(1) < sheet.getColumnWidth(2));
assertEquals(sheet.getColumnWidth(4), sheet.getColumnWidth(3));
assertTrue(sheet.getColumnWidth(5) > sheet.getColumnWidth(4)); //larger font results in a wider column width
}

public void testRotatedText(){
Workbook workbook = _testDataProvider.createWorkbook();
Sheet sheet = workbook.createSheet();
Row row = sheet.createRow(0);

CellStyle style1 = workbook.createCellStyle();
style1.setRotation((short)90);

Cell cell0 = row.createCell(0);
cell0.setCellValue("Apache Software Foundation");
cell0.setCellStyle(style1);

Cell cell1 = row.createCell(1);
cell1.setCellValue("Apache Software Foundation");

for (int i = 0; i < 2; i++) sheet.autoSizeColumn(i);

int w0 = sheet.getColumnWidth(0);
int w1 = sheet.getColumnWidth(1);

assertTrue(w0*5 < w1); // rotated text occupies at least five times less horizontal space than normal text
}

public void testMergedCells(){
Workbook workbook = _testDataProvider.createWorkbook();
Sheet sheet = workbook.createSheet();

Row row = sheet.createRow(0);
sheet.addMergedRegion(CellRangeAddress.valueOf("A1:B1"));

Cell cell0 = row.createCell(0);
cell0.setCellValue("Apache Software Foundation");

int defaulWidth = sheet.getColumnWidth(0);
sheet.autoSizeColumn(0);
// column is unchanged if merged regions are ignored (Excel like behavior)
assertEquals(defaulWidth, sheet.getColumnWidth(0));

sheet.autoSizeColumn(0, true);
assertTrue(sheet.getColumnWidth(0) > defaulWidth);
}

}

Loading…
Cancel
Save