How to create a Progress Bar within a cell using apache poi? - excel

I would like to create a progress bar within a Excel-sheet cell. I must use Apache Poi library, but I do not know how to even start. (Something like this, but using the Java library) http://www.tech-recipes.com/rx/35064/excel-2013-create-progress-bars/
I guess I must put a conditional formating, but I do know how it works and I can not find a solution anywhere ... somebody can help me out?
Thanks in advance.

As you suggested, I've used your link to create an example xlsx and simply recreated the necessary xml structures, i.e. open the xlsx file as zip archive and have a look at xl/worksheets/sheet1.xml. Beside the poi-ooxml.jar you'll need the ooxml-schemas-1.1.jar.
(tested with Libre Office 4.0, Excel Viewer 2010, POI 3.10-beta1)
import java.io.FileOutputStream;
import java.lang.reflect.*;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.*;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.*;
public class Databar {
public static void main(String[] args) throws Exception {
Workbook wb = new XSSFWorkbook();
Sheet sheet = wb.createSheet();
for (int i=0; i<4; i++) {
sheet.createRow(i).createCell(0).setCellValue(new int[]{12,38,93,42}[i]);
}
SheetConditionalFormatting cf = sheet.getSheetConditionalFormatting();
XSSFConditionalFormattingRule xcfrule =
(XSSFConditionalFormattingRule)cf.createConditionalFormattingRule("");
Method m = XSSFConditionalFormattingRule.class.getDeclaredMethod("getCTCfRule");
m.setAccessible(true);
CTCfRule cfRule = (CTCfRule)m.invoke(xcfrule);
cfRule.removeFormula(0); // cleanup
cfRule.setType(STCfType.DATA_BAR);
CTDataBar databar = cfRule.addNewDataBar();
CTCfvo vfoMin = databar.addNewCfvo();
vfoMin.setType(STCfvoType.NUM);
vfoMin.setVal("0");
CTCfvo vfoMax = databar.addNewCfvo();
vfoMax.setType(STCfvoType.NUM);
vfoMax.setVal("100");
CTColor color = databar.addNewColor();
color.setRgb(new byte[]{(byte)0xFF, 0x00, 0x00, (byte)0xFF});
CellRangeAddress cra[] = {new CellRangeAddress(0, 3, 0, 0)};
cf.addConditionalFormatting(cra, xcfrule);
FileOutputStream fos = new FileOutputStream("databar-out.xlsx");
wb.write(fos);
fos.close();
}
}

Related

Excel Sheet names in Apache POI (without "user" model) [duplicate]

I have the following code that I use to get the sheet names of an excel file(.xlsx)
XSSFWorkbook workBookXlsx = new XSSFWorkbook(new FileInputStream(pathToFile));
ArrayList<String> sheetNames = new ArrayList<>();
int numberOfSheets = workBookXlsx.getNumberOfSheets();
for (int i = 0; i < numberOfSheets; i++) {
sheetNames.add(workBookXlsx.getSheetAt(i).getSheetName());
}
workBookXlsx = null;
The issue I have with the above code is that it takes a lot of memory(~700MB) & a long time(5-6s) to create the XSSFWorkbook for a file of size 9MB. Even setting the workBookXlsx to null doesn't release the memory taken by the javaw(I know gc may or maynot be called & JVM wont release memory just because I have set a variable to null)
I did go through the documentation of Workbook, XSSFWorkbook & from what I understood, there is no method that will help me get the sheet names with low memory imprint.
The one solution I have found is to manually unzip the .xlsx file and read the contents of the .\xl\woorkbook.xml to get the sheet names and the r:id
Is there an API for getting the sheet names in an .xlsx file without large memory imprint?
To show what #Gagravarr probably meant with his comment:
The XSSFReader contains a method XSSFReader.getSheetsData which "Returns an Iterator which will let you get at all the different Sheets in turn. Each sheet's InputStream is only opened when fetched from the Iterator. It's up to you to close the InputStreams when done with each one.". But as often this is not the whole truth. In truth it returns a XSSFReader.SheetIterator which has a method XSSFReader.SheetIterator.getSheetName to get the sheet names.
Example:
import java.io.InputStream;
import java.io.FileInputStream;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.xssf.eventusermodel.XSSFReader;
import java.util.Iterator;
public class ExcelXSSFGetSheetNamesXSSFReader {
public static void main(String[] args) throws Exception {
OPCPackage pkg = OPCPackage.open(new FileInputStream("Example.xlsx"));
XSSFReader r = new XSSFReader( pkg );
Iterator<InputStream> sheets = r.getSheetsData();
if (sheets instanceof XSSFReader.SheetIterator) {
XSSFReader.SheetIterator sheetiterator = (XSSFReader.SheetIterator)sheets;
while (sheetiterator.hasNext()) {
InputStream dummy = sheetiterator.next();
System.out.println(sheetiterator.getSheetName());
dummy.close();
}
}
pkg.close();
}
}
Conclusion: Currently you cannot work with apache poi only by trusting the API documentation. Instead you must always have a look at the source code.

Example of using Excel built in function in Apache POI

All:
I am pretty new to Apache POI, I wonder if I want to use the builtin function from excel VBA, how can I do that?
For example:
In VBA, I can write something like:
Dim month as String
month = MonthName( Month( Sheets1.Range("C12") ) )
How can I use that function in Apache POI(I do not wanna give that formula to a cell and evaluate it)?
Thanks,
POI does not provide a VBA engine, so you cannot execute VBA with POI.
But there's a way to implement equivalent code in Java and use it with POI. The link below gives an example of how to do that.
https://poi.apache.org/spreadsheet/user-defined-functions.html
Apache poi cannot interpret VBA but of course it has a formula evaluator. So of course you can evaluate Excel formulas directly in apache poi code.
But the VBA function MonthName is not implemented by default. So either you do getting the date from the cell, what is clearly possible using apache poi. And then, you get the month and the month name out of that date using Java code. This is called first approach in following example.
Or you are using a implemented Excel function. TEXT for example. This is called second approach in following example.
import java.io.FileInputStream;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.*;
import org.apache.poi.ss.formula.WorkbookEvaluator;
import org.apache.poi.ss.formula.eval.*;
public class ExcelEvaluateMonthFunctions{
public static void main(String[] args) throws Exception {
Workbook workbook = WorkbookFactory.create(new FileInputStream("workbook.xlsx"));
Sheet sheet = workbook.getSheet("Sheet2");
java.util.Locale.setDefault(java.util.Locale.US);
//first approach:
String monthname = null;
java.util.Date date = sheet.getRow(11).getCell(2).getDateCellValue(); //Sheet2!C12 must contain a date
java.time.LocalDate localDate = date.toInstant().atZone(java.time.ZoneId.systemDefault()).toLocalDate();
monthname = localDate.getMonth().getDisplayName(java.time.format.TextStyle.FULL, java.util.Locale.getDefault());
System.out.println(monthname);
//second approach:
monthname = null;
CreationHelper helper = workbook.getCreationHelper();
XSSFFormulaEvaluator formulaevaluator = (XSSFFormulaEvaluator)helper.createFormulaEvaluator();
WorkbookEvaluator workbookevaluator = formulaevaluator._getWorkbookEvaluator();
ValueEval valueeval = null;
valueeval = workbookevaluator.evaluate("TEXT(" + sheet.getSheetName() +"!C12, \"MMMM\")", null); //Sheet2!C12 must contain a date
if (valueeval instanceof StringValueEval) {
monthname = ((StringValueEval)valueeval).getStringValue();
}
System.out.println(monthname);
workbook.close();
}
}

Write image in excel at row 3000 to 3005 in java using apache poi when using LibreOffice Calc to open file

I am using poi 3.9 to create xlsx files everything is fine until my file length crossed 1600 rows. After crossing 1600 rows I am able to write data but I am unable to write images all images get appended on each other at row number 1640 this is weird and I am working on poi from long time and picked the issue that its library limitation and updated my poi to 3.15 but same issue, here I am able to write images up to 2000 rows then I tried poi 3.16 but again issue is same but here I can write Images up to 2500 rows.
Below is my code for writing images
private void drawImageOnExcelSheet(XSSFSheet sitePhotosSheet, int row1,
int row2, int col1, int col2, String fileName) {
try {
InputStream is = new FileInputStream(fileName);
byte[] bytes = IOUtils.toByteArray(is);
int pictureIdx = sitePhotosSheet.getWorkbook().addPicture(bytes,Workbook.PICTURE_TYPE_JPEG);
is.close();
CreationHelper helper = sitePhotosSheet.getWorkbook().getCreationHelper();
Drawing drawing = sitePhotosSheet.createDrawingPatriarch();
ClientAnchor anchor = helper.createClientAnchor();
anchor.setAnchorType(AnchorType.MOVE_AND_RESIZE);
anchor.setCol1(col1);
anchor.setCol2(col2);
anchor.setRow1(row1);
anchor.setRow2(row2);
drawing.createPicture(anchor, pictureIdx);
} catch(Exception e) {
e.printStackTrace();
}
Note: that I am able to write data but getting issue only for images. Please suggest how can I solve this issue.
Please see image below here you can see that at row 1639 two images are appended on each other and there are many images behind these two ones since my last row for images that I have printed on console is 3400.
Using apache poi version 3.16 and Java 8 (I'm not a friend of ancient software versions)
Having following code:
import java.io.*;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.*;
import org.apache.poi.ss.usermodel.ClientAnchor.AnchorType;
import org.apache.poi.util.IOUtils;
public class ExcelDrawImage {
private static void drawImageOnExcelSheet(XSSFSheet sitePhotosSheet, int row1,
int row2, int col1, int col2, String fileName) {
try {
InputStream is = new FileInputStream(fileName);
byte[] bytes = IOUtils.toByteArray(is);
int pictureIdx = sitePhotosSheet.getWorkbook().addPicture(bytes,Workbook.PICTURE_TYPE_JPEG);
is.close();
CreationHelper helper = sitePhotosSheet.getWorkbook().getCreationHelper();
Drawing drawing = sitePhotosSheet.createDrawingPatriarch();
ClientAnchor anchor = helper.createClientAnchor();
anchor.setAnchorType(AnchorType.MOVE_AND_RESIZE);
anchor.setCol1(col1);
anchor.setCol2(col2);
anchor.setRow1(row1);
anchor.setRow2(row2);
drawing.createPicture(anchor, pictureIdx);
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws Exception {
Workbook wb = new XSSFWorkbook();
Sheet sheet = wb.createSheet();
for (int r = 0; r < 10000; r+=10 ) {
sheet.createRow(r).createCell(1).setCellValue("Picture " + (r/10+1) + ":");
drawImageOnExcelSheet((XSSFSheet)sheet, r+1, r+6, 1, 4, "samplePict.jpeg");
}
wb.write(new FileOutputStream("ExcelDrawImage.xlsx"));
wb.close();
}
}
Result:
Same in Ubuntu Linux:
Result in Calc:

dropdown Validation not working if it exceeds 50 rows in the Export To Excel

I am generating Excel File(.xlsx) using apache poi jar (poi-ooxml-3.9.jar), I added dropdown validation for 10 columns in my excel file, If I generate the Excel File with 50 rows, drop down validation is working. If it exceeds more than 50 rows, drop down validation is not coming in the Excel File, When I open the excel File I get the message as "We found a problem with some content in fileName.xlsx. Do you want us to try to recover as much as we can ? If you trust the source of this workbook, click Yes ". when click on Yes, all the dropdown validation it is removing. Kindly need solution to fix this issue.
Do not create DataValidationConstraint for each single cell but only for each varying list you need. Then create DataValidation using those DataValidationConstraint for continuous CellRangeAddressList which are as big as possible and also are not all single cells.
Example creates ten different list validations for column 1 to 10 in rows 1 to 10000.
import java.io.*;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.*;
import org.apache.poi.hssf.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddressList;
class DataValidationList {
public static void main(String[] args) throws Exception {
Workbook workbook = new XSSFWorkbook(); // or new HSSFWorkbook
Sheet sheet = workbook.createSheet("Data Validation");
DataValidationHelper dvHelper = sheet.getDataValidationHelper();
for (int col = 0; col < 10; col++) {
DataValidationConstraint dvConstraint = dvHelper.createExplicitListConstraint(
new String[]{"Col "+(col+1)+" one","Col "+(col+1)+" two","Col "+(col+1)+" three"});
CellRangeAddressList addressList = new CellRangeAddressList(0, 9999, 0, col);
DataValidation validation = dvHelper.createValidation(
dvConstraint, addressList);
if(validation instanceof XSSFDataValidation) {
validation.setSuppressDropDownArrow(true);
validation.setShowErrorBox(true);
}
else {
validation.setSuppressDropDownArrow(false);
}
sheet.addValidationData(validation);
}
String filename;
if(workbook instanceof XSSFWorkbook) {
filename = "DataValidationList.xlsx";
} else {
filename = "DataValidationList.xls";
}
FileOutputStream out = new FileOutputStream(filename);
workbook.write(out);
out.close();
workbook.close();
}
}

How to change only one style property in xssf by keeping all the other properties same

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.

Resources