Apache POI autoSizeColumn resizes to minimum width - java

When working with values that are formulas, I am having difficulty getting the columns to autoresize properly.
I have "solved" this by making a hidden row that has maximum values as constant strings values, but that is far from elegant and often requires evaluating the formulas in each cell to get the largest strings that are generated. While this kind of works for such a small spreadsheet, it becomes very impractical for sheets that are ~16 columns x ~6000 rows.
The following code renders as in OpenOffice.
package com.shagie.poipoc;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.*;
import java.io.FileOutputStream;
public class SimpleBug {
public static void main(String[] args) {
try {
Workbook wb = new HSSFWorkbook();
FileOutputStream fileOut = new FileOutputStream("workbook.xls");
Sheet sheet = wb.createSheet("new sheet");
Row row = sheet.createRow(0);
CellStyle style = wb.createCellStyle();
style.setDataFormat(wb.createDataFormat().getFormat("[h]:mm"));
Cell cell = row.createCell(0);
cell.setCellValue(123.12);
cell.setCellStyle(style);
row.createCell(1).setCellFormula("A1");
row.createCell(2)
.setCellFormula("TRUNC(A1) & \"d \" & TRUNC(24 * MOD(A1,1))" +
" & \"h \" & TRUNC(MOD(60 * 24 * MOD(A1,1),60)) & \"m\"");
row.createCell(3).setCellValue("foo");
for(int i = 0; i < 4; i++) {
sheet.autoSizeColumn(i);
}
wb.write(fileOut);
fileOut.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
Related and tried:
Apache POI autoSizeColumn Resizes Incorrectly I've tried the font in the style. I got a list of all the fonts Java was aware of with
GraphicsEnvironment e = GraphicsEnvironment.getLocalGraphicsEnvironment();
for(String font:e.getAvailableFontFamilyNames()) {
System.out.println(font);
}
I tried several of the fonts listed with that loop, and while OpenOffice changed the font, the columns where still sized incorrectly.

Assuming you're looking to have the correct size of the column based on the formula results, just insert the following line right before you do the autoSizeColumn, in this case before your for loop:
HSSFFormulaEvaluator.evaluateAllFormulaCells(wb);
The reason is autoSizeColumn sizes your cell based on the cached formula evaluated results and if the formula was never evaluated, it would not know what size to set for it.
Code:
...
row.createCell(3).setCellValue("foo");
HSSFFormulaEvaluator.evaluateAllFormulaCells(wb);
for (int i = 0; i < 4; i++) {
sheet.autoSizeColumn(i);
}
...
Output (in OpenOffice)

Related

Cell style is lost or not displayed in Excel 97-2003 (.xls)

I am using the Apache POI library to export data to Excel. I have tried all the latest versions (3.17, 4.1.2, and 5.2.1).
I have a problem with Excel 97 (.xls) format in relation to cell styles. The cell style somehow is lost (or not displayed) after a certain number of columns.
Here is my sample code:
private void exportXls() {
try (
OutputStream os = new FileOutputStream("test.xls");
Workbook wb = new HSSFWorkbook();) {
Sheet sh = wb.createSheet("test");
Row r = sh.createRow(0);
for (int i = 0; i < 50; i++) {
Cell c = r.createCell(i);
c.setCellValue(i + 1);
CellStyle cs = wb.createCellStyle();
cs.setFillBackgroundColor(IndexedColors.WHITE.index);
cs.setFillPattern(FillPatternType.SOLID_FOREGROUND);
cs.setFillForegroundColor(IndexedColors.LIGHT_BLUE.getIndex());
c.setCellStyle(cs);
}
wb.write(os);
os.flush();
} catch (Exception e) {
e.printStackTrace();
}
}
And the result as viewed by MS Excel 2019
Viewed by MS Excel
As you can see, the style/format is lost after cell 43rd.
But, when I open the same file by other applications like XLS Viewer Free (from Microsoft Store) or Google Sheets (online), the style/format still exists and is displayed well.
Viewed by XLS Viewer Free
Viewed by Google Sheets
Could anyone please tell me what is going on here?
Did I miss something in my code?
Is there any hidden setting in MS Excel that causes this problem?
Thank you.
Creating cell styles for each single cell is not a good idea using apache poi. Cell styles are stored on workbook level in Excel. The sheets and cells share the cell styles if possible.
And there are limits for maximum count of different cell styles in all Excel versions. The limit for the binary *.xls is less than the one for the OOXML *.xlsx.
The limit alone cannot be the only reason for the result you have. But it seems as if Excel is not very happy with the 50 exactly same cell styles in workbook. Those are memory waste as only one shared style would be necessary as all the 50 cells share the same style.
Solutions are:
Do creating the cell styles on workbook level outside cell creating loops and only set the styles to the cells in the loop.
Example:
private static void exportXlsCorrect() {
try (
OutputStream os = new FileOutputStream("testCorrect.xls");
Workbook wb = new HSSFWorkbook();) {
CellStyle cs = wb.createCellStyle();
cs.setFillBackgroundColor(IndexedColors.WHITE.index);
cs.setFillPattern(FillPatternType.SOLID_FOREGROUND);
cs.setFillForegroundColor(IndexedColors.LIGHT_BLUE.getIndex());
Sheet sh = wb.createSheet("test");
Row r = sh.createRow(0);
for (int i = 0; i < 50; i++) {
Cell c = r.createCell(i);
c.setCellValue(i + 1);
c.setCellStyle(cs);
}
wb.write(os);
os.flush();
} catch (Exception e) {
e.printStackTrace();
}
}
Sometimes it is not really possible to know all possible needed cell styles before creating the cells. Then CellUtil can be used. This has a method CellUtil.setCellStyleProperties which is able to set specific style properties to cells. Doing that new cell styles are created on workbook level only if needed. If already present, the present cell styles are used.
Example:
private static void exportXlsUsingCellUtil() {
try (
OutputStream os = new FileOutputStream("testUsingCellUtil.xls");
Workbook wb = new HSSFWorkbook();) {
Sheet sh = wb.createSheet("test");
Row r = sh.createRow(0);
for (int i = 0; i < 50; i++) {
Cell c = r.createCell(i);
c.setCellValue(i + 1);
java.util.Map<java.lang.String,java.lang.Object> properties = new java.util.HashMap<java.lang.String,java.lang.Object>();
properties.put(org.apache.poi.ss.util.CellUtil.FILL_BACKGROUND_COLOR, IndexedColors.WHITE.index);
properties.put(org.apache.poi.ss.util.CellUtil.FILL_FOREGROUND_COLOR, IndexedColors.LIGHT_BLUE.getIndex());
properties.put(org.apache.poi.ss.util.CellUtil.FILL_PATTERN, FillPatternType.SOLID_FOREGROUND);
org.apache.poi.ss.util.CellUtil.setCellStyleProperties(c, properties);
}
wb.write(os);
os.flush();
} catch (Exception e) {
e.printStackTrace();
}
}

Apache poi ConditionalFormatting not working properly

I'm using apache poi to create excels in my java application.
My use case is when the value in A1 is Change it.
Style in cells A10 to A14 will be changed.
For this I'm following feature of ConditionalFormatting provided by poi.
But the style is only getting applied to cell A10 & not till A14.
What am I missing?
Code:
private void addValidations(Sheet sheet) {
SheetConditionalFormatting sheetCF = sheet.getSheetConditionalFormatting();
ConditionalFormattingRule rule1 = sheetCF.createConditionalFormattingRule("=A1=\"Change it\"");
FontFormatting fontFmt = rule1.createFontFormatting();
fontFmt.setFontStyle(true, false);
fontFmt.setFontColorIndex(IndexedColors.YELLOW.index);
BorderFormatting bordFmt = rule1.createBorderFormatting();
bordFmt.setBorderBottom(BorderStyle.THIN);
bordFmt.setBorderTop(BorderStyle.THICK);
bordFmt.setBorderLeft(BorderStyle.DASHED);
bordFmt.setBorderRight(BorderStyle.DOTTED);
ConditionalFormattingRule [] cfRules =
{
rule1
};
CellRangeAddress[] regions = {
CellRangeAddress.valueOf("A10:A14")
};
sheetCF.addConditionalFormatting(regions, cfRules);
}
The code sheetCF.createConditionalFormattingRule("=A1=\"Change it\""); cannot work at all. In HSSF it throws org.apache.poi.ss.formula.FormulaParseException: The specified formula '=A1="Change it"' starts with an equals sign which is not allowed.. In XSSF it creates a corrupt *.xlsx file. The leading equals sign is not stored in Excel formula cells. t only is shown in Excel's GUI. So it would must be sheetCF.createConditionalFormattingRule("A1=\"Change it\"");.
And the formula =A1="Change it" is relative in column letter as well as in row number. So applied to cell A10 it means =A1="Change it". But applied to cell A11 it means =A2="Change it". And applied to cell A12 it means =A3="Change it" and so on. So if the need is changing all font colors in A10:A14 if content of A1 changes, then the reference in the formula would must be fixed using $. So it would must be sheetCF.createConditionalFormattingRule("A$1=\"Change it\"");.
Complete Example:
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.util.CellRangeAddress;
import java.io.FileOutputStream;
public class ConditionalFormatting {
public static void main(String[] args) throws Exception {
Workbook workbook = new XSSFWorkbook(); String filePath ="./ConditionalFormatting.xlsx";
//Workbook workbook = new HSSFWorkbook(); String filePath ="./ConditionalFormatting.xls";
Sheet sheet = workbook.createSheet();
for (int r = 9; r < 14; r++) {
sheet.createRow(r).createCell(0).setCellValue("Text in Cell A" + (r+1));
}
SheetConditionalFormatting sheetCF = sheet.getSheetConditionalFormatting();
ConditionalFormattingRule rule = sheetCF.createConditionalFormattingRule("A$1=\"Change it\"");
FontFormatting fontFormatting = rule.createFontFormatting();
fontFormatting.setFontStyle(false, true);
fontFormatting.setFontColorIndex(IndexedColors.YELLOW.index);
ConditionalFormattingRule[] cfRules = new ConditionalFormattingRule[]{rule};
CellRangeAddress[] regions = new CellRangeAddress[]{CellRangeAddress.valueOf("A10:A14")};
sheetCF.addConditionalFormatting(regions, cfRules);
FileOutputStream out = new FileOutputStream(filePath);
workbook.write(out);
out.close();
workbook.close();
}
}
This changes font color in A10:A14 to yellow if cell content in A1 is Change it.
Try this if this works.
CellRangeAddress[] regions = new CellRangeAddress[]{ CellRangeAddress.valueOf("A10:A14") };

Apache POI page break in excel

Hi guys I just want to ask about how to remove the page break in column N in excel using the Apache poi in Java. I've already set up in my code that my print area is until column P. But when I try to print the generated excel it always set that the page break is in column N.
How can I adjust the page break in column using Apache poi on java?
If the goal is that at print all columns (A:P) fit to one page width, then one could solve this using Scale a worksheet.
The following code uses Sheet.setAutobreaks and Sheet.setFitToPage combinated with PrintSetup.setFitWidth and PrintSetup.setFitHeight to set the print setup to scale to fit. The width is set to fit all columns to 1 page width and the height is set automatic, so multiple pages are used when enough rows are present.
import java.io.FileOutputStream;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
public class CreateExcelAllColumnsFitToOnePageWidth {
public static void main(String[] args) throws Exception {
//Workbook workbook = new HSSFWorkbook();
Workbook workbook = new XSSFWorkbook();
Sheet sheet = workbook.createSheet();
for (int r = 0; r < 200; r++) {
Row row = sheet.createRow(r);
for (int c = 0; c < 16; c++) {
Cell cell = row.createCell(c);
cell.setCellValue("Lorem ipsum");
if (r==0) sheet.autoSizeColumn(c);
}
}
sheet.setAutobreaks(true);
sheet.setFitToPage(true);
PrintSetup printSetup = sheet.getPrintSetup();
printSetup.setFitHeight((short)0);
printSetup.setFitWidth((short)1);
String fileout = (workbook instanceof HSSFWorkbook)?"Excel.xls":"Excel.xlsx";
FileOutputStream out = new FileOutputStream(fileout);
workbook.write(out);
out.close();
}
}

I want to arrange entire cells in specific column, instead of individual cells

I used POI and tried to arrange one entire column. But only the way I found is arrange individual cell. Although I found sheet.setDefaultColumnStyle() and tried to use this function, it doesn't work at all.
could you let me know the way of using setDefaultColumnStyle() or another way.
below code is my code to arrange individual cell.
xlsxFile = new File("data.xlsx");
wb = new XSSFWorkbook();
cellStyle = wb.createCellStyle();
cellStyle.setAlignment(CellStyle.ALIGN_CENTER);
cellStyle.setVerticalAlignment(CellStyle.VERTICAL_CENTER);
row = sheet1.createRow(0);
cell = row.createCell(1);
cell.setCellValue("name");
cell.setCellStyle(cellStyle);
My english skill is a little awkward. Thank you for reading. If there is anything weird, please let me know.
This seems to be an bug in Apache POI. There are two issues:
First: After using Sheet.setDefaultColumnStyle with a style which defines alignments, POI does not set applyAlignment="true" in the xf element's tag in styles.xml. But it should, because only that will cause Excel to apply the alignments from that style to new cells.
Second: POI itself does not apply this style to new cells in that column. It should set s="1", where 1 is the style number, in the corresponding c tag of Sheet1.xml.
So we have to workaround:
import org.apache.poi.xssf.usermodel.*;
import org.apache.poi.ss.usermodel.*;
import java.io.FileOutputStream;
import java.io.IOException;
class CenteredColumn {
public static void main(String[] args) {
try {
Workbook wb = new XSSFWorkbook();
Sheet sheet = wb.createSheet("Sheet1");
CellStyle cellStyle = wb.createCellStyle();
cellStyle.setAlignment(CellStyle.ALIGN_CENTER);
sheet.setDefaultColumnStyle(1, cellStyle);
//Workaround 1: We set setApplyAlignment(true) into the `xf` element's tag in styles.xml.
//This causes Excel applying alignments from this style to new cells in that column.
for (int i = 0; i < ((XSSFWorkbook)wb).getStylesSource().getNumCellStyles(); i++) {
if (((XSSFWorkbook)wb).getStylesSource().getStyleAt(i).equals(cellStyle)) {
((XSSFWorkbook)wb).getStylesSource().getCellXfAt(i).setApplyAlignment(true);
}
}
Row row = sheet.getRow(0);
if (row == null) row = sheet.createRow(0);
Cell cell = row.getCell(1);
if (cell == null) cell = row.createCell(1);
cell.setCellValue("name");
//Workaround 2: We set the cellStyle to the new cell because POI will not do this itself.
cell.setCellStyle(cellStyle);
FileOutputStream fileOut = new FileOutputStream("workbook.xlsx");
wb.write(fileOut);
} catch (IOException ioex) {
}
}
}

Apache POI autoSizeColumn Resizes Incorrectly

I'm using Apache POI in java to create an excel file. I fill in the data then try to autosize each column, however the sizes are always wrong (and I think consistent). The first two rows are always(?) completely collapsed. When I autosize the columns in excel, it works perfectly.
No blank cells are being written (I believe) and the resizing is the last thing I do.
Here's the relevant code: This is a boiled down version without error handling, etc.
public static synchronized String storeResults(ArrayList<String> resultList, String file) {
if (resultList == null || resultList.size() == 0) {
return file;
}
FileOutputStream stream = new FileOutputStream(file);
//Create workbook and result sheet
XSSFWorkbook book = new XSSFWorkbook();
Sheet results = book.createSheet("Results");
//Write results to workbook
for (int x = 0; x < resultList.size(); x++) {
String[] items = resultList.get(x).split(PRIM_DELIM);
Row row = results.createRow(x);
for (int i = 0; i < items.length; i++) {
row.createCell(i).setCellValue(items[i]);
}
}
//Auto size all the columns
for (x = 0; x < results.getRow(0).getPhysicalNumberOfCells(); x++) {
results.autoSizeColumn(x);
}
//Write the book and close the stream
book.write(stream);
stream.flush();
stream.close();
return file;
}
I know there are a few questions out there similar, but most of them are simply a case of sizing before filling in the data. And the few that aren't are more complicated/unanswered.
EDIT: I tried using a couple different fonts and it didn't work. Which isn't too surprising, as no matter what the font either all the columns should be completely collapsed or none should be.
Also, because the font issue came up, I'm running the program on Windows 7.
SOLVED: It was a font issue. The only font that I found that worked was Serif.
Just to make an answer out of my comment. The rows couldn't size properly because Java was unaware of the font you were trying to use this link should help if you want to install new fonts into Java so you could use something fancier. It also has the list of default fonts that Java knows.
Glad this helped and you got your issue solved!
I was also running into this issue and this was my solution.
Steps:
Create workbook
Create spreadsheet
Create row
Create/Set font to "Arial"
Create/Set style with font
Create/Set cell with value and style
autoSizeColumn
Create File
Code:
// initialize objects
XSSFWorkbook workbook = new XSSFWorkbook();
XSSFSheet spreadsheet = workbook.createSheet(sheetName);
XSSFRow row = spreadsheet.createRow(0);
XSSFCell cell;
// font/style
XSSFFont font = workbook.createFont();
font.setFontName("Arial");
XSSFCellStyle style = workbook.createCellStyle();
style.setFont(font);
// create/set cell & style
cell = row.createCell(0);
cell.setCellValue("New Cell");
cell.setCellStyle(style);
// auto size
spreadsheet.autoSizeColumn(0);
// create file
File aFile = new File("Your Filename");
FileOutputStream out = new FileOutputStream(aFile);
workbook.write(out);
Resources:
http://www.tutorialspoint.com/apache_poi/index.htm
This is probably related to this POI Bug which is related to Java Bug JDK-8013716: Renderer for Calibri and Cambria Fonts fails since update 45.
In this case changing the Font or using JRE above 6u45 / 7u21 should fix the issue.
You can also mtigitate the issue and avoid the columns from being totally collapsed by using a code like this:
sheet.autoSizeColumn(x);
if (sheet.getColumnWidth(x) == 0) {
// autosize failed use MIN_WIDTH
sheet.setColumnWidth(x, MIN_WIDTH);
}
I had a similar issue on Windows 7.
I was using the Calibri font (that is supported in my JVM).
With that font the getBounds().getWidth() of the java.awt.font.TextLayout used by the autoSizeColumn() POI method returns 0.
Changing the font to Calibri-Regular solved the issue in my case.
Here are my 2 cents -
I was using the default font (Arial in my case) using it to make certain fields bold in the xls. Worked like a charm in Windows along with autoSizeColumn() function.
Linux wasn't that forgiving. The auto-sizing was improper at places. After going through this thread and other I came up with the following solution.
I copied the Arial font's .tff files into the JAVA/jre/lib/fonts directory and re-ran the application. Worked just fine.
I've used Helvetica font trying to replace Arial font (in fact, Helvetica is similar to Arial).
XSSFFont font = wb.createFont();
font.setFontName("Helvetica");
I found that auto-sizing didn't make the column wide enough when the widest string began with spaces, e.g.
cell.setCellValue(" New Cell");
This can be fixed by using indentations instead, e.g.
// font/style
XSSFFont font = workbook.createFont();
font.setFontName("Arial");
XSSFCellStyle style = workbook.createCellStyle();
style.setFont(font);
style.setIndention((short)2);
// create/set cell & style
cell = row.createCell(0);
cell.setCellValue("New Cell");
cell.setCellStyle(style);
// auto size
spreadsheet.autoSizeColumn(0);
The following works for me.
I set the font and use autoSizeColumn() after enter all the data.
public static XSSFWorkbook createExcel(List<ChannelVodFileInfoList> resList) {
XSSFWorkbook hwb = new XSSFWorkbook();
String[] title = { "1", "2", "3", "4"};
XSSFSheet sheet = hwb.createSheet("dataStats");
XSSFRow firstrow = sheet.createRow(0);
for (int i = 0; i < title.length; i++) {
XSSFCell xh = firstrow.createCell(i);
xh.setCellValue(title[i]);
}
if (resList == null || resList.size() == 0) {
return hwb;
}
for (int i = 0; i < resList.size(); i++) {
ChannelVodFileInfoList doTemp = resList.get(i);
XSSFRow row = sheet.createRow(i + 1);
for (int j = 0; j < title.length; j++) {
XSSFCell cell = row.createCell(j);
Font font111 = hwb.createFont();
font111.setBoldweight(Font.BOLDWEIGHT_NORMAL);
XSSFCellStyle cellStyle111 = hwb.createCellStyle();
cellStyle111.setFont(font111);
cell.setCellStyle(cellStyle111);
if (j == 0) {
cell.setCellValue(dateStr);
} else if (j == 1) {
cell.setCellValue(doTemp.getChannelName());
}else if (j == 2) {
cell.setCellValue(doTemp.getBitrate());
}else if (j == 3) {
cell.setCellValue(doTemp.getWh());
}
}
for (int j = 0; j < title.length; j++) {
sheet.autoSizeColumn(j);
}
return hwb;
}
A Hacky Way
My case was after auto-sizing, those cells were slightly smaller than the width of the actual content.
What I did is just added extra 200 widths after the autoSizeColumn method is being called.
sheet.autoSizeColumn(columnIndex);
sheet.setColumnWidth(columnIndex, sheet.getColumnWidth(columnIndex) + 200);
This worked fine for me :)
In the event users are running into the issue where they have a sheet with both cells and merged cells, Gagravarr's answer on this post really helped me:
https://stackoverflow.com/a/31425163/9407809
Posting here for future users looking for this.

Categories