From 48a5e63ad714f01ef5915ddbdafdf7ea4446a8f8 Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Sat, 20 Jan 2018 23:01:18 +0000 Subject: [PATCH] [Bug 61972] Adding chart in Document of MS-Word File without reading Temp MS-Word File. Thanks to Sandeep Tiwari. This closes #91 git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1821764 13f79535-47bb-0310-9956-ffa450edef68 --- .../org/apache/poi/POIXMLDocumentPart.java | 272 ++++++------- .../poi/xddf/usermodel/chart/XDDFChart.java | 304 ++++++++++++++- .../poi/xslf/usermodel/XMLSlideShow.java | 105 +++-- .../apache/poi/xslf/usermodel/XSLFChart.java | 150 +------- .../poi/xslf/usermodel/XSLFRelation.java | 359 +++++++++--------- .../poi/xssf/usermodel/XSSFWorkbook.java | 9 + .../apache/poi/xwpf/usermodel/XWPFChart.java | 201 +++++++++- .../poi/xwpf/usermodel/XWPFDocument.java | 75 +++- .../poi/xwpf/usermodel/XWPFRelation.java | 10 + .../apache/poi/xwpf/usermodel/XWPFRun.java | 82 +++- .../poi/xwpf/usermodel/TestXWPFChart.java | 41 +- 11 files changed, 1029 insertions(+), 579 deletions(-) diff --git a/src/ooxml/java/org/apache/poi/POIXMLDocumentPart.java b/src/ooxml/java/org/apache/poi/POIXMLDocumentPart.java index 6c1bba379..e977e6ea3 100644 --- a/src/ooxml/java/org/apache/poi/POIXMLDocumentPart.java +++ b/src/ooxml/java/org/apache/poi/POIXMLDocumentPart.java @@ -39,11 +39,12 @@ import org.apache.poi.openxml4j.opc.TargetMode; import org.apache.poi.util.Internal; import org.apache.poi.util.POILogFactory; import org.apache.poi.util.POILogger; +import org.apache.poi.xddf.usermodel.chart.XDDFChart; import org.apache.poi.xssf.usermodel.XSSFRelation; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; /** * Represents an entry of a OOXML package. - * *

* Each POIXMLDocumentPart keeps a reference to the underlying a {@link org.apache.poi.openxml4j.opc.PackagePart}. *

@@ -54,7 +55,26 @@ public class POIXMLDocumentPart { private String coreDocumentRel = PackageRelationshipTypes.CORE_DOCUMENT; private PackagePart packagePart; private POIXMLDocumentPart parent; - private Map relations = new LinkedHashMap<>(); + private Map relations = new LinkedHashMap<>(); + private boolean isCommited = false; + + /** + * to check whether embedded part is already committed + * + * @return return true if embedded part is committed + */ + public boolean isCommited() { + return isCommited; + } + + /** + * setter method to set embedded part is committed + * + * @param isCommited boolean value + */ + public void setCommited(boolean isCommited) { + this.isCommited = isCommited; + } /** * The RelationPart is a cached relationship between the document, which contains the RelationPart, @@ -65,27 +85,26 @@ public class POIXMLDocumentPart { public static class RelationPart { private final PackageRelationship relationship; private final POIXMLDocumentPart documentPart; - + RelationPart(PackageRelationship relationship, POIXMLDocumentPart documentPart) { this.relationship = relationship; this.documentPart = documentPart; } - + /** - * @return the cached relationship, which uniquely identifies this child document part within the parent + * @return the cached relationship, which uniquely identifies this child document part within the parent */ public PackageRelationship getRelationship() { return relationship; } - + /** * @param the cast of the caller to a document sub class - * * @return the child document part */ @SuppressWarnings("unchecked") public T getDocumentPart() { - return (T)documentPart; + return (T) documentPart; } } @@ -111,7 +130,7 @@ public class POIXMLDocumentPart { /** * Construct POIXMLDocumentPart representing a "core document" package part. - * + * * @param pkg the OPCPackage containing this document */ public POIXMLDocumentPart(OPCPackage pkg) { @@ -120,15 +139,15 @@ public class POIXMLDocumentPart { /** * Construct POIXMLDocumentPart representing a custom "core document" package part. - * - * @param pkg the OPCPackage containing this document - * @param coreDocumentRel the relation type of this document + * + * @param pkg the OPCPackage containing this document + * @param coreDocumentRel the relation type of this document */ public POIXMLDocumentPart(OPCPackage pkg, String coreDocumentRel) { this(getPartFromOPCPackage(pkg, coreDocumentRel)); this.coreDocumentRel = coreDocumentRel; } - + /** * Creates new POIXMLDocumentPart - called by client code to create new parts from scratch. * @@ -143,7 +162,6 @@ public class POIXMLDocumentPart { * * @param part - The package part that holds xml data representing this sheet. * @see #read(POIXMLFactory, java.util.Map) - * * @since POI 3.14-Beta1 */ public POIXMLDocumentPart(PackagePart part) { @@ -155,9 +173,8 @@ public class POIXMLDocumentPart { * Called by {@link #read(POIXMLFactory, java.util.Map)} when reading in an existing file. * * @param parent - Parent part - * @param part - The package part that holds xml data representing this sheet. + * @param part - The package part that holds xml data representing this sheet. * @see #read(POIXMLFactory, java.util.Map) - * * @since POI 3.14-Beta1 */ public POIXMLDocumentPart(POIXMLDocumentPart parent, PackagePart part) { @@ -167,21 +184,20 @@ public class POIXMLDocumentPart { /** * When you open something like a theme, call this to - * re-base the XML Document onto the core child of the - * current core document - * + * re-base the XML Document onto the core child of the + * current core document + * * @param pkg the package to be rebased - * - * @throws InvalidFormatException if there was an error in the core document relation - * @throws IllegalStateException if there are more than one core document relations + * @throws InvalidFormatException if there was an error in the core document relation + * @throws IllegalStateException if there are more than one core document relations */ protected final void rebase(OPCPackage pkg) throws InvalidFormatException { PackageRelationshipCollection cores = - packagePart.getRelationshipsByType(coreDocumentRel); - if(cores.size() != 1) { + packagePart.getRelationshipsByType(coreDocumentRel); + if (cores.size() != 1) { throw new IllegalStateException( - "Tried to rebase using " + coreDocumentRel + - " but found " + cores.size() + " parts of the right type" + "Tried to rebase using " + coreDocumentRel + + " but found " + cores.size() + " parts of the right type" ); } packagePart = packagePart.getRelatedPart(cores.getRelationship(0)); @@ -192,7 +208,7 @@ public class POIXMLDocumentPart { * * @return the underlying PackagePart */ - public final PackagePart getPackagePart(){ + public final PackagePart getPackagePart() { return packagePart; } @@ -201,7 +217,7 @@ public class POIXMLDocumentPart { * * @return child relations */ - public final List getRelations(){ + public final List getRelations() { List l = new ArrayList<>(); for (RelationPart rp : relations.values()) { l.add(rp.getDocumentPart()); @@ -226,8 +242,7 @@ public class POIXMLDocumentPart { * {@link POIXMLDocumentPart} with a {@link PackageRelationship#getId()} * matching the given parameter value. * - * @param id - * The relation id to look for + * @param id The relation id to look for * @return the target part of the relation, or null, if none exists */ public final POIXMLDocumentPart getRelationById(String id) { @@ -242,10 +257,8 @@ public class POIXMLDocumentPart { * {@link POIXMLDocumentPart} with a {@link PackageRelationship#getId()} * matching the given parameter value. * - * @param id - * The relation id to look for + * @param id The relation id to look for * @return the target relation part, or null, if none exists - * * @since 4.0.0 */ public final RelationPart getRelationPartById(String id) { @@ -257,15 +270,14 @@ public class POIXMLDocumentPart { * {@link PackageRelationship}, that sources from the {@link PackagePart} of * this {@link POIXMLDocumentPart} to the {@link PackagePart} of the given * parameter value.

- * + *

* There can be multiple references to the given {@link POIXMLDocumentPart} * and only the first in the order of creation is returned. * - * @param part - * The {@link POIXMLDocumentPart} for which the according - * relation-id shall be found. + * @param part The {@link POIXMLDocumentPart} for which the according + * relation-id shall be found. * @return The value of the {@link PackageRelationship#getId()} or null, if - * parts are not related. + * parts are not related. */ public final String getRelationId(POIXMLDocumentPart part) { for (RelationPart rp : relations.values()) { @@ -279,12 +291,10 @@ public class POIXMLDocumentPart { /** * Add a new child POIXMLDocumentPart * - * @param relId the preferred relation id, when null the next free relation id will be used + * @param relId the preferred relation id, when null the next free relation id will be used * @param relationshipType the package relationship type - * @param part the child to add - * + * @param part the child to add * @return the new RelationPart - * * @since 3.14-Beta1 */ public final RelationPart addRelation(String relId, POIXMLRelation relationshipType, POIXMLDocumentPart part) { @@ -301,11 +311,11 @@ public class POIXMLDocumentPart { /** * Add a new child POIXMLDocumentPart * - * @param pr the relationship of the child + * @param pr the relationship of the child * @param part the child to add */ private void addRelation(PackageRelationship pr, POIXMLDocumentPart part) { - relations.put(pr.getId(), new RelationPart(pr,part)); + relations.put(pr.getId(), new RelationPart(pr, part)); part.incrementRelationCounter(); } @@ -313,30 +323,28 @@ public class POIXMLDocumentPart { /** * Remove the relation to the specified part in this package and remove the * part, if it is no longer needed.

- * + *

* If there are multiple relationships to the same part, this will only * remove the first relationship in the order of creation. The removal * via the part id ({@link #removeRelation(String)} is preferred. - * + * * @param part the part which relation is to be removed from this document */ - protected final void removeRelation(POIXMLDocumentPart part){ - removeRelation(part,true); + protected final void removeRelation(POIXMLDocumentPart part) { + removeRelation(part, true); } /** * Remove the relation to the specified part in this package and remove the * part, if it is no longer needed and flag is set to true.

- * + *

* If there are multiple relationships to the same part, this will only * remove the first relationship in the order of creation. The removal - * via the part id ({@link #removeRelation(String,boolean)} is preferred. + * via the part id ({@link #removeRelation(String, boolean)} is preferred. * - * @param part - * The related part, to which the relation shall be removed. - * @param removeUnusedParts - * true, if the part shall be removed from the package if not - * needed any longer. + * @param part The related part, to which the relation shall be removed. + * @param removeUnusedParts true, if the part shall be removed from the package if not + * needed any longer. * @return true, if the relation was removed */ protected final boolean removeRelation(POIXMLDocumentPart part, boolean removeUnusedParts) { @@ -347,13 +355,12 @@ public class POIXMLDocumentPart { /** * Remove the relation to the specified part in this package and remove the * part, if it is no longer needed.

- * + *

* If there are multiple relationships to the same part, this will only * remove the first relationship in the order of creation. The removal * via the part id ({@link #removeRelation(String)} is preferred. - * + * * @param partId the part id which relation is to be removed from this document - * * @since 4.0.0 */ protected final void removeRelation(String partId) { @@ -364,13 +371,10 @@ public class POIXMLDocumentPart { * Remove the relation to the specified part in this package and remove the * part, if it is no longer needed and flag is set to true.

* - * @param partId - * The related part id, to which the relation shall be removed. - * @param removeUnusedParts - * true, if the part shall be removed from the package if not - * needed any longer. + * @param partId The related part id, to which the relation shall be removed. + * @param removeUnusedParts true, if the part shall be removed from the package if not + * needed any longer. * @return true, if the relation was removed - * * @since 4.0.0 */ private final boolean removeRelation(String partId, boolean removeUnusedParts) { @@ -401,28 +405,27 @@ public class POIXMLDocumentPart { return true; } - - + /** * Returns the parent POIXMLDocumentPart. All parts except root have not-null parent. * * @return the parent POIXMLDocumentPart or null for the root element. */ - public final POIXMLDocumentPart getParent(){ + public final POIXMLDocumentPart getParent() { return parent; } @Override - public String toString(){ + public String toString() { return packagePart == null ? "" : packagePart.toString(); } /** * Save the content in the underlying package part. * Default implementation is empty meaning that the package part is left unmodified. - * + *

* Sub-classes should override and add logic to marshal the "model" into Ooxml4J. - * + *

* For example, the code saving a generic XML entry may look as follows: *

      * protected void commit() throws IOException {
@@ -433,7 +436,7 @@ public class POIXMLDocumentPart {
      *   out.close();
      * }
      * 
- * + * * @throws IOException a subclass may throw an IOException if the changes can't be committed */ protected void commit() throws IOException { @@ -444,17 +447,21 @@ public class POIXMLDocumentPart { * Save changes in the underlying OOXML package. * Recursively fires {@link #commit()} for each package part * - * @param alreadySaved context set containing already visited nodes - * + * @param alreadySaved context set containing already visited nodes * @throws IOException a related part may throw an IOException if the changes can't be saved */ - protected final void onSave(Set alreadySaved) throws IOException{ + protected final void onSave(Set alreadySaved) throws IOException { + //if part is already committed then return + if (this.isCommited) { + return; + } + // this usually clears out previous content in the part... prepareForCommit(); commit(); alreadySaved.add(this.getPackagePart()); - for(RelationPart rp : relations.values()){ + for (RelationPart rp : relations.values()) { POIXMLDocumentPart p = rp.getDocumentPart(); if (!alreadySaved.contains(p.getPackagePart())) { p.onSave(alreadySaved); @@ -465,13 +472,13 @@ public class POIXMLDocumentPart { /** * Ensure that a memory based package part does not have lingering data from previous * commit() calls. - * + *

* Note: This is overwritten for some objects, as *PictureData seem to store the actual content * in the part directly without keeping a copy like all others therefore we need to handle them differently. */ protected void prepareForCommit() { PackagePart part = this.getPackagePart(); - if(part != null) { + if (part != null) { part.clear(); } } @@ -480,14 +487,13 @@ public class POIXMLDocumentPart { * Create a new child POIXMLDocumentPart * * @param descriptor the part descriptor - * @param factory the factory that will create an instance of the requested relation + * @param factory the factory that will create an instance of the requested relation * @return the created child POIXMLDocumentPart - * @throws PartAlreadyExistsException - * If rule M1.12 is not verified : Packages shall not contain - * equivalent part names and package implementers shall neither - * create nor recognize packages with equivalent part names. + * @throws PartAlreadyExistsException If rule M1.12 is not verified : Packages shall not contain + * equivalent part names and package implementers shall neither + * create nor recognize packages with equivalent part names. */ - public final POIXMLDocumentPart createRelationship(POIXMLRelation descriptor, POIXMLFactory factory){ + public final POIXMLDocumentPart createRelationship(POIXMLRelation descriptor, POIXMLFactory factory) { return createRelationship(descriptor, factory, -1, false).getDocumentPart(); } @@ -495,37 +501,36 @@ public class POIXMLDocumentPart { * Create a new child POIXMLDocumentPart * * @param descriptor the part descriptor - * @param factory the factory that will create an instance of the requested relation - * @param idx part number + * @param factory the factory that will create an instance of the requested relation + * @param idx part number * @return the created child POIXMLDocumentPart - * @throws PartAlreadyExistsException - * If rule M1.12 is not verified : Packages shall not contain - * equivalent part names and package implementers shall neither - * create nor recognize packages with equivalent part names. + * @throws PartAlreadyExistsException If rule M1.12 is not verified : Packages shall not contain + * equivalent part names and package implementers shall neither + * create nor recognize packages with equivalent part names. */ - public final POIXMLDocumentPart createRelationship(POIXMLRelation descriptor, POIXMLFactory factory, int idx){ + public final POIXMLDocumentPart createRelationship(POIXMLRelation descriptor, POIXMLFactory factory, int idx) { return createRelationship(descriptor, factory, idx, false).getDocumentPart(); } - + /** * Identifies the next available part number for a part of the given type, - * if possible, otherwise -1 if none are available. + * if possible, otherwise -1 if none are available. * The found (valid) index can then be safely given to - * {@link #createRelationship(POIXMLRelation, POIXMLFactory, int)} or - * {@link #createRelationship(POIXMLRelation, POIXMLFactory, int, boolean)} - * without naming clashes. + * {@link #createRelationship(POIXMLRelation, POIXMLFactory, int)} or + * {@link #createRelationship(POIXMLRelation, POIXMLFactory, int, boolean)} + * without naming clashes. * If parts with other types are already claiming a name for this relationship - * type (eg a {@link XSSFRelation#CHART} using the drawing part namespace - * normally used by {@link XSSFRelation#DRAWINGS}), those will be considered - * when finding the next spare number. + * type (eg a {@link XSSFRelation#CHART} using the drawing part namespace + * normally used by {@link XSSFRelation#DRAWINGS}), those will be considered + * when finding the next spare number. * * @param descriptor The relationship type to find the part number for - * @param minIdx The minimum free index to assign, use -1 for any + * @param minIdx The minimum free index to assign, use -1 for any * @return The next free part number, or -1 if none available */ protected final int getNextPartNumber(POIXMLRelation descriptor, int minIdx) { OPCPackage pkg = packagePart.getPackage(); - + try { String name = descriptor.getDefaultFileName(); if (name.equals(descriptor.getFileName(9999))) { @@ -539,7 +544,7 @@ public class POIXMLDocumentPart { return 0; } } - + // Default to searching from 1, unless they asked for 0+ int idx = (minIdx < 0) ? 1 : minIdx; int maxIdx = minIdx + pkg.getParts().size(); @@ -562,21 +567,20 @@ public class POIXMLDocumentPart { * Create a new child POIXMLDocumentPart * * @param descriptor the part descriptor - * @param factory the factory that will create an instance of the requested relation - * @param idx part number + * @param factory the factory that will create an instance of the requested relation + * @param idx part number * @param noRelation if true, then no relationship is added. * @return the created child POIXMLDocumentPart - * @throws PartAlreadyExistsException - * If rule M1.12 is not verified : Packages shall not contain - * equivalent part names and package implementers shall neither - * create nor recognize packages with equivalent part names. + * @throws PartAlreadyExistsException If rule M1.12 is not verified : Packages shall not contain + * equivalent part names and package implementers shall neither + * create nor recognize packages with equivalent part names. */ - public final RelationPart createRelationship(POIXMLRelation descriptor, POIXMLFactory factory, int idx, boolean noRelation){ + public final RelationPart createRelationship(POIXMLRelation descriptor, POIXMLFactory factory, int idx, boolean noRelation) { try { PackagePartName ppName = PackagingURIHelper.createPartName(descriptor.getFileName(idx)); PackageRelationship rel = null; PackagePart part = packagePart.getPackage().createPart(ppName, descriptor.getContentType()); - if(!noRelation) { + if (!noRelation) { /* only add to relations, if according relationship is being created. */ rel = packagePart.addRelationship(ppName, TargetMode.INTERNAL, descriptor.getRelation()); } @@ -585,17 +589,17 @@ public class POIXMLDocumentPart { doc.parent = this; if (!noRelation) { /* only add to relations, if according relationship is being created. */ - addRelation(rel,doc); + addRelation(rel, doc); } - return new RelationPart(rel,doc); + return new RelationPart(rel, doc); } catch (PartAlreadyExistsException pae) { - // Return the specific exception so the user knows - // that the name is already taken - throw pae; - } catch (Exception e){ - // Give a general wrapped exception for the problem - throw new POIXMLException(e); + // Return the specific exception so the user knows + // that the name is already taken + throw pae; + } catch (Exception e) { + // Give a general wrapped exception for the problem + throw new POIXMLException(e); } } @@ -603,9 +607,8 @@ public class POIXMLDocumentPart { * Iterate through the underlying PackagePart and create child POIXMLFactory instances * using the specified factory * - * @param factory the factory object that creates POIXMLFactory instances - * @param context context map containing already visited noted keyed by targetURI - * + * @param factory the factory object that creates POIXMLFactory instances + * @param context context map containing already visited noted keyed by targetURI * @throws OpenXML4JException thrown when a related part can't be read */ protected void read(POIXMLFactory factory, Map context) throws OpenXML4JException { @@ -623,12 +626,12 @@ public class POIXMLDocumentPart { // scan breadth-first, so parent-relations are hopefully the shallowest element for (PackageRelationship rel : rels) { - if(rel.getTargetMode() == TargetMode.INTERNAL){ + if (rel.getTargetMode() == TargetMode.INTERNAL) { URI uri = rel.getTargetURI(); // check for internal references (e.g. '#Sheet1!A1') PackagePartName relName; - if(uri.getRawFragment() != null) { + if (uri.getRawFragment() != null) { relName = PackagingURIHelper.createPartName(uri.getPath()); } else { relName = PackagingURIHelper.createPartName(uri); @@ -643,13 +646,18 @@ public class POIXMLDocumentPart { POIXMLDocumentPart childPart = context.get(p); if (childPart == null) { childPart = factory.createDocumentPart(this, p); + //here we are checking if part if embedded and excel then set it to chart class + //so that at the time to writing we can also write updated embedded part + if (this instanceof XDDFChart && childPart instanceof XSSFWorkbook) { + ((XDDFChart) this).setWorkbook((XSSFWorkbook) childPart); + } childPart.parent = this; // already add child to context, so other children can reference it context.put(p, childPart); readLater.add(childPart); } - addRelation(rel,childPart); + addRelation(rel, childPart); } } @@ -672,7 +680,7 @@ public class POIXMLDocumentPart { /** * Fired when a new package part is created - * + * * @throws IOException a subclass may throw an IOException on document creation */ protected void onDocumentCreate() throws IOException { @@ -681,7 +689,7 @@ public class POIXMLDocumentPart { /** * Fired when a package part is read - * + * * @throws IOException a subclass may throw an IOException when a document is read */ protected void onDocumentRead() throws IOException { @@ -690,7 +698,7 @@ public class POIXMLDocumentPart { /** * Fired when a package part is about to be removed from the package - * + * * @throws IOException a subclass may throw an IOException when a document is removed */ protected void onDocumentRemove() throws IOException { @@ -702,19 +710,19 @@ public class POIXMLDocumentPart { *

* This method only exists to allow access to protected {@link POIXMLDocumentPart#onDocumentRead()} * from {@link org.apache.poi.xwpf.usermodel.XWPFDocument} without reflection. It should be removed. - * + * * @param part the part which is to be read - * * @throws IOException if the part can't be read */ - @Internal @Deprecated + @Internal + @Deprecated public static void _invokeOnDocumentRead(POIXMLDocumentPart part) throws IOException { part.onDocumentRead(); } /** * Retrieves the core document part - * + * * @since POI 3.14-Beta1 */ private static PackagePart getPartFromOPCPackage(OPCPackage pkg, String coreDocumentRel) { @@ -723,11 +731,11 @@ public class POIXMLDocumentPart { if (coreRel != null) { PackagePart pp = pkg.getPart(coreRel); if (pp == null) { - throw new POIXMLException("OOXML file structure broken/invalid - core document '"+coreRel.getTargetURI()+"' not found."); + throw new POIXMLException("OOXML file structure broken/invalid - core document '" + coreRel.getTargetURI() + "' not found."); } return pp; } - + coreRel = pkg.getRelationshipsByType(PackageRelationshipTypes.STRICT_CORE_DOCUMENT).getRelationship(0); if (coreRel != null) { throw new POIXMLException("Strict OOXML isn't currently supported, please see bug #57699"); diff --git a/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFChart.java b/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFChart.java index 0f6bad2a0..4c302b83e 100644 --- a/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFChart.java +++ b/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFChart.java @@ -22,6 +22,7 @@ package org.apache.poi.xddf.usermodel.chart; import static org.apache.poi.POIXMLTypeLoader.DEFAULT_XML_OPTIONS; import java.io.IOException; +import java.io.OutputStream; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -29,12 +30,32 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; +import javax.xml.namespace.QName; + +import org.apache.poi.POIXMLDocument; import org.apache.poi.POIXMLDocumentPart; +import org.apache.poi.POIXMLException; +import org.apache.poi.POIXMLFactory; +import org.apache.poi.POIXMLRelation; +import org.apache.poi.openxml4j.exceptions.InvalidFormatException; +import org.apache.poi.openxml4j.exceptions.NotOfficeXmlFileException; import org.apache.poi.openxml4j.opc.PackagePart; +import org.apache.poi.openxml4j.opc.PackageRelationship; +import org.apache.poi.ss.util.CellRangeAddress; +import org.apache.poi.ss.util.CellReference; import org.apache.poi.util.Beta; import org.apache.poi.util.Internal; import org.apache.poi.xddf.usermodel.XDDFShapeProperties; +import org.apache.poi.xslf.usermodel.XSLFChart; +import org.apache.poi.xslf.usermodel.XSLFFactory; +import org.apache.poi.xslf.usermodel.XSLFRelation; +import org.apache.poi.xssf.usermodel.XSSFRow; +import org.apache.poi.xssf.usermodel.XSSFSheet; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; +import org.apache.poi.xwpf.usermodel.XWPFFactory; +import org.apache.poi.xwpf.usermodel.XWPFRelation; import org.apache.xmlbeans.XmlException; +import org.apache.xmlbeans.XmlOptions; import org.openxmlformats.schemas.drawingml.x2006.chart.CTBarChart; import org.openxmlformats.schemas.drawingml.x2006.chart.CTBoolean; import org.openxmlformats.schemas.drawingml.x2006.chart.CTCatAx; @@ -54,6 +75,13 @@ import org.openxmlformats.schemas.drawingml.x2006.main.CTShapeProperties; @Beta public abstract class XDDFChart extends POIXMLDocumentPart { + /** + * Underlying workbook + */ + private XSSFWorkbook workbook; + + private int chartIndex = 0; + protected List axes = new ArrayList<>(); /** @@ -81,8 +109,7 @@ public abstract class XDDFChart extends POIXMLDocumentPart { * Construct a DrawingML chart from a package part. * * @param part the package part holding the chart data, - * the content type must be application/vnd.openxmlformats-officedocument.drawingml.chart+xml - * + * the content type must be application/vnd.openxmlformats-officedocument.drawingml.chart+xml * @since POI 3.14-Beta1 */ protected XDDFChart(PackagePart part) throws IOException, XmlException { @@ -125,7 +152,7 @@ public abstract class XDDFChart extends POIXMLDocumentPart { /** * @return true if only visible cells will be present on the chart, - * false otherwise + * false otherwise */ public boolean isPlotOnlyVisibleCells() { if (chart.isSetPlotVisOnly()) { @@ -137,7 +164,7 @@ public abstract class XDDFChart extends POIXMLDocumentPart { /** * @param only a flag specifying if only visible cells should be - * present on the chart + * present on the chart */ public void setPlotOnlyVisibleCells(boolean only) { if (!chart.isSetPlotVisOnly()) { @@ -206,8 +233,10 @@ public abstract class XDDFChart extends POIXMLDocumentPart { } public void plot(XDDFChartData data) { + XSSFSheet sheet = getSheet(); for (XDDFChartData.Series series : data.getSeries()) { series.plot(); + fillSheet(sheet, series.getCategoryData(), series.getValuesData()); } } @@ -306,18 +335,18 @@ public abstract class XDDFChart extends POIXMLDocumentPart { Map mapValues = Collections.singletonMap(values.getId(), values); final CTPlotArea plotArea = getCTPlotArea(); switch (type) { - case BAR: - return new XDDFBarChartData(plotArea.addNewBarChart(), categories, mapValues); - case LINE: - return new XDDFLineChartData(plotArea.addNewLineChart(), categories, mapValues); - case PIE: - return new XDDFPieChartData(plotArea.addNewPieChart()); - case RADAR: - return new XDDFRadarChartData(plotArea.addNewRadarChart(), categories, mapValues); - case SCATTER: - return new XDDFScatterChartData(plotArea.addNewScatterChart(), categories, mapValues); - default: - return null; + case BAR: + return new XDDFBarChartData(plotArea.addNewBarChart(), categories, mapValues); + case LINE: + return new XDDFLineChartData(plotArea.addNewLineChart(), categories, mapValues); + case PIE: + return new XDDFPieChartData(plotArea.addNewPieChart()); + case RADAR: + return new XDDFRadarChartData(plotArea.addNewRadarChart(), categories, mapValues); + case SCATTER: + return new XDDFScatterChartData(plotArea.addNewScatterChart(), categories, mapValues); + default: + return null; } } @@ -347,4 +376,247 @@ public abstract class XDDFChart extends POIXMLDocumentPart { } } + /** + * method to create relationship with embedded part + * for example writing xlsx file stream into output stream + * + * @param chartRelation relationship object + * @param chartFactory ChartFactory object + * @param chartIndex index used to suffix on file + * @return return relation part which used to write relation in .rels file and get relation id + * @since POI 4.0.0 + */ + public PackageRelationship createRelationshipInChart(POIXMLRelation chartRelation, POIXMLFactory chartFactory, int chartIndex) { + POIXMLDocumentPart documentPart = createRelationship(chartRelation, chartFactory, chartIndex, true).getDocumentPart(); + documentPart.setCommited(true); + return this.addRelation(null, chartRelation, documentPart).getRelationship(); + } + + /** + * if embedded part was null then create new part + * + * @param chartRelation chart relation object + * @param chartWorkbookRelation chart workbook relation object + * @param chartFactory factory object of POIXMLFactory (XWPFFactory/XSLFFactory) + * @return return the new package part + * @throws InvalidFormatException + * @since POI 4.0.0 + */ + private PackagePart createWorksheetPart(POIXMLRelation chartRelation, POIXMLRelation chartWorkbookRelation, POIXMLFactory chartFactory) throws InvalidFormatException { + PackageRelationship xlsx = createRelationshipInChart(XSLFRelation.WORKBOOK_RELATIONSHIP, XSLFFactory.getInstance(), chartIndex); + this.setExternalId(xlsx.getId()); + return getTargetPart(xlsx); + } + + /** + * this method write the XSSFWorkbook object data into embedded excel file + * + * @param workbook XSSFworkbook object + * @throws IOException + * @throws InvalidFormatException + * @since POI 4.0.0 + */ + public void saveWorkbook(XSSFWorkbook workbook) throws IOException, InvalidFormatException { + PackagePart worksheetPart = getWorksheetPart(true); + if (worksheetPart == null) { + POIXMLRelation chartRelation = null; + POIXMLRelation chartWorkbookRelation = null; + POIXMLFactory chartFactory = null; + if (this instanceof XSLFChart) { + chartRelation = XSLFRelation.CHART; + chartWorkbookRelation = XSLFRelation.WORKBOOK_RELATIONSHIP; + chartFactory = XSLFFactory.getInstance(); + } else { + chartRelation = XWPFRelation.CHART; + chartRelation = XWPFRelation.WORKBOOK_RELATIONSHIP; + chartFactory = XWPFFactory.getInstance(); + } + worksheetPart = createWorksheetPart(chartRelation, chartWorkbookRelation, chartFactory); + } + try (OutputStream xlsOut = worksheetPart.getOutputStream()) { + workbook.write(xlsOut); + } + } + + /** + * this method writes the data into sheet + * + * @param sheet sheet of embedded excel + * @param categoryData category values + * @param valuesData data values + * @since POI 4.0.0 + */ + protected void fillSheet(XSSFSheet sheet, XDDFDataSource categoryData, XDDFNumericalDataSource valuesData) { + int numOfPoints = categoryData.getPointCount(); + for (int i = 0; i < numOfPoints; i++) { + XSSFRow row = sheet.createRow(i + 1); // first row is for title + row.createCell(0).setCellValue(categoryData.getPointAt(i).toString()); + row.createCell(1).setCellValue(valuesData.getPointAt(i).doubleValue()); + } + } + + /** + * import content from other chart to created chart + * + * @param other chart object + * @since POI 4.0.0 + */ + public void importContent(XDDFChart other) { + this.chart.set(other.chart); + } + + /** + * save chart xml + */ + @Override + protected void commit() throws IOException { + XmlOptions xmlOptions = new XmlOptions(DEFAULT_XML_OPTIONS); + xmlOptions.setSaveSyntheticDocumentElement(new QName(CTChartSpace.type.getName().getNamespaceURI(), "chartSpace", "c")); + + if (workbook != null) { + try { + saveWorkbook(workbook); + } catch (InvalidFormatException e) { + throw new POIXMLException(e); + } + } + + PackagePart part = getPackagePart(); + try (OutputStream out = part.getOutputStream()) { + chartSpace.save(out, xmlOptions); + } + } + + /** + * set sheet time in excel file + * + * @param title title of sheet + * @return return cell reference + * @since POI 4.0.0 + */ + public CellReference setSheetTitle(String title) { + XSSFSheet sheet = getSheet(); + sheet.createRow(0).createCell(1).setCellValue(title); + return new CellReference(sheet.getSheetName(), 0, 1, true, true); + } + + /** + * @param range + * @return + * @since POI 4.0.0 + */ + public String formatRange(CellRangeAddress range) { + return range.formatAsString(getSheet().getSheetName(), true); + } + + /** + * get sheet object of embedded excel file + * + * @return excel sheet object + * @since POI 4.0.0 + */ + private XSSFSheet getSheet() { + XSSFSheet sheet = null; + try { + sheet = getWorkbook().getSheetAt(0); + } catch (InvalidFormatException ife) { + } catch (IOException ioe) { + } + return sheet; + } + + /** + * default method for worksheet part + * + * @return return embedded worksheet part + * @throws InvalidFormatException + * @since POI 4.0.0 + */ + private PackagePart getWorksheetPart() throws InvalidFormatException { + return getWorksheetPart(false); + } + + /** + * this method is used to get worksheet part + * if call is from saveworkbook method then check isCommitted + * isCommitted variable shows that we are writing xssfworkbook object into output stream of embedded part + * + * @param isCommitted if it's true then it shows that we are writing xssfworkbook object into output stream of embedded part + * @return returns the packagepart of embedded file + * @throws InvalidFormatException + * @since POI 4.0.0 + */ + private PackagePart getWorksheetPart(boolean isCommitted) throws InvalidFormatException { + for (RelationPart part : getRelationParts()) { + if (POIXMLDocument.PACK_OBJECT_REL_TYPE.equals(part.getRelationship().getRelationshipType())) { + if (isCommitted) { + part.getDocumentPart().setCommited(true); + } + return getTargetPart(part.getRelationship()); + } + } + return null; + } + + /** + * @return returns the workbook object of embedded excel file + * @throws IOException + * @throws InvalidFormatException + * @since POI 4.0.0 + */ + public XSSFWorkbook getWorkbook() throws IOException, InvalidFormatException { + if (workbook == null) { + try { + PackagePart worksheetPart = getWorksheetPart(); + if (worksheetPart == null) { + workbook = new XSSFWorkbook(); + workbook.createSheet(); + } else { + workbook = new XSSFWorkbook(worksheetPart.getInputStream()); + } + } catch (NotOfficeXmlFileException e) { + workbook = new XSSFWorkbook(); + workbook.createSheet(); + } + } + return workbook; + } + + /** + * while reading chart from template file then we need to parse and store embedded excel + * file in chart object show that we can modify value according to use + * + * @param workbook workbook object which we read from chart embedded part + * @since POI 4.0.0 + */ + public void setWorkbook(XSSFWorkbook workbook) { + this.workbook = workbook; + } + + /** + * set the relation id of embedded excel relation id into external data relation tag + * + * @param id relation id of embedded excel relation id into external data relation tag + * @since POI 4.0.0 + */ + public void setExternalId(String id) { + getCTChartSpace().addNewExternalData().setId(id); + } + + /** + * @return method return chart index + * @since POI 4.0.0 + */ + protected int getChartIndex() { + return chartIndex; + } + + /** + * set chart index which can be use for relation part + * + * @param chartIndex chart index which can be use for relation part + */ + public void setChartIndex(int chartIndex) { + this.chartIndex = chartIndex; + } } diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XMLSlideShow.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XMLSlideShow.java index 1f66768cd..a935174f1 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XMLSlideShow.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XMLSlideShow.java @@ -67,12 +67,12 @@ import org.openxmlformats.schemas.presentationml.x2006.main.PresentationDocument /** * High level representation of a ooxml slideshow. * This is the first object most users will construct whether - * they are reading or writing a slideshow. It is also the - * top level object for creating new slides/etc. + * they are reading or writing a slideshow. It is also the + * top level object for creating new slides/etc. */ @Beta public class XMLSlideShow extends POIXMLDocument -implements SlideShow { + implements SlideShow { private static final POILogger LOG = POILogFactory.getLogger(XMLSlideShow.class); //arbitrarily selected; may need to increase private static final int MAX_RECORD_LENGTH = 1_000_000; @@ -94,13 +94,13 @@ implements SlideShow { super(pkg); try { - if(getCorePart().getContentType().equals(XSLFRelation.THEME_MANAGER.getContentType())) { + if (getCorePart().getContentType().equals(XSLFRelation.THEME_MANAGER.getContentType())) { rebase(getPackage()); } //build a tree of POIXMLDocumentParts, this presentation being the root load(XSLFFactory.getInstance()); - } catch (Exception e){ + } catch (Exception e) { throw new POIXMLException(e); } } @@ -116,7 +116,7 @@ implements SlideShow { } try { return OPCPackage.open(is); - } catch (Exception e){ + } catch (Exception e) { throw new POIXMLException(e); } finally { IOUtils.closeQuietly(is); @@ -144,17 +144,17 @@ implements SlideShow { } } else if (p instanceof XSLFSlideMaster) { masterMap.put(getRelationId(p), (XSLFSlideMaster) p); - } else if (p instanceof XSLFTableStyles){ - _tableStyles = (XSLFTableStyles)p; + } else if (p instanceof XSLFTableStyles) { + _tableStyles = (XSLFTableStyles) p; } else if (p instanceof XSLFNotesMaster) { - _notesMaster = (XSLFNotesMaster)p; + _notesMaster = (XSLFNotesMaster) p; } else if (p instanceof XSLFCommentAuthors) { - _commentAuthors = (XSLFCommentAuthors)p; + _commentAuthors = (XSLFCommentAuthors) p; } } _charts = new ArrayList<>(chartMap.size()); - for(XSLFChart chart : chartMap.values()) { + for (XSLFChart chart : chartMap.values()) { _charts.add(chart); } @@ -200,10 +200,10 @@ implements SlideShow { @Override public List getPictureData() { - if(_pictures == null){ + if (_pictures == null) { List mediaParts = getPackage().getPartsByName(Pattern.compile("/ppt/media/.*?")); _pictures = new ArrayList<>(mediaParts.size()); - for(PackagePart part : mediaParts){ + for (PackagePart part : mediaParts) { XSLFPictureData pd = new XSLFPictureData(part); pd.setIndex(_pictures.size()); _pictures.add(pd); @@ -226,8 +226,8 @@ implements SlideShow { slideList = _presentation.addNewSldIdLst(); } else { slideList = _presentation.getSldIdLst(); - for(CTSlideIdListEntry slideId : slideList.getSldIdArray()){ - slideNumber = (int)Math.max(slideId.getId() + 1, slideNumber); + for (CTSlideIdListEntry slideId : slideList.getSldIdArray()) { + slideNumber = (int) Math.max(slideId.getId() + 1, slideNumber); cnt++; } @@ -253,7 +253,7 @@ implements SlideShow { private int findNextAvailableFileNameIndex(XSLFRelation relationType, int idx) { // Bug 55791: We also need to check that the resulting file name is not already taken // this can happen when removing/adding slides, notes or charts - while(true) { + while (true) { String fileName = relationType.getFileName(idx); boolean found = false; for (POIXMLDocumentPart relation : getRelations()) { @@ -265,7 +265,7 @@ implements SlideShow { } } - if(!found && + if (!found && getPackage().getPartsByName(Pattern.compile(Pattern.quote(fileName))).size() > 0) { // name is taken => try next one found = true; @@ -305,16 +305,11 @@ implements SlideShow { int chartIdx = findNextAvailableFileNameIndex(XSLFRelation.CHART, _charts.size() + 1); XSLFChart chart = (XSLFChart) createRelationship(XSLFRelation.CHART, XSLFFactory.getInstance(), chartIdx, true).getDocumentPart(); slide.addRelation(null, XSLFRelation.CHART, chart); - createWorkbookRelationship(chart, chartIdx); + chart.setChartIndex(chartIdx); _charts.add(chart); return chart; } - protected PackageRelationship createWorkbookRelationship(XSLFChart chart, int chartIdx) { - POIXMLDocumentPart worksheet = createRelationship(XSLFChart.WORKBOOK_RELATIONSHIP, XSLFFactory.getInstance(), chartIdx, true).getDocumentPart(); - return chart.addRelation(null, XSLFChart.WORKBOOK_RELATIONSHIP, worksheet).getRelationship(); - } - /** * Return notes slide for the specified slide or create new if it does not exist yet. */ @@ -344,7 +339,7 @@ implements SlideShow { // add notes slide to presentation XSLFNotes notesSlide = (XSLFNotes) createRelationship - (relationType, XSLFFactory.getInstance(), slideIndex); + (relationType, XSLFFactory.getInstance(), slideIndex); // link slide and notes slide with each other slide.addRelation(null, relationType, notesSlide); notesSlide.addRelation(null, XSLFRelation.NOTES_MASTER, _notesMaster); @@ -360,7 +355,7 @@ implements SlideShow { */ public void createNotesMaster() { RelationPart rp = createRelationship - (XSLFRelation.NOTES_MASTER, XSLFFactory.getInstance(), 1, false); + (XSLFRelation.NOTES_MASTER, XSLFFactory.getInstance(), 1, false); _notesMaster = rp.getDocumentPart(); CTNotesMasterIdList notesMasterIdList = _presentation.addNewNotesMasterIdLst(); @@ -390,7 +385,7 @@ implements SlideShow { } XSLFTheme theme = (XSLFTheme) createRelationship - (XSLFRelation.THEME, XSLFFactory.getInstance(), themeIndex); + (XSLFRelation.THEME, XSLFFactory.getInstance(), themeIndex); theme.importTheme(getSlides().get(0).getTheme()); _notesMaster.addRelation(null, XSLFRelation.THEME, theme); @@ -433,12 +428,11 @@ implements SlideShow { } /** - * * @param newIndex 0-based index of the slide */ - public void setSlideOrder(XSLFSlide slide, int newIndex){ + public void setSlideOrder(XSLFSlide slide, int newIndex) { int oldIndex = _slides.indexOf(slide); - if(oldIndex == -1) { + if (oldIndex == -1) { throw new IllegalArgumentException("Slide not found"); } if (oldIndex == newIndex) { @@ -461,7 +455,7 @@ implements SlideShow { sldIdLst.setSldIdArray(entries); } - public XSLFSlide removeSlide(int index){ + public XSLFSlide removeSlide(int index) { XSLFSlide slide = _slides.remove(index); removeRelation(slide); _presentation.getSldIdLst().removeSldId(index); @@ -479,15 +473,15 @@ implements SlideShow { } @Override - public Dimension getPageSize(){ + public Dimension getPageSize() { CTSlideSize sz = _presentation.getSldSz(); int cx = sz.getCx(); int cy = sz.getCy(); - return new Dimension((int)Units.toPoints(cx), (int)Units.toPoints(cy)); + return new Dimension((int) Units.toPoints(cx), (int) Units.toPoints(cy)); } @Override - public void setPageSize(Dimension pgSize){ + public void setPageSize(Dimension pgSize) { CTSlideSize sz = CTSlideSize.Factory.newInstance(); sz.setCx(Units.toEMU(pgSize.getWidth())); sz.setCy(Units.toEMU(pgSize.getHeight())); @@ -496,16 +490,15 @@ implements SlideShow { @Internal - public CTPresentation getCTPresentation(){ + public CTPresentation getCTPresentation() { return _presentation; } /** * Adds a picture to the workbook. * - * @param pictureData The bytes of the picture - * @param format The format of the picture. - * + * @param pictureData The bytes of the picture + * @param format The format of the picture. * @return the picture data */ @Override @@ -518,7 +511,7 @@ implements SlideShow { int imageNumber = _pictures.size(); XSLFRelation relType = XSLFPictureData.getRelationForType(format); if (relType == null) { - throw new IllegalArgumentException("Picture type "+format+" is not supported."); + throw new IllegalArgumentException("Picture type " + format + " is not supported."); } img = createRelationship(relType, XSLFFactory.getInstance(), imageNumber + 1, true).getDocumentPart(); @@ -538,15 +531,13 @@ implements SlideShow { /** * Adds a picture to the slideshow. * - * @param is The stream to read image from - * @param format The format of the picture - * + * @param is The stream to read image from + * @param format The format of the picture * @return the picture data * @since 3.15 beta 2 */ @Override - public XSLFPictureData addPicture(InputStream is, PictureType format) throws IOException - { + public XSLFPictureData addPicture(InputStream is, PictureType format) throws IOException { return addPicture(IOUtils.toByteArray(is), format); } @@ -554,15 +545,13 @@ implements SlideShow { /** * Adds a picture to the presentation. * - * @param pict The file containing the image to add - * @param format The format of the picture. - * + * @param pict The file containing the image to add + * @param format The format of the picture. * @return the picture data * @since 3.15 beta 2 */ @Override - public XSLFPictureData addPicture(File pict, PictureType format) throws IOException - { + public XSLFPictureData addPicture(File pict, PictureType format) throws IOException { int length = (int) pict.length(); byte[] data = IOUtils.safelyAllocate(length, MAX_RECORD_LENGTH); try (InputStream is = new FileInputStream(pict)) { @@ -583,10 +572,10 @@ implements SlideShow { public XSLFPictureData findPictureData(byte[] pictureData) { long checksum = IOUtils.calculateChecksum(pictureData); byte cs[] = new byte[LittleEndianConsts.LONG_SIZE]; - LittleEndian.putLong(cs,0,checksum); + LittleEndian.putLong(cs, 0, checksum); - for(XSLFPictureData pic : getPictureData()){ - if(Arrays.equals(pic.getChecksum(), cs)) { + for (XSLFPictureData pic : getPictureData()) { + if (Arrays.equals(pic.getChecksum(), cs)) { return pic; } } @@ -597,7 +586,7 @@ implements SlideShow { /** * Scan the master slides for the first slide layout with the given name. * - * @param name The layout name (case-insensitive). Cannot be null. + * @param name The layout name (case-insensitive). Cannot be null. * @return the first layout found or null on failure */ public XSLFSlideLayout findLayout(String name) { @@ -611,23 +600,23 @@ implements SlideShow { } - public XSLFTableStyles getTableStyles(){ + public XSLFTableStyles getTableStyles() { return _tableStyles; } CTTextParagraphProperties getDefaultParagraphStyle(int level) { XmlObject[] o = _presentation.selectPath( "declare namespace p='http://schemas.openxmlformats.org/presentationml/2006/main' " + - "declare namespace a='http://schemas.openxmlformats.org/drawingml/2006/main' " + - ".//p:defaultTextStyle/a:lvl" +(level+1)+ "pPr"); - if(o.length == 1){ - return (CTTextParagraphProperties)o[0]; + "declare namespace a='http://schemas.openxmlformats.org/drawingml/2006/main' " + + ".//p:defaultTextStyle/a:lvl" + (level + 1) + "pPr"); + if (o.length == 1) { + return (CTTextParagraphProperties) o[0]; } return null; } @Override - public MasterSheet createMasterSheet() throws IOException { + public MasterSheet createMasterSheet() throws IOException { // TODO: implement! throw new UnsupportedOperationException(); } diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFChart.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFChart.java index 30e594df8..a04b61e14 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFChart.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFChart.java @@ -19,56 +19,23 @@ package org.apache.poi.xslf.usermodel; -import static org.apache.poi.POIXMLTypeLoader.DEFAULT_XML_OPTIONS; - import java.io.IOException; -import java.io.OutputStream; - -import javax.xml.namespace.QName; import org.apache.poi.POIXMLDocument; -import org.apache.poi.POIXMLDocumentPart; -import org.apache.poi.POIXMLException; import org.apache.poi.POIXMLRelation; -import org.apache.poi.openxml4j.exceptions.InvalidFormatException; -import org.apache.poi.openxml4j.exceptions.NotOfficeXmlFileException; import org.apache.poi.openxml4j.opc.PackagePart; -import org.apache.poi.ss.util.CellRangeAddress; -import org.apache.poi.ss.util.CellReference; import org.apache.poi.util.Beta; import org.apache.poi.xddf.usermodel.chart.XDDFChart; -import org.apache.poi.xddf.usermodel.chart.XDDFChartData; -import org.apache.poi.xddf.usermodel.chart.XDDFDataSource; -import org.apache.poi.xddf.usermodel.chart.XDDFNumericalDataSource; -import org.apache.poi.xssf.usermodel.XSSFRow; -import org.apache.poi.xssf.usermodel.XSSFSheet; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.apache.xmlbeans.XmlException; -import org.apache.xmlbeans.XmlOptions; -import org.openxmlformats.schemas.drawingml.x2006.chart.CTChartSpace; import org.openxmlformats.schemas.drawingml.x2006.chart.CTTitle; import org.openxmlformats.schemas.drawingml.x2006.main.CTTextBody; /** * Represents a Chart in a .pptx presentation - * - * */ @Beta public final class XSLFChart extends XDDFChart { - protected static final POIXMLRelation WORKBOOK_RELATIONSHIP = new POIXMLRelation( - "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", - POIXMLDocument.PACK_OBJECT_REL_TYPE, - "/ppt/embeddings/Microsoft_Excel_Worksheet#.xlsx", - XSSFWorkbook.class - ){}; - - - /** - * Underlying workbook - */ - private XSSFWorkbook workbook; - /** * Construct a PresentationML chart. @@ -81,8 +48,7 @@ public final class XSLFChart extends XDDFChart { * Construct a PresentationML chart from a package part. * * @param part the package part holding the chart data, - * the content type must be application/vnd.openxmlformats-officedocument.drawingml.chart+xml - * + * the content type must be application/vnd.openxmlformats-officedocument.drawingml.chart+xml * @since POI 3.14-Beta1 */ protected XSLFChart(PackagePart part) throws IOException, XmlException { @@ -110,118 +76,4 @@ public final class XSLFChart extends XDDFChart { }; } } - - public CellReference setSheetTitle(String title) { - XSSFSheet sheet = getSheet(); - sheet.createRow(0).createCell(1).setCellValue(title); - return new CellReference(sheet.getSheetName(), 0, 1, true, true); - } - - public String formatRange(CellRangeAddress range) { - return range.formatAsString(getSheet().getSheetName(), true); - } - - private XSSFSheet getSheet() { - XSSFSheet sheet = null; - try { - sheet = getWorkbook().getSheetAt(0); - } catch (InvalidFormatException ife) { - } catch (IOException ioe) { - } - return sheet; - } - - private PackagePart getWorksheetPart() throws InvalidFormatException { - for (RelationPart part : getRelationParts()) { - if (WORKBOOK_RELATIONSHIP.getRelation().equals(part.getRelationship().getRelationshipType())) { - return getTargetPart(part.getRelationship()); - } - } - return null; - } - - protected XSSFWorkbook getWorkbook() throws IOException, InvalidFormatException { - if (workbook == null) { - try { - PackagePart worksheetPart = getWorksheetPart(); - if (worksheetPart == null) { - workbook = new XSSFWorkbook(); - workbook.createSheet(); - } else { - workbook = new XSSFWorkbook(worksheetPart.getInputStream()); - } - } catch (NotOfficeXmlFileException e) { - workbook = new XSSFWorkbook(); - workbook.createSheet(); - } - } - return workbook; - } - - private XMLSlideShow getSlideShow() { - POIXMLDocumentPart p = getParent(); - while(p != null) { - if(p instanceof XMLSlideShow){ - return (XMLSlideShow)p; - } - p = p.getParent(); - } - throw new IllegalStateException("SlideShow was not found"); - } - - private PackagePart createWorksheetPart() throws InvalidFormatException { - Integer chartIdx = XSLFRelation.CHART.getFileNameIndex(this); - return getTargetPart(getSlideShow().createWorkbookRelationship(this, chartIdx)); - } - - protected void saveWorkbook(XSSFWorkbook workbook) throws IOException, InvalidFormatException { - PackagePart worksheetPart = getWorksheetPart(); - if (worksheetPart == null) { - worksheetPart = createWorksheetPart(); - } - try (OutputStream xlsOut = worksheetPart.getOutputStream()) { - workbook.write(xlsOut); - } - } - - private void fillSheet(XSSFSheet sheet, XDDFDataSource categoryData, XDDFNumericalDataSource valuesData) { - int numOfPoints = categoryData.getPointCount(); - for (int i = 0; i < numOfPoints; i++) { - XSSFRow row = sheet.createRow(i + 1); // first row is for title - row.createCell(0).setCellValue(categoryData.getPointAt(i).toString()); - row.createCell(1).setCellValue(valuesData.getPointAt(i).doubleValue()); - } - } - - @Override - public void plot(XDDFChartData data) { - super.plot(data); - XSSFSheet sheet = getSheet(); - for(XDDFChartData.Series series : data.getSeries()) { - fillSheet(sheet, series.getCategoryData(), series.getValuesData()); - } - } - - public void importContent(XSLFChart other) { - this.chart.set(other.chart); - } - - @Override - protected void commit() throws IOException { - XmlOptions xmlOptions = new XmlOptions(DEFAULT_XML_OPTIONS); - xmlOptions.setSaveSyntheticDocumentElement(new QName(CTChartSpace.type.getName().getNamespaceURI(), "chartSpace", "c")); - - if (workbook != null) { - try { - saveWorkbook(workbook); - } catch (InvalidFormatException e) { - throw new POIXMLException(e); - } - } - - PackagePart part = getPackagePart(); - try (OutputStream out = part.getOutputStream()) { - chartSpace.save(out, xmlOptions); - } - } } diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFRelation.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFRelation.java index 54ede0913..77c91b62d 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFRelation.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFRelation.java @@ -19,103 +19,105 @@ package org.apache.poi.xslf.usermodel; import java.util.HashMap; import java.util.Map; +import org.apache.poi.POIXMLDocument; import org.apache.poi.POIXMLDocumentPart; import org.apache.poi.POIXMLRelation; import org.apache.poi.sl.usermodel.PictureData.PictureType; import org.apache.poi.util.Beta; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; @Beta public class XSLFRelation extends POIXMLRelation { - /** - * A map to lookup POIXMLRelation by its relation type - */ - private static final Map _table = new HashMap<>(); - - public static final XSLFRelation MAIN = new XSLFRelation( - "application/vnd.openxmlformats-officedocument.presentationml.presentation.main+xml", - null, null, null - ); - - public static final XSLFRelation MACRO = new XSLFRelation( - "application/vnd.ms-powerpoint.slideshow.macroEnabled.main+xml", - null, null, null - ); - - public static final XSLFRelation MACRO_TEMPLATE = new XSLFRelation( - "application/vnd.ms-powerpoint.template.macroEnabled.main+xml", - null, null, null - ); - - public static final XSLFRelation PRESENTATIONML = new XSLFRelation( - "application/vnd.openxmlformats-officedocument.presentationml.slideshow.main+xml", - null, null, null - ); - - public static final XSLFRelation PRESENTATIONML_TEMPLATE = new XSLFRelation( - "application/vnd.openxmlformats-officedocument.presentationml.template.main+xml", - null, null, null - ); - - public static final XSLFRelation PRESENTATION_MACRO = new XSLFRelation( - "application/vnd.ms-powerpoint.presentation.macroEnabled.main+xml", - null, null, null - ); - - public static final XSLFRelation THEME_MANAGER = new XSLFRelation( - "application/vnd.openxmlformats-officedocument.themeManager+xml", - null, null, null - ); - - public static final XSLFRelation NOTES = new XSLFRelation( - "application/vnd.openxmlformats-officedocument.presentationml.notesSlide+xml", - "http://schemas.openxmlformats.org/officeDocument/2006/relationships/notesSlide", - "/ppt/notesSlides/notesSlide#.xml", - XSLFNotes.class - ); - - public static final XSLFRelation SLIDE = new XSLFRelation( - "application/vnd.openxmlformats-officedocument.presentationml.slide+xml", - "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide", - "/ppt/slides/slide#.xml", - XSLFSlide.class - ); - - public static final XSLFRelation SLIDE_LAYOUT = new XSLFRelation( - "application/vnd.openxmlformats-officedocument.presentationml.slideLayout+xml", - "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout", - "/ppt/slideLayouts/slideLayout#.xml", - XSLFSlideLayout.class - ); - - public static final XSLFRelation SLIDE_MASTER = new XSLFRelation( - "application/vnd.openxmlformats-officedocument.presentationml.slideMaster+xml", - "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideMaster", - "/ppt/slideMasters/slideMaster#.xml", - XSLFSlideMaster.class - ); + /** + * A map to lookup POIXMLRelation by its relation type + */ + private static final Map _table = new HashMap<>(); - public static final XSLFRelation NOTES_MASTER = new XSLFRelation( - "application/vnd.openxmlformats-officedocument.presentationml.notesMaster+xml", - "http://schemas.openxmlformats.org/officeDocument/2006/relationships/notesMaster", - "/ppt/notesMasters/notesMaster#.xml", - XSLFNotesMaster.class - ); + public static final XSLFRelation MAIN = new XSLFRelation( + "application/vnd.openxmlformats-officedocument.presentationml.presentation.main+xml", + null, null, null + ); + + public static final XSLFRelation MACRO = new XSLFRelation( + "application/vnd.ms-powerpoint.slideshow.macroEnabled.main+xml", + null, null, null + ); + + public static final XSLFRelation MACRO_TEMPLATE = new XSLFRelation( + "application/vnd.ms-powerpoint.template.macroEnabled.main+xml", + null, null, null + ); + + public static final XSLFRelation PRESENTATIONML = new XSLFRelation( + "application/vnd.openxmlformats-officedocument.presentationml.slideshow.main+xml", + null, null, null + ); + + public static final XSLFRelation PRESENTATIONML_TEMPLATE = new XSLFRelation( + "application/vnd.openxmlformats-officedocument.presentationml.template.main+xml", + null, null, null + ); + + public static final XSLFRelation PRESENTATION_MACRO = new XSLFRelation( + "application/vnd.ms-powerpoint.presentation.macroEnabled.main+xml", + null, null, null + ); + + public static final XSLFRelation THEME_MANAGER = new XSLFRelation( + "application/vnd.openxmlformats-officedocument.themeManager+xml", + null, null, null + ); + + public static final XSLFRelation NOTES = new XSLFRelation( + "application/vnd.openxmlformats-officedocument.presentationml.notesSlide+xml", + "http://schemas.openxmlformats.org/officeDocument/2006/relationships/notesSlide", + "/ppt/notesSlides/notesSlide#.xml", + XSLFNotes.class + ); + + public static final XSLFRelation SLIDE = new XSLFRelation( + "application/vnd.openxmlformats-officedocument.presentationml.slide+xml", + "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide", + "/ppt/slides/slide#.xml", + XSLFSlide.class + ); + + public static final XSLFRelation SLIDE_LAYOUT = new XSLFRelation( + "application/vnd.openxmlformats-officedocument.presentationml.slideLayout+xml", + "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout", + "/ppt/slideLayouts/slideLayout#.xml", + XSLFSlideLayout.class + ); + + public static final XSLFRelation SLIDE_MASTER = new XSLFRelation( + "application/vnd.openxmlformats-officedocument.presentationml.slideMaster+xml", + "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideMaster", + "/ppt/slideMasters/slideMaster#.xml", + XSLFSlideMaster.class + ); + + public static final XSLFRelation NOTES_MASTER = new XSLFRelation( + "application/vnd.openxmlformats-officedocument.presentationml.notesMaster+xml", + "http://schemas.openxmlformats.org/officeDocument/2006/relationships/notesMaster", + "/ppt/notesMasters/notesMaster#.xml", + XSLFNotesMaster.class + ); + + public static final XSLFRelation COMMENTS = new XSLFRelation( + "application/vnd.openxmlformats-officedocument.presentationml.comments+xml", + "http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments", + "/ppt/comments/comment#.xml", + XSLFComments.class + ); + + public static final XSLFRelation COMMENT_AUTHORS = new XSLFRelation( + "application/vnd.openxmlformats-officedocument.presentationml.commentAuthors+xml", + "http://schemas.openxmlformats.org/officeDocument/2006/relationships/commentAuthors", + "/ppt/commentAuthors.xml", + XSLFCommentAuthors.class + ); - public static final XSLFRelation COMMENTS = new XSLFRelation( - "application/vnd.openxmlformats-officedocument.presentationml.comments+xml", - "http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments", - "/ppt/comments/comment#.xml", - XSLFComments.class - ); - - public static final XSLFRelation COMMENT_AUTHORS = new XSLFRelation( - "application/vnd.openxmlformats-officedocument.presentationml.commentAuthors+xml", - "http://schemas.openxmlformats.org/officeDocument/2006/relationships/commentAuthors", - "/ppt/commentAuthors.xml", - XSLFCommentAuthors.class - ); - public static final XSLFRelation HYPERLINK = new XSLFRelation( null, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink", @@ -123,19 +125,26 @@ public class XSLFRelation extends POIXMLRelation { null ); - public static final XSLFRelation THEME = new XSLFRelation( - "application/vnd.openxmlformats-officedocument.theme+xml", - "http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme", - "/ppt/theme/theme#.xml", - XSLFTheme.class - ); - - public static final XSLFRelation VML_DRAWING = new XSLFRelation( - "application/vnd.openxmlformats-officedocument.vmlDrawing", - "http://schemas.openxmlformats.org/officeDocument/2006/relationships/vmlDrawing", - "/ppt/drawings/vmlDrawing#.vml", - null - ); + public static final XSLFRelation THEME = new XSLFRelation( + "application/vnd.openxmlformats-officedocument.theme+xml", + "http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme", + "/ppt/theme/theme#.xml", + XSLFTheme.class + ); + + public static final XSLFRelation VML_DRAWING = new XSLFRelation( + "application/vnd.openxmlformats-officedocument.vmlDrawing", + "http://schemas.openxmlformats.org/officeDocument/2006/relationships/vmlDrawing", + "/ppt/drawings/vmlDrawing#.vml", + null + ); + + public static final XSLFRelation WORKBOOK_RELATIONSHIP = new XSLFRelation( + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + POIXMLDocument.PACK_OBJECT_REL_TYPE, + "/ppt/embeddings/Microsoft_Excel_Worksheet#.xlsx", + XSSFWorkbook.class + ); public static final XSLFRelation CHART = new XSLFRelation( "application/vnd.openxmlformats-officedocument.drawingml.chart+xml", @@ -145,113 +154,113 @@ public class XSLFRelation extends POIXMLRelation { ); public static final XSLFRelation IMAGE_EMF = new XSLFRelation( - PictureType.EMF.contentType, - "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image", - "/ppt/media/image#.emf", - XSLFPictureData.class + PictureType.EMF.contentType, + "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image", + "/ppt/media/image#.emf", + XSLFPictureData.class ); public static final XSLFRelation IMAGE_WMF = new XSLFRelation( - PictureType.WMF.contentType, - "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image", - "/ppt/media/image#.wmf", - XSLFPictureData.class + PictureType.WMF.contentType, + "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image", + "/ppt/media/image#.wmf", + XSLFPictureData.class ); public static final XSLFRelation IMAGE_PICT = new XSLFRelation( - PictureType.PICT.contentType, - "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image", - "/ppt/media/image#.pict", - XSLFPictureData.class + PictureType.PICT.contentType, + "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image", + "/ppt/media/image#.pict", + XSLFPictureData.class ); public static final XSLFRelation IMAGE_JPEG = new XSLFRelation( - PictureType.JPEG.contentType, - "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image", - "/ppt/media/image#.jpeg", - XSLFPictureData.class + PictureType.JPEG.contentType, + "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image", + "/ppt/media/image#.jpeg", + XSLFPictureData.class ); public static final XSLFRelation IMAGE_PNG = new XSLFRelation( - PictureType.PNG.contentType, - "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image", - "/ppt/media/image#.png", - XSLFPictureData.class + PictureType.PNG.contentType, + "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image", + "/ppt/media/image#.png", + XSLFPictureData.class ); public static final XSLFRelation IMAGE_DIB = new XSLFRelation( - PictureType.DIB.contentType, - "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image", - "/ppt/media/image#.dib", - XSLFPictureData.class + PictureType.DIB.contentType, + "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image", + "/ppt/media/image#.dib", + XSLFPictureData.class ); public static final XSLFRelation IMAGE_GIF = new XSLFRelation( - PictureType.GIF.contentType, - "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image", - "/ppt/media/image#.gif", - XSLFPictureData.class + PictureType.GIF.contentType, + "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image", + "/ppt/media/image#.gif", + XSLFPictureData.class ); public static final XSLFRelation IMAGE_TIFF = new XSLFRelation( - PictureType.TIFF.contentType, - "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image", - "/ppt/media/image#.tiff", - XSLFPictureData.class + PictureType.TIFF.contentType, + "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image", + "/ppt/media/image#.tiff", + XSLFPictureData.class ); public static final XSLFRelation IMAGE_EPS = new XSLFRelation( - PictureType.EPS.contentType, - "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image", - "/ppt/media/image#.eps", - XSLFPictureData.class + PictureType.EPS.contentType, + "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image", + "/ppt/media/image#.eps", + XSLFPictureData.class ); public static final XSLFRelation IMAGE_BMP = new XSLFRelation( - PictureType.BMP.contentType, - "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image", - "/ppt/media/image#.bmp", - XSLFPictureData.class + PictureType.BMP.contentType, + "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image", + "/ppt/media/image#.bmp", + XSLFPictureData.class ); public static final XSLFRelation IMAGE_WPG = new XSLFRelation( - PictureType.WPG.contentType, - "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image", - "/ppt/media/image#.wpg", - XSLFPictureData.class + PictureType.WPG.contentType, + "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image", + "/ppt/media/image#.wpg", + XSLFPictureData.class ); public static final XSLFRelation IMAGE_WDP = new XSLFRelation( - PictureType.WDP.contentType, - "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image", - "/ppt/media/image#.wdp", - XSLFPictureData.class + PictureType.WDP.contentType, + "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image", + "/ppt/media/image#.wdp", + XSLFPictureData.class ); public static final XSLFRelation IMAGES = new XSLFRelation( null, - "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image", - null, - XSLFPictureData.class + "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image", + null, + XSLFPictureData.class ); public static final XSLFRelation TABLE_STYLES = new XSLFRelation( - "application/vnd.openxmlformats-officedocument.presentationml.tableStyles+xml", - "http://schemas.openxmlformats.org/officeDocument/2006/relationships/tableStyles", - "/ppt/tableStyles.xml", - XSLFTableStyles.class + "application/vnd.openxmlformats-officedocument.presentationml.tableStyles+xml", + "http://schemas.openxmlformats.org/officeDocument/2006/relationships/tableStyles", + "/ppt/tableStyles.xml", + XSLFTableStyles.class ); public static final XSLFRelation OLE_OBJECT = new XSLFRelation( - "application/vnd.openxmlformats-officedocument.oleObject", - "http://schemas.openxmlformats.org/officeDocument/2006/relationships/oleObject", - "/ppt/embeddings/oleObject#.bin", - XSLFObjectData.class + "application/vnd.openxmlformats-officedocument.oleObject", + "http://schemas.openxmlformats.org/officeDocument/2006/relationships/oleObject", + "/ppt/embeddings/oleObject#.bin", + XSLFObjectData.class ); - - - private XSLFRelation(String type, String rel, String defaultName, Class cls) { - super(type, rel, defaultName, cls); - _table.put(rel, this); - } - /** - * Get POIXMLRelation by relation type - * - * @param rel relation type, for example, - * http://schemas.openxmlformats.org/officeDocument/2006/relationships/image - * @return registered POIXMLRelation or null if not found - */ - public static XSLFRelation getInstance(String rel){ - return _table.get(rel); - } + + private XSLFRelation(String type, String rel, String defaultName, Class cls) { + super(type, rel, defaultName, cls); + _table.put(rel, this); + } + + /** + * Get POIXMLRelation by relation type + * + * @param rel relation type, for example, + * http://schemas.openxmlformats.org/officeDocument/2006/relationships/image + * @return registered POIXMLRelation or null if not found + */ + public static XSLFRelation getInstance(String rel) { + return _table.get(rel); + } } diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFWorkbook.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFWorkbook.java index 78ec93481..6bd11e93d 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFWorkbook.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFWorkbook.java @@ -329,6 +329,15 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook { this(openPackage(path)); } + /** + * Constructs a XSSFWorkbook object using Package Part. + * @param part package part + * @since POI 4.0.0 + */ + public XSSFWorkbook(PackagePart part) throws IOException { + this(part.getInputStream()); + } + protected void beforeDocumentRead() { // Ensure it isn't a XLSB file, which we don't support if (getCorePart().getContentType().equals(XSSFRelation.XLSB_BINARY_WORKBOOK.getContentType())) { diff --git a/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFChart.java b/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFChart.java index dbc68c215..b48a3e02c 100644 --- a/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFChart.java +++ b/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFChart.java @@ -17,13 +17,8 @@ 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 javax.xml.namespace.QName; import org.apache.poi.POIXMLException; import org.apache.poi.openxml4j.opc.PackagePart; @@ -31,40 +26,52 @@ import org.apache.poi.util.Beta; import org.apache.poi.util.IOUtils; import org.apache.poi.xddf.usermodel.chart.XDDFChart; import org.apache.xmlbeans.XmlException; -import org.apache.xmlbeans.XmlOptions; -import org.openxmlformats.schemas.drawingml.x2006.chart.CTChartSpace; +import org.openxmlformats.schemas.drawingml.x2006.wordprocessingDrawing.CTInline; /** * Represents a Chart in a .docx file */ @Beta public class XWPFChart extends XDDFChart { + /** + * default width of chart in emu + */ + public static final int DEFAULT_WIDTH = 500000; + + /** + * default height of chart in emu + */ + public static final int DEFAULT_HEIGHT = 500000; // lazy initialization private Long checksum; + /** + * this object is used to modify drawing properties + */ + private CTInline ctInline; + + /** + * constructor to + * Create a new chart in document + * + * @since POI 4.0.0 + */ + protected XWPFChart() { + super(); + } + /** * Construct a chart from a package part. * * @param part the package part holding the chart data, - * the content type must be application/vnd.openxmlformats-officedocument.drawingml.chart+xml - * + * the content type must be application/vnd.openxmlformats-officedocument.drawingml.chart+xml * @since POI 4.0.0 */ protected XWPFChart(PackagePart part) throws IOException, XmlException { super(part); } - @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; @@ -120,4 +127,160 @@ public class XWPFChart extends XDDFChart { public int hashCode() { return getChecksum().hashCode(); } + + /** + * initialize in line object + * + * @param inline this object is used to adjust the margin and dimension of chart + * @since POI 4.0.0 + */ + protected void setAttachTo(CTInline ctInline) { + this.ctInline = ctInline; + } + + /** + * set chart height + * + * @param height height of chart + * @since POI 4.0.0 + */ + public void setChartHeight(long height) { + ctInline.getExtent().setCy(height); + } + + /** + * set chart width + * + * @param width width of chart + * @since POI 4.0.0 + */ + public void setChartWidth(long width) { + ctInline.getExtent().setCx(width); + } + + /** + * get chart height + * + * @since POI 4.0.0 + */ + public long getChartHeight() { + return ctInline.getExtent().getCy(); + } + + /** + * get chart width + * + * @since POI 4.0.0 + */ + public long getChartWidth() { + return ctInline.getExtent().getCx(); + } + + /** + * set chart height and width + * + * @param width width of chart + * @param height height of chart + * @since POI 4.0.0 + */ + public void setChartBoundingBox(long width, long height) { + this.setChartWidth(width); + this.setChartHeight(height); + } + + /** + * set margin from top + * + * @param margin margin from top + * @since POI 4.0.0 + */ + public void setChartTopMargin(long margin) { + ctInline.setDistT(margin); + } + + /** + * get margin from Top + * + * @param margin + * @since POI 4.0.0 + */ + public long getChartTopMargin(long margin) { + return ctInline.getDistT(); + } + + /** + * set margin from bottom + * + * @param margin margin from Bottom + * @since POI 4.0.0 + */ + public void setChartBottomMargin(long margin) { + ctInline.setDistB(margin); + } + + /** + * get margin from Bottom + * + * @param margin + * @since POI 4.0.0 + */ + public long getChartBottomMargin(long margin) { + return ctInline.getDistB(); + } + + /** + * set margin from left + * + * @param margin margin from left + * @since POI 4.0.0 + */ + public void setChartLeftMargin(long margin) { + ctInline.setDistL(margin); + } + + /** + * get margin from left + * + * @param margin + * @since POI 4.0.0 + */ + public long getChartLeftMargin(long margin) { + return ctInline.getDistL(); + } + + /** + * set margin from Right + * + * @param margin from right + * @since POI 4.0.0 + */ + public void setChartRightMargin(long margin) { + ctInline.setDistR(margin); + } + + /** + * get margin from Right + * + * @param margin + * @since POI 4.0.0 + */ + public long getChartRightMargin(long margin) { + return ctInline.getDistR(); + } + + /** + * set chart margin + * + * @param top margin from top + * @param right margin from right + * @param bottom margin from bottom + * @param left margin from left + * @since POI 4.0.0 + */ + public void setChartMargin(long top, long right, long bottom, long left) { + this.setChartBottomMargin(bottom); + this.setChartRightMargin(right); + this.setChartLeftMargin(left); + this.setChartRightMargin(right); + } } diff --git a/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFDocument.java b/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFDocument.java index 31412e8c8..824bdb00f 100644 --- a/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFDocument.java +++ b/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFDocument.java @@ -63,6 +63,7 @@ import org.apache.xmlbeans.XmlCursor; import org.apache.xmlbeans.XmlException; import org.apache.xmlbeans.XmlObject; import org.apache.xmlbeans.XmlOptions; +import org.openxmlformats.schemas.drawingml.x2006.wordprocessingDrawing.CTInline; import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTBody; import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTComment; import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTDocument1; @@ -245,7 +246,6 @@ public class XWPFDocument extends POIXMLDocument implements Document, IBody { } else if (relation.equals(XWPFRelation.CHART.getRelation())) { //now we can use all methods to modify charts in XWPFDocument XWPFChart chartData = (XWPFChart) p; -// chartData.onDocumentRead(); // ??? there is nothing to be done there!!! charts.add(chartData); } else if (relation.equals(XWPFRelation.GLOSSARY_DOCUMENT.getRelation())) { // We don't currently process the glossary itself @@ -356,6 +356,7 @@ public class XWPFDocument extends POIXMLDocument implements Document, IBody { public List getCharts() { return Collections.unmodifiableList(charts); } + /** * @see org.apache.poi.xwpf.usermodel.IBody#getTableArray(int) */ @@ -375,7 +376,7 @@ public class XWPFDocument extends POIXMLDocument implements Document, IBody { } public XWPFFooter getFooterArray(int pos) { - if(pos >=0 && pos < footers.size()) { + if (pos >= 0 && pos < footers.size()) { return footers.get(pos); } return null; @@ -389,7 +390,7 @@ public class XWPFDocument extends POIXMLDocument implements Document, IBody { } public XWPFHeader getHeaderArray(int pos) { - if(pos >=0 && pos < headers.size()) { + if (pos >= 0 && pos < headers.size()) { return headers.get(pos); } return null; @@ -468,11 +469,12 @@ public class XWPFDocument extends POIXMLDocument implements Document, IBody { public XWPFHeaderFooterPolicy getHeaderFooterPolicy() { return headerFooterPolicy; } + public XWPFHeaderFooterPolicy createHeaderFooterPolicy() { if (headerFooterPolicy == null) { -// if (! ctDocument.getBody().isSetSectPr()) { -// ctDocument.getBody().addNewSectPr(); -// } + // if (! ctDocument.getBody().isSetSectPr()) { + // ctDocument.getBody().addNewSectPr(); + // } headerFooterPolicy = new XWPFHeaderFooterPolicy(this); } return headerFooterPolicy; @@ -493,7 +495,7 @@ public class XWPFDocument extends POIXMLDocument implements Document, IBody { CTOnOff titlePg = ctSectPr.addNewTitlePg(); titlePg.setVal(STOnOff.ON); } - // } else if (type == HeaderFooterType.EVEN) { + // } else if (type == HeaderFooterType.EVEN) { // TODO Add support for Even/Odd headings and footers } return hfPolicy.createHeader(STHdrFtr.Enum.forInt(type.toInt())); @@ -515,7 +517,7 @@ public class XWPFDocument extends POIXMLDocument implements Document, IBody { CTOnOff titlePg = ctSectPr.addNewTitlePg(); titlePg.setVal(STOnOff.ON); } - // } else if (type == HeaderFooterType.EVEN) { + // } else if (type == HeaderFooterType.EVEN) { // TODO Add support for Even/Odd headings and footers } return hfPolicy.createFooter(STHdrFtr.Enum.forInt(type.toInt())); @@ -1014,7 +1016,7 @@ public class XWPFDocument extends POIXMLDocument implements Document, IBody { ctDocument.getBody().setTblArray(pos, table.getCTTbl()); } - /** + /** * Verifies that the documentProtection tag in settings.xml file
* specifies that the protection is enforced (w:enforcement="1")
*
@@ -1608,4 +1610,59 @@ public class XWPFDocument extends POIXMLDocument implements Document, IBody { public XWPFDocument getXWPFDocument() { return this; } + + /** + * This method is used to create template for chart XML + * no need to read MS-Word file and modify charts + * + * @return This method return object of XWPFChart Object with default height and width + * @throws InvalidFormatException + * @throws IOException + * @since POI 4.0.0 + */ + public XWPFChart createChart() throws InvalidFormatException, IOException { + return createChart(XWPFChart.DEFAULT_WIDTH, XWPFChart.DEFAULT_HEIGHT); + } + + /** + * This method is used to create template for chart XML + * no need to read MS-Word file and modify charts + * + * @param width width of chart in document + * @param height height of chart in document + * @return This method return object of XWPFChart + * @throws InvalidFormatException + * @throws IOException + * @since POI 4.0.0 + */ + public XWPFChart createChart(int width, int height) throws InvalidFormatException, IOException { + + //get chart number + int chartNumber = getPackagePart().getPackage(). + getPartsByContentType(XWPFRelation.CHART.getContentType()).size() + 1; + + //create relationship in document for new chart + RelationPart rp = createRelationship( + XWPFRelation.CHART, XWPFFactory.getInstance(), chartNumber, false); + + //get chart relationship id + String chartId = rp.getRelationship().getId(); + + //create paragraph and run object + XWPFRun xRun = this.createParagraph().createRun(); + + CTInline inline = xRun.addChart(width, height, chartId); + + //get package part of xwpfchart object + XWPFChart xwpfChart = rp.getDocumentPart(); + + xwpfChart.setChartIndex(chartNumber); + + //set in line object into xwpfchart object + xwpfChart.setAttachTo(inline); + + //add chart object to chart list + charts.add(xwpfChart); + return xwpfChart; + } } diff --git a/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFRelation.java b/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFRelation.java index f8da0c504..d0d44e398 100644 --- a/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFRelation.java +++ b/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFRelation.java @@ -20,9 +20,11 @@ package org.apache.poi.xwpf.usermodel; import java.util.HashMap; import java.util.Map; +import org.apache.poi.POIXMLDocument; import org.apache.poi.POIXMLDocumentPart; import org.apache.poi.POIXMLRelation; import org.apache.poi.openxml4j.opc.PackageRelationshipTypes; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; /** * @author Yegor Kozlov @@ -112,6 +114,14 @@ public final class XWPFRelation extends POIXMLRelation { "/word/theme/theme#.xml", null ); + + public static final XWPFRelation WORKBOOK_RELATIONSHIP = new XWPFRelation( + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + POIXMLDocument.PACK_OBJECT_REL_TYPE, + "/word/embeddings/Microsoft_Excel_Worksheet#.xlsx", + XSSFWorkbook.class + ); + public static final XWPFRelation CHART = new XWPFRelation( "application/vnd.openxmlformats-officedocument.drawingml.chart+xml", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/chart", diff --git a/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFRun.java b/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFRun.java index fdfe2cc6f..9c2d79ee0 100644 --- a/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFRun.java +++ b/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFRun.java @@ -39,6 +39,8 @@ import org.apache.xmlbeans.XmlObject; import org.apache.xmlbeans.XmlString; import org.apache.xmlbeans.XmlToken; import org.apache.xmlbeans.impl.values.XmlAnyTypeImpl; +import org.openxmlformats.schemas.drawingml.x2006.chart.CTChart; +import org.openxmlformats.schemas.drawingml.x2006.chart.CTRelId; import org.openxmlformats.schemas.drawingml.x2006.main.CTBlip; import org.openxmlformats.schemas.drawingml.x2006.main.CTBlipFillProperties; import org.openxmlformats.schemas.drawingml.x2006.main.CTGraphicalObject; @@ -242,9 +244,9 @@ public class XWPFRun implements ISDTContents, IRunElement, CharacterRun { return true; final STOnOff.Enum val = onoff.getVal(); return ( - (STOnOff.TRUE == val) || - (STOnOff.X_1 == val) || - (STOnOff.ON == val) + (STOnOff.TRUE == val) || + (STOnOff.X_1 == val) || + (STOnOff.ON == val) ); } @@ -256,7 +258,7 @@ public class XWPFRun implements ISDTContents, IRunElement, CharacterRun { public String getLang() { CTRPr pr = run.getRPr(); Object lang = pr == null || !pr.isSetLang() ? null : pr.getLang().getVal(); - return (String)lang; + return (String) lang; } /** @@ -917,7 +919,7 @@ public class XWPFRun implements ISDTContents, IRunElement, CharacterRun { * @param width width in EMUs. To convert to / from points use {@link org.apache.poi.util.Units} * @param height height in EMUs. To convert to / from points use {@link org.apache.poi.util.Units} * @throws InvalidFormatException If the format of the picture is not known. - * @throws IOException If reading the picture-data from the stream fails. + * @throws IOException If reading the picture-data from the stream fails. * @see org.apache.poi.xwpf.usermodel.Document#PICTURE_TYPE_EMF * @see org.apache.poi.xwpf.usermodel.Document#PICTURE_TYPE_WMF * @see org.apache.poi.xwpf.usermodel.Document#PICTURE_TYPE_PICT @@ -929,12 +931,12 @@ public class XWPFRun implements ISDTContents, IRunElement, CharacterRun { throws InvalidFormatException, IOException { String relationId; XWPFPictureData picData; - + // Work out what to add the picture to, then add both the // picture and the relationship for it // TODO Should we have an interface for this sort of thing? if (parent.getPart() instanceof XWPFHeaderFooter) { - XWPFHeaderFooter headerFooter = (XWPFHeaderFooter)parent.getPart(); + XWPFHeaderFooter headerFooter = (XWPFHeaderFooter) parent.getPart(); relationId = headerFooter.addPictureData(pictureData, pictureType); picData = (XWPFPictureData) headerFooter.getRelationById(relationId); } else { @@ -1025,6 +1027,62 @@ public class XWPFRun implements ISDTContents, IRunElement, CharacterRun { } } + /** + * this method add chart template into document + * + * @param width set width of chart object + * @param height set height of chart object + * @param chartRelId relation id of chart in document relation file + * @throws InvalidFormatException + * @throws IOException + * @since POI 4.0.0 + */ + @Internal + public CTInline addChart(int width, int height, String chartRelId) + throws InvalidFormatException, IOException { + try { + CTInline inline = run.addNewDrawing().addNewInline(); + + //xml part of chart in document + String xml = + "" + + "" + + "" + + "" + + ""; + + InputSource is = new InputSource(new StringReader(xml)); + + org.w3c.dom.Document doc = DocumentHelper.readDocument(is); + + inline.set(XmlToken.Factory.parse(doc.getDocumentElement(), DEFAULT_XML_OPTIONS)); + + // Setup the inline with 0 margin + inline.setDistT(0); + inline.setDistR(0); + inline.setDistB(0); + inline.setDistL(0); + + CTNonVisualDrawingProps docPr = inline.addNewDocPr(); + long id = getParent().getDocument().getDrawingIdManager().reserveNew(); + docPr.setId(id); + //This name is not visible in Word anywhere. + docPr.setName("chart " + id); + + CTPositiveSize2D extent = inline.addNewExtent(); + //set hegiht and width of drawaing object; + extent.setCx(width); + extent.setCy(height); + + return inline; + } catch (XmlException e) { + throw new IllegalStateException(e); + } catch (SAXException e) { + throw new IllegalStateException(e); + } + } + + /** * Returns the embedded pictures of the run. These * are pictures which reference an external, @@ -1040,7 +1098,7 @@ public class XWPFRun implements ISDTContents, IRunElement, CharacterRun { public String toString() { String phonetic = getPhonetic(); if (phonetic.length() > 0) { - return text() +" ("+ phonetic +")"; + return text() + " (" + phonetic + ")"; } else { return text(); } @@ -1071,7 +1129,6 @@ public class XWPFRun implements ISDTContents, IRunElement, CharacterRun { } /** - * * @return the phonetic (ruby) string associated with this run or an empty String if none exists */ public String getPhonetic() { @@ -1096,9 +1153,8 @@ public class XWPFRun implements ISDTContents, IRunElement, CharacterRun { } /** - * - * @param rubyObj rubyobject - * @param text buffer to which to append the content + * @param rubyObj rubyobject + * @param text buffer to which to append the content * @param extractPhonetic extract the phonetic (rt) component or the base component */ private void handleRuby(XmlObject rubyObj, StringBuilder text, boolean extractPhonetic) { @@ -1124,7 +1180,7 @@ public class XWPFRun implements ISDTContents, IRunElement, CharacterRun { } else { if (extractPhonetic && inRT) { _getText(o, text); - } else if (! extractPhonetic && inBase) { + } else if (!extractPhonetic && inBase) { _getText(o, text); } } diff --git a/src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFChart.java b/src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFChart.java index cb5537116..719fbb9d3 100644 --- a/src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFChart.java +++ b/src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFChart.java @@ -20,6 +20,7 @@ package org.apache.poi.xwpf.usermodel; import java.io.IOException; import java.util.List; +import org.apache.poi.openxml4j.exceptions.InvalidFormatException; import org.apache.poi.xddf.usermodel.chart.XDDFBarChartData; import org.apache.poi.xddf.usermodel.chart.XDDFChartData; import org.apache.poi.xwpf.XWPFTestDataSamples; @@ -37,8 +38,7 @@ public class TestXWPFChart extends TestCase { /** * test method to check charts are not null */ - public void testRead() throws IOException - { + public void testRead() throws IOException { XWPFDocument sampleDoc = XWPFTestDataSamples.openSampleDocument("61745.docx"); List charts = sampleDoc.getCharts(); assertNotNull(charts); @@ -58,11 +58,10 @@ public class TestXWPFChart extends TestCase { /** * test method to add chart title and check whether it's set */ - public void testChartTitle() throws IOException - { + public void testChartTitle() throws IOException { XWPFDocument sampleDoc = XWPFTestDataSamples.openSampleDocument("61745.docx"); List charts = sampleDoc.getCharts(); - XWPFChart chart=charts.get(0); + XWPFChart chart = charts.get(0); CTChart ctChart = chart.getCTChart(); CTTitle title = ctChart.getTitle(); CTTx tx = title.addNewTx(); @@ -75,16 +74,42 @@ public class TestXWPFChart extends TestCase { r.setT("XWPF CHART"); assertEquals("XWPF CHART", chart.getCTChart().getTitle().getTx().getRich().getPArray(0).getRArray(0).getT()); } + /** * test method to check relationship */ - public void testChartRelation() throws IOException - { + public void testChartRelation() throws IOException { XWPFDocument sampleDoc = XWPFTestDataSamples.openSampleDocument("61745.docx"); List charts = sampleDoc.getCharts(); - XWPFChart chart=charts.get(0); + XWPFChart chart = charts.get(0); assertEquals(XWPFRelation.CHART.getContentType(), chart.getPackagePart().getContentType()); assertEquals("/word/document.xml", chart.getParent().getPackagePart().getPartName().getName()); assertEquals("/word/charts/chart1.xml", chart.getPackagePart().getPartName().getName()); } + + /** + * test method to check adding chart in document + */ + public static void testAddChartsToNewDocument() throws InvalidFormatException, IOException { + + XWPFDocument document = new XWPFDocument(); + + XWPFChart chart = document.createChart(); + assertEquals(1, document.getCharts().size()); + assertNotNull(chart); + assertNotNull(chart.getCTChartSpace()); + assertNotNull(chart.getCTChart()); + assertEquals(XWPFChart.DEFAULT_HEIGHT, chart.getChartHeight()); + assertEquals(XWPFChart.DEFAULT_WIDTH, chart.getChartWidth()); + + XWPFChart chart2 = document.createChart(); + assertEquals(2, document.getCharts().size()); + assertNotNull(chart2); + assertNotNull(chart2.getCTChartSpace()); + assertNotNull(chart2.getCTChart()); + chart.setChartHeight(500500); + assertEquals(500500, chart.getChartHeight()); + + assertNotNull(XWPFTestDataSamples.writeOutAndReadBack(document)); + } }