I'm working on a project that will take an existing PowerPoint presentation with a number of charts embedded in it, update the data then output a new copy. I've found a few hints in the forum about how this should work but missing is an example for saving the binary data back into the EmbeddedPackagePart. All of the examples I've seen on the forum appear to read in existing spreadsheets from a discreet disk file.
Here's how I'm updating Cell values (a naive example):
Using java Syntax Highlighting
String xlsPartName = "/ppt/embeddings/Microsoft_Excel_Sheet1.xlsx";
EmbeddedPackagePart epp = (EmbeddedPackagePart) ppt
.getParts().get(new PartName(xlsPartName));
InputStream is = BufferUtil.newInputStream(epp.getBuffer());
SpreadsheetMLPackage pkg = (SpreadsheetMLPackage) SpreadsheetMLPackage.load(is);
Map<PartName,Part> partsMap = pkg.getParts().getParts();
Iterator<Entry<PartName, Part>> it = partsMap.entrySet().iterator();
while(it.hasNext()) {
Map.Entry<PartName, Part> pairs = it.next();
logger.info(" Key: " + pairs.getKey() + " value: " + partsMap.get(pairs.getKey()));
if (partsMap.get(pairs.getKey()) instanceof WorksheetPart) {
WorksheetPart wsp = (WorksheetPart) partsMap.get(pairs.getKey()) ;
SheetData sheetData = wsp.getJaxbElement().getSheetData();
List<Row> rows = sheetData.getRow();
for (Row row : rows)
{
List<Cell> cells = row.getC();
for (Cell cell : cells)
{
if (cell.getR().equals("B2") && cell.getV() != null) {
logger.info("B2 CELL VAL: " + cell.getV());
// change the B2 cell value
cell.setV("14.03");
}
else if (cell.getR().equals("B3") && cell.getV() != null) {
logger.info("B3 CELL VAL: " + cell.getV());
// Change the B3 cell value
cell.setV("22.12");
}
}
}
// TODO Is this necessary?
spreadSheet.addTargetPart(wsp, AddPartBehaviour.OVERWRITE_IF_NAME_EXISTS);
}
}
// TODO ... update binary data
// TODO ... update chart
// TODO ... update slide
EmbeddedPackagePart epp = (EmbeddedPackagePart) ppt
.getParts().get(new PartName(xlsPartName));
InputStream is = BufferUtil.newInputStream(epp.getBuffer());
SpreadsheetMLPackage pkg = (SpreadsheetMLPackage) SpreadsheetMLPackage.load(is);
Map<PartName,Part> partsMap = pkg.getParts().getParts();
Iterator<Entry<PartName, Part>> it = partsMap.entrySet().iterator();
while(it.hasNext()) {
Map.Entry<PartName, Part> pairs = it.next();
logger.info(" Key: " + pairs.getKey() + " value: " + partsMap.get(pairs.getKey()));
if (partsMap.get(pairs.getKey()) instanceof WorksheetPart) {
WorksheetPart wsp = (WorksheetPart) partsMap.get(pairs.getKey()) ;
SheetData sheetData = wsp.getJaxbElement().getSheetData();
List<Row> rows = sheetData.getRow();
for (Row row : rows)
{
List<Cell> cells = row.getC();
for (Cell cell : cells)
{
if (cell.getR().equals("B2") && cell.getV() != null) {
logger.info("B2 CELL VAL: " + cell.getV());
// change the B2 cell value
cell.setV("14.03");
}
else if (cell.getR().equals("B3") && cell.getV() != null) {
logger.info("B3 CELL VAL: " + cell.getV());
// Change the B3 cell value
cell.setV("22.12");
}
}
}
// TODO Is this necessary?
spreadSheet.addTargetPart(wsp, AddPartBehaviour.OVERWRITE_IF_NAME_EXISTS);
}
}
// TODO ... update binary data
// TODO ... update chart
// TODO ... update slide
Parsed in 0.018 seconds, using GeSHi 1.0.8.4
Of the 3 steps at the end of the snippet above the only one I haven't been able to figure out is transforming the updated spreadsheet to binary data.
I tried simply saving the modified spreadsheet to disk then reading it in as a FileInputStream but that threw this Exception:
- Code: Select all
[com.sun.istack.internal.SAXException2: unable to marshal type "org.xlsx4j.sml.CTTable" as an element because it is missing an @XmlRootElement annotation]
at com.sun.xml.internal.bind.v2.runtime.MarshallerImpl.write(MarshallerImpl.java:317)
at com.sun.xml.internal.bind.v2.runtime.MarshallerImpl.marshal(MarshallerImpl.java:243)
at javax.xml.bind.helpers.AbstractMarshallerImpl.marshal(AbstractMarshallerImpl.java:75)
at org.docx4j.openpackaging.parts.JaxbXmlPart.marshal(JaxbXmlPart.java:197)
at org.docx4j.openpackaging.parts.JaxbXmlPart.marshal(JaxbXmlPart.java:175)
at org.docx4j.openpackaging.io.SaveToZipFile.saveRawXmlPart(SaveToZipFile.java:246)
at org.docx4j.openpackaging.io.SaveToZipFile.saveRawXmlPart(SaveToZipFile.java:195)
at org.docx4j.openpackaging.io.SaveToZipFile.savePart(SaveToZipFile.java:419)
at org.docx4j.openpackaging.io.SaveToZipFile.addPartsFromRelationships(SaveToZipFile.java:382)
at org.docx4j.openpackaging.io.SaveToZipFile.savePart(SaveToZipFile.java:437)
at org.docx4j.openpackaging.io.SaveToZipFile.addPartsFromRelationships(SaveToZipFile.java:382)
at org.docx4j.openpackaging.io.SaveToZipFile.savePart(SaveToZipFile.java:437)
at org.docx4j.openpackaging.io.SaveToZipFile.addPartsFromRelationships(SaveToZipFile.java:382)
at org.docx4j.openpackaging.io.SaveToZipFile.save(SaveToZipFile.java:165)
at org.docx4j.openpackaging.io.SaveToZipFile.save(SaveToZipFile.java:94)
at com.cogine.test.ppt.PptTestMain.updateChart(PptTestMain.java:166)
at com.cogine.test.ppt.PptTestMain.main(PptTestMain.java:89)
Caused by: com.sun.istack.internal.SAXException2: unable to marshal type "org.xlsx4j.sml.CTTable" as an element because it is missing an @XmlRootElement annotation
at com.sun.xml.internal.bind.v2.runtime.XMLSerializer.reportError(XMLSerializer.java:216)
at com.sun.xml.internal.bind.v2.runtime.ClassBeanInfoImpl.serializeRoot(ClassBeanInfoImpl.java:286)
at com.sun.xml.internal.bind.v2.runtime.XMLSerializer.childAsRoot(XMLSerializer.java:462)
at com.sun.xml.internal.bind.v2.runtime.MarshallerImpl.write(MarshallerImpl.java:314)
... 16 more
org.docx4j.openpackaging.exceptions.Docx4JException: Failed to add parts from relationships
at org.docx4j.openpackaging.io.SaveToZipFile.addPartsFromRelationships(SaveToZipFile.java:387)
at org.docx4j.openpackaging.io.SaveToZipFile.save(SaveToZipFile.java:165)
at org.docx4j.openpackaging.io.SaveToZipFile.save(SaveToZipFile.java:94)
at com.cogine.test.ppt.PptTestMain.updateChart(PptTestMain.java:166)
at com.cogine.test.ppt.PptTestMain.main(PptTestMain.java:89)
Caused by: org.docx4j.openpackaging.exceptions.Docx4JException: Failed to add parts from relationships
at org.docx4j.openpackaging.io.SaveToZipFile.addPartsFromRelationships(SaveToZipFile.java:387)
at org.docx4j.openpackaging.io.SaveToZipFile.savePart(SaveToZipFile.java:437)
at org.docx4j.openpackaging.io.SaveToZipFile.addPartsFromRelationships(SaveToZipFile.java:382)
... 4 more
Caused by: org.docx4j.openpackaging.exceptions.Docx4JException: Failed to add parts from relationships
at org.docx4j.openpackaging.io.SaveToZipFile.addPartsFromRelationships(SaveToZipFile.java:387)
at org.docx4j.openpackaging.io.SaveToZipFile.savePart(SaveToZipFile.java:437)
at org.docx4j.openpackaging.io.SaveToZipFile.addPartsFromRelationships(SaveToZipFile.java:382)
... 6 more
Caused by: org.docx4j.openpackaging.exceptions.Docx4JException: Problem saving part xl/tables/table1.xml
at org.docx4j.openpackaging.io.SaveToZipFile.saveRawXmlPart(SaveToZipFile.java:304)
at org.docx4j.openpackaging.io.SaveToZipFile.saveRawXmlPart(SaveToZipFile.java:195)
at org.docx4j.openpackaging.io.SaveToZipFile.savePart(SaveToZipFile.java:419)
at org.docx4j.openpackaging.io.SaveToZipFile.addPartsFromRelationships(SaveToZipFile.java:382)
... 8 more
Here a link to another topic that deals with the issue but leaves out how to convert a SpreadsheetMLPackage into binary format.
http://www.docx4java.org/forums/docx-java-f6/how-to-edit-an-embedded-xlsx-values-programatically-t1042.html#p3521
Note that when I have a solution I will happily contribute an example since this use case appears to pop up regularly.
Thanks,
Jeff