Number and cell Formatting in apache poi - apache-poi

I am creating excel sheet using apache poi. I have numbers like - 337499.939437217, which I want to show as it is in excel without rounding off. Also the cell format should be number (for some columns) and currency (for some columns).
Please suggest which BuiltinFormats should I use to achieve this.
Many thanks for the help.

At first you need to know how to use DataFormats. Then you need to know the guidelines for customizing a number format.
For your number -337499.939437217 which will be displayed rounded with general number format, you could use format #.###############. The # means a digit which will be displayed only if needed (is not leading zero and/or is not zero as last decimal digit) - see guidelines. So the whole format means show up to 15 decimal digits if needed but only as much as needed.
For currency you should really using a built in number format for currency. So the currency symbol depends on the locale settings of Excel. The following BuiltinFormats are usable with apache poi. Using a built in number format you need only the hexadecimal format numbers.
Example:
import java.io.*;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
public class CreateNumberFormats {
public static void main(String[] args) throws Exception {
Workbook wb = new XSSFWorkbook();
Sheet sheet = wb.createSheet("format sheet");
CellStyle style;
DataFormat format = wb.createDataFormat();
Row row;
Cell cell;
short rowNum = 0;
short colNum = 0;
row = sheet.createRow(rowNum++);
cell = row.createCell(colNum);
cell.setCellValue(-337499.939437217); // general format
style = wb.createCellStyle();
style.setDataFormat(format.getFormat("#.###############")); // custom number format
row = sheet.createRow(rowNum++);
cell = row.createCell(colNum);
cell.setCellValue(-337499.939437217);
cell.setCellStyle(style);
row = sheet.createRow(rowNum++);
cell = row.createCell(colNum);
cell.setCellValue(123.456789012345);
cell.setCellStyle(style);
row = sheet.createRow(rowNum++);
cell = row.createCell(colNum);
cell.setCellValue(123456789.012345);
cell.setCellStyle(style);
style = wb.createCellStyle();
style.setDataFormat((short)0x7); // builtin currency format
row = sheet.createRow(rowNum++);
cell = row.createCell(colNum);
cell.setCellValue(-1234.5678);
cell.setCellStyle(style);
sheet.autoSizeColumn(0);
FileOutputStream fileOut = new FileOutputStream("CreateNumberFormats.xlsx");
wb.write(fileOut);
fileOut.close();
wb.close();
}
}

Related

Read excel columns as string even it contains numeric value without formatting using apache-poi

I need help about an issue that try to reading excel values as it looks. Let me explain with example,
In excel my rows with first column (mixed with string values),
10,4
10,2
11.5 //some columns using . notation
someString
When I try to read values using apache-poi
outputs: (it replaces "," with "." not wanted)
10.4
10.2
11.5
someString
it should give excatly same output. if it use ".", output should "10.4 if it use "," it should "10,4"
Even I format cells the column as text from excel, still get same output.
I don't want to use string replace because input might be 10.4 with real dot.
Here as example code I am using,
Sheet firstSheet = excel.getSheetAt(0);
for (Row s : firstSheet) {
Iterator<Cell> cellIterator = s.iterator();
while (cellIterator.hasNext()) {
Cell currentCell = cellIterator.next();
if (currentCell.getCellType() == CellType.STRING) {
System.out.print(currentCell.getStringCellValue() + "--");
} else if (currentCell.getCellType() == CellType.NUMERIC) {
System.out.print(currentCell.getNumericCellValue() + "--");
String textValue = NumberToTextConverter.toText(currentCell.getNumericCellValue());
}
}
}
Notes:
Apache-poi version: 4.0
Java: 8.0
Thanks in advance
If the need is to get all cell values as string, then you should not get the cell values by CellType. Instead you should using apache poi's DataFormatter. The DataFormatter can have set a Locale. This Locale then determines using what decimal delimiters and thousands separators numeric values are shown.
Example:
import org.apache.poi.ss.usermodel.*;
import java.io.FileInputStream;
import java.util.Locale;
class GetDataFromExcelUsingDataFormatter {
public static void main(String[] args) throws Exception {
Workbook workbook = WorkbookFactory.create(new FileInputStream("ExcelExample.xlsx"));
DataFormatter dataFormatter = new DataFormatter(Locale.GERMANY);
FormulaEvaluator formulaEvaluator = workbook.getCreationHelper().createFormulaEvaluator();
Sheet sheet = workbook.getSheetAt(0);
for (Row row : sheet) {
for (Cell cell : row) {
String cellValue = dataFormatter.formatCellValue(cell, formulaEvaluator);
System.out.println(cellValue);
}
}
workbook.close();
}
}
This shows numeric values like 10.4 as 10,4 because of the German locale set.
The Excel file always contains numeric content in en_US locale form. So the content 10.4 will be stored as 10.4 if it is numeric. It is the Excel application then which transforms the content into other locale forms according to the locale of the operating system. That's why apache poi's DataFormatter also needs locale settings.
A Excel workbook either has dot as decimal delimiter or it has comma as decimal delimiter. It cannot have both the same time. So if in the same Excel sheet one cell contains 10.4 and another cell contains 10,4 then one of both must be text. In same Excel sheet 10.4 and 10,4 cannot be numeric the same time. The DataFormatter hands over text content totally untouched. So if 10,4 is the numeric value, then DataFormatterformats it using the Locale. But the 10.4 then must be text and DataFormatter will hand over it untouched as 10.4.

How to set a cell's color to an RGB value in apache poi 4.0

The example here does not compile. setFillForegroundColor now seems to want an index, I can trick it to show specific colors, but trying any method at specifying an rgb value just creates a black cell.
Below is apache's own example which seems to be out of date with 4.0.
https://poi.apache.org/components/spreadsheet/quick-guide.html#CustomColors
XSSFWorkbook wb = new XSSFWorkbook();
XSSFSheet sheet = wb.createSheet();
XSSFRow row = sheet.createRow(0);
XSSFCell cell = row.createCell( 0);
cell.setCellValue("custom XSSF colors");
XSSFCellStyle style1 = wb.createCellStyle();
style1.setFillForegroundColor(new XSSFColor(new java.awt.Color(128, 0, 128)));
style1.setFillPattern(FillPatternType.SOLID_FOREGROUND);

Apache POI - How to set correct column width in Word table

I have an existing Word document containing a table. The first row of the table has two cells, but all the other rows have four cells and each cell has a different width.
I need to insert new rows via POI that also have four cells with widths that match those of the existing 4-cell rows.
The basic code is:
XWPFTable table = doc.getTableArray(0);
XWPFTableRow oldRow = table.getRow(2);
table.insertNewTableRow(3);
XWPFTableRow newRow = table.getRow(3);
XWPFTableCell cell;
for (int i = 0; i < oldRow.getTableCells().size(); i++) {
cell = newRow.createCell();
CTTblWidth cellWidth = cell.getCTTc().addNewTcPr().addNewTcW();
BigInteger width = oldRow.getCell(i).getCTTc().getTcPr().getTcW().getW();
cellWidth.setW(width); // sets width
XWPFRun run = cell.getParagraphs().get(0).createRun();
run.setText("NewRow C" + i);
}
The result of this is that row 3 has four cells but their widths do not match those of row 2. The total new row width ends up being the same as the total width of the first three cells of row 2. (Sorry, I don't know how to paste the Word table here).
However, if I first manually edit the source document so that the first table row also has four cells, then everything works perfectly. Similarly, if I get a reference to an existing row and add it to the table, then the cell widths are also correct (but I have the same row object twice so can't modify it).
It seems that the number of cells in the first row influences how other rows are inserted. Does this make sense to anyone and can you suggest how to override it? Also, is there a document anywhere that I can study to understand how this works? Thanks.
Accordiing to your mention: "The first row of the table has two cells, but all the other rows have four cells and each cell has a different width." I suspect this will be a very messy table. Although Word is supporting such tables, I would try to avoid such. But if it must be, you need to know that there is a table grid also for those messy tables. Unzip the *.docx and have a look at /word/document.xml there you will find it.
So if we want to insert rows into such messy tables, we also must respect the table grid. For this there is a GridSpan element in the CTTcPr. This we must also copy from the oldRow and not only copy the CTTblWidth.
Also the CTTblWidth has not only a width but also a type. This we also should copy.
Example:
The source.docx looks like this:
As you see the table grid has 10 columns in total. "Cell 2 1" spans 3 columns, "Cell 2 2" spans 3 columns, "Cell 2 3" spans 0 columns (is its own column), "Cell 2 4" spans 3 columns.
With code:
import java.io.*;
import org.apache.poi.xwpf.usermodel.*;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTblWidth;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTcPr;
import java.math.BigInteger;
public class WordInsertTableRow {
public static void main(String[] args) throws IOException, InvalidFormatException {
XWPFDocument doc = new XWPFDocument(new FileInputStream("source.docx"));
XWPFTable table = doc.getTableArray(0);
XWPFTableRow oldRow = table.getRow(2);
table.insertNewTableRow(3);
XWPFTableRow newRow = table.getRow(3);
XWPFTableCell cell;
for (int i = 0; i < oldRow.getTableCells().size(); i++) {
cell = newRow.createCell();
CTTcPr ctTcPr = cell.getCTTc().addNewTcPr();
CTTblWidth cellWidth = ctTcPr.addNewTcW();
cellWidth.setType(oldRow.getCell(i).getCTTc().getTcPr().getTcW().getType()); // sets type of width
BigInteger width = oldRow.getCell(i).getCTTc().getTcPr().getTcW().getW();
cellWidth.setW(width); // sets width
if (oldRow.getCell(i).getCTTc().getTcPr().getGridSpan() != null) {
ctTcPr.setGridSpan(oldRow.getCell(i).getCTTc().getTcPr().getGridSpan()); // sets grid span if any
}
XWPFRun run = cell.getParagraphs().get(0).createRun();
run.setText("NewRow C" + i);
}
doc.write(new FileOutputStream("result.docx"));
doc.close();
System.out.println("Done");
}
}
The result.docx looks like:

Using POI to write date (without time)

When i write out a date using POI, I also see the time in Excel. (the time shown is the time on my machine when the cell was written).
This is the code that i have used
XSSFCellStyle dateStyle = wb.createCellStyle();
CreationHelper createHelper = wb.getCreationHelper();
dateStyle.setDataFormat(createHelper.createDataFormat().getFormat("dd-mmm-yyyy"));
Calendar cal = Calendar.getInstance();
cal.set(xx.getYear(), xx.getMonthOfYear(), xx.getDayOfMonth());
cell.setCellStyle(dateStyle);
cell.setCellValue(cal);
the date in the cell is correct i.e 12-Dec-2013 but for that cell, IN THE FORMULA bar, the time also shows. So cell shows 12-Dec-2013 and the formula bar show 12-Dec-2013 7:14:39AM. I checked the format of the cell in excel and it shows as custom dd-mm-yyyy, which is what i expect.
Just too be clear - the cell itself show 12-12-2012 but for that cell in the formula bar the time also shows.
I also replaced Calendar with Date - same issue.
Addl info: In excel i changed the format of the col to 'general' - for the cells that were addined in by POI, i see that the values is xxx.xxx like 41319.3769490278, while when i just enter the date by hand the value looks something like 41319. It looks like the digits after the decimal point is causing the time to show up. Not sure how to avoid this when i use POI to write it out
Ok solved. Putting this out there for others who run into the same problem.
i looked into the POI source code and realized that from the calendar, a double is computed, and the cell value is set to that. From the comments its clear that the digits after the decimal point represent the time. So all i did in my code is to truncate that double. The changed lines are commented in the code snippet below.
XSSFCellStyle dateStyle = wb.createCellStyle();
CreationHelper createHelper = wb.getCreationHelper();
dateStyle.setDataFormat(createHelper.createDataFormat().getFormat("dd-mmm-yyyy"));
Calendar cal = Calendar.getInstance();
cal.set(xx.getYear(), xx.getMonthOfYear(), xx.getDayOfMonth());
cell.setCellStyle(dateStyle);
double d = DateUtil.getExcelDate(cal, false); //get double value f
cell.setCellValue((int)d); //get int value of the double
You're not fully clearing the Calendar instance you're getting the date from, that's why the time is coming through
You need to also set the time values to zero, so your code would want to look something like:
Calendar cal = Calendar.getInstance();
cal.set(xx.getYear(), xx.getMonthOfYear(), xx.getDayOfMonth());
calendar.set(Calendar.HOUR, 0);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MILLISECOND, 0);

Setting Time in Excel using POI

I am trying to create an Excel Work sheet using POI api in Java. In that Excel Work Sheet I want to have a cell with TIME alone. By setting this we can include the cell in summation of that particuluar column as we do in number columns. For this we need to format the cell as Time >> 13:30:55. (The internal format is 'h:mm:ss;#' ). And we need to remove the date part from the cell.
When I read the cell the cell value using the POI, it is returning as 'Sun Dec 31 01:00:00 IST 1899' ( When i set the value as 1:00 ), the cell format index is 166 and the cell format string is 'h:mm:ss;#'.
After setting the formats and style which were read from the excel and the cell value as 1800-December-31 and with the time value, the new excel shows cell as '######' (error) and cell value is setted as '-1'. Below is the code I have used. Did I miss anything ? Is it possible to set the value as I required.
InputStream is = new BufferedInputStream(new FileInputStream("<FileName>"));
XSSFWorkbook wb = new XSSFWorkbook(is);
is.close();
XSSFSheet sheet = wb.getSheetAt(0);
XSSFRow row = sheet.getRow(2);
XSSFCell cell = row.getCell(18);
System.out.println("ExcelFileReader main cell.getDateCellValue() : '" + cell.getDateCellValue() + "'");
System.out.println("ExcelFileReader main cell.getCellStyle().getDataFormat() : '" + cell.getCellStyle().getDataFormat() + "'");
System.out.println("ExcelFileReader main cell.getCellStyle().getDataFormat() : '" + cell.getCellStyle().getDataFormatString() + "'");
XSSFRow row1 = sheet.createRow(21);
XSSFCell cell1 = row1.createCell(2);
cell1.setCellStyle(cell.getCellStyle());
cell1.setCellValue(cell.getDateCellValue());
Calendar dummy = Calendar.getInstance();
dummy.setLenient(false);
dummy.set(Calendar.YEAR, 1899);
dummy.set(Calendar.MONTH, Calendar.DECEMBER);
dummy.set(Calendar.DATE, 31);
dummy.set(Calendar.HOUR, 00);
dummy.set(Calendar.MINUTE, 00);
dummy.set(Calendar.SECOND, 00);
dummy.set(Calendar.MILLISECOND, 00);
Calendar cc = Calendar.getInstance();
XSSFRow row2 = sheet.createRow(25);
XSSFCell cell2 = row2.createCell(2);
dummy.set(Calendar.HOUR, cc.get(Calendar.HOUR));
dummy.set(Calendar.MINUTE, cc.get(Calendar.MINUTE));
dummy.set(Calendar.SECOND, cc.get(Calendar.SECOND));
dummy.set(Calendar.MILLISECOND, cc.get(Calendar.MILLISECOND));
System.out.println("ExcelFileReader main dummy : '" + dummy.getTime() + "'");
cell2.setCellValue(dummy.getTime());
CellStyle style = wb.createCellStyle();
DataFormat df = wb.createDataFormat();
style.setDataFormat(df.getFormat("[h]:mm:ss;#"));
cell2.setCellStyle(style);
FileOutputStream fos = new FileOutputStream(new File("<New Excel file>"));
wb.write(fos);
fos.close();
System.out.println("ExcelFileReader DONE");
The following is the output of the program.
ExcelFileReader main cell.getDateCellValue() : 'Sun Dec 31 00:15:00 IST 1899'
ExcelFileReader main cell.getCellStyle().getDataFormat() : '166'
ExcelFileReader main cell.getCellStyle().getDataFormat() : 'h:mm:ss;#'
ExcelFileReader main dummy : 'Sun Dec 31 11:32:24 IST 1899'
ExcelFileReader DONE
I have used the following code to generate the time cell, in Excel using POI. And I am able to use the cell in
XSSFRow row2 = sheet.createRow(25);
XSSFCell cell1 = row2.createCell(4);
XSSFCell cell2 = row2.createCell(5);
CellStyle style = wb.createCellStyle();
DataFormat df = wb.createDataFormat();
style.setDataFormat(df.getFormat("[h]:mm:ss;#"));
cell1.setCellFormula("TIME(0,15,00)"); // 00:15:00
cell1.setCellType(Cell.CELL_TYPE_FORMULA);
cell1.setCellStyle(style);
evaluator.evaluateFormulaCell(cell1);
cell2.setCellFormula("TIME(0,30,00)"); //00:30:00
cell2.setCellType(Cell.CELL_TYPE_FORMULA);
evaluator.evaluateFormulaCell(cell2);
cell2.setCellStyle(style);
Dates and Times in Excel are normally stored as floating point numbers, as full and fractional days since 1900 or 1904. You can see this by typing 0.5 into an excel cell and formatting as a time - it'll show as 12:00:00, or set a value of 1.5 and it'll show as a date in 1900/1904 as a date, 12:00:00 as a time, or 36:00:00 if a time that allows >24 hours (it's a different format code)
What you'll want to do is fire up a copy of Excel, type in the time value you want to have displayed, and work out the format string that makes it show up as you want it to. Then, make a note of that format code, and give that to POI as the data format for the cell. Now, use something like DateUtil in POI to build up your Date object, and set that to the cell as the value. With the right format string in, Excel will show only the time part and not the date
You can use DateUtil.convertTime(String time) method where time is like "HH:MM" or "HH:MM:SS". This method will give you a double value which you can set as value in your cell (cell.setCellValue(double value)).
But convertTime method doesn't work with hours > 23. If you need this opportunity, you can write your own convertTime method, somthing like this:
double convertTime(long hours, long minutes, long seconds) {
double totalSeconds = seconds + (minutes + (hours) * 60) * 60;
return totalSeconds / (SECONDS_PER_DAY);
}
SECONDS_PER_DAY = 60 * 60 * 24. You can simply take it from DateUtil class.
Remember, you must define your cell format properly that excel can put in hours more than 23.

Resources