bug 61745: add support for charts in XWPF/docx. Thanks to Sandeep Tiwari for the patch

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1815047 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Javen O'Neal 2017-11-12 21:48:41 +00:00
parent bac484ba37
commit a1b8f3ff14
6 changed files with 273 additions and 1 deletions

View File

@ -0,0 +1,165 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
package org.apache.poi.xwpf.usermodel;
import static org.apache.poi.POIXMLTypeLoader.DEFAULT_XML_OPTIONS;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;
import javax.xml.namespace.QName;
import org.apache.poi.POIXMLDocumentPart;
import org.apache.poi.POIXMLException;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.openxml4j.opc.PackagePart;
import org.apache.poi.openxml4j.opc.PackageRelationship;
import org.apache.poi.util.Beta;
import org.apache.poi.util.IOUtils;
import org.apache.poi.util.Internal;
import org.apache.xmlbeans.XmlException;
import org.apache.xmlbeans.XmlOptions;
import org.openxmlformats.schemas.drawingml.x2006.chart.CTChart;
import org.openxmlformats.schemas.drawingml.x2006.chart.CTChartSpace;
import org.openxmlformats.schemas.drawingml.x2006.chart.ChartSpaceDocument;
/**
* Represents a Chart in a .docx file
*/
@Beta
public class XWPFChart extends POIXMLDocumentPart {
/**
* Root element of the Chart part
*/
private final CTChartSpace chartSpace;
/**
* The Chart within that
*/
private final CTChart chart;
// lazy initialization
private Long checksum;
/**
* Construct a chart from a package part.
*
* @param part the package part holding the chart data,
* the content type must be <code>application/vnd.openxmlformats-officedocument.drawingml.chart+xml</code>
*
* @since POI 4.0.0
*/
protected XWPFChart(PackagePart part) throws IOException, XmlException {
super(part);
chartSpace = ChartSpaceDocument.Factory.parse(part.getInputStream(), DEFAULT_XML_OPTIONS).getChartSpace();
chart = chartSpace.getChart();
}
@Override
protected void onDocumentRead() throws IOException {
super.onDocumentRead();
}
/**
* Return the underlying CTChartSpace bean, the root element of the Chart part.
*
* @return the underlying CTChartSpace bean
*/
@Internal
public CTChartSpace getCTChartSpace() {
return chartSpace;
}
/**
* Return the underlying CTChart bean, within the Chart Space
*
* @return the underlying CTChart bean
*/
@Internal
public CTChart getCTChart() {
return chart;
}
@Override
protected void commit() throws IOException {
XmlOptions xmlOptions = new XmlOptions(DEFAULT_XML_OPTIONS);
xmlOptions.setSaveSyntheticDocumentElement(new QName(CTChartSpace.type.getName().getNamespaceURI(), "chartSpace", "c"));
try (OutputStream out = getPackagePart().getOutputStream()) {
chartSpace.save(out, xmlOptions);
}
}
public Long getChecksum() {
if (this.checksum == null) {
InputStream is = null;
byte[] data;
try {
is = getPackagePart().getInputStream();
data = IOUtils.toByteArray(is);
} catch (IOException e) {
throw new POIXMLException(e);
} finally {
try {
if (is != null) is.close();
} catch (IOException e) {
throw new POIXMLException(e);
}
}
this.checksum = IOUtils.calculateChecksum(data);
}
return this.checksum;
}
@Override
public boolean equals(Object obj) {
/**
* In case two objects ARE equal, but its not the same instance, this
* implementation will always run through the whole
* byte-array-comparison before returning true. If this will turn into a
* performance issue, two possible approaches are available:<br>
* a) Use the checksum only and take the risk that two images might have
* the same CRC32 sum, although they are not the same.<br>
* b) Use a second (or third) checksum algorithm to minimise the chance
* that two images have the same checksums but are not equal (e.g.
* CRC32, MD5 and SHA-1 checksums, additionally compare the
* data-byte-array lengths).
*/
if (obj == this) {
return true;
}
if (obj == null) {
return false;
}
if (!(obj instanceof XWPFChart)) {
return false;
}
return false;
}
@Override
public int hashCode() {
return getChecksum().hashCode();
}
}

View File

@ -96,6 +96,7 @@ public class XWPFDocument extends POIXMLDocument implements Document, IBody {
protected XWPFFootnotes footnotes; protected XWPFFootnotes footnotes;
private CTDocument1 ctDocument; private CTDocument1 ctDocument;
private XWPFSettings settings; private XWPFSettings settings;
protected final List<XWPFChart> charts = new ArrayList<>();
/** /**
* Keeps track on all id-values used in this document and included parts, like headers, footers, etc. * Keeps track on all id-values used in this document and included parts, like headers, footers, etc.
*/ */
@ -220,6 +221,11 @@ public class XWPFDocument extends POIXMLDocument implements Document, IBody {
picData.onDocumentRead(); picData.onDocumentRead();
registerPackagePictureData(picData); registerPackagePictureData(picData);
pictures.add(picData); pictures.add(picData);
} else if (relation.equals(XWPFRelation.CHART.getRelation())) {
//now we can use all methods to modify charts in XWPFDocument
XWPFChart chartData = (XWPFChart) p;
chartData.onDocumentRead();
charts.add(chartData);
} else if (relation.equals(XWPFRelation.GLOSSARY_DOCUMENT.getRelation())) { } else if (relation.equals(XWPFRelation.GLOSSARY_DOCUMENT.getRelation())) {
// We don't currently process the glossary itself // We don't currently process the glossary itself
// Until we do, we do need to load the glossary child parts of it // Until we do, we do need to load the glossary child parts of it
@ -323,6 +329,12 @@ public class XWPFDocument extends POIXMLDocument implements Document, IBody {
return Collections.unmodifiableList(tables); return Collections.unmodifiableList(tables);
} }
/**
* @return list of XWPFCharts in this document
*/
public List<XWPFChart> getCharts() {
return Collections.unmodifiableList(charts);
}
/** /**
* @see org.apache.poi.xwpf.usermodel.IBody#getTableArray(int) * @see org.apache.poi.xwpf.usermodel.IBody#getTableArray(int)
*/ */

View File

@ -112,6 +112,12 @@ public final class XWPFRelation extends POIXMLRelation {
"/word/theme/theme#.xml", "/word/theme/theme#.xml",
null null
); );
public static final XWPFRelation CHART = new XWPFRelation(
"application/vnd.openxmlformats-officedocument.drawingml.chart+xml",
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/chart",
"/word/charts/chart#.xml",
XWPFChart.class
);
public static final XWPFRelation HYPERLINK = new XWPFRelation( public static final XWPFRelation HYPERLINK = new XWPFRelation(
null, null,
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink",

View File

@ -19,6 +19,7 @@ package org.apache.poi.xwpf;
import org.apache.poi.xwpf.extractor.TestXWPFWordExtractor; import org.apache.poi.xwpf.extractor.TestXWPFWordExtractor;
import org.apache.poi.xwpf.model.TestXWPFHeaderFooterPolicy; import org.apache.poi.xwpf.model.TestXWPFHeaderFooterPolicy;
import org.apache.poi.xwpf.usermodel.TestXWPFChart;
import org.apache.poi.xwpf.usermodel.TestXWPFDocument; import org.apache.poi.xwpf.usermodel.TestXWPFDocument;
import org.apache.poi.xwpf.usermodel.TestXWPFHeader; import org.apache.poi.xwpf.usermodel.TestXWPFHeader;
import org.apache.poi.xwpf.usermodel.TestXWPFHeadings; import org.apache.poi.xwpf.usermodel.TestXWPFHeadings;
@ -38,6 +39,7 @@ import org.junit.runners.Suite;
@Suite.SuiteClasses({ @Suite.SuiteClasses({
TestXWPFBugs.class, TestXWPFBugs.class,
org.apache.poi.xwpf.usermodel.TestXWPFBugs.class, org.apache.poi.xwpf.usermodel.TestXWPFBugs.class,
TestXWPFChart.class,
TestXWPFDocument.class, TestXWPFDocument.class,
TestXWPFWordExtractor.class, TestXWPFWordExtractor.class,
TestXWPFHeaderFooterPolicy.class, TestXWPFHeaderFooterPolicy.class,
@ -53,4 +55,4 @@ import org.junit.runners.Suite;
TestPackageCorePropertiesGetKeywords.class TestPackageCorePropertiesGetKeywords.class
}) })
public final class AllXWPFTests { public final class AllXWPFTests {
} }

View File

@ -0,0 +1,87 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
package org.apache.poi.xwpf.usermodel;
import java.io.IOException;
import java.util.List;
import org.apache.poi.xwpf.XWPFTestDataSamples;
import org.openxmlformats.schemas.drawingml.x2006.chart.CTChart;
import org.openxmlformats.schemas.drawingml.x2006.chart.CTTitle;
import org.openxmlformats.schemas.drawingml.x2006.chart.CTTx;
import org.openxmlformats.schemas.drawingml.x2006.main.CTRegularTextRun;
import org.openxmlformats.schemas.drawingml.x2006.main.CTTextBody;
import org.openxmlformats.schemas.drawingml.x2006.main.CTTextParagraph;
import junit.framework.TestCase;
public class TestXWPFChart extends TestCase {
/**
* test method to check charts are null
*
* @throws IOException
*/
public void testRead() throws IOException
{
XWPFDocument sampleDoc = XWPFTestDataSamples.openSampleDocument("61745.docx");
List<XWPFChart> charts = sampleDoc.getCharts();
assertNotNull(charts);
assertEquals(2, charts.size());
assertNotNull(charts.get(0));
assertNotNull(charts.get(1));
}
/**
* test method to add chart title and check whether it's set
*
* @throws IOException
*/
public void testChartTitle() throws IOException
{
XWPFDocument sampleDoc = XWPFTestDataSamples.openSampleDocument("61745.docx");
List<XWPFChart> charts = sampleDoc.getCharts();
XWPFChart chart=charts.get(0);
CTChart ctChart = chart.getCTChart();
CTTitle title = ctChart.getTitle();
CTTx tx = title.addNewTx();
CTTextBody rich = tx.addNewRich();
rich.addNewBodyPr();
rich.addNewLstStyle();
CTTextParagraph p = rich.addNewP();
CTRegularTextRun r = p.addNewR();
r.addNewRPr();
r.setT("XWPF CHART");
assertEquals("XWPF CHART", chart.getCTChart().getTitle().getTx().getRich().getPArray(0).getRArray(0).getT().toString());
}
/**
* test method to check relationship
*
* @throws IOException
*/
public void testChartRelation() throws IOException
{
XWPFDocument sampleDoc = XWPFTestDataSamples.openSampleDocument("61745.docx");
List<XWPFChart> charts = sampleDoc.getCharts();
XWPFChart chart=charts.get(0);
assertEquals(XWPFRelation.CHART.getContentType(), chart.getPackagePart().getContentType().toString());
assertEquals("/word/document.xml", chart.getParent().getPackagePart().getPartName().toString());
assertEquals("/word/charts/chart1.xml", chart.getPackagePart().getPartName().toString());
}
}

Binary file not shown.