I want to make the header row of my excel uneditable using poi.
I got various solutions around the internet which say to firstly do sheet.protectSheet("password") which ultimately makes entire sheet uneditable and then loop through all cells which can be editable and set cellStyle for them as cellStyle.setLocked(false).
In my case since the excel just contains headers and rest of the rows will be filled up by the user I can't make the entire sheet uneditable, I just want the headers to be uneditable by the user. How can I achieve this?
Using XSSF the following could be achieved:
Set a CellStyle having setLocked false as the default style for all columns. This is possible by setting a org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCol element having min col 1 and max col 16384 having set that style.
Then take out row 1 from using that style by setting CustomFormat true for that row. So it does not use the default style for all columns. Additional set a CellStyle having setLocked true as the default style for that row. This is possible by getting a org.openxmlformats.schemas.spreadsheetml.x2006.main.CTRow element from that row and set CustomFormat and S(style) there.
Result: All cells are unlocked except row 1.
Example:
import java.io.*;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.*;
public class CreateExcelSheetProtectOnlyFirstRow {
public static void main(String[] args) throws Exception {
Workbook workbook = new XSSFWorkbook();
//create a CellStyle having setLocked false
CellStyle cellstyleUnprotect = workbook.createCellStyle();
cellstyleUnprotect.setLocked(false);
//create a CellStyle having setLocked true
CellStyle cellstyleProtect = workbook.createCellStyle();
cellstyleProtect.setLocked(true);
Sheet sheet = workbook.createSheet("Sheet1");
//set the CellStyle having setLocked false as the default style for all columns
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(cellstyleUnprotect.getIndex());
Row row = sheet.createRow(0);
//set CustomFormat true for that row
//so it does not using the default style for all columns
//and set the CellStyle having setLocked true as the default style for that row
org.openxmlformats.schemas.spreadsheetml.x2006.main.CTRow cTRow =
((XSSFRow)row).getCTRow();
cTRow.setCustomFormat(true);
cTRow.setS(cellstyleProtect.getIndex());
for (int c = 0; c < 3; c++) {
row.createCell(c).setCellValue("Header " + (c+1));
}
sheet.protectSheet("password"); // protect sheet
workbook.write(new FileOutputStream("CreateExcelSheetProtectOnlyFirstRow.xlsx"));
workbook.close();
}
}
Related
I am generating excel sheets using Apache POI. When selecting a cell to edit the background of that cell becomes black. I have shared my code and a screenshot of the excel sheet. I didn't set the background to black from anywhere. I have struggled to solve this error for several hours. If anyone know how to solve this please help me.
public static void writeSummaryToExcel(int rowNumber, int columnNumber, String workBookName, String sheetName,
int sheetColumnSize, Map<String, List<CommonDto>> appDataMap,
List<CommonDto> summaryData) {
log.info("AppSummarySheetGenerator - writeSummaryToExcel() called");
PropertyTemplate propertyTemplate = new PropertyTemplate();
int rowCount = rowNumber;
int columnCount = columnNumber;
try {
File file = new File(workBookName);
// initialing workbook and sheet
workbook = CommonExcelUtils.getWorkbook(file, workBookName);
Sheet sheet = CommonExcelUtils.getWorkbookSheet(workbook, sheetName, sheetColumnSize, false);
Row row;
Cell cell;
if (workbook != null && sheet != null) {
//sheet setting up to first sheet
workbook.setSheetOrder(sheet.getSheetName(), 0);
// cell styles
CellStyle fontRightAlignStyle = workbook.createCellStyle();
fontRightAlignStyle.setAlignment(HorizontalAlignment.RIGHT);
Font boldFont = workbook.createFont();
boldFont.setBold(true);
boldFont.setFontHeightInPoints((short) 12);
CellStyle totalLCellStyle = getCommonTotalCellStyle(boldFont);
totalLCellStyle.setAlignment(HorizontalAlignment.LEFT);
CellStyle totalRCellStyle = getCommonTotalCellStyle(boldFont);
totalRCellStyle.setAlignment(HorizontalAlignment.RIGHT);
CellStyle appCategoryStyle = workbook.createCellStyle();
appCategoryStyle.setFont(boldFont);
appCategoryStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
appCategoryStyle.setFillForegroundColor(IndexedColors.LEMON_CHIFFON.index);
appCategoryStyle.setAlignment(HorizontalAlignment.CENTER_SELECTION);
//other code
}
private static CellStyle getCommonTotalCellStyle(Font boldFont) {
CellStyle commonTotalCellStyle = workbook.createCellStyle();
commonTotalCellStyle.setFont(boldFont);
commonTotalCellStyle.setBorderBottom(BorderStyle.MEDIUM);
commonTotalCellStyle.setBorderTop(BorderStyle.MEDIUM);
commonTotalCellStyle.setBorderLeft(BorderStyle.MEDIUM);
commonTotalCellStyle.setBorderRight(BorderStyle.MEDIUM);
commonTotalCellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
commonTotalCellStyle.setFillForegroundColor(IndexedColors.YELLOW.index);
return commonTotalCellStyle;
}
Screenshot of the excel sheet
The only reason I am aware of that this may happen is if there is a cell style applied to the cell which has a fill background color set but either not has a fill pattern set or has a no-fill-pattern set. Then the fill background color gets visible if the cell is in edit mode.
Complete example to test:
import java.io.FileOutputStream;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
class CreateExcelCellBackgroudColor {
public static void main(String[] args) throws Exception {
try (Workbook workbook = new XSSFWorkbook();
FileOutputStream fileout = new FileOutputStream("./Excel.xlsx") ) {
CellStyle style = workbook.createCellStyle();
//style.setFillForegroundColor(IndexedColors.YELLOW.getIndex());
//style.setFillBackgroundColor(IndexedColors.BLACK.getIndex());
style.setFillBackgroundColor((short)0);
//style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
style.setFillPattern(FillPatternType.NO_FILL);
Sheet sheet = workbook.createSheet();
Row row = sheet.createRow(0);
Cell cell = row.createCell(0);
cell.setCellStyle(style);
workbook.write(fileout);
}
}
}
Here cell A1 gets black when in edit mode.
Why is that?
In Excel the cell interiors have pattern fill. There fill foreground color is the color of the pattern and fill background color is the color behind the pattern. If there is a fill background color set but not a pattern, then the background color may get visible.
So the problem is not in the code you have shown. Have a look whether you set CellStyle.setFillBackgroundColor somewhere. But maybe the problem is in the source file already. If so, then the source file probably is not created by Excel.
Title says it all -- need to hide all rows and columns that are outside of the the rows and columns containing my data.
I have tried several options:
How to hide the following Un-used rows in Excel sheet using Java Apache POI?
Permanently Delete Empty Rows Apache POI using JAVA in Excel Sheet
How to hide the following Un-used rows in Excel sheet using Java Apache POI?
But these never produce the desired effect. I'm using apache poi version 4.1.1
See the following screenshots showing the excel format I have versus the format I want. (Since I am new on stackoverflow, it doesn't allow me to embed the pictures directly. Weird I know.)
What I have
What I need
Hiding unused rows and columns is not provided by high level classes of apache poi until now.
Hiding unused rows is a setting in sheet format properties of Office Open XML, the format of XSSF (*.xlsx). There is defined how to handle rows per default. For example default row height. But there also can be set that rows are zero height per default. So only used rows, which have cells having content or format are visible. As apache poi does not have a method to set SheetFormatPr.setZeroHeight we need using the underlaying org.openxmlformats.schemas.spreadsheetml.x2006.main.* classes.
In binary BIFF format of HSSF (*.xls) hiding unused rows is a setting in DEFAULTROWHEIGHT record within the worksheet's record stream. There option flags can be set. Option flag 0x0002 means hiding unused rows. To set that using apache poi we need access to the org.apache.poi.hssf.record.DefaultRowHeightRecord. This only can be got from InternalSheet.
Hiding columns could be done using Sheet.setColumnHidden, but only for single columns. So to hide 100 columns one needs calling Sheet.setColumnHidden 100 times.
Excel also provides settings for column ranges from min column to max column. But Apache poi does not providing high level methods for this.
Using XSSF (Office Open XML) we need the org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCols to get or set a org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCol having the appropriate min and max and setHidden(true).
Using HSSF (BIFF) we need get or set the COLINFOrecord from/to the the worksheet's record stream which has the appropriate min and max and and setHidden(true).
The following complete example shows the code sample for the above. It uses ExcelExampleIn.xlsx or ExcelExampleIn.xls as input and sets unused rows hidden and sets columns hidden from given min to max column.
Tested and works using apache poi 4.1.1.
import java.io.FileInputStream;
import java.io.FileOutputStream;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.*;
import org.apache.poi.hssf.usermodel.*;
import org.apache.poi.hssf.model.InternalSheet;
import org.apache.poi.hssf.record.DefaultRowHeightRecord;
import org.apache.poi.hssf.record.ColumnInfoRecord;
import org.apache.poi.hssf.record.RecordBase;
import java.util.List;
public class ExcelHideUnusedRowsAndColumns {
static void setUnusedRowsHidden(Sheet sheet) throws Exception {
if (sheet instanceof XSSFSheet) {
// in OOXML set zeroHeight property true for all undefined rows, so only rows having special settings are visible
XSSFSheet xssfSheet = (XSSFSheet)sheet;
org.openxmlformats.schemas.spreadsheetml.x2006.main.CTWorksheet ctWorksheet = xssfSheet.getCTWorksheet();
org.openxmlformats.schemas.spreadsheetml.x2006.main.CTSheetFormatPr ctSheetFormatPr = ctWorksheet.getSheetFormatPr();
if (ctSheetFormatPr == null) ctSheetFormatPr = ctWorksheet.addNewSheetFormatPr();
ctSheetFormatPr.setZeroHeight(true);
} else if (sheet instanceof HSSFSheet) {
// in BIFF file format set option flag 0x0002 in DEFAULTROWHEIGHT record
HSSFSheet hssfSheet= (HSSFSheet)sheet;
java.lang.reflect.Field _sheet = HSSFSheet.class.getDeclaredField("_sheet");
_sheet.setAccessible(true);
InternalSheet internalSheet = (InternalSheet)_sheet.get(hssfSheet);
java.lang.reflect.Field defaultrowheight = InternalSheet.class.getDeclaredField("defaultrowheight");
defaultrowheight.setAccessible(true);
DefaultRowHeightRecord defaultRowHeightRecord = (DefaultRowHeightRecord)defaultrowheight.get(internalSheet);
defaultRowHeightRecord.setOptionFlags((short)2);
}
}
static void setColumnsHidden(Sheet sheet, int min, int max) throws Exception {
if (sheet instanceof XSSFSheet) {
// respect max column count 16384 (1 to 16384) for OOXML
if (max > 16384) max = 16384;
// in OOXML set cols settings in XML
XSSFSheet xssfSheet = (XSSFSheet)sheet;
org.openxmlformats.schemas.spreadsheetml.x2006.main.CTWorksheet ctWorksheet = xssfSheet.getCTWorksheet();
org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCols ctCols = ctWorksheet.getColsArray(0);
boolean colSettingFound = false;
for (org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCol ctCol : ctCols.getColList()) {
if (ctCol.getMin() == min && ctCol.getMax() == max) {
colSettingFound = true;
ctCol.setHidden(true);
}
System.out.println(ctCol);
}
if (!colSettingFound) {
org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCol ctCol = ctCols.addNewCol();
ctCol.setMin(min);
ctCol.setMax(max);
ctCol.setHidden(true);
System.out.println(ctCol);
}
} else if (sheet instanceof HSSFSheet) {
// in BIFF min and max are 0-based
min = min -1;
max = max -1;
// respect max column count 256 (0 to 255) for BIFF
if (max > 255) max = 255;
// in BIFF file format set hidden property in COLINFO record
HSSFSheet hssfSheet= (HSSFSheet)sheet;
java.lang.reflect.Field _sheet = HSSFSheet.class.getDeclaredField("_sheet");
_sheet.setAccessible(true);
InternalSheet internalSheet = (InternalSheet)_sheet.get(hssfSheet);
java.lang.reflect.Field _records = InternalSheet.class.getDeclaredField("_records");
_records.setAccessible(true);
#SuppressWarnings("unchecked")
List<RecordBase> records = (List<RecordBase>)_records.get(internalSheet);
boolean colInfoFound = false;
for (RecordBase record : records) {
if (record instanceof ColumnInfoRecord) {
ColumnInfoRecord columnInfoRecord = (ColumnInfoRecord)record;
if (columnInfoRecord.getFirstColumn() == min && columnInfoRecord.getLastColumn() == max) {
colInfoFound = true;
columnInfoRecord.setHidden(true);
}
System.out.println(columnInfoRecord);
}
}
if (!colInfoFound) {
ColumnInfoRecord columnInfoRecord = new ColumnInfoRecord();
columnInfoRecord.setFirstColumn(min);
columnInfoRecord.setLastColumn(max);
columnInfoRecord.setHidden(true);
records.add(records.size()-1, columnInfoRecord);
System.out.println(columnInfoRecord);
}
}
}
public static void main(String[] args) throws Exception {
String inFilePath = "./ExcelExampleIn.xlsx"; String outFilePath = "./ExcelExampleOut.xlsx";
//String inFilePath = "./ExcelExampleIn.xls"; String outFilePath = "./ExcelExampleOut.xls";
try (Workbook workbook = WorkbookFactory.create(new FileInputStream(inFilePath));
FileOutputStream out = new FileOutputStream(outFilePath ) ) {
Sheet sheet = workbook.getSheetAt(0);
//set unused rows hidden
setUnusedRowsHidden(sheet);
//set multiple columns hidden, here column 7 (G) to last column 16384 (XFD)
setColumnsHidden(sheet, 7, 16384);
workbook.write(out);
}
}
}
Mark the first "outside" column, hold CTRL + SHIFT and then right arrow. Then, all columns should be highlighted. Right click, select "Hide".
Repeat the same with rows, select the first row outside of your data, hold CTRL + SHIFT and press Arrow Down.
Best of luck! ^_^
In my case I need to protect single cell, to achieve this initially I used HSSFCellStyle and setLocked(true) and protect the sheet using Sheet.protectSheet("password"). This will protect non empty cell also so I am using DataValidation with single option, It is working as expected but it allows to delete the cell content without validation.Below is my sample code.Thanks in advance for your help.
String errorBoxTitle = "Warning";
String errorBoxMessage = "Invalid Data";
String [] valueArr = {"cellValue"};
CellRangeAddressList cellValueAddress = new CellRangeAddressList(row.getRowNum(), row.getRowNum(), cell.getColumnIndex(), cell.getColumnIndex());
DVConstraint cellValueConstraint = DVConstraint.createExplicitListConstraint(valueArr);
DataValidation cellValueValidation = new HSSFDataValidation(cellValueAddress , cellValueConstraint );
cellValueValidation .setSuppressDropDownArrow(true);
cellValueValidation .createErrorBox(errorBoxTitle, errorBoxMessage);
cellValueValidation .setEmptyCellAllowed(false);
sheet.addValidationData(cellValueValidation );
A cell either can be locked or not locked. If it is locked, then it cannot be changed and also not be deleted. If a cell is not locked, then of course it also can be deleted. So since data validation needs to be used in not locked cells, data validation is not an option to protect against deleting.
If the goal is to have only some cells locked when the sheet is protected but most of the cells shall be not locked, then the only way is creating a cell style having setLocked(false) set and applying that cell style to all cells which shall be not locked. That is because it is the default in Excel that cells are locked when the sheet is protected.
If new cells in whole columns shall be not locked, then this notLocked cell style can be set as the default column style.
In the following example only the header cells A1:C1 and all cells in columns greater than C are locked. The cells in A2:C4 are not locked because the notLocked cell style is applied to that cells. Also the empty cells in columns A:C for rows greater than 4 are not locked because the notLocked cell style is applied as the default column style for columns A:C.
import java.io.FileOutputStream;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.poi.ss.usermodel.*;
public class CreateExcelDefaultColumnStyleNotLocked {
public static void main(String[] args) throws Exception {
//Workbook workbook = new XSSFWorkbook(); String filePath = "./CreateExcelDefaultColumnStyleNotLocked.xlsx";
Workbook workbook = new HSSFWorkbook(); String filePath = "./CreateExcelDefaultColumnStyleNotLocked.xls";
CellStyle notLocked = workbook.createCellStyle();
notLocked.setLocked(false);
Sheet sheet = workbook.createSheet();
Row row = sheet.createRow(0);
Cell cell = null;
for (int c = 0; c < 3; c++) {
cell = row.createCell(c);
cell.setCellValue("Col " + (c+1));
}
for (int r = 1; r < 4; r++) {
row = sheet.createRow(r);
for (int c = 0; c < 3; c++) {
cell = row.createCell(c);
cell.setCellValue(r * (c+1));
cell.setCellStyle(notLocked);
}
}
sheet.setDefaultColumnStyle(0, notLocked);
sheet.setDefaultColumnStyle(1, notLocked);
sheet.setDefaultColumnStyle(2, notLocked);
sheet.protectSheet("");
FileOutputStream out = new FileOutputStream(filePath);
workbook.write(out);
out.close();
workbook.close();
}
}
How to change only font color of one cell without altering the workbooks previous style properties. Please look at the attachment "workbook" for clear understanding. The column delta contribution font color should be changed but its background style properties should not be altered.
EDIT:
I have changed the code.
The columns rank and mean contribution in the template have a predefined design of some alternate colors which are set in the excel itself. The template is designed by my team and I am afraid I can't change it from Java.
My work is to populate the last column Delta Contribution whose background styles should be same as the total sheet provided the color change according to the conditions.
String deltaContribution = line.getDeltaContribution() != null
? Double.parseDouble(line.getDeltaContribution()) + "" : "";
if (!deltaContribution.equals("")) {
XSSFCell cell = (XSSFCell) row.getCell(8);
XSSFCellStyle style = cell.getCellStyle();
XSSFFont redFont = style.getFont();
XSSFFont blueFont = style.getFont();
XSSFFont greenFont = style.getFont();
if(Double.parseDouble(deltaContribution) >= 0.20) {
redFont.setColor(IndexedColors.RED.getIndex());
CellUtil.setFont(cell, workbook, redFont);
//log.info("The colour is " + colour.getARGBHex());
}
else if(Double.parseDouble(deltaContribution) <= -0.20) {
greenFont.setColor(IndexedColors.GREEN.getIndex());
CellUtil.setFont(cell, workbook, greenFont);
//log.info("The colour is " + colour.getARGBHex());
}
else {
blueFont.setColor(IndexedColors.BLUE.getIndex());
CellUtil.setFont(cell, workbook, blueFont);
//log.info("The colour is " + colour.getARGBHex());
}
row.getCell(8).setCellValue(line.getDeltaContribution() != null
? formatDecimalPlaces(line.getDeltaContribution()) : "");
}
I should not change the previous styles applied to the sheet, I should just edit one property of style. After changing the code, whole column is populated with green color.Last column
workbook:
There are two approaches.
First approach is using conditional formatting. This is my preferred approach.
Example:
import org.apache.poi.xssf.usermodel.*;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import java.io.FileOutputStream;
class ConditionalFormattingCellValues {
public static void main(String[] args) throws Exception {
Workbook wb = new XSSFWorkbook();
Sheet sheet = wb.createSheet("Sheet1");
SheetConditionalFormatting sheetCF = sheet.getSheetConditionalFormatting();
ConditionalFormattingRule cfRule2 = sheetCF.createConditionalFormattingRule(ComparisonOperator.GE, "0.20");
FontFormatting fontFormatting = cfRule2.createFontFormatting();
fontFormatting.setFontStyle(false, false);
fontFormatting.setFontColorIndex(IndexedColors.RED.index);
ConditionalFormattingRule cfRule1 = sheetCF.createConditionalFormattingRule(ComparisonOperator.LT, "0.20");
fontFormatting = cfRule1.createFontFormatting();
fontFormatting.setFontStyle(false, false);
fontFormatting.setFontColorIndex(IndexedColors.BLUE.index);
ConditionalFormattingRule [] cfRules = {cfRule1, cfRule2};
CellRangeAddress[] regions = {CellRangeAddress.valueOf("I2:I10")};
sheetCF.addConditionalFormatting(regions, cfRules);
for (int r = 1; r < 10; r++) {
Row row = sheet.createRow(r);
Cell cell = row.createCell(8);
cell.setCellValue(1d/Math.sqrt(r)-0.2);
}
FileOutputStream fileOut = new FileOutputStream("ConditionalFormattingCellValues.xlsx");
wb.write(fileOut);
wb.close();
}
}
Second approach is using CellUtil. This provides "Various utility functions that make working with a cells and rows easier. The various methods that deal with style's allow you to create your CellStyles as you need them. When you apply a style change to a cell, the code will attempt to see if a style already exists that meets your needs. If not, then it will create a new style. This is to prevent creating too many styles. there is an upper limit in Excel on the number of styles that can be supported."
Example:
import org.apache.poi.xssf.usermodel.*;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellUtil;
import java.io.FileOutputStream;
class DirectlyFormattingCellValues {
public static void main(String[] args) throws Exception {
Workbook wb = new XSSFWorkbook();
Font redFont = wb.createFont();
redFont.setColor(IndexedColors.RED.getIndex());
Font blueFont = wb.createFont();
blueFont.setColor(IndexedColors.BLUE.getIndex());
Sheet sheet = wb.createSheet("Sheet1");
for (int r = 1; r < 10; r++) {
Row row = sheet.createRow(r);
Cell cell = row.createCell(8);
String deltaContribution = String.valueOf(1d/Math.sqrt(r)-0.2);
if(Double.parseDouble(deltaContribution)>=0.20) {
CellUtil.setFont(cell, redFont);
} else {
CellUtil.setFont(cell, blueFont);
}
cell.setCellValue(Double.valueOf(deltaContribution));
}
FileOutputStream fileOut = new FileOutputStream("DirectlyFormattingCellValues.xlsx");
wb.write(fileOut);
wb.close();
}
}
As said already, using conditional formatting should be preferred.
But according your screen-shot all your numbers seems to be text strings instead of really numeric values. This should be avoided since Excel cannot using such text strings in formula calculation. And without changing that, only second approach will be usable since conditional formatting also needs really numeric values for comparison.
I need to store numeric in number category (right click->Catergory=number). I have tried using the below code, but it saves in general format.
String valueAsString = "2345";
HSSFCell cellE1 = row1.createCell((short) 4);
cellE1.setCellValue(new BigDecimal(valueAsString).doubleValue());
You need to set a cell style to the cell, which formats it as you want. Something like
Worbook wb = new HSSFWorkbook();
DataFormat fmts = wb.getCreationHelper().createDataFormat();
// Cell Styles apply to the whole workbook, only create once
CellStyle numericStyle = wb.createCellStyle();
numericStyle.setDataFormat(fmts.getFormat("0")); // Format string
....
// Apply to the cells
Row r = sheet.createRow(0);
Cell c = r.createCell(0); // A1
c.setCellStyle(numericStyle);
c.setCellValue(Double.parseDouble("12345"));