I am trying to change the default cell style for an entire Excel workbook (XSSF) using Apache POI. This should be applied to new cells a user might create (after the workbook has been saved by POI). I am trying to do this by calling workbook.getCellStyleAt(0) -- which I understand to be the default style for the workbook -- and then by modifying this style to what I want for the new default.
This works when I read in an existing XSLX file (a "template" file) and modify the default style. But when I create a new XSLX file from scratch using POI, it does not work.
When stepping through using a debugger I can see that, when using a "template" file, there is a "theme" assigned to the cell style at index 0 (probably because the template file was originally created using Excel). But when creating a file from scratch (using POI), the cell style at index 0 has a null theme. (This might be a factor in why this works using one approach but not the other.)
Any suggestions on how to reliably change the default cell style for a workbook (XSSF) regardless of how the workbook was originally created? Thanks!
There are two possibilities to achieve this with XSSF.
First: If you select all cells in a XSSF worksheet in Excel and apply a style to them, then a cols element is added to the sheet with a style definition for all columns:
<cols>
<col min="1" max="16384" style="1"/>
</cols>
This can be achieved with apache poi like so:
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.*;
import java.io.FileOutputStream;
import java.io.IOException;
class ExcelCellStyleAllColumns
{
public static void main(String[] args) {
try {
Workbook wb = new XSSFWorkbook();
Font font = wb.createFont();
font.setFontHeightInPoints((short)24);
font.setFontName("Courier New");
font.setItalic(true);
font.setBold(true);
CellStyle style = wb.createCellStyle();
style.setFont(font);
Sheet sheet = wb.createSheet();
org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCol cTCol =
((XSSFSheet)sheet).getCTWorksheet().getColsArray(0).addNewCol();
cTCol.setMin(1);
cTCol.setMax(16384);
cTCol.setWidth(12.7109375);
cTCol.setStyle(style.getIndex());
Row row = sheet.createRow(0);
Cell cell = row.createCell(0);
cell.setCellValue("test");
cell.setCellStyle(style);
FileOutputStream os = new FileOutputStream("ExcelCellStyleAllColumns.xlsx");
wb.write(os);
os.close();
} catch (IOException ioex) {
}
}
}
This will change the default cell style of all cells in the sheet.
Second: You can modify the style definitions of the normal cell style like so:
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.*;
import java.io.FileOutputStream;
import java.io.IOException;
class ExcelDefaultCellStyle {
public static void main(String[] args) {
try {
Workbook wb = new XSSFWorkbook();
Font font = wb.getFontAt((short)0);
font.setFontHeightInPoints((short)24);
font.setFontName("Courier New");
((XSSFFont)font).setFamily(3);
((XSSFFont)font).setScheme(FontScheme.NONE);
font.setItalic(true);
font.setBold(true);
CellStyle style = wb.getCellStyleAt(0);
style.setVerticalAlignment(CellStyle.VERTICAL_CENTER);
style.setWrapText(true);
((XSSFWorkbook) wb).getStylesSource().getCTStylesheet().addNewCellStyles().addNewCellStyle().setXfId(0);
((XSSFCellStyle)style).getStyleXf().addNewAlignment().setVertical(
org.openxmlformats.schemas.spreadsheetml.x2006.main.STVerticalAlignment.CENTER);
((XSSFCellStyle)style).getStyleXf().getAlignment().setWrapText(true);
Sheet sheet = wb.createSheet();
Row row = sheet.createRow(0);
Cell cell = row.createCell(0);
cell.setCellValue("test");
FileOutputStream os = new FileOutputStream("ExcelDefaultCellStyle.xlsx");
wb.write(os);
os.close();
} catch (IOException ioex) {
}
}
}
This will change the default cell style of all cells in the whole workbook.
The XML in styles.xml shows:
<cellStyleXfs count="1">
<xf numFmtId="0" fontId="0" fillId="0" borderId="0">
<alignment vertical="center" wrapText="true"/>
</xf>
</cellStyleXfs>
<cellXfs count="1">
<xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0">
<alignment vertical="center" wrapText="true"/>
</xf>
</cellXfs>
<cellStyles>
<cellStyle xfId="0"/>
</cellStyles>
As you see the normal cell style is the first one in cellStyles. It refers to xfId="0" which refers to numFmtId="0" fontId="0" fillId="0" borderId="0". That means the very first definitions of number format, font, fill format and border is used in normal cell style.
Related
I have posed a question for copying Excel worksheet contents to a different workbook using Apache POI, while preserving formatting. I have received a good suggestion, which I have gone ahead and implemented (see below). The idea is to copy from the source worksheet to the destination worksheet, row by row.
Initially I got the following exception:
java.lang.IllegalArgumentException: This Style does not belong to the supplied
Workbook Styles Source. Are you trying to assign a style from one workbook to
the cell of a different workbook?
I have tried to fix it but I am now getting the following exception:
java.lang.IllegalArgumentException: Can only clone from one XSSFCellStyle to
another, not between HSSFCellStyle and XSSFCellStyle
This exception is a little weird because I am not using HSSFCellStyle.
Here is my source code:
import java.io.File;
import java.io.IOException;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.ss.usermodel.CellCopyPolicy;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.xssf.model.StylesTable;
import org.apache.poi.xssf.usermodel.XSSFCellStyle;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
public class ExternalXSSFSheetCopier {
// Create source and destination workbook objects, given the filenames
XSSFWorkbook srcWorkbook = new XSSFWorkbook(new File(srcFilename));
XSSFWorkbook destWorkbook = new XSSFWorkbook(new FileInputStream(destFilename));
// Destination workbook instantiated differently to get past the following exception:
// org.apache.poi.ooxml.POIXMLException: java.io.EOFException:
// Unexpected end of ZLIB input stream
// As per https://stackoverflow.com/a/54695626
// Instantiate the sheet objects
XSSFSheet srcSheet = srcWorkbook.getSheet(srcSheetname);
XSSFSheet destSheet = destWorkbook.createSheet(destSheetname);
// Instantiate CellCopyPolicy object and set all policies to true
CellCopyPolicy copyPolicy = new CellCopyPolicy();
copyPolicy.setCondenseRows(true);
copyPolicy.setCopyCellFormula(true);
copyPolicy.setCopyCellStyle(true);
copyPolicy.setCopyCellValue(true);
copyPolicy.setCopyHyperlink(true);
copyPolicy.setCopyMergedRegions(true);
copyPolicy.setCopyRowHeight(true);
copyPolicy.setMergeHyperlink(true);
// Iterate over the source sheet, row by row, and copy into the destination sheet
int destRowNum = 0;
for (Row srcRow: srcSheet) {
XSSFRow srcXSSFRow = (XSSFRow) srcRow;
XSSFRow destXSSFRow = destSheet.createRow(destRowNum++);
// Introduced the following block of code, as suggested by
// https://stackoverflow.com/questions/10773961/apache-poi-apply-one-style-to-different-workbooks
// to get past the following exception:
// java.lang.IllegalArgumentException: This Style does not belong to the supplied
// Workbook Styles Source. Are you trying to assign a style from one workbook to the
// cell of a different workbook?
XSSFCellStyle srcStyle = srcXSSFRow.getRowStyle();
XSSFCellStyle destStyle = new XSSFCellStyle(new StylesTable());
destStyle.cloneStyleFrom(srcStyle);
destXSSFRow.setRowStyle(destStyle);
// With this block of code we now get the following exception:
// java.lang.IllegalArgumentException: Can only clone from one XSSFCellStyle to
// another, not between HSSFCellStyle and XSSFCellStyle
destXSSFRow.copyRowFrom(srcXSSFRow, copyPolicy);
}
// Final cleanup
srcWorkbook.close();
FileOutputStream fos = new FileOutputStream(new File(destFilename));
destWorkbook.write(fos);
destWorkbook.close();
fos.close();
}
I try to change background color of a row, or highlight it with a different color with use of following code:
FileInputStream fis = new FileInputStream(src);
HSSFWorkbook wb = new HSSFWorkbook(fis);
HSSFSheet sheet = wb.getSheetAt(0);
r = sheet.getRow(5);
CellStyle style = wb.createCellStyle();
style.setFillForegroundColor(IndexedColors.RED.getIndex());
style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
r.setRowStyle(style);
FileOutputStream fileOut = new FileOutputStream(excelFileName);
wb.write(fileOut);
wb.close();
fileOut.flush();
fileOut.close();
I create a style, set it to a row and after that I write it out to same file. File is modified when I execute code, but background color isn't changed.
setRowStyle(CellStyle style) doesn't work as you would expect. Taking a look at the XSSFRow source code you will not find an iteration over the cells in the row or something similar.
/**
* Applies a whole-row cell styling to the row.
* If the value is null then the style information is removed,
* causing the cell to used the default workbook style.
*/
#Override
public void setRowStyle(CellStyle style) {
if(style == null) {
if(_row.isSetS()) {
_row.unsetS();
_row.unsetCustomFormat();
}
} else {
StylesTable styleSource = getSheet().getWorkbook().getStylesSource();
XSSFCellStyle xStyle = (XSSFCellStyle)style;
xStyle.verifyBelongsToStylesSource(styleSource);
long idx = styleSource.putStyle(xStyle);
_row.setS(idx);
_row.setCustomFormat(true);
}
}
To my knowledge it is more like setting a default row style. But even when you set a row style this way afterwards created cells in this row won't get this style. Most probably you will have to do the styling cell by cell.
Sorry my English not good.
My code: styledate
CreationHelper createHelper = workbook.getCreationHelper();
styledate.setDataFormat(
createHelper.createDataFormat().getFormat("d-mmm"));
When I create a excel file, the cell set styledate not display "16-Jun". It's "06/16/2018".
If I create input on excel file, it's ok "16-Jun".
I want when I create file, cell will display "16-Jun". Thank for your help.
Whether a cell style will work or not depends on the cell value set into the cell. Your creating of the style looks correct but you do not show how you are setting the cell value into the cell. The style can only work if the cell value is a date value. If it is a string value, then the style cannot work.
The following complete example shows the problem:
import java.io.FileOutputStream;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
public class CreateExcelCustomDateFormat {
public static void main(String[] args) throws Exception {
Workbook workbook = new XSSFWorkbook();
CreationHelper createHelper = workbook.getCreationHelper();
CellStyle styledate = workbook.createCellStyle();
styledate.setDataFormat(createHelper.createDataFormat().getFormat("d-MMM"));
Sheet sheet = workbook.createSheet();
Cell cell;
//This sets date value into cell and does formatting it.
cell = sheet.createRow(0).createCell(0);
cell.setCellValue(java.util.Date.from(java.time.Instant.now()));
cell.setCellStyle(styledate);
String date = "06/17/2018";
//This sets string value into cell. There the format will not work.
cell = sheet.createRow(1).createCell(0);
cell.setCellValue(date);
cell.setCellStyle(styledate);
//This converts string value to date and then sets the date into cell. There the format will work.
cell = sheet.createRow(2).createCell(0);
cell.setCellValue(java.util.Date.from(
java.time.LocalDate.parse(
date,
java.time.format.DateTimeFormatter.ofPattern("MM/dd/yyyy")
).atStartOfDay(java.time.ZoneId.systemDefault()).toOffsetDateTime().toInstant()
)); //yes, java.time is a monster in usage ;-)
cell.setCellStyle(styledate);
try (FileOutputStream fos = new FileOutputStream("CreateExcelCustomDateFormat.xlsx")) {
workbook.write(fos);
workbook.close();
}
}
}
I have to write an excel File where some rows and Columns are locked and the rest is editable. I know this was asked and answered beofre, but the latest answer is from 2012 and the solution given there doesn't work anymore. Can anyone who worked with this give a solution that works now?
This is the code that was given as solution
String file = "c:\\poitest.xlsx";
FileOutputStream outputStream = new FileOutputStream(file);
Workbook wb = new XSSFWorkbook();
CellStyle unlockedCellStyle = wb.createCellStyle();
unlockedCellStyle.setLocked(false);
Sheet sheet = wb.createSheet();
sheet.protectSheet("password");
Row row = sheet.createRow(0);
Cell cell = row.createCell(0);
cell.setCellValue("TEST");
cell.setCellStyle(unlockedCellStyle);
wb.write(outputStream);
outputStream.close();
The effect now is a sheet that is locked completely.
You wrote, you'd like to lock certain cells and the default should be unlocked, but your code actually unlocks a given cell.
So I go for your original request and have kind of a quick hack as I haven't found a decent method on a quick view to set a whole range of columns:
import java.io.FileOutputStream;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCol;
public class XSSFLockTest {
public static void main(String args[]) throws Exception {
XSSFWorkbook wb = new XSSFWorkbook();
CellStyle unlockedCellStyle = wb.createCellStyle();
unlockedCellStyle.setLocked(false);
CellStyle lockedCellStyle = wb.createCellStyle();
lockedCellStyle.setLocked(true);
XSSFSheet sheet = wb.createSheet();
CTCol col = sheet.getCTWorksheet().getColsArray(0).addNewCol();
col.setMin(1);
col.setMax(16384);
col.setWidth(9.15);
col.setStyle(unlockedCellStyle.getIndex());
sheet.protectSheet("password");
Row row = sheet.createRow(0);
Cell cell = row.createCell(0);
cell.setCellValue("TEST");
cell.setCellStyle(lockedCellStyle);
FileOutputStream outputStream = new FileOutputStream("bla.xlsx");
wb.write(outputStream);
outputStream.close();
}
}
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) {
}
}
}