I am exporting some data from my system. I want to visualize these datasets in an excel chart. I have found and old, closed question, where the solution was missing. The charts should redraw when i change a datafield, this is excel standard, i guess.
I think it may work this way:
export Data
create manually a chart with MS-Excel
save and load this as a template in all other future exports
Do you know how to do it with POI using Java? Especially the import of the chart as template?
POI doesn't give you that functionality but You can convert or copy charts(graphs) using J XL or Aspose Cells(Aspose is not free).
This is the code snippet to extract excel chart to image
public class ExportChartToImage
{
public static void main(String[] args) throws Exception
{
//Start Excel
Application excelApp = new Application();
excelApp.setVisible(true);
//Create test workbook
Workbook workbook = excelApp.createWorkbook("/home/tejus/Desktop/Chart Test");
//Get the first (and the only) worksheet
final Worksheet worksheet1 = workbook.getWorksheet(1);
//Fill-in the first worksheet with sample data
worksheet1.getCell("A1").setValue("Date");
worksheet1.getCell("A2").setValue("March 1");
worksheet1.getCell("A3").setValue("March 8");
worksheet1.getCell("A4").setValue("March 15");
worksheet1.getCell("B1").setValue("Customer");
worksheet1.getCell("B2").setValue("Smith");
worksheet1.getCell("B3").setValue("Jones");
worksheet1.getCell("B4").setValue("James");
worksheet1.getCell("C1").setValue("Sales");
worksheet1.getCell("C2").setValue("23");
worksheet1.getCell("C3").setValue("17");
worksheet1.getCell("C4").setValue("39");
excelApp.getOleMessageLoop().doInvokeAndWait(new Runnable()
{
public void run()
{
final Variant unspecified = Variant.createUnspecifiedParameter();
final Int32 localeID = new Int32(LocaleID.LOCALE_SYSTEM_DEFAULT);
Range sourceDataNativePeer = worksheet1.getRange("A1:C4").getPeer();
_Worksheet worksheetNativePeer = worksheet1.getPeer();
IDispatch chartObjectDispatch = worksheetNativePeer.chartObjects(unspecified, localeID);
ChartObjectsImpl chartObjects = new ChartObjectsImpl(chartObjectDispatch);
ChartObject chartObject = chartObjects.add(new DoubleFloat(100), new DoubleFloat(150), new DoubleFloat(300), new DoubleFloat(225));
_Chart chart = chartObject.getChart();
chart.setSourceData(sourceDataNativePeer, new Variant(XlRowCol.xlRows));
BStr fileName = new BStr("/home/tejus/Desktop/chart.gif");
Variant filterName = new Variant("gif");
Variant interactive = new Variant(false);
chart.export(fileName, filterName, interactive);
chart.setAutoDelete(false);
chart.release();
chartObject.setAutoDelete(false);
chartObject.release();
chartObjects.setAutoDelete(false);
chartObjects.release();
chartObjectDispatch.setAutoDelete(false);
chartObjectDispatch.release();
}
});
System.out.println("Press 'Enter' to terminate the application");
System.in.read();
//Close the MS Excel application.
boolean saveChanges = false;
workbook.close(saveChanges);
boolean forceQuit = true;
excelApp.close(forceQuit);
}
}
i used J excel
Till now as the apache POI limitation is saying "You can not currently create charts. You can however create a chart in Excel, modify the chart data values using HSSF and write a new spreadsheet out. This is possible because POI attempts to keep existing records intact as far as possible".
However in my case, I have created a chart manually on excel sheet using Named Ranges, and using java, I am updating named ranges as per my requirement. Since the chart is based on named ranges so it also get updated.
For updation please check here
Related
I am trying to create an Excel file and send it to the SFTP location using the Apache Camel. I can create a PSV from a Java object like this:
Java POJO:
#CsvRecord(separator = ",", skipFirstLine = true, generateHeaderColumns = true)
public class IexPerson implements Serializable {
private static final long serialVersionUID = 1234069326527342909L;
#DataField(pos = 1, columnName = "Person Name")
private String personName;
#DataField(pos = 2, columnName = "Gender")
private String gender;
// other fields ...
and then I convert a list of IexPersons in the route:
DataFormat iexPersonFormat = new BindyCsvDataFormat(IexPerson.class);
from("direct-get-list-of-persons")
.setHeader(Exchange.FILE_NAME).simple("Persons_${date:now:yyyyMMdd-HHmmssSSS}_${random(1000,10000000)}.csv")
.marshal(iexPersonFormat)
.to("file:///tmp?fileName=${header.CamelFileName}");
This is working fine, but now I need to create an Excel file from the same list and send it to another location. I didn't manage to get it to work.
I didn't find anything on the internet that would help me.
Posting here for future users who need help with the same topic.
I was able to convert the Java Object to a working excel file by using the Apache POI library.
I created a service and inside it a method that converts an object to a file, which I called from my Camel route. This is well explained here.
Here is the code:
// Create a blank Workbook
try (Workbook workbook = new XSSFWorkbook()) {
// Create a blank Sheet
Sheet sheet = workbook.createSheet("IexPersons");
// column names
List <String> columns = new ArrayList<>();
columns.add("Person Name");
columns.add("Gender");
Row headerRow = sheet.createRow(0);
// Create columns/first row in a file
for (int i = 0; i < columns.size(); i++) {
Cell cell = headerRow.createCell(i);
cell.setCellValue(columns.get(i));
}
int rowNum = 1;
// iterate over the list of persons and for each person write its values to the excel row
for (IexPerson iexPerson : getListOfPersons()) {
Row row = sheet.createRow(rowNum++);
// populate file with the values for each column
row.createCell(0).setCellValue(iexPerson.getName());
row.createCell(1).setCellValue(iexPerson.getGender());
}
// create file
FileOutputStream out = new FileOutputStream(new File("iexpersons.xlsx"));
// write data to file
workbook.write(out);
// close the output stream
out.close();
} catch (IOException e) {
e.printStackTrace();
}
I usually use XSL transformations to create Excel files in the Microsoft Office XML format.
You can find an example XML for an Excel spreadsheet on the linked Wikipedia page.
However, I usually create a simple Excel file that represents what I want and then I save it as "XML Spreadsheet 2003".
The result is the XML file structure that I need to generate with an XSL stylesheet.
There is a question which solves how to add a background image for an Excel Comment in versions previous to 2007 (format .xsl), with HSSF Apache POI.
apache poi insert comment with picture
But looking the doc, I cannot locate an equivalent method for XSSF Apache POI (.xslx formats).
It seems this key method was removed when moving from HSSF to XSSF:
HSSFComment comment;
...
comment.setBackgroundImage(picIndex); // set picture as background image
It is not supported using a method of XSSFComment. But if one knows what needs to be created, then it is not impossible.
First we need creating a default comment as shown in Quick-Quide CellComments.
Then we need adding picture data to this workbook as shown in Quick-Guide Images. We need the XSSFPictureData for adding references later.
Then we need getting the VML drawing. XSSFComments are stored in VML drawings and not in default XSSFDrawings. This is not public provided, so we need using reflection to do so.
Now we need setting the relation to the picture data in VML drawing.
At last we need getting the comment shape out of the VML drawing to set the fill of that comment shape to show the picture. There are no high level methods for this. So we need using methods of low level com.microsoft.schemas.vml.* classes.
The following example needs the full jar of all of the schemas ooxml-schemas-1.4.jar as mentioned in FAQ. It is tested using apache poi 4.1.1.
Complete example:
import java.io.FileOutputStream;
import java.io.FileInputStream;
import java.io.InputStream;
import org.apache.poi.xssf.usermodel.*;
import org.apache.poi.util.IOUtils;
class CreateXSSFCommentWithPicture {
public static void main(String[] args) throws Exception {
try (XSSFWorkbook workbook = new XSSFWorkbook();
FileOutputStream fileout = new FileOutputStream("Excel.xlsx") ) {
// First we create a default XSSFComment:
XSSFCreationHelper factory = workbook.getCreationHelper();
XSSFSheet sheet = workbook.createSheet("Sheet");
XSSFRow row = sheet.createRow(3);
XSSFCell cell = row.createCell(5);
cell.setCellValue("F4");
XSSFDrawing drawing = sheet.createDrawingPatriarch();
XSSFClientAnchor anchor = factory.createClientAnchor();
anchor.setCol1(cell.getColumnIndex());
anchor.setCol2(cell.getColumnIndex()+2);
anchor.setRow1(row.getRowNum());
anchor.setRow2(row.getRowNum()+5);
XSSFComment comment = drawing.createCellComment(anchor);
XSSFRichTextString str = factory.createRichTextString("Hello, World!");
comment.setString(str);
comment.setAuthor("Apache POI");
// assign the comment to the cell
cell.setCellComment(comment);
// Now we put the image as fill of the comment's shape:
// add picture data to this workbook
InputStream is = new FileInputStream("samplePict.jpeg");
byte[] bytes = IOUtils.toByteArray(is);
int pictureIdx = workbook.addPicture(bytes, XSSFWorkbook.PICTURE_TYPE_JPEG);
is.close();
// get picture data
XSSFPictureData pictureData = workbook.getAllPictures().get(pictureIdx);
// get VML drawing
java.lang.reflect.Method getVMLDrawing = XSSFSheet.class.getDeclaredMethod("getVMLDrawing", boolean.class);
getVMLDrawing.setAccessible(true);
XSSFVMLDrawing vml = (XSSFVMLDrawing)getVMLDrawing.invoke(sheet, true);
// set relation to the picture data in VML drawing
org.apache.poi.ooxml.POIXMLDocumentPart.RelationPart rp = vml.addRelation(null, XSSFRelation.IMAGES, pictureData);
// get comment shape
com.microsoft.schemas.vml.CTShape commentShape = vml.findCommentShape(cell.getRow().getRowNum(), cell.getColumnIndex());
// get fill of comment shape
com.microsoft.schemas.vml.CTFill fill = commentShape.getFillArray(0);
// already set color needs to be color2 now
fill.setColor2(fill.getColor());
fill.unsetColor();
// set relation Id of the picture
fill.setRelid(rp.getRelationship().getId());
// set some other properties
fill.setTitle("samplePict");
fill.setRecolor(com.microsoft.schemas.vml.STTrueFalse.T);
fill.setRotate(com.microsoft.schemas.vml.STTrueFalse.T);
fill.setType(com.microsoft.schemas.vml.STFillType.FRAME);
workbook.write(fileout);
}
}
}
According to this, adding image to comments was only added for HSSF.
I suppose you'll have to use another approach, like in apache poi guide.
I am working on a large CSV (~200 mb of text file) which I would like to convert into excel sheet but the workbook becomes so memory consuming that in the middle of the process, Java throws "GC Overhead limit exceeded"!
I have checked the code if I am generating dummy references but I think none exists.
In my opinion those library calls from Apachi - POI might generate some references that keeps garbage collector so busy.
My question is if I could just write the workbook into a file chunk by chunk like text file something like appending to a text file without bringing it into memory. Is there any solution for that or am I missing something here?
GC throws the exception in the following code:
private void updateExcelWorkbook(String input, String fileName, Workbook workbook) {
try {
Sheet sheet = workbook.createSheet(fileName);
// Create a new font and alter it.
Font font = workbook.createFont();
font.setFontHeightInPoints((short) 11);
font.setBold(true);
// Fonts are set into a style so create a new one to use.
CellStyle style = workbook.createCellStyle();
style.setFont(font);
Row row;
Cell cell;
String[] columns;
String[] lines = input.split("\n");
int colIndex;
int rowIndex = 1;
for (String line : lines) {
row = sheet.createRow(rowIndex++);
columns = line.split("\t");
colIndex = 0;
for (String column: columns) {
cell = row.createCell(colIndex++);
if (rowIndex == 1)
cell.setCellStyle(style);
cell.setCellValue(column);
}
}
} catch (Exception ex) {
System.out.println(ex.getMessage());
}
}
Seems you are using the POI usermodel, which has a very high memory footprint, because it keeps the entire worksheet in memory, similar to how DOM keeps an entire XML document in memory.
You need to use a streaming API. Using POI, you can create .xlsx files using the SXSSF Buffered Streaming API, as mentioned here: https://poi.apache.org/spreadsheet/index.html#SXSSF+(Since+POI+3.8+beta3)
The page linked above has this image, showing the Spreadsheet API Feature Summary of POI:
(source: apache.org)
I'm using "Apache POI" to generate Excel report. I've a well designed Excel template. I want to create a report by filling the data into predefined locations of the template. That is to say, I do not want to care about the formats of the report. Is that possible? Could you give me some instructions?
I got my answer. I can use the "Cell Naming" utility in Microsoft Excel and then use the following code to locate the cell and do something.
CellReference[] crefs = this.getCellsByName(wb, cName);
// Locate the cell position
Sheet sheet = wb.getSheet(crefs[0].getSheetName());
Row row = sheet.getRow(crefs[0].getRow());
Cell cell = row.getCell(crefs[0].getCol());
// Write in data
cell.setCellValue(cellRegion.getContent());
"cName" is the cell's name predefined in Microsoft Excel.
You can have a look at jXLS, I think that's what you are looking for.
It takes a Excel as template and you can write a Java app to fill the data:
http://jxls.sourceforge.net/
You can load you template file like any other XLS. And then make the changes you want to the specific cells and write it out into another file.
Some sample code:
Load file
InputStream inputStream = new FileInputStream ("D:\\book_original.xls");
POIFSFileSystem fileSystem = new POIFSFileSystem (inputStream);
HSSFWorkbook workBook = new HSSFWorkbook (fileSystem);
do stuff
HSSFSheet sheet1 = workBook.getSheetAt (0);
Iterator<Row> rows = sheet1.rowIterator ();
while (rows.hasNext ())
{
Row row = rows.next ();
// do stuff
if (row.getCell(0).getCellType() == HSSFCell.CELL_TYPE_NUMERIC)
System.out.println ("Row No.: " + row.getRowNum ()+ " " + row.getCell(0).getNumericCellValue());
HSSFCell cell = row.createCell(0);
cell.setCellValue("100");
}
Write the output to a file
FileOutputStream fileOut1 = new FileOutputStream("D:\\book_modified.xls");
workBook.write(fileOut1);
fileOut1.close();
You may also take a look at Xylophone. This is Java library built on top of Apache POI. It uses spreadsheet templates in XLS(X) format and consumes data in XML format.
I've got a series of Excel spreadsheets, each with at least one page of data and one page of a chart created from the data. I need to capture ( not regenerate from the data ) the existing chart as a web friendly image. Is this possible via Java or .Net? I know the POI stuff (Java) won't do it (or so I'm told, haven't tried it myself).
Have you tried using the Chart.Export Method?
The example in help is:
Worksheets("Sheet1").ChartObjects(1).Chart. Export _
FileName:="current_sales.gif", FilterName:="GIF"
From memory, I think you can export to PNG as well.
Chart images are not stored in workbooks, so you need a component which can render Excel compatible charts.
SpreadsheetGear for .NET will let you load Excel workbooks, optionally plug in new values / formulas / formats / etc..., calculate, and then get an image from a range of cells or from a chart.
You can see some samples here and download the free trial here if you want to try it yourself.
Disclaimer: I own SpreadsheetGear LLC
You can convert or copy charts(graphs) using J XL or Aspose Cells(Aspose is not free).
This is the code snippet to extract excel chart to image
public class ExportChartToImage
{
public static void main(String[] args) throws Exception
{
//Start Excel
Application excelApp = new Application();
excelApp.setVisible(true);
//Create test workbook
Workbook workbook = excelApp.createWorkbook("/home/tejus/Desktop/Chart Test");
//Get the first (and the only) worksheet
final Worksheet worksheet1 = workbook.getWorksheet(1);
//Fill-in the first worksheet with sample data
worksheet1.getCell("A1").setValue("Date");
worksheet1.getCell("A2").setValue("March 1");
worksheet1.getCell("A3").setValue("March 8");
worksheet1.getCell("A4").setValue("March 15");
worksheet1.getCell("B1").setValue("Customer");
worksheet1.getCell("B2").setValue("Smith");
worksheet1.getCell("B3").setValue("Jones");
worksheet1.getCell("B4").setValue("James");
worksheet1.getCell("C1").setValue("Sales");
worksheet1.getCell("C2").setValue("23");
worksheet1.getCell("C3").setValue("17");
worksheet1.getCell("C4").setValue("39");
excelApp.getOleMessageLoop().doInvokeAndWait(new Runnable()
{
public void run()
{
final Variant unspecified = Variant.createUnspecifiedParameter();
final Int32 localeID = new Int32(LocaleID.LOCALE_SYSTEM_DEFAULT);
Range sourceDataNativePeer = worksheet1.getRange("A1:C4").getPeer();
_Worksheet worksheetNativePeer = worksheet1.getPeer();
IDispatch chartObjectDispatch = worksheetNativePeer.chartObjects(unspecified, localeID);
ChartObjectsImpl chartObjects = new ChartObjectsImpl(chartObjectDispatch);
ChartObject chartObject = chartObjects.add(new DoubleFloat(100), new DoubleFloat(150), new DoubleFloat(300), new DoubleFloat(225));
_Chart chart = chartObject.getChart();
chart.setSourceData(sourceDataNativePeer, new Variant(XlRowCol.xlRows));
BStr fileName = new BStr("/home/tejus/Desktop/chart.gif");
Variant filterName = new Variant("gif");
Variant interactive = new Variant(false);
chart.export(fileName, filterName, interactive);
chart.setAutoDelete(false);
chart.release();
chartObject.setAutoDelete(false);
chartObject.release();
chartObjects.setAutoDelete(false);
chartObjects.release();
chartObjectDispatch.setAutoDelete(false);
chartObjectDispatch.release();
}
});
System.out.println("Press 'Enter' to terminate the application");
System.in.read();
//Close the MS Excel application.
boolean saveChanges = false;
workbook.close(saveChanges);
boolean forceQuit = true;
excelApp.close(forceQuit);
}
}
i used J excel