I am using poi-ooxml#3.17 to read and write excel file. I have added some styles/protection on some of cells. When i read the file i am not able to get cell styles applied to cells with no value as when i tries to access row/cell with empty value it returns null.
Below is code to write data in same excel file.
public static void writeDataToSheet(final Sheet sheet, final List<Map<String, Object>> sheetData) {
List<String> columns = getColumnNames(sheet);
LOGGER.debug("Inside XLSXHelper writeDataToSheet {}", Arrays.asList(columns));
IntStream.range(0, sheetData.size()).forEach((index) -> {
if (Objects.isNull(sheet.getRow(index + 1))) {
sheet.createRow(index + 1);
}
Row row = sheet.getRow(index + 1);
Map<String, Object> data = sheetData.get(index);
IntStream.range(0, columns.size()).forEach((colIndex) -> {
String column = columns.get(colIndex);
Cell cell = row.getCell(colIndex);
if (Objects.isNull(cell)) {
cell = row.createCell(colIndex);
}
cell.setCellValue(data.get(column) != null ? data.get(column).toString() : null);
});
});
}
Could anyone provide me a solution where i can read the styles applied to cell when cell is empty?
Thanks.
Cells without content or explicit style applied are not present in the sheet because of not to increase the file size unnecessarily. So apache poi returns null for such cells.
If you are looking at the sheet in spreadsheet application, then maybe it looks as if all cells in a row or all cells in a column have the same style applied to. But this is not the case. In real the row and/or the column has the style applied to. Only cells in intersection of styled rows and columns must be present in the sheet having the last applied style.
If a new cell needs to be created, then the spreadsheet application gets the preferred style for that cell. This is either the already applied cell style or if that not present, then the row style (default cell style for this row) or if that not present, then the column style (default cell style for this column). Unfortunately apache poi does not do so. So we need doing this ourself:
public CellStyle getPreferredCellStyle(Cell cell) {
// a method to get the preferred cell style for a cell
// this is either the already applied cell style
// or if that not present, then the row style (default cell style for this row)
// or if that not present, then the column style (default cell style for this column)
CellStyle cellStyle = cell.getCellStyle();
if (cellStyle.getIndex() == 0) cellStyle = cell.getRow().getRowStyle();
if (cellStyle == null) cellStyle = cell.getSheet().getColumnStyle(cell.getColumnIndex());
if (cellStyle == null) cellStyle = cell.getCellStyle();
return cellStyle;
}
This method may be used in code every time a new cell needs to be created:
...
if (Objects.isNull(cell)) {
cell = row.createCell(colIndex);
cell.setCellStyle(getPreferredCellStyle(cell));
}
...
Related
Java 8 and Apache POI 4.1.x here. I need to:
Read in a template Excel file, that just has a single row of pre-styled header columns
Write a List<Fizz> (POJO list) to a new Excel file that uses that template
I do this because the template Excel has lots of complicated stylings, and so just using an existing one as a template input was much easier than me trying to manually generate the stylings myself via POI and CellUtil.
I have this working perfectly fine like so:
List<Fizz> fizzes = getSomehow();
InputStream inp = this.getClass().getClassLoader().getResource("my-template.xlsx").openStream();
Workbook workbook = WorkbookFactory.create(inp);
Sheet sheet = workbook.getSheetAt(0);
// the header is at row = 0 (0-based rows)
// so start writing the list on the row=1 (1st data/non-header row)
int rowNum = 1;
for (Fizz fizz : fizzes) {
Row nextRow = sheet.createRow(rowNum);
Cell itemNumber = nextRow.createCell(0);
itemNumber.setCellValue(fizz.getItemNumber());
Cell description = nextRow.createCell(1);
description.setCellValue(fizz.getDescription());
rowNum++;
}
// resize the columns appropriately
for (int c = 0; c < 2; c++) {
sheet.autoSizeColumn(c);
}
// export to file system
FileOutputStream fos = new FileOutputStream("some-output.xlsx");
workbook.write(fos);
fos.close();
inp.close();
workbook.close();
Everything works great, with one exception: often the Fizz#description is pretty lengthy, and is sometimes hundreds of characters long. Because there is no use of text wrapping in my code, and because I use autoSizeColumn for all columns, POI is setting the Description column width to the length of the longest-description.
Instead, I now want to stop using autoSizeColumn (I think!) and instead:
Force the output file's (in our case, "some-output.xlsx") Description column width to be the same as the input template file's Description's column. So if the Description column on my-template.xlsx is, say, 54, then I want some-output.xlsx's Description column to also be 54.
If the current row's Fizz description is too long for that width, I want to apply text wrapping so that the column stays fixed (again, in this case, 54) but that the description fits inside of it.
I would like to do this for every column, and ideally, I would like the code to detect what the width is for each column on the template. I say that because I would like to be able to open the template in Excel, change its width manually, save it, then re-run my code and have it pick up on that width change.
Any ideas on what I can do to accomplish this?
First approach
Set wrap text cell style for whole description column (column B) in your template using Excel's GUI. Then do using following getPreferredCellStyle method to get that column cell style and set it as the preferred cell style for each cell in description column (column B).
CellStyle getPreferredCellStyle(Cell cell) {
// a method to get the preferred cell style for a cell
// this is either the already applied cell style
// or if that not present, then the row style (default cell style for this row)
// or if that not present, then the column style (default cell style for this column)
CellStyle cellStyle = cell.getCellStyle();
// if no explicit cell style applied then cellStyle.getIndex() is 0 for XSSF
// or 15 (0xF = the index to the default ExtendedFormatRecord (0xF)) for HSSF
if ((cell instanceof XSSFCell && cellStyle.getIndex() == 0) || (cell instanceof HSSFCell && cellStyle.getIndex() == 15)) cellStyle = cell.getRow().getRowStyle();
if (cellStyle == null) cellStyle = cell.getSheet().getColumnStyle(cell.getColumnIndex());
if (cellStyle == null) cellStyle = cell.getCellStyle();
return cellStyle;
}
Then
...
Cell description = nextRow.createCell(1);
description.setCellValue(fizz.getDescription());
description.setCellStyle(getPreferredCellStyle(description));
...
Second approach
Do using CellUtil to set wrap text cell style for each cell in description column.
...
Cell description = nextRow.createCell(1);
description.setCellValue(fizz.getDescription());
CellUtil.setCellStyleProperty(description, CellUtil.WRAP_TEXT, true);
...
For both approaches
Do not set autoSizeColumn for the description column (column B). In your example, only autosize column 0 (A) but not column 1 (B):
...
sheet.autoSizeColumn(0);
...
So the column width of column B remains unchanged as width as it is in the template.
I'm using Apache POI 3.17 to read some excel data. My second column (index of 1 because of 0 index) is empty and I want to be able to read it, but can't get my code to read the cell as empty. I have this which isn't working:
Cell c = row.getCell(1, Row.RETURN_BLANK_AS_NULL);
if (c == null) {
// do whatever
}
But the second parameter can't be taken in. I get "RETURN_BLANK_AS_NULL cannot be resolved or is not a field"
The constants in the Row class itself were deprecated as of POI-3.15-beta2, marked for removal as of POI-3.17. This diff shows when those constants were deprecated in June 2016. They were removed in 3.17.
Before 3.17, the enum Row.MissingCellPolicy was already defined as a replacement. If you're using 3.17, then you must use that enum; it is defined as a member of the Row interface. Try
Cell c = row.getCell(1, Row.MissingCellPolicy.RETURN_BLANK_AS_NULL);
Try using org.apache.poi.ss.usermodel.DataFormatter.
DataFormatter formatter = new DataFormatter();
Workbook workbook = WorkbookFactory.create(new File("yourFileName.xls"));
Sheet sheet = workbook.getSheetAt(0);
for (Row row : sheet) {
for(Cell cell : row)
{
String text = formatter.formatCellValue(cell);
System.out.println(text);
}
}
workbook.close();
I want to export data to excel using Apache poi. Now the problem that I am facing is that I am unable to merge rows and align them in the center.
Code for export data is:
List<LinkedHashMap<String,Object>> lstReportHeader = null;
HSSFWorkbook wb = new HSSFWorkbook();
HSSFSheet sheet = wb.createSheet();
//Set Header Font
HSSFFont headerFont = wb.createFont();
headerFont.setBoldweight(headerFont.BOLDWEIGHT_BOLD);
headerFont.setFontHeightInPoints((short) 12);
//Set Header Style
CellStyle headerStyle = wb.createCellStyle();
headerStyle.setFillBackgroundColor(IndexedColors.BLACK.getIndex());
headerStyle.setAlignment(headerStyle.ALIGN_CENTER);
headerStyle.setFont(headerFont);
headerStyle.setBorderBottom(HSSFCellStyle.BORDER_MEDIUM);
int rowCount= 0;
Row header;
header = sheet.createRow(0);//its for header
Cell cell ;//= header.createCell(0);
for(int j = 0;j < 4; j++) {
cell = header.createCell(j);
if(j == 0) {
cell.setCellValue("ItemWise List");
}
cell.setCellStyle(headerStyle);
}
sheet.addMergedRegion(new CellRangeAddress(rowCount, rowCount, 0, lstReportFormHeader.size()-1));
header = sheet.createRow(0);
cell = header.createCell(0);
cell.setCellValue("Sr. No");
cell = header.createCell(1);
cell.setCellValue("Item Name");
cell = header.createCell(2);
cell.setCellValue("Qty");
cell = header.createCell(3);
cell.setCellValue("Rate");
Now I want to ItemWise List merge and make it align center.
My solution was to merge the cells by their positions, then created a cell (reference to the first block of the merged cells) to assign a value and then set the alignment throught the CellUtil
// Merges the cells
CellRangeAddress cellRangeAddress = new CellRangeAddress(start, start, j, j + 1);
sheet.addMergedRegion(cellRangeAddress);
// Creates the cell
Cell cell = CellUtil.createCell(row, j, entry.getKey());
// Sets the allignment to the created cell
CellUtil.setAlignment(cell, workbook, CellStyle.ALIGN_CENTER);
Merge like:::
Workbook wb = new HSSFWorkbook();
Sheet sheet = wb.createSheet("new sheet");
Row row = sheet.createRow((short) 1);
Cell cell = row.createCell((short) 1);
cell.setCellValue("This is a test of merging");
sheet.addMergedRegion(new CellRangeAddress(
1, //first row (0-based)
1, //last row (0-based)
1, //first column (0-based)
2 //last column (0-based)
));
// Write the output to a file
FileOutputStream fileOut = new FileOutputStream("workbook.xls");
wb.write(fileOut);
fileOut.close();
For aligning also check the below official link of Apache poi:::
http://poi.apache.org/spreadsheet/quick-guide.html#Alignment
After study I found that after merging 7 cells, merged cell id will be 0 so I applied following style to cell id 0 using following style.
headerStyle.setAlignment(headerStyle.ALIGN_CENTER);
This worked for me and I think it's cleaner:
/**
* Merge and center the cells specified by range
* #param startCell the first cell in the cells to be merged
* #param range the range of the cells to be merged
*/
private static void mergeAndCenter(Cell startCell, CellRangeAddress range) {
startCell.getSheet().addMergedRegion(range);
CellStyle style = startCell.getSheet().getWorkbook().createCellStyle();
style.setAlignment(CellStyle.ALIGN_CENTER);
style.setVerticalAlignment(CellStyle.VERTICAL_CENTER);
startCell.setCellStyle(style);
}
As per my understanding, you have start and end cells for merging and you want to merge the cell ranges and align the cell content. If I am right, you can use the following method:
/**
* #param startCell: first cell of merging area
* #param endCell: last cell of merging area
*/
public static void mergeAndAlignCenter(HSSFCell startCell, HSSFCell endCell){
//finding reference of start and end cell; will result like $A$1
CellReference startCellRef= new CellReference(startCell.getRowIndex(),startCell.getColumnIndex());
CellReference endCellRef = new CellReference(endCell.getRowIndex(),endCell.getColumnIndex());
// forming string of references; will result like $A$1:$B$5
String cellRefernce = startCellRef.formatAsString()+":"+endCellRef.formatAsString();
//removing $ to make cellRefernce like A1:B5
cellRefernce = cellRefernce.replace("$","");
//passing cellRefernce to make a region
CellRangeAddress region = CellRangeAddress.valueOf(cellRefernce);
//use region to merge; though other method like sheet.addMergedRegion(new CellRangeAddress(1,1,4,1));
// is also available, but facing some problem right now.
startCell.getRow().getSheet().addMergedRegion( region );
//setting alignment to center
CellUtil.setAlignment(startCell, wb, CellStyle.ALIGN_CENTER);
}
Well what worked for me is to set all the merged cells' Cellstyle to CENTER ALIGN. Whether you put the XSSFSheet.addMergedRegion() method before or after setting the cellstyle values to center don't matter.
private void insertXlsHeader(XSSFSheet sheet){
....
//first cell for row1
cell = row1.createCell(colstart);
cell.setCellType(org.apache.poi.ss.usermodel.Cell.CELL_TYPE_STRING);
cell.setCellValue("COURSES");
setHeaderCellStyle(sheet,cell);
//first cell for row2
cell = row2.createCell(colstart);
setHeaderCellStyle(sheet,cell);
//first cell for row3
cell = row3.createCell(colstart);
setHeaderCellStyle(sheet,cell);
//merged the first cells of rows 1 to 3
sheet.addMergedRegion(new CellRangeAddress(ROW1, ROW3, colstart, colstart));
...
}
private void setHeaderCellStyle(XSSFSheet sheet,org.apache.poi.ss.usermodel.Cell cell) {
CellStyle s = null;
s = sheet.getWorkbook().createCellStyle();
cell.setCellStyle(s);
Font f = sheet.getWorkbook().createFont();
f.setBoldweight(Font.BOLDWEIGHT_BOLD);
s.setBorderBottom(CellStyle.BORDER_THIN);
s.setBorderLeft(CellStyle.BORDER_THIN);
s.setBorderRight(CellStyle.BORDER_THIN);
s.setBorderTop(CellStyle.BORDER_THIN);
s.setVerticalAlignment(CellStyle.VERTICAL_CENTER);
s.setAlignment(CellStyle.ALIGN_CENTER);
s.setFont(f);
}
As answered above, merging cells can be achieved using
sheet.addMergedRegion(new CellRangeAddress(frstRow, lastRow, firstColumnIndex, lastColumnIndex));
But for aligning cells vertically,recently I faced similar issue and I tried above answer, but using
CellUtil.setAlignment(dataCell, workbook, CellStyle.VERTICAL_CENTER);
aligned Date formatted cells to Horizontal Left aligned. So I used following method to set only Vertical Alignment of Cell content.
CellUtil.setCellStyleProperty(dataCell, workbook,CellUtil.VERTICAL_ALIGNMENT,CellStyle.VERTICAL_CENTER);
I hope this helps!!
Happy Coding
Use
style.setVerticalAlignment()
to set the vertical alignments instead of
style.setAlignment().
We can merge the column along with we can vertically and horizontally align too.
I had the rows 2 to 10 of column A having the same values.
I used the below code to merge the data where the variable sheet is XSSFSheet. The parameters of CellRangeAddress have the parameters are start row, last row, start column and last column. In my example, the value USA starts from 2nd row (index is 1) and the last value of USA is in 10th row and column is the 1st column.
CellRangeAddress ca = new CellRangeAddress(1,9,0,0);
sheet.addMergedRegion(ca);
When I executed the above code, the cell was merged but the text was not aligned to center.
To overcome this issue, I utilized the class CellStyle and Cell. Get the 2nd row of 1st column text to cell variable. Now set the vertical and horizontal alignment to the cellStyle and set this style to the cell which will align the text to the center.
Cell cell = sheet.getRow(1).getCell(0);
CellStyle cellStyle = workbook.createCellStyle();
cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
cellStyle.setAlignment(HorizontalAlignment.CENTER);
cell.setCellStyle(cellStyle);
Below is the final result
Additional references :
Jar files used
Import statements
I'm using Apache POI to export data to a .xlsx file and I want to style some of the rows and cells contained in the file.
I'm using XSSF since the file is going to be read in Excel 2007+.
Basically, my problem is that I'm trying to set a row style like in the following example, which sets a black foreground color for the entire row at index 0. It works fine, but whenever I create a new cell, the newly created cell has no style, as if it's overriding the row style I specified.
Here's a code snippet to demonstrate what I'm doing:
XSSFWorkbook wb = new XSSFWorkbook();
XSSFSheet sheet = wb.createSheet("mySheet");
XSSFRow row = sheet.createRow(0);
XSSFCellStyle myStyle = wb.createCellStyle();
myStyle.setFillForegroundColor(new XSSFColor(new Color(255, 255, 255)));
myStyle.setFillPattern(CellStyle.SOLID_FOREGROUND);
row.setRowStyle(myStyle); //This works, the whole row is now black
row.createCell(0); // This cell doesn't have a style, the rest of the line stays stylized
row.getCell(0).setCellValue("Test");
I also tried *row.createCell(0, Cell.CELL_TYPE_STRING);*, but it didn't change anything.
What is the correct way of accomplishing what I want to do? I wanted to do it this way so I didn't have to set each cell's style after creating it since all cells on the same row have the same style.
Set the style into newly created cell as well e.g. below:
XSSFCell newCell = row.createCell(0);
newCell.setCellStyle(myStyle);
Even you create a row with style, it will not effect to created cell of its. The create cell have their own cell style.
The row style will not override to cell style automatically. If you would like use row style in cell, you have to set again.
Even if you set row style at end, it will not effect to cell.
Example
CreationHelper createHelper = wb.getCreationHelper();
Sheet sheet = wb.createSheet("new sheet");
Row r = sheet.createRow(0);
r.setRowStyle(rowStyle);
Cell c1 = r.createCell(0);
c1.setCellValue("Test 1");
c1.setCellStyle(rowStyle);
I'm agree that "setRowStyle" doesn't work as it should be.
I created my own function to apply a style to a range ( could be a row or multiple row )
public void applyStyleToRange(Sheet sheet, CellStyle style, int rowStart, int colStart, int rowEnd, int colEnd) {
for (int r = rowStart; r <= rowEnd; r++) {
for (int c = colStart; c <= colEnd; c++) {
Row row = sheet.getRow(r);
if (row != null) {
Cell cell = row.getCell(c);
if (cell != null) {
cell.setCellStyle(style);
}
}
}
}
}
I'd like to find a cell in an Excel sheet by its text. The text is something like %t:
sheet.findCell("%t"); // pseudo-code, not working
My goal is to enable the user to provide kind of template, in which data is written. Colours and fonts, as well as data's position can be configured by the user in an Excel file. This %t cell is the top-left corner of the data table.
Additional question: Is there a more elegant way to get this job done?
EDIT I'm iterating over the rows and cells to find it. I'm afraid it's not really efficient, but it works so far:
public static Cell findCell(XSSFSheet sheet, String text) {
for(Row row : sheet) {
for(Cell cell : row) {
if(text.equals(cell.getStringCellValue()))
return cell;
}
}
return null;
}
You can iterate through the cells of the sheet and investigate the contents. I don't think there is an easier method.
Its an old post but still i want to publish my code.
You can define a file path.
String inputFile = "src\main\resources\file.xlsx";
XSSFWorkbook xssfWorkbook = new XSSFWorkbook(new FileInputStream(inputFile));
DataFormatter formatter = new DataFormatter();
for (XSSFSheet sheet : xssfWorkbook) {
for (Row row : sheet) {
for (Cell cell : row) {
if (formatter.formatCellValue(cell).contains("name")){
cell.setCellValue("test");
}
}
}
}
xssfWorkbook.write(new FileOutputStream(inputFile));