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
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 trying to make the code I already wrote for an XSSF workbook compatible to both xls and xlsx. Given below a piece of code as an example.
Workbook workbook = WorkbookFactory.create(new File("F:\\JavaEE\\test.xls"));
Sheet sheet = workbook.getSheetAt(0);
short cellBorderColour = IndexedColors.GREY_80_PERCENT.getIndex();
Font font = workbook.createFont();
font.setBoldweight(Font.BOLDWEIGHT_BOLD);
font.setColor(IndexedColors.BLUE_GREY.index);
CellStyle cellStyle = workbook.createCellStyle();
cellStyle.setFillPattern(CellStyle.SOLID_FOREGROUND);
cellStyle.setAlignment(CellStyle.ALIGN_CENTER);
cellStyle.setFont(font);
cellStyle.setBorderLeft(CellStyle.BORDER_HAIR);
cellStyle.setLeftBorderColor(cellBorderColour);
cellStyle.setBorderTop(CellStyle.BORDER_HAIR);
cellStyle.setTopBorderColor(cellBorderColour);
cellStyle.setBorderRight(CellStyle.BORDER_HAIR);
cellStyle.setRightBorderColor(cellBorderColour);
cellStyle.setBorderBottom(CellStyle.BORDER_MEDIUM);
cellStyle.setBottomBorderColor(cellBorderColour);
Row row = sheet.createRow(1);
Cell cell = row.createCell(1);
sheet.addMergedRegion(new CellRangeAddress(1, 1, 1, 4));
borderRowCells(1, 4, row, cell, cellStyle);
cell.setCellValue("Value");
FileOutputStream fos = new FileOutputStream("F:\\JavaEE\\new_test.xls");
workbook.write(fos);
//Take care of exception handling and closing of the stream.
The utility method as used by the above code to border across merged cells.
private void borderRowCells(int from, int to, Row row, Cell cell, CellStyle cellStyle) {
for (int i = from; i <= to; ++i) {
cell = row.createCell(i);
cell.setCellStyle(cellStyle);
}
}
This code is meant to apply some borders in conjunction with other styles across some merged cells.
The border across merged cells is however, not applied correctly as can be seen in the following picture.
The borders are expected to be applied starting from B2 to E2. The actual border area however, covers only from C2 to E2
The cell value Value is also not aligned center which is specified in the cell style.
How to apply this border correctly across the merged cells along with center alignment?
Also the list of colours provided by IndexedColors.COLOR_NAME.getIndex() is very limited. The list is very unlikely to fulfill the real needs of colours in different cell styles.
Can we use RGB colours in the ss model as we do in the xssf model using XSSFColor as follows?
XSSFColor commonColor = new XSSFColor(new java.awt.Color(240, 240, 240));
Or even something better/different?
PS : I'm using Apache POI 3.10.1.
Working with iText and using table cells.
I have 25 cells (columns) in a table.
The content of each column is rotated 90 degree and is required to be fitted to the height of each cell.
In some cases when the length of the content exceeds the height of the cell, not all the content is visible (Only the part of content is visible that is fitted to the height of the cell, the rest is dropped). I want to get that dropped content and want to show it in the next adjacent cell.
The following code is used -
PdfPCell cell;
for(int i = 0;i< 25;i++)
{
if(locs.get(i) == null)
{
cell = new PdfPCell();
cell.setBorder(0);
table.addCell(cell);
}
else
{
Font font = new Font(FontFamily.HELVETICA, 9, Font.BOLD, BaseColor.BLACK);
cell = new PdfPCell(new Phrase(locs.get(i), font));
cell.setRotation(90);
cell.setBorder(0);
cell.setFixedHeight(110f);
//cell.setMinimumHeight(10f);
table.addCell(cell);
}
}
So if the value of locs.get(i) is greater then the height of the cell (cell height is fixed to 110f in the above code), some content that doesn't fit gets dropped to be shown in the view.
How to get that content and show it to the adjacent cell ?
The method used that solved the purpose is the following:
PdfPCell cell;
for(int i = 0;i< 25;i++)
{
if(locs.get(i) != null)
{
Font font = new Font(FontFamily.HELVETICA, 9, Font.BOLD, BaseColor.BLACK);
cell = new PdfPCell(new Phrase(locs.get(i), font));
cell.setRotation(90);
cell.setBorder(0);
cell.setFixedHeight(110f);
cell.setColspan(2);
table.addCell(cell);
}
else
{
cell = new PdfPCell();
cell.setBorder(0);
table.addCell(cell);
}
}
So setting the colspan to 2 ensures that if the contents exceeds the length of the first column then move the remaining contents to the next column of the cell (One cell having two columns now after adding the colspan of 2).
If anyone knows the better way to do the same thing then you are welcome!
I am inputting values into a spreadsheet using Apache POI. These values have newlines, and I was able to use this code successfully:
CellStyle style = cell.getCellStyle()
style.setWrapText(true)
cell.setCellStyle(style)
Unfortunately, while the text is wrapping correctly, the rows are not always growing in height enough to show the content. How do I ensure that my rows are always the correct height?
currentRow.setHeight((short)-1)
Works for XSSFCell and Excel 2013
HSSFWorkbook workbook=new HSSFWorkbook();
HSSFSheet sheet = workbook.createSheet("FirstSheet");
HSSFRow rowhead= sheet.createRow((short)0);
HSSFCellStyle style = workbook.createCellStyle();
style.setWrapText(true);
row.setRowStyle(style);
row.getCell(0).setCellStyle(style);
The above code will generate dynamic height of rows.
The only way I got this to work was write my own implementation to calculate the row height. The code is now released as the Taro project, so you could use that. It has numerous convenience methods to let you write an Excel file in far fewer lines of code.
If you prefer to put the implementation in your own code, you can find it in the SpreadsheetTab class. There is an autoSizeRow(int rowIndex) method half way down. It basically iterates down the row and for each cell finds the number of lines of text, then uses the font size to calculate the optimal cell height. It then sets the row height to the height of the tallest cell.
See all this link, which provides some code to manually calculate the correct height for a row, based on the column width and cell content. I've not personally tested it. Also pasted below for convenience:
// Create Font object with Font attribute (e.g. Font family, Font size, etc) for calculation
java.awt.Font currFont = new java.awt.Font(fontName, 0, fontSize);
AttributedString attrStr = new AttributedString(cellValue);
attrStr.addAttribute(TextAttribute.FONT, currFont);
// Use LineBreakMeasurer to count number of lines needed for the text
FontRenderContext frc = new FontRenderContext(null, true, true);
LineBreakMeasurer measurer = new LineBreakMeasurer(attrStr.getIterator(), frc);
int nextPos = 0;
int lineCnt = 0;
while (measurer.getPosition() < cellValue.length())
{
nextPos = measurer.nextOffset(mergedCellWidth); // mergedCellWidth is the max width of each line
lineCnt++;
measurer.setPosition(nextPos);
}
Row currRow = currSht.getRow(rowNum);
currRow.setHeight((short)(currRow.getHeight() * lineCnt));
// The above solution doesn't handle the newline character, i.e. "\n", and only
// tested under horizontal merged cells.
cell.getRow().setHeight((short) -1);
Worked for HSSFCell in apache poi 3.9 or above
It works in Excel 2010.
I set the limit of cell length of 50 characters
Row row = sheet.createRow(0);
CellStyle style = workbook.createCellStyle();
style.setWrapText(true);
if (data.length() > 50) {
for (int i = 1; i <= Math.abs(data.length() / 50); i++) {
data = data.substring(0, i * 50) + "\n" + data.substring(i * 50);
}
Cell cell = row.createCell(0);
row.setRowStyle(style);
cell.setCellStyle(style);
cell.setCellValue(data);
sheet.autoSizeColumn(0);
}
In my case a robust solution was to calculate the number of lines and set the row height to a multiple of the default row height:
int numberOfLines = cell.getStringCellValue().split("\n").length;
row.setHeightInPoints(numberOfLines*sheet.getDefaultRowHeightInPoints());
You can't adjust cell height directly.
But you can change the row's height
final HSSFSheet fs = wb.createSheet("sheet1");
final HSSFRow row0 = fs.createRow(0);
final HSSFCell cellA1 = row0.createCell(0);
row0.setHeight((short)700);
Row aitosize work for me:
cell.getRow().setHeight((short)0);
Here 0 for calculate autoheight.
Workaround for “LibreOffice Calc“ and “WPS Spreadsheet” with auto height for merged sells.
I add a column out to the right of a main document (In my case it was 32 column)
Set width as all merged cells with same text.
Set style WrapText to true
Set style to Align Top
Copy content which will be displayed in the merged cells
Set that column to be hidden
Set a row height = -1
A sample of code:
private void applyRowHightWorkaroundForMergedCells(HSSFCell cell0) {
HSSFSheet sheet = cell0.getSheet();
HSSFRow row = cell0.getRow();
String value = cell0.getStringCellValue();
HSSFCell cell = row.createCell(32);
sheet.setColumnWidth(32, 32000);
cell.getCellStyle().setWrapText(true);
cell.getCellStyle().setVerticalAlignment(VerticalAlignment.TOP);
cell.setCellValue(value);
sheet.setColumnHidden(32, true);
row.setHeight((short) -1);
}
//we can use column width for sheet
Ex: sheet.setColumnWidth(0, 2000);
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);
}
}
}
}
}