mirror of https://github.com/apache/poi.git
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:
parent
622b84c633
commit
d96f9ce495
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -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)
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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 {
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.
Loading…
Reference in New Issue