From: Yegor Kozlov Date: Wed, 1 Jun 2011 12:07:16 +0000 (+0000) Subject: bug 51265: Enhanced Handling of Picture Parts in XWPF X-Git-Tag: REL_3_8_BETA4~407 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=b988ca3855855b9f78ccdb7b263906d7f69c5496;p=poi.git bug 51265: Enhanced Handling of Picture Parts in XWPF git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1130120 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/src/documentation/content/xdocs/status.xml b/src/documentation/content/xdocs/status.xml index 730555cbb9..090b2109f4 100644 --- a/src/documentation/content/xdocs/status.xml +++ b/src/documentation/content/xdocs/status.xml @@ -34,10 +34,11 @@ + 51265 - Enhanced Handling of Picture Parts in XWPF 51292 - Additional HWPF Table Cell Descriptor values - - 51098 - Correct calculate image width/height, if image fits into one cell + + 51098 - Correctly calculate image width/height, if image fits into one cell 47147 - Correct extra paragraphs from XWPF Table Cells 51188 - Support for getting and setting XPWF zoom settings 51134 - Support for adding Numbering and Styles to a XWPF document that doesn't already have them diff --git a/src/java/org/apache/poi/util/IOUtils.java b/src/java/org/apache/poi/util/IOUtils.java index d67bde2643..4812584898 100644 --- a/src/java/org/apache/poi/util/IOUtils.java +++ b/src/java/org/apache/poi/util/IOUtils.java @@ -23,6 +23,8 @@ import java.io.InputStream; import java.io.OutputStream; import java.nio.ByteBuffer; import java.nio.channels.ReadableByteChannel; +import java.util.zip.CRC32; +import java.util.zip.Checksum; public final class IOUtils { private IOUtils() { @@ -129,4 +131,10 @@ public final class IOUtils { } } } + + public static long calculateChecksum(byte[] data) { + Checksum sum = new CRC32(); + sum.update(data, 0, data.length); + return sum.getValue(); + } } diff --git a/src/ooxml/java/org/apache/poi/POIXMLDocument.java b/src/ooxml/java/org/apache/poi/POIXMLDocument.java index d8fb78f1c2..075c9c84fe 100644 --- a/src/ooxml/java/org/apache/poi/POIXMLDocument.java +++ b/src/ooxml/java/org/apache/poi/POIXMLDocument.java @@ -30,10 +30,8 @@ import org.apache.poi.openxml4j.exceptions.InvalidFormatException; import org.apache.poi.openxml4j.exceptions.OpenXML4JException; import org.apache.poi.openxml4j.opc.OPCPackage; import org.apache.poi.openxml4j.opc.PackagePart; -import org.apache.poi.openxml4j.opc.PackagePartName; import org.apache.poi.openxml4j.opc.PackageRelationship; import org.apache.poi.openxml4j.opc.PackageRelationshipCollection; -import org.apache.poi.openxml4j.opc.PackagingURIHelper; import org.apache.poi.poifs.common.POIFSConstants; import org.apache.poi.util.IOUtils; @@ -80,33 +78,6 @@ public abstract class POIXMLDocument extends POIXMLDocumentPart{ return getPackagePart(); } - /** - * Get the PackagePart that is the target of a relationship. - * - * @param rel The relationship - * @return The target part - * @throws InvalidFormatException - */ - protected PackagePart getTargetPart(PackageRelationship rel) throws InvalidFormatException { - return getTargetPart(getPackage(), rel); - } - /** - * Get the PackagePart that is the target of a relationship. - * - * @param rel The relationship - * @param pkg The package to fetch from - * @return The target part - * @throws InvalidFormatException - */ - protected static PackagePart getTargetPart(OPCPackage pkg, PackageRelationship rel) throws InvalidFormatException { - PackagePartName relName = PackagingURIHelper.createPartName(rel.getTargetURI()); - PackagePart part = pkg.getPart(relName); - if (part == null) { - throw new IllegalArgumentException("No part found for relationship " + rel); - } - return part; - } - /** * Retrieves all the PackageParts which are defined as * relationships of the base document with the @@ -125,8 +96,6 @@ public abstract class POIXMLDocument extends POIXMLDocumentPart{ return parts; } - - /** * Checks that the supplied InputStream (which MUST * support mark and reset, or be a PushbackInputStream) @@ -153,10 +122,10 @@ public abstract class POIXMLDocument extends POIXMLDocumentPart{ // Did it match the ooxml zip signature? return ( - header[0] == POIFSConstants.OOXML_FILE_HEADER[0] && - header[1] == POIFSConstants.OOXML_FILE_HEADER[1] && - header[2] == POIFSConstants.OOXML_FILE_HEADER[2] && - header[3] == POIFSConstants.OOXML_FILE_HEADER[3] + header[0] == POIFSConstants.OOXML_FILE_HEADER[0] && + header[1] == POIFSConstants.OOXML_FILE_HEADER[1] && + header[2] == POIFSConstants.OOXML_FILE_HEADER[2] && + header[3] == POIFSConstants.OOXML_FILE_HEADER[3] ); } @@ -181,14 +150,14 @@ public abstract class POIXMLDocument extends POIXMLDocumentPart{ public abstract List getAllEmbedds() throws OpenXML4JException; protected final void load(POIXMLFactory factory) throws IOException { - Map context = new HashMap(); + Map context = new HashMap(); try { read(factory, context); } catch (OpenXML4JException e){ throw new POIXMLException(e); } - onDocumentRead(); - context.clear(); + onDocumentRead(); + context.clear(); } /** diff --git a/src/ooxml/java/org/apache/poi/POIXMLDocumentPart.java b/src/ooxml/java/org/apache/poi/POIXMLDocumentPart.java index ec10c638ba..d81391035d 100644 --- a/src/ooxml/java/org/apache/poi/POIXMLDocumentPart.java +++ b/src/ooxml/java/org/apache/poi/POIXMLDocumentPart.java @@ -17,15 +17,23 @@ package org.apache.poi; import java.io.IOException; -import java.util.*; import java.net.URI; +import java.util.*; +import java.util.Map.Entry; -import org.apache.xmlbeans.XmlOptions; -import org.apache.poi.util.POILogger; -import org.apache.poi.util.POILogFactory; import org.apache.poi.openxml4j.exceptions.InvalidFormatException; import org.apache.poi.openxml4j.exceptions.OpenXML4JException; -import org.apache.poi.openxml4j.opc.*; +import org.apache.poi.openxml4j.opc.OPCPackage; +import org.apache.poi.openxml4j.opc.PackagePart; +import org.apache.poi.openxml4j.opc.PackagePartName; +import org.apache.poi.openxml4j.opc.PackageRelationship; +import org.apache.poi.openxml4j.opc.PackageRelationshipCollection; +import org.apache.poi.openxml4j.opc.PackageRelationshipTypes; +import org.apache.poi.openxml4j.opc.PackagingURIHelper; +import org.apache.poi.openxml4j.opc.TargetMode; +import org.apache.poi.util.POILogFactory; +import org.apache.poi.util.POILogger; +import org.apache.xmlbeans.XmlOptions; /** * Represents an entry of a OOXML package. @@ -47,19 +55,55 @@ public class POIXMLDocumentPart { DEFAULT_XML_OPTIONS.setSaveAggressiveNamespaces(); } + private PackagePart packagePart; private PackageRelationship packageRel; private POIXMLDocumentPart parent; - private List relations; + private Map relations = new LinkedHashMap(); + + /** + * Get the PackagePart that is the target of a relationship. + * + * @param rel The relationship + * @param pkg The package to fetch from + * @return The target part + * @throws InvalidFormatException + */ + protected static PackagePart getTargetPart(OPCPackage pkg, PackageRelationship rel) + throws InvalidFormatException { + PackagePartName relName = PackagingURIHelper.createPartName(rel.getTargetURI()); + PackagePart part = pkg.getPart(relName); + if (part == null) { + throw new IllegalArgumentException("No part found for relationship " + rel); + } + return part; + } + /** + * Counter that provides the amount of incoming relations from other parts + * to this part. + */ + private int relationCounter = 0; + + int incrementRelationCounter() { + relationCounter++; + return relationCounter; + } + + int decrementRelationCounter() { + relationCounter--; + return relationCounter; + } + + int getRelationCounter() { + return relationCounter; + } /** * Construct POIXMLDocumentPart representing a "core document" package part. */ public POIXMLDocumentPart(OPCPackage pkg) { - PackageRelationship coreRel = pkg.getRelationshipsByType( - PackageRelationshipTypes.CORE_DOCUMENT).getRelationship(0); + PackageRelationship coreRel = pkg.getRelationshipsByType(PackageRelationshipTypes.CORE_DOCUMENT).getRelationship(0); - this.relations = new LinkedList(); this.packagePart = pkg.getPart(coreRel); this.packageRel = coreRel; } @@ -70,7 +114,6 @@ public class POIXMLDocumentPart { * @see #createRelationship(POIXMLRelation, POIXMLFactory, int, boolean) */ public POIXMLDocumentPart(){ - this.relations = new LinkedList(); } /** @@ -82,7 +125,6 @@ public class POIXMLDocumentPart { * @see #read(POIXMLFactory, java.util.Map) */ public POIXMLDocumentPart(PackagePart part, PackageRelationship rel){ - this.relations = new LinkedList(); this.packagePart = part; this.packageRel = rel; } @@ -97,7 +139,6 @@ public class POIXMLDocumentPart { * @see #read(POIXMLFactory, java.util.Map) */ public POIXMLDocumentPart(POIXMLDocumentPart parent, PackagePart part, PackageRelationship rel){ - this.relations = new LinkedList(); this.packagePart = part; this.packageRel = rel; this.parent = parent; @@ -109,16 +150,16 @@ public class POIXMLDocumentPart { * current core document */ protected final void rebase(OPCPackage pkg) throws InvalidFormatException { - PackageRelationshipCollection cores = - packagePart.getRelationshipsByType(PackageRelationshipTypes.CORE_DOCUMENT); - if(cores.size() != 1) { - throw new IllegalStateException( + PackageRelationshipCollection cores = + packagePart.getRelationshipsByType(PackageRelationshipTypes.CORE_DOCUMENT); + if(cores.size() != 1) { + throw new IllegalStateException( "Tried to rebase using " + PackageRelationshipTypes.CORE_DOCUMENT + " but found " + cores.size() + " parts of the right type" - ); - } - packageRel = cores.getRelationship(0); - packagePart = POIXMLDocument.getTargetPart(pkg, packageRel); + ); + } + packageRel = cores.getRelationship(0); + packagePart = POIXMLDocument.getTargetPart(pkg, packageRel); } /** @@ -145,7 +186,46 @@ public class POIXMLDocumentPart { * @return child relations */ public final List getRelations(){ - return relations; + return Collections.unmodifiableList(new ArrayList(relations.values())); + } + + /** + * Returns the target {@link POIXMLDocumentPart}, where a + * {@link PackageRelationship} is set from the {@link PackagePart} of this + * {@link POIXMLDocumentPart} to the {@link PackagePart} of the target + * {@link POIXMLDocumentPart} with a {@link PackageRelationship#getId()} + * matching the given parameter value. + * + * @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) { + return relations.get(id); + } + + /** + * Returns the {@link PackageRelationship#getId()} of the + * {@link PackageRelationship}, that sources from the {@link PackagePart} of + * this {@link POIXMLDocumentPart} to the {@link PackagePart} of the given + * parameter value. + * + * @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. + */ + public final String getRelationId(POIXMLDocumentPart part) { + Iterator> iter = relations.entrySet().iterator(); + while (iter.hasNext()) + { + Entry entry = iter.next(); + if (entry.getValue() == part) { + return entry.getKey(); + } + } + return null; } /** @@ -153,17 +233,54 @@ public class POIXMLDocumentPart { * * @param part the child to add */ - protected final void addRelation(POIXMLDocumentPart part){ - relations.add(part); + protected final void addRelation(String id,POIXMLDocumentPart part){ + relations.put(id,part); + part.incrementRelationCounter(); } /** - * Remove the specified part in this package. + * Remove the relation to the specified part in this package and remove the + * part, if it is no longer needed. */ - public final void removeRelation(POIXMLDocumentPart part){ - getPackagePart().removeRelationship(part.getPackageRelationship().getId()); - getPackagePart().getPackage().removePart(part.getPackagePart()); - relations.remove(part); + 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. + * + * @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. + */ + protected final boolean removeRelation(POIXMLDocumentPart part, boolean removeUnusedParts){ + String id = getRelationId(part); + if (id == null) { + // part is not related with this POIXMLDocumentPart + return false; + } + /* decrement usage counter */ + part.decrementRelationCounter(); + /* remove packagepart relationship */ + getPackagePart().removeRelationship(id); + /* remove POIXMLDocument from relations */ + relations.remove(id); + + if (removeUnusedParts) { + /* if last relation to target part was removed, delete according target part */ + if (part.getRelationCounter() == 0) { + try { + part.onDocumentRemove(); + } catch (IOException e) { + throw new POIXMLException(e); + } + getPackagePart().getPackage().removePart(part.getPackagePart()); + } + } + return true; } /** @@ -209,13 +326,13 @@ public class POIXMLDocumentPart { * @param alreadySaved context set containing already visited nodes */ protected final void onSave(Set alreadySaved) throws IOException{ - commit(); - alreadySaved.add(this.getPackagePart()); - for(POIXMLDocumentPart p : relations){ + commit(); + alreadySaved.add(this.getPackagePart()); + for(POIXMLDocumentPart p : relations.values()){ if (!alreadySaved.contains(p.getPackagePart())) { - p.onSave(alreadySaved); - } - } + p.onSave(alreadySaved); + } + } } /** @@ -246,15 +363,19 @@ public class POIXMLDocumentPart { try { PackagePartName ppName = PackagingURIHelper.createPartName(descriptor.getFileName(idx)); PackageRelationship rel = null; + PackagePart part = packagePart.getPackage().createPart(ppName, descriptor.getContentType()); if(!noRelation) { - rel = packagePart.addRelationship(ppName, TargetMode.INTERNAL, descriptor.getRelation()); + /* only add to relations, if according relationship is being created. */ + rel = packagePart.addRelationship(ppName, TargetMode.INTERNAL, descriptor.getRelation()); } - PackagePart part = packagePart.getPackage().createPart(ppName, descriptor.getContentType()); POIXMLDocumentPart doc = factory.newDocumentPart(descriptor); doc.packageRel = rel; doc.packagePart = part; doc.parent = this; - addRelation(doc); + if(!noRelation) { + /* only add to relations, if according relationship is being created. */ + addRelation(rel.getId(),doc); + } return doc; } catch (Exception e){ throw new POIXMLException(e); @@ -269,40 +390,40 @@ public class POIXMLDocumentPart { * @param context context map containing already visited noted keyed by targetURI */ protected void read(POIXMLFactory factory, Map context) throws OpenXML4JException { - PackageRelationshipCollection rels = packagePart.getRelationships(); - for (PackageRelationship rel : rels) { - if(rel.getTargetMode() == TargetMode.INTERNAL){ - URI uri = rel.getTargetURI(); - - PackagePart p; - if(uri.getRawFragment() != null) { - /* - * For internal references (e.g. '#Sheet1!A1') the package part is null - */ - p = null; - } else { - PackagePartName relName = PackagingURIHelper.createPartName(uri); - p = packagePart.getPackage().getPart(relName); - if(p == null) { - logger.log(POILogger.ERROR, "Skipped invalid entry " + rel.getTargetURI()); - continue; - } - } - - if (!context.containsKey(p)) { - POIXMLDocumentPart childPart = factory.createDocumentPart(this, rel, p); - childPart.parent = this; - addRelation(childPart); - if(p != null){ - context.put(p, childPart); - if(p.hasRelationships()) childPart.read(factory, context); - } - } - else { - addRelation(context.get(p)); - } - } - } + PackageRelationshipCollection rels = packagePart.getRelationships(); + for (PackageRelationship rel : rels) { + if(rel.getTargetMode() == TargetMode.INTERNAL){ + URI uri = rel.getTargetURI(); + + PackagePart p; + if(uri.getRawFragment() != null) { + /* + * For internal references (e.g. '#Sheet1!A1') the package part is null + */ + p = null; + } else { + PackagePartName relName = PackagingURIHelper.createPartName(uri); + p = packagePart.getPackage().getPart(relName); + if(p == null) { + logger.log(POILogger.ERROR, "Skipped invalid entry " + rel.getTargetURI()); + continue; + } + } + + if (!context.containsKey(p)) { + POIXMLDocumentPart childPart = factory.createDocumentPart(this, rel, p); + childPart.parent = this; + addRelation(rel.getId(),childPart); + if(p != null){ + context.put(p, childPart); + if(p.hasRelationships()) childPart.read(factory, context); + } + } + else { + addRelation(rel.getId(),context.get(p)); + } + } + } } /** @@ -315,7 +436,25 @@ public class POIXMLDocumentPart { /** * Fired when a package part is read */ - protected void onDocumentRead() throws IOException{ + protected void onDocumentRead() throws IOException { + + } + + /** + * Get the PackagePart that is the target of a relationship. + * + * @param rel The relationship + * @return The target part + * @throws InvalidFormatException + */ + protected PackagePart getTargetPart(PackageRelationship rel) throws InvalidFormatException { + return getTargetPart(getPackagePart().getPackage(), rel); + } + + /** + * Fired when a package part is about to be removed from the package + */ + protected void onDocumentRemove() throws IOException { } } diff --git a/src/ooxml/java/org/apache/poi/openxml4j/opc/OPCPackage.java b/src/ooxml/java/org/apache/poi/openxml4j/opc/OPCPackage.java index 7b77016444..a9b08eec3a 100644 --- a/src/ooxml/java/org/apache/poi/openxml4j/opc/OPCPackage.java +++ b/src/ooxml/java/org/apache/poi/openxml4j/opc/OPCPackage.java @@ -31,7 +31,10 @@ import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Date; import java.util.Hashtable; +import java.util.List; import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import org.apache.poi.openxml4j.exceptions.InvalidFormatException; import org.apache.poi.openxml4j.exceptions.InvalidOperationException; @@ -553,6 +556,25 @@ public abstract class OPCPackage implements RelationshipSource, Closeable { return retArr; } + /** + * @return + */ + public List getPartsByName(final Pattern namePattern) { + if (namePattern == null) { + throw new IllegalArgumentException("name pattern must not be null"); + } + ArrayList result = new ArrayList(); + for (PackagePart part : partList.values()) { + PackagePartName partName = part.getPartName(); + String name = partName.getName(); + Matcher matcher = namePattern.matcher(name); + if (matcher.matches()) { + result.add(part); + } + } + return result; + } + /** * Get the target part from the specified relationship. * diff --git a/src/ooxml/java/org/apache/poi/openxml4j/opc/internal/PackagePropertiesPart.java b/src/ooxml/java/org/apache/poi/openxml4j/opc/internal/PackagePropertiesPart.java index af40d361aa..677a513226 100644 --- a/src/ooxml/java/org/apache/poi/openxml4j/opc/internal/PackagePropertiesPart.java +++ b/src/ooxml/java/org/apache/poi/openxml4j/opc/internal/PackagePropertiesPart.java @@ -22,7 +22,7 @@ import java.io.OutputStream; import java.text.ParsePosition; import java.text.SimpleDateFormat; import java.util.Date; -import java.util.Locale; + import java.util.TimeZone; import org.apache.poi.openxml4j.exceptions.InvalidFormatException; diff --git a/src/ooxml/java/org/apache/poi/util/IdentifierManager.java b/src/ooxml/java/org/apache/poi/util/IdentifierManager.java new file mode 100644 index 0000000000..77fe79c630 --- /dev/null +++ b/src/ooxml/java/org/apache/poi/util/IdentifierManager.java @@ -0,0 +1,268 @@ +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ +package org.apache.poi.util; + +import java.util.LinkedList; +import java.util.ListIterator; + +/** + *

+ * 24.08.2009
+ *

+ * + * @author Stefan Stern
+ */ + +public class IdentifierManager { + + public static final long MAX_ID = Long.MAX_VALUE - 1; + + public static final long MIN_ID = 0L; + + /** + * + */ + private final long upperbound; + + /** + * + */ + private final long lowerbound; + + /** + * List of segments of available identifiers + */ + private LinkedList segments; + + /** + * @param lowerbound the lower limit of the id-range to manage. Must be greater than or equal to {@link #MIN_ID}. + * @param upperbound the upper limit of the id-range to manage. Must be less then or equal {@link #MAX_ID}. + */ + public IdentifierManager(long lowerbound, long upperbound) { + if (lowerbound > upperbound) { + String message = "lowerbound must not be greater than upperbound"; + throw new IllegalArgumentException(message); + } + else if (lowerbound < MIN_ID) { + String message = "lowerbound must be greater than or equal to " + Long.toString(MIN_ID); + throw new IllegalArgumentException(message); + } + else if (upperbound > MAX_ID) { + /* + * while MAX_ID is Long.MAX_VALUE, this check is pointless. But if + * someone subclasses / tweaks the limits, this check if fine. + */ + String message = "upperbound must be less thean or equal " + Long.toString(MAX_ID); + throw new IllegalArgumentException(message); + } + this.lowerbound = lowerbound; + this.upperbound = upperbound; + this.segments = new LinkedList(); + segments.add(new Segment(lowerbound, upperbound)); + } + + public long reserve(long id) { + if (id < lowerbound || id > upperbound) { + throw new IllegalArgumentException("Value for parameter 'id' was out of bounds"); + } + verifyIdentifiersLeft(); + + if (id == upperbound) { + Segment lastSegment = segments.getLast(); + if (lastSegment.end == upperbound) { + lastSegment.end = upperbound - 1; + if (lastSegment.start > lastSegment.end) { + segments.removeLast(); + } + return id; + } + return reserveNew(); + } + + if (id == lowerbound) { + Segment firstSegment = segments.getFirst(); + if (firstSegment.start == lowerbound) { + firstSegment.start = lowerbound + 1; + if (firstSegment.end < firstSegment.start) { + segments.removeFirst(); + } + return id; + } + return reserveNew(); + } + + ListIterator iter = segments.listIterator(); + while (iter.hasNext()) { + Segment segment = iter.next(); + if (segment.end < id) { + continue; + } + else if (segment.start > id) { + break; + } + else if (segment.start == id) { + segment.start = id + 1; + if (segment.end < segment.start) { + iter.remove(); + } + return id; + } + else if (segment.end == id) { + segment.end = id - 1; + if (segment.start > segment.end) { + iter.remove(); + } + return id; + } + else { + iter.add(new Segment(id + 1, segment.end)); + segment.end = id - 1; + return id; + } + } + return reserveNew(); + } + + /** + * @return a new identifier. + * @throws IllegalStateException if no more identifiers are available, then an Exception is raised. + */ + public long reserveNew() { + verifyIdentifiersLeft(); + Segment segment = segments.getFirst(); + long result = segment.start; + segment.start += 1; + if (segment.start > segment.end) { + segments.removeFirst(); + } + return result; + } + + /** + * @param id + * the identifier to release. Must be greater than or equal to + * {@link #lowerbound} and must be less than or equal to {@link #upperbound} + * @return true, if the identifier was reserved and has been successfully + * released, false, if the identifier was not reserved. + */ + public boolean release(long id) { + if (id < lowerbound || id > upperbound) { + throw new IllegalArgumentException("Value for parameter 'id' was out of bounds"); + } + + if (id == upperbound) { + Segment lastSegment = segments.getLast(); + if (lastSegment.end == upperbound - 1) { + lastSegment.end = upperbound; + return true; + } else if (lastSegment.end == upperbound) { + return false; + } else { + segments.add(new Segment(upperbound, upperbound)); + return true; + } + } + + if (id == lowerbound) { + Segment firstSegment = segments.getFirst(); + if (firstSegment.start == lowerbound + 1) { + firstSegment.start = lowerbound; + return true; + } else if (firstSegment.start == lowerbound) { + return false; + } else { + segments.addFirst(new Segment(lowerbound, lowerbound)); + return true; + } + } + + long higher = id + 1; + long lower = id - 1; + ListIterator iter = segments.listIterator(); + + while (iter.hasNext()) { + Segment segment = iter.next(); + if (segment.end < lower) { + continue; + } + if (segment.start > higher) { + iter.previous(); + iter.add(new Segment(id, id)); + return true; + } + if (segment.start == higher) { + segment.start = id; + return true; + } + else if (segment.end == lower) { + segment.end = id; + /* check if releasing this elements glues two segments into one */ + if (iter.hasNext()) { + Segment next = iter.next(); + if (next.start == segment.end + 1) { + segment.end = next.end; + iter.remove(); + } + } + return true; + } + else { + /* id was not reserved, return false */ + break; + } + } + return false; + } + + public long getRemainingIdentifiers() { + long result = 0; + for (Segment segment : segments) { + result = result - segment.start; + result = result + segment.end + 1; + } + return result; + } + + /** + * + */ + private void verifyIdentifiersLeft() { + if (segments.isEmpty()) { + throw new IllegalStateException("No identifiers left"); + } + } + + private static class Segment { + + public Segment(long start, long end) { + this.start = start; + this.end = end; + } + + public long start; + public long end; + + /* + * (non-Javadoc) + * + * @see java.lang.Object#toString() + */ + public String toString() { + return "[" + start + "; " + end + "]"; + } + } +} diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFDrawing.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFDrawing.java index b0cff7c675..5c449473e8 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFDrawing.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFDrawing.java @@ -213,7 +213,7 @@ public final class XSSFDrawing extends POIXMLDocumentPart implements Drawing { XSSFPictureData data = wb.getAllPictures().get(pictureIndex); PackagePartName ppName = data.getPackagePart().getPartName(); PackageRelationship rel = getPackagePart().addRelationship(ppName, TargetMode.INTERNAL, XSSFRelation.IMAGES.getRelation()); - addRelation(new XSSFPictureData(data.getPackagePart(), rel)); + addRelation(rel.getId(),new XSSFPictureData(data.getPackagePart(), rel)); return rel; } diff --git a/src/ooxml/java/org/apache/poi/xwpf/model/XWPFHeaderFooterPolicy.java b/src/ooxml/java/org/apache/poi/xwpf/model/XWPFHeaderFooterPolicy.java index 65020e6935..1228a31ed8 100644 --- a/src/ooxml/java/org/apache/poi/xwpf/model/XWPFHeaderFooterPolicy.java +++ b/src/ooxml/java/org/apache/poi/xwpf/model/XWPFHeaderFooterPolicy.java @@ -24,7 +24,6 @@ import java.util.List; import java.util.Map; import org.apache.poi.POIXMLDocumentPart; -import org.apache.poi.openxml4j.opc.PackagePart; import org.apache.poi.xwpf.usermodel.XWPFDocument; import org.apache.poi.xwpf.usermodel.XWPFFactory; import org.apache.poi.xwpf.usermodel.XWPFFooter; @@ -68,244 +67,234 @@ import schemasMicrosoftComVml.STTrueFalse; * the right headers and footers for the document. */ public class XWPFHeaderFooterPolicy { - public static final Enum DEFAULT = STHdrFtr.DEFAULT; - public static final Enum EVEN = STHdrFtr.EVEN; - public static final Enum FIRST = STHdrFtr.FIRST; - - private XWPFDocument doc; - - private XWPFHeader firstPageHeader; - private XWPFFooter firstPageFooter; - - private XWPFHeader evenPageHeader; - private XWPFFooter evenPageFooter; - - private XWPFHeader defaultHeader; - private XWPFFooter defaultFooter; - - /** - * Figures out the policy for the given document, - * and creates any header and footer objects - * as required. - */ - public XWPFHeaderFooterPolicy(XWPFDocument doc) throws IOException, XmlException { - this(doc, doc.getDocument().getBody().getSectPr()); - } + public static final Enum DEFAULT = STHdrFtr.DEFAULT; + public static final Enum EVEN = STHdrFtr.EVEN; + public static final Enum FIRST = STHdrFtr.FIRST; - /** - * Figures out the policy for the given document, - * and creates any header and footer objects - * as required. - */ - public XWPFHeaderFooterPolicy(XWPFDocument doc, CTSectPr sectPr) throws IOException, XmlException { - // Grab what headers and footers have been defined - // For now, we don't care about different ranges, as it - // doesn't seem that .docx properly supports that - // feature of the file format yet - this.doc = doc; - for(int i=0; i relations = doc.getRelations(); + int i = 1; + for (Iterator it = relations.iterator(); it.hasNext() ; ) { + POIXMLDocumentPart item = it.next(); + if (item.getPackageRelationship().getRelationshipType().equals(relation.getRelation())) { + i++; + } + } + return i; + } + + private CTHdrFtr buildFtr(Enum type, String pStyle, XWPFHeaderFooter wrapper, XWPFParagraph[] pars) { + //CTHdrFtr ftr = buildHdrFtr(pStyle, pars); // MB 24 May 2010 + CTHdrFtr ftr = buildHdrFtr(pStyle, pars, wrapper); // MB 24 May 2010 + setFooterReference(type, wrapper); + return ftr; + } + + private CTHdrFtr buildHdr(Enum type, String pStyle, XWPFHeaderFooter wrapper, XWPFParagraph[] pars) { + //CTHdrFtr hdr = buildHdrFtr(pStyle, pars); // MB 24 May 2010 + CTHdrFtr hdr = buildHdrFtr(pStyle, pars, wrapper); // MB 24 May 2010 + setHeaderReference(type, wrapper); + return hdr; + } + + private CTHdrFtr buildHdrFtr(String pStyle, XWPFParagraph[] paragraphs) { + CTHdrFtr ftr = CTHdrFtr.Factory.newInstance(); + if (paragraphs != null) { + for (int i = 0 ; i < paragraphs.length ; i++) { + CTP p = ftr.addNewP(); + //ftr.setPArray(0, paragraphs[i].getCTP()); // MB 23 May 2010 + ftr.setPArray(i, paragraphs[i].getCTP()); // MB 23 May 2010 + } + } + else { + CTP p = ftr.addNewP(); + byte[] rsidr = doc.getDocument().getBody().getPArray(0).getRsidR(); + byte[] rsidrdefault = doc.getDocument().getBody().getPArray(0).getRsidRDefault(); + p.setRsidP(rsidr); + p.setRsidRDefault(rsidrdefault); + CTPPr pPr = p.addNewPPr(); + pPr.addNewPStyle().setVal(pStyle); + } + return ftr; + } + + /** + * MB 24 May 2010. Created this overloaded buildHdrFtr() method because testing demonstrated + * that the XWPFFooter or XWPFHeader object returned by calls to the createHeader(int, XWPFParagraph[]) + * and createFooter(int, XWPFParagraph[]) methods or the getXXXXXHeader/Footer methods where + * headers or footers had been added to a document since it had been created/opened, returned + * an object that contained no XWPFParagraph objects even if the header/footer itself did contain + * text. The reason was that this line of code; CTHdrFtr ftr = CTHdrFtr.Factory.newInstance(); + * created a brand new instance of the CTHDRFtr class which was then populated with data when + * it should have recovered the CTHdrFtr object encapsulated within the XWPFHeaderFooter object + * that had previoulsy been instantiated in the createHeader(int, XWPFParagraph[]) or + * createFooter(int, XWPFParagraph[]) methods. + */ + private CTHdrFtr buildHdrFtr(String pStyle, XWPFParagraph[] paragraphs, XWPFHeaderFooter wrapper) { + CTHdrFtr ftr = wrapper._getHdrFtr(); + if (paragraphs != null) { + for (int i = 0 ; i < paragraphs.length ; i++) { + CTP p = ftr.addNewP(); + ftr.setPArray(i, paragraphs[i].getCTP()); + } + } + else { + CTP p = ftr.addNewP(); + byte[] rsidr = doc.getDocument().getBody().getPArray(0).getRsidR(); + byte[] rsidrdefault = doc.getDocument().getBody().getPArray(0).getRsidRDefault(); + p.setRsidP(rsidr); + p.setRsidRDefault(rsidrdefault); + CTPPr pPr = p.addNewPPr(); + pPr.addNewPStyle().setVal(pStyle); + } + return ftr; + } + + + private void setFooterReference(Enum type, XWPFHeaderFooter wrapper) { + CTHdrFtrRef ref = doc.getDocument().getBody().getSectPr().addNewFooterReference(); + ref.setType(type); + ref.setId(wrapper.getPackageRelationship().getId()); } - private int getRelationIndex(XWPFRelation relation) { - List relations = doc.getRelations(); - int i = 1; - for (Iterator it = relations.iterator(); it.hasNext() ; ) { - POIXMLDocumentPart item = it.next(); - if (item.getPackageRelationship().getRelationshipType().equals(relation.getRelation())) { - i++; - } - } - return i; - } - - - private CTHdrFtr buildFtr(Enum type, String pStyle, XWPFHeaderFooter wrapper, XWPFParagraph[] pars) { - //CTHdrFtr ftr = buildHdrFtr(pStyle, pars); // MB 24 May 2010 - CTHdrFtr ftr = buildHdrFtr(pStyle, pars, wrapper); // MB 24 May 2010 - setFooterReference(type, wrapper); - return ftr; - } - - - private CTHdrFtr buildHdr(Enum type, String pStyle, XWPFHeaderFooter wrapper, XWPFParagraph[] pars) { - //CTHdrFtr hdr = buildHdrFtr(pStyle, pars); // MB 24 May 2010 - CTHdrFtr hdr = buildHdrFtr(pStyle, pars, wrapper); // MB 24 May 2010 - setHeaderReference(type, wrapper); - return hdr; - } - - private CTHdrFtr buildHdrFtr(String pStyle, XWPFParagraph[] paragraphs) { - CTHdrFtr ftr = CTHdrFtr.Factory.newInstance(); - if (paragraphs != null) { - for (int i = 0 ; i < paragraphs.length ; i++) { - CTP p = ftr.addNewP(); - //ftr.setPArray(0, paragraphs[i].getCTP()); // MB 23 May 2010 - ftr.setPArray(i, paragraphs[i].getCTP()); // MB 23 May 2010 - } - } - else { - CTP p = ftr.addNewP(); - byte[] rsidr = doc.getDocument().getBody().getPArray(0).getRsidR(); - byte[] rsidrdefault = doc.getDocument().getBody().getPArray(0).getRsidRDefault(); - p.setRsidP(rsidr); - p.setRsidRDefault(rsidrdefault); - CTPPr pPr = p.addNewPPr(); - pPr.addNewPStyle().setVal(pStyle); - } - return ftr; - } - - /** - * MB 24 May 2010. Created this overloaded buildHdrFtr() method because testing demonstrated - * that the XWPFFooter or XWPFHeader object returned by calls to the createHeader(int, XWPFParagraph[]) - * and createFooter(int, XWPFParagraph[]) methods or the getXXXXXHeader/Footer methods where - * headers or footers had been added to a document since it had been created/opened, returned - * an object that contained no XWPFParagraph objects even if the header/footer itself did contain - * text. The reason was that this line of code; CTHdrFtr ftr = CTHdrFtr.Factory.newInstance(); - * created a brand new instance of the CTHDRFtr class which was then populated with data when - * it should have recovered the CTHdrFtr object encapsulated within the XWPFHeaderFooter object - * that had previoulsy been instantiated in the createHeader(int, XWPFParagraph[]) or - * createFooter(int, XWPFParagraph[]) methods. - */ - private CTHdrFtr buildHdrFtr(String pStyle, XWPFParagraph[] paragraphs, XWPFHeaderFooter wrapper) { - CTHdrFtr ftr = wrapper._getHdrFtr(); - if (paragraphs != null) { - for (int i = 0 ; i < paragraphs.length ; i++) { - CTP p = ftr.addNewP(); - ftr.setPArray(i, paragraphs[i].getCTP()); - } - } - else { - CTP p = ftr.addNewP(); - byte[] rsidr = doc.getDocument().getBody().getPArray(0).getRsidR(); - byte[] rsidrdefault = doc.getDocument().getBody().getPArray(0).getRsidRDefault(); - p.setRsidP(rsidr); - p.setRsidRDefault(rsidrdefault); - CTPPr pPr = p.addNewPPr(); - pPr.addNewPStyle().setVal(pStyle); - } - return ftr; - } - - - private void setFooterReference(Enum type, XWPFHeaderFooter wrapper) { - CTHdrFtrRef ref = doc.getDocument().getBody().getSectPr().addNewFooterReference(); - ref.setType(type); - ref.setId(wrapper.getPackageRelationship().getId()); - } - - - private void setHeaderReference(Enum type, XWPFHeaderFooter wrapper) { - CTHdrFtrRef ref = doc.getDocument().getBody().getSectPr().addNewHeaderReference(); - ref.setType(type); - ref.setId(wrapper.getPackageRelationship().getId()); - } - - - private XmlOptions commit(XWPFHeaderFooter wrapper) { - XmlOptions xmlOptions = new XmlOptions(wrapper.DEFAULT_XML_OPTIONS); + private void setHeaderReference(Enum type, XWPFHeaderFooter wrapper) { + CTHdrFtrRef ref = doc.getDocument().getBody().getSectPr().addNewHeaderReference(); + ref.setType(type); + ref.setId(wrapper.getPackageRelationship().getId()); + } + + + private XmlOptions commit(XWPFHeaderFooter wrapper) { + XmlOptions xmlOptions = new XmlOptions(wrapper.DEFAULT_XML_OPTIONS); Map map = new HashMap(); map.put("http://schemas.openxmlformats.org/officeDocument/2006/math", "m"); map.put("urn:schemas-microsoft-com:office:office", "o"); @@ -317,154 +306,152 @@ public class XWPFHeaderFooterPolicy { map.put("http://schemas.microsoft.com/office/word/2006/wordml", "wne"); map.put("http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing", "wp"); xmlOptions.setSaveSuggestedPrefixes(map); - return xmlOptions; - } - - public XWPFHeader getFirstPageHeader() { - return firstPageHeader; - } - public XWPFFooter getFirstPageFooter() { - return firstPageFooter; - } - /** - * Returns the odd page header. This is - * also the same as the default one... - */ - public XWPFHeader getOddPageHeader() { - return defaultHeader; - } - /** - * Returns the odd page footer. This is - * also the same as the default one... - */ - public XWPFFooter getOddPageFooter() { - return defaultFooter; - } - public XWPFHeader getEvenPageHeader() { - return evenPageHeader; - } - public XWPFFooter getEvenPageFooter() { - return evenPageFooter; - } - public XWPFHeader getDefaultHeader() { - return defaultHeader; - } - public XWPFFooter getDefaultFooter() { - return defaultFooter; - } - - /** - * Get the header that applies to the given - * (1 based) page. - * @param pageNumber The one based page number - */ - public XWPFHeader getHeader(int pageNumber) { - if(pageNumber == 1 && firstPageHeader != null) { - return firstPageHeader; - } - if(pageNumber % 2 == 0 && evenPageHeader != null) { - return evenPageHeader; - } - return defaultHeader; - } - /** - * Get the footer that applies to the given - * (1 based) page. - * @param pageNumber The one based page number - */ - public XWPFFooter getFooter(int pageNumber) { - if(pageNumber == 1 && firstPageFooter != null) { - return firstPageFooter; - } - if(pageNumber % 2 == 0 && evenPageFooter != null) { - return evenPageFooter; - } - return defaultFooter; - } - - - public void createWatermark(String text) { - XWPFParagraph[] pars = new XWPFParagraph[1]; - try { - pars[0] = getWatermarkParagraph(text, 1); - createHeader(DEFAULT, pars); - pars[0] = getWatermarkParagraph(text, 2); - createHeader(FIRST, pars); - pars[0] = getWatermarkParagraph(text, 3); - createHeader(EVEN, pars); - } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } - - - /* - * This is the default Watermark paragraph; the only variable is the text message - * TODO: manage all the other variables - */ - private XWPFParagraph getWatermarkParagraph(String text, int idx) { - CTP p = CTP.Factory.newInstance(); - byte[] rsidr = doc.getDocument().getBody().getPArray(0).getRsidR(); - byte[] rsidrdefault = doc.getDocument().getBody().getPArray(0).getRsidRDefault(); - p.setRsidP(rsidr); - p.setRsidRDefault(rsidrdefault); - CTPPr pPr = p.addNewPPr(); - pPr.addNewPStyle().setVal("Header"); - // start watermark paragraph - CTR r = p.addNewR(); - CTRPr rPr = r.addNewRPr(); - rPr.addNewNoProof(); - CTPicture pict = r.addNewPict(); - CTGroup group = CTGroup.Factory.newInstance(); - CTShapetype shapetype = group.addNewShapetype(); - shapetype.setId("_x0000_t136"); - shapetype.setCoordsize("1600,21600"); - shapetype.setSpt(136); - shapetype.setAdj("10800"); - shapetype.setPath2("m@7,0l@8,0m@5,21600l@6,21600e"); - CTFormulas formulas = shapetype.addNewFormulas(); - formulas.addNewF().setEqn("sum #0 0 10800"); - formulas.addNewF().setEqn("prod #0 2 1"); - formulas.addNewF().setEqn("sum 21600 0 @1"); - formulas.addNewF().setEqn("sum 0 0 @2"); - formulas.addNewF().setEqn("sum 21600 0 @3"); - formulas.addNewF().setEqn("if @0 @3 0"); - formulas.addNewF().setEqn("if @0 21600 @1"); - formulas.addNewF().setEqn("if @0 0 @2"); - formulas.addNewF().setEqn("if @0 @4 21600"); - formulas.addNewF().setEqn("mid @5 @6"); - formulas.addNewF().setEqn("mid @8 @5"); - formulas.addNewF().setEqn("mid @7 @8"); - formulas.addNewF().setEqn("mid @6 @7"); - formulas.addNewF().setEqn("sum @6 0 @5"); - CTPath path = shapetype.addNewPath(); - path.setTextpathok(STTrueFalse.T); - path.setConnecttype(STConnectType.CUSTOM); - path.setConnectlocs("@9,0;@10,10800;@11,21600;@12,10800"); - path.setConnectangles("270,180,90,0"); - CTTextPath shapeTypeTextPath = shapetype.addNewTextpath(); - shapeTypeTextPath.setOn(STTrueFalse.T); - shapeTypeTextPath.setFitshape(STTrueFalse.T); - CTHandles handles = shapetype.addNewHandles(); - CTH h = handles.addNewH(); - h.setPosition("#0,bottomRight"); - h.setXrange("6629,14971"); - CTLock lock = shapetype.addNewLock(); - lock.setExt(STExt.EDIT); - CTShape shape = group.addNewShape(); - shape.setId("PowerPlusWaterMarkObject" + idx); - shape.setSpid("_x0000_s102" + (4+idx)); - shape.setType("#_x0000_t136"); - shape.setStyle("position:absolute;margin-left:0;margin-top:0;width:415pt;height:207.5pt;z-index:-251654144;mso-wrap-edited:f;mso-position-horizontal:center;mso-position-horizontal-relative:margin;mso-position-vertical:center;mso-position-vertical-relative:margin"); - shape.setWrapcoords("616 5068 390 16297 39 16921 -39 17155 7265 17545 7186 17467 -39 17467 18904 17467 10507 17467 8710 17545 18904 17077 18787 16843 18358 16297 18279 12554 19178 12476 20701 11774 20779 11228 21131 10059 21248 8811 21248 7563 20975 6316 20935 5380 19490 5146 14022 5068 2616 5068"); - shape.setFillcolor("black"); - shape.setStroked(STTrueFalse.FALSE); - CTTextPath shapeTextPath = shape.addNewTextpath(); - shapeTextPath.setStyle("font-family:"Cambria";font-size:1pt"); - shapeTextPath.setString(text); - pict.set(group); - // end watermark paragraph - return new XWPFParagraph(p, doc); - } + return xmlOptions; + } + + public XWPFHeader getFirstPageHeader() { + return firstPageHeader; + } + public XWPFFooter getFirstPageFooter() { + return firstPageFooter; + } + /** + * Returns the odd page header. This is + * also the same as the default one... + */ + public XWPFHeader getOddPageHeader() { + return defaultHeader; + } + /** + * Returns the odd page footer. This is + * also the same as the default one... + */ + public XWPFFooter getOddPageFooter() { + return defaultFooter; + } + public XWPFHeader getEvenPageHeader() { + return evenPageHeader; + } + public XWPFFooter getEvenPageFooter() { + return evenPageFooter; + } + public XWPFHeader getDefaultHeader() { + return defaultHeader; + } + public XWPFFooter getDefaultFooter() { + return defaultFooter; + } + + /** + * Get the header that applies to the given + * (1 based) page. + * @param pageNumber The one based page number + */ + public XWPFHeader getHeader(int pageNumber) { + if(pageNumber == 1 && firstPageHeader != null) { + return firstPageHeader; + } + if(pageNumber % 2 == 0 && evenPageHeader != null) { + return evenPageHeader; + } + return defaultHeader; + } + /** + * Get the footer that applies to the given + * (1 based) page. + * @param pageNumber The one based page number + */ + public XWPFFooter getFooter(int pageNumber) { + if(pageNumber == 1 && firstPageFooter != null) { + return firstPageFooter; + } + if(pageNumber % 2 == 0 && evenPageFooter != null) { + return evenPageFooter; + } + return defaultFooter; + } + + public void createWatermark(String text) { + XWPFParagraph[] pars = new XWPFParagraph[1]; + try { + pars[0] = getWatermarkParagraph(text, 1); + createHeader(DEFAULT, pars); + pars[0] = getWatermarkParagraph(text, 2); + createHeader(FIRST, pars); + pars[0] = getWatermarkParagraph(text, 3); + createHeader(EVEN, pars); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + /* + * This is the default Watermark paragraph; the only variable is the text message + * TODO: manage all the other variables + */ + private XWPFParagraph getWatermarkParagraph(String text, int idx) { + CTP p = CTP.Factory.newInstance(); + byte[] rsidr = doc.getDocument().getBody().getPArray(0).getRsidR(); + byte[] rsidrdefault = doc.getDocument().getBody().getPArray(0).getRsidRDefault(); + p.setRsidP(rsidr); + p.setRsidRDefault(rsidrdefault); + CTPPr pPr = p.addNewPPr(); + pPr.addNewPStyle().setVal("Header"); + // start watermark paragraph + CTR r = p.addNewR(); + CTRPr rPr = r.addNewRPr(); + rPr.addNewNoProof(); + CTPicture pict = r.addNewPict(); + CTGroup group = CTGroup.Factory.newInstance(); + CTShapetype shapetype = group.addNewShapetype(); + shapetype.setId("_x0000_t136"); + shapetype.setCoordsize("1600,21600"); + shapetype.setSpt(136); + shapetype.setAdj("10800"); + shapetype.setPath2("m@7,0l@8,0m@5,21600l@6,21600e"); + CTFormulas formulas = shapetype.addNewFormulas(); + formulas.addNewF().setEqn("sum #0 0 10800"); + formulas.addNewF().setEqn("prod #0 2 1"); + formulas.addNewF().setEqn("sum 21600 0 @1"); + formulas.addNewF().setEqn("sum 0 0 @2"); + formulas.addNewF().setEqn("sum 21600 0 @3"); + formulas.addNewF().setEqn("if @0 @3 0"); + formulas.addNewF().setEqn("if @0 21600 @1"); + formulas.addNewF().setEqn("if @0 0 @2"); + formulas.addNewF().setEqn("if @0 @4 21600"); + formulas.addNewF().setEqn("mid @5 @6"); + formulas.addNewF().setEqn("mid @8 @5"); + formulas.addNewF().setEqn("mid @7 @8"); + formulas.addNewF().setEqn("mid @6 @7"); + formulas.addNewF().setEqn("sum @6 0 @5"); + CTPath path = shapetype.addNewPath(); + path.setTextpathok(STTrueFalse.T); + path.setConnecttype(STConnectType.CUSTOM); + path.setConnectlocs("@9,0;@10,10800;@11,21600;@12,10800"); + path.setConnectangles("270,180,90,0"); + CTTextPath shapeTypeTextPath = shapetype.addNewTextpath(); + shapeTypeTextPath.setOn(STTrueFalse.T); + shapeTypeTextPath.setFitshape(STTrueFalse.T); + CTHandles handles = shapetype.addNewHandles(); + CTH h = handles.addNewH(); + h.setPosition("#0,bottomRight"); + h.setXrange("6629,14971"); + CTLock lock = shapetype.addNewLock(); + lock.setExt(STExt.EDIT); + CTShape shape = group.addNewShape(); + shape.setId("PowerPlusWaterMarkObject" + idx); + shape.setSpid("_x0000_s102" + (4+idx)); + shape.setType("#_x0000_t136"); + shape.setStyle("position:absolute;margin-left:0;margin-top:0;width:415pt;height:207.5pt;z-index:-251654144;mso-wrap-edited:f;mso-position-horizontal:center;mso-position-horizontal-relative:margin;mso-position-vertical:center;mso-position-vertical-relative:margin"); + shape.setWrapcoords("616 5068 390 16297 39 16921 -39 17155 7265 17545 7186 17467 -39 17467 18904 17467 10507 17467 8710 17545 18904 17077 18787 16843 18358 16297 18279 12554 19178 12476 20701 11774 20779 11228 21131 10059 21248 8811 21248 7563 20975 6316 20935 5380 19490 5146 14022 5068 2616 5068"); + shape.setFillcolor("black"); + shape.setStroked(STTrueFalse.FALSE); + CTTextPath shapeTextPath = shape.addNewTextpath(); + shapeTextPath.setStyle("font-family:"Cambria";font-size:1pt"); + shapeTextPath.setString(text); + pict.set(group); + // end watermark paragraph + return new XWPFParagraph(p, doc); + } } diff --git a/src/ooxml/java/org/apache/poi/xwpf/usermodel/IBody.java b/src/ooxml/java/org/apache/poi/xwpf/usermodel/IBody.java index a85ec7408a..8d15b07279 100644 --- a/src/ooxml/java/org/apache/poi/xwpf/usermodel/IBody.java +++ b/src/ooxml/java/org/apache/poi/xwpf/usermodel/IBody.java @@ -19,6 +19,7 @@ package org.apache.poi.xwpf.usermodel; import java.util.List; +import org.apache.poi.POIXMLDocumentPart; import org.apache.xmlbeans.XmlCursor; import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTP; import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTbl; @@ -42,7 +43,7 @@ public interface IBody { * belongs. * @return the Part, to which the body belongs */ - IBody getPart(); + POIXMLDocumentPart getPart(); /** * get the PartType of the body, for example diff --git a/src/ooxml/java/org/apache/poi/xwpf/usermodel/IBodyElement.java b/src/ooxml/java/org/apache/poi/xwpf/usermodel/IBodyElement.java index 527eb7ae4d..70009be3b5 100644 --- a/src/ooxml/java/org/apache/poi/xwpf/usermodel/IBodyElement.java +++ b/src/ooxml/java/org/apache/poi/xwpf/usermodel/IBodyElement.java @@ -17,6 +17,7 @@ package org.apache.poi.xwpf.usermodel; +import org.apache.poi.POIXMLDocumentPart; /** * 9 Jan 2010 @@ -24,7 +25,8 @@ package org.apache.poi.xwpf.usermodel; * */ public interface IBodyElement{ - IBody getPart(); + IBody getBody(); + POIXMLDocumentPart getPart(); BodyType getPartType(); BodyElementType getElementType(); } 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 7fed7b3e90..517c72358e 100644 --- a/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFDocument.java +++ b/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFDocument.java @@ -16,12 +16,12 @@ ==================================================================== */ package org.apache.poi.xwpf.usermodel; -import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; @@ -36,6 +36,7 @@ import org.apache.poi.POIXMLDocument; import org.apache.poi.POIXMLDocumentPart; import org.apache.poi.POIXMLException; import org.apache.poi.POIXMLProperties; +import org.apache.poi.POIXMLRelation; import org.apache.poi.openxml4j.exceptions.InvalidFormatException; import org.apache.poi.openxml4j.exceptions.OpenXML4JException; import org.apache.poi.openxml4j.opc.OPCPackage; @@ -46,6 +47,7 @@ import org.apache.poi.openxml4j.opc.PackageRelationshipTypes; import org.apache.poi.openxml4j.opc.PackagingURIHelper; import org.apache.poi.openxml4j.opc.TargetMode; import org.apache.poi.util.IOUtils; +import org.apache.poi.util.IdentifierManager; import org.apache.poi.util.Internal; import org.apache.poi.util.PackageHelper; import org.apache.poi.xwpf.model.XWPFHeaderFooterPolicy; @@ -85,16 +87,21 @@ public class XWPFDocument extends POIXMLDocument implements Document, IBody { private CTDocument1 ctDocument; private XWPFSettings settings; - protected List footers; - protected List headers; - protected List comments; - protected List hyperlinks; - protected List paragraphs; - protected List tables; - protected List bodyElements; - protected List pictures; - protected Map footnotes; - protected Map endnotes; + /** + * Keeps track on all id-values used in this document and included parts, like headers, footers, etc. + */ + private IdentifierManager drawingIdManager = new IdentifierManager(1L,4294967295L); + protected List footers = new ArrayList(); + protected List headers = new ArrayList(); + protected List comments = new ArrayList(); + protected List hyperlinks = new ArrayList(); + protected List paragraphs = new ArrayList(); + protected List tables = new ArrayList(); + protected List bodyElements = new ArrayList(); + protected List pictures = new ArrayList(); + protected Map> packagePictures = new HashMap>(); + protected Map footnotes = new HashMap(); + protected Map endnotes = new HashMap(); protected XWPFNumbering numbering; protected XWPFStyles styles; @@ -122,80 +129,78 @@ public class XWPFDocument extends POIXMLDocument implements Document, IBody { @Override protected void onDocumentRead() throws IOException { - hyperlinks = new ArrayList(); - comments = new ArrayList(); - paragraphs = new ArrayList(); - tables= new ArrayList(); - bodyElements = new ArrayList(); - footers = new ArrayList(); - headers = new ArrayList(); - footnotes = new HashMap(); - endnotes = new HashMap(); - try { DocumentDocument doc = DocumentDocument.Factory.parse(getPackagePart().getInputStream()); ctDocument = doc.getDocument(); initFootnotes(); - - + // parse the document with cursor and add // the XmlObject to its lists - XmlCursor cursor = ctDocument.getBody().newCursor(); + XmlCursor cursor = ctDocument.getBody().newCursor(); cursor.selectPath("./*"); while (cursor.toNextSelection()) { XmlObject o = cursor.getObject(); if (o instanceof CTP) { - XWPFParagraph p = new XWPFParagraph((CTP)o, this); - bodyElements.add(p); - paragraphs.add(p); - } - if (o instanceof CTTbl) { - XWPFTable t = new XWPFTable((CTTbl)o, this); - bodyElements.add(t); - tables.add(t); + XWPFParagraph p = new XWPFParagraph((CTP) o, this); + bodyElements.add(p); + paragraphs.add(p); + } else if (o instanceof CTTbl) { + XWPFTable t = new XWPFTable((CTTbl) o, this); + bodyElements.add(t); + tables.add(t); } } cursor.dispose(); - + // Sort out headers and footers - if (doc.getDocument().getBody().getSectPr() != null) - headerFooterPolicy = new XWPFHeaderFooterPolicy(this); - - // Create for each XML-part in the Package a PartClass - for(POIXMLDocumentPart p : getRelations()){ + if (doc.getDocument().getBody().getSectPr() != null) + headerFooterPolicy = new XWPFHeaderFooterPolicy(this); + + // Create for each XML-part in the Package a PartClass + for (POIXMLDocumentPart p : getRelations()) { String relation = p.getPackageRelationship().getRelationshipType(); - if(relation.equals(XWPFRelation.STYLES.getRelation())){ - this.styles = (XWPFStyles) p; - } else if (relation.equals(XWPFRelation.NUMBERING.getRelation())){ - this.numbering = (XWPFNumbering) p; - } else if (relation.equals(XWPFRelation.FOOTER.getRelation())){ - footers.add((XWPFFooter)p); - } else if (relation.equals(XWPFRelation.HEADER.getRelation())){ - headers.add((XWPFHeader)p); - } else if (relation.equals(XWPFRelation.COMMENT.getRelation())){ + if (relation.equals(XWPFRelation.STYLES.getRelation())) { + this.styles = (XWPFStyles) p; + this.styles.onDocumentRead(); + } else if (relation.equals(XWPFRelation.NUMBERING.getRelation())) { + this.numbering = (XWPFNumbering) p; + this.numbering.onDocumentRead(); + } else if (relation.equals(XWPFRelation.FOOTER.getRelation())) { + XWPFFooter footer = (XWPFFooter) p; + footers.add(footer); + footer.onDocumentRead(); + } else if (relation.equals(XWPFRelation.HEADER.getRelation())) { + XWPFHeader header = (XWPFHeader) p; + headers.add(header); + header.onDocumentRead(); + } else if (relation.equals(XWPFRelation.COMMENT.getRelation())) { + // TODO Create according XWPFComment class, extending POIXMLDocumentPart CommentsDocument cmntdoc = CommentsDocument.Factory.parse(p.getPackagePart().getInputStream()); - for(CTComment ctcomment : cmntdoc.getComments().getCommentList()) { + for (CTComment ctcomment : cmntdoc.getComments().getCommentList()) { comments.add(new XWPFComment(ctcomment, this)); } - } else if (relation.equals(XWPFRelation.SETTINGS.getRelation())){ - settings = (XWPFSettings)p; + } else if (relation.equals(XWPFRelation.SETTINGS.getRelation())) { + settings = (XWPFSettings) p; + settings.onDocumentRead(); + } else if (relation.equals(XWPFRelation.IMAGES.getRelation())) { + XWPFPictureData picData = (XWPFPictureData) p; + picData.onDocumentRead(); + registerPackagePictureData(picData); + pictures.add(picData); } } - initHyperlinks(); } catch (XmlException e) { throw new POIXMLException(e); } - // create for every Graphic-Part in Package a new XWPFGraphic - getAllPictures(); } private void initHyperlinks(){ // Get the hyperlinks // TODO: make me optional/separated in private function - try { - Iterator relIter = + try { + Iterator relIter = getPackagePart().getRelationshipsByType(XWPFRelation.HYPERLINK.getRelation()).iterator(); while(relIter.hasNext()) { PackageRelationship rel = relIter.next(); @@ -251,20 +256,10 @@ public class XWPFDocument extends POIXMLDocument implements Document, IBody { */ @Override protected void onDocumentCreate() { - hyperlinks = new ArrayList(); - comments = new ArrayList(); - paragraphs = new ArrayList(); - tables= new ArrayList(); - bodyElements = new ArrayList(); - footers = new ArrayList(); - headers = new ArrayList(); - footnotes = new HashMap(); - endnotes = new HashMap(); - ctDocument = CTDocument1.Factory.newInstance(); ctDocument.addNewBody(); - - settings = (XWPFSettings) createRelationship(XWPFRelation.SETTINGS, XWPFFactory.getInstance()); + + settings = (XWPFSettings) createRelationship(XWPFRelation.SETTINGS,XWPFFactory.getInstance()); POIXMLProperties.ExtendedProperties expProps = getProperties().getExtendedProperties(); expProps.getUnderlyingProperties().setApplication(DOCUMENT_CREATOR); @@ -277,6 +272,10 @@ public class XWPFDocument extends POIXMLDocument implements Document, IBody { public CTDocument1 getDocument() { return ctDocument; } + + IdentifierManager getDrawingIdManager() { + return drawingIdManager; + } /** * returns an Iterator with paragraphs and tables @@ -340,8 +339,7 @@ public class XWPFDocument extends POIXMLDocument implements Document, IBody { public XWPFHyperlink getHyperlinkByID(String id) { Iterator iter = hyperlinks.iterator(); - while(iter.hasNext()) - { + while (iter.hasNext()) { XWPFHyperlink link = iter.next(); if(link.getId().equals(id)) return link; @@ -363,15 +361,12 @@ public class XWPFDocument extends POIXMLDocument implements Document, IBody { } public XWPFHyperlink[] getHyperlinks() { - return hyperlinks.toArray( - new XWPFHyperlink[hyperlinks.size()] - ); + return hyperlinks.toArray(new XWPFHyperlink[hyperlinks.size()]); } public XWPFComment getCommentByID(String id) { Iterator iter = comments.iterator(); - while(iter.hasNext()) - { + while (iter.hasNext()) { XWPFComment comment = iter.next(); if(comment.getId().equals(id)) return comment; @@ -379,10 +374,9 @@ public class XWPFDocument extends POIXMLDocument implements Document, IBody { return null; } + public XWPFComment[] getComments() { - return comments.toArray( - new XWPFComment[comments.size()] - ); + return comments.toArray(new XWPFComment[comments.size()]); } /** @@ -391,10 +385,8 @@ public class XWPFDocument extends POIXMLDocument implements Document, IBody { */ public PackagePart getPartById(String id) { try { - return getTargetPart( - getCorePart().getRelationship(id) - ); - } catch(InvalidFormatException e) { + return getTargetPart(getCorePart().getRelationship(id)); + } catch (InvalidFormatException e) { throw new IllegalArgumentException(e); } } @@ -422,8 +414,7 @@ public class XWPFDocument extends POIXMLDocument implements Document, IBody { throw new IllegalStateException("Expecting one Styles document part, but found " + parts.length); } - StylesDocument sd = - StylesDocument.Factory.parse(parts[0].getInputStream()); + StylesDocument sd = StylesDocument.Factory.parse(parts[0].getInputStream()); return sd.getStyles(); } @@ -435,15 +426,17 @@ public class XWPFDocument extends POIXMLDocument implements Document, IBody { List embedds = new LinkedList(); // Get the embeddings for the workbook - for(PackageRelationship rel : getPackagePart().getRelationshipsByType(OLE_OBJECT_REL_TYPE)) + for (PackageRelationship rel : getPackagePart().getRelationshipsByType(OLE_OBJECT_REL_TYPE)) { embedds.add(getTargetPart(rel)); + } - for(PackageRelationship rel : getPackagePart().getRelationshipsByType(PACK_OBJECT_REL_TYPE)) + for (PackageRelationship rel : getPackagePart().getRelationshipsByType(PACK_OBJECT_REL_TYPE)) { embedds.add(getTargetPart(rel)); + } return embedds; } - + /** * Finds that for example the 2nd entry in the body list is the 1st paragraph */ @@ -452,7 +445,7 @@ public class XWPFDocument extends POIXMLDocument implements Document, IBody { if(list.size() == 0) { return -1; } - + if(pos >= 0 && pos < bodyElements.size()) { // Ensure the type is correct IBodyElement needle = bodyElements.get(pos); @@ -460,7 +453,7 @@ public class XWPFDocument extends POIXMLDocument implements Document, IBody { // Wrong type return -1; } - + // Work back until we find it int startPos = Math.min(pos, list.size()-1); for(int i=startPos; i>=0; i--) { @@ -475,17 +468,21 @@ public class XWPFDocument extends POIXMLDocument implements Document, IBody { } /** - * get with the position of a Paragraph in the bodyelement array list - * the position of this paragraph in the paragraph array list - * @param pos position of the paragraph in the bodyelement array list - * @return if there is a paragraph at the position in the bodyelement array list, - * else it will return -1 - * + * Look up the paragraph at the specified position in the body elemnts list + * and return this paragraphs position in the paragraphs list + * + * @param pos + * The position of the relevant paragraph in the body elements + * list + * @return the position of the paragraph in the paragraphs list, if there is + * a paragraph at the position in the bodyelements list. Else it + * will return -1 + * */ - public int getParagraphPos(int pos){ + public int getParagraphPos(int pos) { return getBodyElementSpecificPos(pos, paragraphs); } - + /** * get with the position of a table in the bodyelement array list * the position of this table in the table array list @@ -493,98 +490,135 @@ public class XWPFDocument extends POIXMLDocument implements Document, IBody { * @return if there is a table at the position in the bodyelement array list, * else it will return null. */ - public int getTablePos(int pos){ + public int getTablePos(int pos) { return getBodyElementSpecificPos(pos, tables); } - + /** - * add a new paragraph at position of the cursor + * add a new paragraph at position of the cursor. The cursor must be on the + * {@link TokenType#START} tag of an subelement of the documents body. When + * this method is done, the cursor passed as parameter points to the + * {@link TokenType#END} of the newly inserted paragraph. + * * @param cursor + * @return the {@link XWPFParagraph} object representing the newly inserted + * CTP object */ - public XWPFParagraph insertNewParagraph(XmlCursor cursor){ - if(isCursorInBody(cursor)){ - String uri = CTP.type.getName().getNamespaceURI(); - String localPart = "p"; - cursor.beginElement(localPart,uri); - cursor.toParent(); - CTP p = (CTP)cursor.getObject(); - XWPFParagraph newP = new XWPFParagraph(p, this); - XmlObject o = null; - while(!(o instanceof CTP)&&(cursor.toPrevSibling())){ - o = cursor.getObject(); - } - if((!(o instanceof CTP)) || (CTP)o == p){ - paragraphs.add(0, newP); - } - else{ - int pos = paragraphs.indexOf(getParagraph((CTP)o))+1; - paragraphs.add(pos,newP); - } - int i=0; - cursor.toCursor(p.newCursor()); - while(cursor.toPrevSibling()){ - o =cursor.getObject(); - if(o instanceof CTP || o instanceof CTTbl) - i++; - } - bodyElements.add(i, newP); - cursor.toCursor(p.newCursor()); - cursor.toEndToken(); - return newP; - } - return null; - } - - public XWPFTable insertNewTbl(XmlCursor cursor) { - if(isCursorInBody(cursor)){ - String uri = CTTbl.type.getName().getNamespaceURI(); - String localPart ="tbl"; - cursor.beginElement(localPart,uri); - cursor.toParent(); - CTTbl t = (CTTbl)cursor.getObject(); - XWPFTable newT = new XWPFTable(t, this); - cursor.removeXmlContents(); - XmlObject o = null; - while(!(o instanceof CTTbl)&&(cursor.toPrevSibling())){ - o = cursor.getObject(); - } - if(!(o instanceof CTTbl)){ - tables.add(0, newT); - } - else{ - int pos = tables.indexOf(getTable((CTTbl)o))+1; - tables.add(pos,newT); - } - int i=0; - cursor = t.newCursor(); - while(cursor.toPrevSibling()){ - o =cursor.getObject(); - if(o instanceof CTP || o instanceof CTTbl) - i++; - } - bodyElements.add(i, newT); - cursor = t.newCursor(); - cursor.toEndToken(); - return newT; - } - return null; - } - - /** - * verifies that cursor is on the right position - * @param cursor - */ - private boolean isCursorInBody(XmlCursor cursor) { - XmlCursor verify = cursor.newCursor(); - verify.toParent(); - if(verify.getObject() == this.ctDocument.getBody()){ - return true; - } - XmlObject o = verify.getObject(); - return false; - - } - + public XWPFParagraph insertNewParagraph(XmlCursor cursor) { + if (isCursorInBody(cursor)) { + String uri = CTP.type.getName().getNamespaceURI(); + /* + * TODO DO not use a coded constant, find the constant in the OOXML + * classes instead, as the child of type CT_Paragraph is defined in the + * OOXML schema as 'p' + */ + String localPart = "p"; + // creates a new Paragraph, cursor is positioned inside the new + // element + cursor.beginElement(localPart, uri); + // move the cursor to the START token to the paragraph just created + cursor.toParent(); + CTP p = (CTP) cursor.getObject(); + XWPFParagraph newP = new XWPFParagraph(p, this); + XmlObject o = null; + /* + * move the cursor to the previous element until a) the next + * paragraph is found or b) all elements have been passed + */ + while (!(o instanceof CTP) && (cursor.toPrevSibling())) { + o = cursor.getObject(); + } + /* + * if the object that has been found is a) not a paragraph or b) is + * the paragraph that has just been inserted, as the cursor in the + * while loop above was not moved as there were no other siblings, + * then the paragraph that was just inserted is the first paragraph + * in the body. Otherwise, take the previous paragraph and calculate + * the new index for the new paragraph. + */ + if ((!(o instanceof CTP)) || (CTP) o == p) { + paragraphs.add(0, newP); + } else { + int pos = paragraphs.indexOf(getParagraph((CTP) o)) + 1; + paragraphs.add(pos, newP); + } + + /* + * create a new cursor, that points to the START token of the just + * inserted paragraph + */ + XmlCursor newParaPos = p.newCursor(); + try { + /* + * Calculate the paragraphs index in the list of all body + * elements + */ + int i = 0; + cursor.toCursor(newParaPos); + while (cursor.toPrevSibling()) { + o = cursor.getObject(); + if (o instanceof CTP || o instanceof CTTbl) + i++; + } + bodyElements.add(i, newP); + cursor.toCursor(newParaPos); + cursor.toEndToken(); + return newP; + } finally { + newParaPos.dispose(); + } + } + return null; + } + + public XWPFTable insertNewTbl(XmlCursor cursor) { + if (isCursorInBody(cursor)) { + String uri = CTTbl.type.getName().getNamespaceURI(); + String localPart = "tbl"; + cursor.beginElement(localPart, uri); + cursor.toParent(); + CTTbl t = (CTTbl) cursor.getObject(); + XWPFTable newT = new XWPFTable(t, this); + cursor.removeXmlContents(); + XmlObject o = null; + while (!(o instanceof CTTbl) && (cursor.toPrevSibling())) { + o = cursor.getObject(); + } + if (!(o instanceof CTTbl)) { + tables.add(0, newT); + } else { + int pos = tables.indexOf(getTable((CTTbl) o)) + 1; + tables.add(pos, newT); + } + int i = 0; + cursor = t.newCursor(); + while (cursor.toPrevSibling()) { + o = cursor.getObject(); + if (o instanceof CTP || o instanceof CTTbl) + i++; + } + bodyElements.add(i, newT); + cursor = t.newCursor(); + cursor.toEndToken(); + return newT; + } + return null; + } + + /** + * verifies that cursor is on the right position + * @param cursor + */ + private boolean isCursorInBody(XmlCursor cursor) { + XmlCursor verify = cursor.newCursor(); + verify.toParent(); + try { + return (verify.getObject() == this.ctDocument.getBody()); + } finally { + verify.dispose(); + } + } + private int getPosOfBodyElement(IBodyElement needle) { BodyElementType type = needle.getElementType(); IBodyElement current; @@ -608,7 +642,7 @@ public class XWPFDocument extends POIXMLDocument implements Document, IBody { public int getPosOfParagraph(XWPFParagraph p){ return getPosOfBodyElement(p); } - + /** * Get the position of the table, within the list of * all the body elements. @@ -666,7 +700,7 @@ public class XWPFDocument extends POIXMLDocument implements Document, IBody { * Appends a new paragraph to this document * @return a new paragraph */ - public XWPFParagraph createParagraph(){ + public XWPFParagraph createParagraph() { XWPFParagraph p = new XWPFParagraph(ctDocument.getBody().addNewP(), this); bodyElements.add(p); paragraphs.add(p); @@ -753,24 +787,28 @@ public class XWPFDocument extends POIXMLDocument implements Document, IBody { * @param paragraph * @param pos */ - public void setParagraph(XWPFParagraph paragraph, int pos){ - paragraphs.set(pos, paragraph); - ctDocument.getBody().setPArray(pos, paragraph.getCTP()); + public void setParagraph(XWPFParagraph paragraph, int pos) { + paragraphs.set(pos, paragraph); + ctDocument.getBody().setPArray(pos, paragraph.getCTP()); + /* TODO update body element, update xwpf element, verify that + * incoming paragraph belongs to this document or if not, XML was + * copied properly (namespace-abbreviations, etc.) + */ } - + /** * @return the LastParagraph of the document */ - public XWPFParagraph getLastParagraph(){ - int lastPos = paragraphs.toArray().length - 1; - return paragraphs.get(lastPos); + public XWPFParagraph getLastParagraph() { + int lastPos = paragraphs.toArray().length - 1; + return paragraphs.get(lastPos); } /** * Create an empty table with one row and one column as default. * @return a new table */ - public XWPFTable createTable(){ + public XWPFTable createTable() { XWPFTable table = new XWPFTable(ctDocument.getBody().addNewTbl(), this); bodyElements.add(table); tables.add(table); @@ -789,37 +827,35 @@ public class XWPFDocument extends POIXMLDocument implements Document, IBody { tables.add(table); return table; } - - + /** * */ public void createTOC() { CTSdtBlock block = this.getDocument().getBody().addNewSdt(); TOC toc = new TOC(block); - for (XWPFParagraph par: paragraphs ) { + for (XWPFParagraph par : paragraphs) { String parStyle = par.getStyle(); if (parStyle != null && parStyle.substring(0, 7).equals("Heading")) { try { int level = Integer.valueOf(parStyle.substring("Heading".length())).intValue(); toc.addRow(level, par.getText(), 1, "112723803"); - } - catch (NumberFormatException e) { + } catch (NumberFormatException e) { e.printStackTrace(); } } } } - + /**Replace content of table in array tables at position pos with a * @param pos * @param table */ - public void setTable(int pos, XWPFTable table){ - tables.set(pos, table); - ctDocument.getBody().setTblArray(pos, table.getCTTbl()); + public void setTable(int pos, XWPFTable table) { + tables.set(pos, table); + ctDocument.getBody().setTblArray(pos, table.getCTTbl()); } - + /** * Verifies that the documentProtection tag in settings.xml file
* specifies that the protection is enforced (w:enforcement="1")
@@ -900,7 +936,7 @@ public class XWPFDocument extends POIXMLDocument implements Document, IBody { * <w:documentProtection w:edit="readOnly" w:enforcement="1"/> * */ - public void enforceReadonlyProtection() { + public void enforceReadonlyProtection() { settings.setEnforcementEditValue(STDocProtect.READ_ONLY); } @@ -918,7 +954,7 @@ public class XWPFDocument extends POIXMLDocument implements Document, IBody { */ public void enforceFillingFormsProtection() { settings.setEnforcementEditValue(STDocProtect.FORMS); - } + } /** * Enforce the Comments protection.
@@ -949,7 +985,7 @@ public class XWPFDocument extends POIXMLDocument implements Document, IBody { * */ public void enforceTrackedChangesProtection() { - settings.setEnforcementEditValue(STDocProtect.TRACKED_CHANGES); + settings.setEnforcementEditValue(STDocProtect.TRACKED_CHANGES); } /** @@ -960,182 +996,220 @@ public class XWPFDocument extends POIXMLDocument implements Document, IBody { public void removeProtectionEnforcement() { settings.removeEnforcement(); } - + /** - * Return the zoom level, as a percentage + * inserts an existing XWPFTable to the arrays bodyElements and tables + * @param pos + * @param table */ - public long getZoomPercent() { - return settings.getZoomPercent(); + public void insertTable(int pos, XWPFTable table) { + bodyElements.add(pos, table); + int i; + for (i = 0; i < ctDocument.getBody().getTblList().size(); i++) { + CTTbl tbl = ctDocument.getBody().getTblArray(i); + if (tbl == table.getCTTbl()) { + break; + } + } + tables.add(i, table); } - + /** - * Sets the zoom level, as a percentage + * Returns all Pictures, which are referenced from the document itself. + * @return a {@link List} of {@link XWPFPictureData}. The returned {@link List} is unmodifiable. Use #a */ - public void setZoomPercent(long zoomPercent) { - settings.setZoomPercent(zoomPercent); - } - - /** - * inserts an existing XWPFTable to the arrays bodyElements and tables - * @param pos - * @param table - */ - public void insertTable(int pos, XWPFTable table) { - bodyElements.add(pos, table); - int i; - for (i = 0; i < ctDocument.getBody().getTblList().size(); i++) { - CTTbl tbl = ctDocument.getBody().getTblArray(i); - if(tbl == table.getCTTbl()){ - break; - } - } - tables.add(i, table); - } - public List getAllPictures() { - if(pictures == null){ - pictures = new ArrayList(); - for (POIXMLDocumentPart poixmlDocumentPart : getRelations()){ - if(poixmlDocumentPart instanceof XWPFPictureData){ - pictures.add((XWPFPictureData)poixmlDocumentPart); - } - } - } - return pictures; + return Collections.unmodifiableList(pictures); } - + /** - * @return all Pictures in this package + * @return all Pictures in this package */ - public List getAllPackagePictures(){ - List pkgpictures = new ArrayList(); - pkgpictures.addAll(getAllPictures()); - for (POIXMLDocumentPart poixmlDocumentPart : getRelations()){ - if(poixmlDocumentPart instanceof XWPFHeaderFooter){ - pkgpictures.addAll(((XWPFHeaderFooter)poixmlDocumentPart).getAllPictures()); - } - } - return pkgpictures; + public List getAllPackagePictures() { + List result = new ArrayList(); + Collection> values = packagePictures.values(); + for (List list : values) { + result.addAll(list); + } + return Collections.unmodifiableList(result); } - - /** - * Adds a picture to the document. Users should normally call - * {@link XWPFRun#addPicture(InputStream, int)} - * - * - * @param is The stream to read image from - * @param format The format of the picture, eg {@link Document#PICTURE_TYPE_JPEG} - * - * @return the index to this picture (0 based), the added picture can be obtained from {@link #getAllPictures()} . - * @throws InvalidFormatException - */ - public int addPicture(InputStream is, int format) throws IOException, InvalidFormatException { - int imageNumber = getNextPicNameNumber(format); - XWPFPictureData img = (XWPFPictureData)createRelationship(XWPFPictureData.RELATIONS[format], XWPFFactory.getInstance(), imageNumber, false); - OutputStream out = img.getPackagePart().getOutputStream(); - IOUtils.copy(is, out); - out.close(); - pictures.add(img); - return getAllPictures().size()-1; + void registerPackagePictureData(XWPFPictureData picData) { + List list = packagePictures.get(picData.getChecksum()); + if (list == null) { + list = new ArrayList(1); + packagePictures.put(picData.getChecksum(), list); + } + if (!list.contains(picData)) + { + list.add(picData); + } } - /** - * Adds a picture to the document. Users should normally call - * {@link XWPFRun#addPicture(InputStream, int)} - * - * @param pictureData The bytes to read image from - * @param format The format of the picture, eg {@link Document#PICTURE_TYPE_JPEG} - * - * @return the index to this picture (0 based), the added picture can be obtained from {@link #getAllPictures()} . - * @throws InvalidFormatException - */ - public int addPicture(byte[] pictureData, int format) throws InvalidFormatException { + XWPFPictureData findPackagePictureData(byte[] pictureData, int format) + { + long checksum = IOUtils.calculateChecksum(pictureData); + XWPFPictureData xwpfPicData = null; + /* + * Try to find PictureData with this checksum. Create new, if none + * exists. + */ + List xwpfPicDataList = packagePictures.get(checksum); + if (xwpfPicDataList != null) { + Iterator iter = xwpfPicDataList.iterator(); + while (iter.hasNext() && xwpfPicData == null) { + XWPFPictureData curElem = iter.next(); + if (Arrays.equals(pictureData, curElem.getData())) { + xwpfPicData = curElem; + } + } + } + return xwpfPicData; + } + + public String addPictureData(byte[] pictureData,int format) throws InvalidFormatException + { + XWPFPictureData xwpfPicData = findPackagePictureData(pictureData, format); + POIXMLRelation relDesc = XWPFPictureData.RELATIONS[format]; + + if (xwpfPicData == null) + { + /* Part doesn't exist, create a new one */ + int idx = getNextPicNameNumber(format); + xwpfPicData = (XWPFPictureData) createRelationship(relDesc, XWPFFactory.getInstance(),idx); + /* write bytes to new part */ + PackagePart picDataPart = xwpfPicData.getPackagePart(); + OutputStream out = null; + try { + out = picDataPart.getOutputStream(); + out.write(pictureData); + } catch (IOException e) { + throw new POIXMLException(e); + } finally { + try { + out.close(); + } catch (IOException e) { + // ignore + } + } + + registerPackagePictureData(xwpfPicData); + pictures.add(xwpfPicData); + + return getRelationId(xwpfPicData); + } + else if (!getRelations().contains(xwpfPicData)) + { + /* + * Part already existed, but was not related so far. Create + * relationship to the already existing part and update + * POIXMLDocumentPart data. + */ + PackagePart picDataPart = xwpfPicData.getPackagePart(); + // TODO add support for TargetMode.EXTERNAL relations. + TargetMode targetMode = TargetMode.INTERNAL; + PackagePartName partName = picDataPart.getPartName(); + String relation = relDesc.getRelation(); + PackageRelationship relShip = getPackagePart().addRelationship(partName,targetMode,relation); + String id = relShip.getId(); + addRelation(id,xwpfPicData); + pictures.add(xwpfPicData); + return id; + } + else + { + /* Part already existed, get relation id and return it */ + return getRelationId(xwpfPicData); + } + } + + public String addPictureData(InputStream is,int format) throws InvalidFormatException + { try { - return addPicture(new ByteArrayInputStream(pictureData), format); - } catch (IOException e){ + byte[] data = IOUtils.toByteArray(is); + return addPictureData(data, format); + } catch (IOException e) { throw new POIXMLException(e); } } - + /** * get the next free ImageNumber * @param format * @return the next free ImageNumber * @throws InvalidFormatException */ - public int getNextPicNameNumber(int format) throws InvalidFormatException{ - int img = getAllPackagePictures().size()+1; - String proposal = XWPFPictureData.RELATIONS[format].getFileName(img); - PackagePartName createPartName = PackagingURIHelper.createPartName(proposal); - while (this.getPackage().getPart(createPartName)!= null){ - img++; - proposal = XWPFPictureData.RELATIONS[format].getFileName(img); - createPartName = PackagingURIHelper.createPartName(proposal); - } - return img; + public int getNextPicNameNumber(int format) throws InvalidFormatException { + int img = getAllPackagePictures().size() + 1; + String proposal = XWPFPictureData.RELATIONS[format].getFileName(img); + PackagePartName createPartName = PackagingURIHelper.createPartName(proposal); + while (this.getPackage().getPart(createPartName) != null) { + img++; + proposal = XWPFPictureData.RELATIONS[format].getFileName(img); + createPartName = PackagingURIHelper.createPartName(proposal); + } + return img; } - + /** * returns the PictureData by blipID * @param blipID * @return XWPFPictureData of a specificID - * @throws Exception */ public XWPFPictureData getPictureDataByID(String blipID) { - for(POIXMLDocumentPart part: getRelations()){ - if(part.getPackageRelationship() != null){ - if(part.getPackageRelationship().getId() != null){ - if(part.getPackageRelationship().getId().equals(blipID)){ - return (XWPFPictureData)part; - } - } - } - } - return null; + POIXMLDocumentPart relatedPart = getRelationById(blipID); + if (relatedPart instanceof XWPFPictureData) { + XWPFPictureData xwpfPicData = (XWPFPictureData) relatedPart; + return xwpfPicData; + } + return null; } - + /** - * getNumbering + * getNumbering * @return numbering */ - public XWPFNumbering getNumbering(){ - return numbering; + public XWPFNumbering getNumbering() { + return numbering; } - /** - * get Styles - * @return styles for this document - */ - public XWPFStyles getStyles(){ - return styles; - } - - /** - * get the paragraph with the CTP class p - * - * @param p - * @return the paragraph with the CTP class p - */ - public XWPFParagraph getParagraph(CTP p){ - for(int i=0; i getTablesIterator() { @@ -1157,18 +1231,17 @@ public class XWPFDocument extends POIXMLDocument implements Document, IBody { return null; } + /** + * returns the Part, to which the body belongs, which you need for adding relationship to other parts + * Actually it is needed of the class XWPFTableCell. Because you have to know to which part the tableCell + * belongs. + * @see org.apache.poi.xwpf.usermodel.IBody#getPart() + */ + public POIXMLDocumentPart getPart() { + return this; + } - /** - * returns the Part, to which the body belongs, which you need for adding relationship to other parts - * Actually it is needed of the class XWPFTableCell. Because you have to know to which part the tableCell - * belongs. - * @see org.apache.poi.xwpf.usermodel.IBody#getPart() - */ - public IBody getPart() { - return this; - } - /** * get the PartType of the body, for example * DOCUMENT, HEADER, FOOTER, FOOTNOTE, @@ -1212,4 +1285,4 @@ public class XWPFDocument extends POIXMLDocument implements Document, IBody { public XWPFDocument getXWPFDocument() { return this; } -}//end class +} // end class diff --git a/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFFooter.java b/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFFooter.java index feaf9f7dd0..c6f6681626 100644 --- a/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFFooter.java +++ b/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFFooter.java @@ -19,17 +19,16 @@ package org.apache.poi.xwpf.usermodel; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import javax.xml.namespace.QName; import org.apache.poi.POIXMLDocumentPart; +import org.apache.poi.POIXMLException; import org.apache.poi.openxml4j.opc.PackagePart; import org.apache.poi.openxml4j.opc.PackageRelationship; import org.apache.xmlbeans.XmlCursor; -import org.apache.xmlbeans.XmlException; import org.apache.xmlbeans.XmlObject; import org.apache.xmlbeans.XmlOptions; import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTHdrFtr; @@ -46,38 +45,34 @@ public class XWPFFooter extends XWPFHeaderFooter { super(); } - public XWPFFooter(XWPFDocument doc, CTHdrFtr hdrFtr) throws IOException { - super(doc, hdrFtr); - bodyElements = new ArrayList(); - paragraphs = new ArrayList(); - tables = new ArrayList(); - XmlCursor cursor = headerFooter.newCursor(); + public XWPFFooter(XWPFDocument doc, CTHdrFtr hdrFtr) throws IOException { + super(doc, hdrFtr); + XmlCursor cursor = headerFooter.newCursor(); cursor.selectPath("./*"); while (cursor.toNextSelection()) { XmlObject o = cursor.getObject(); if (o instanceof CTP) { - XWPFParagraph p = new XWPFParagraph((CTP)o, this); - paragraphs.add(p); - bodyElements.add(p); + XWPFParagraph p = new XWPFParagraph((CTP)o, this); + paragraphs.add(p); + bodyElements.add(p); } if (o instanceof CTTbl) { - XWPFTable t = new XWPFTable((CTTbl)o, this); - tables.add(t); - bodyElements.add(t); + XWPFTable t = new XWPFTable((CTTbl)o, this); + tables.add(t); + bodyElements.add(t); } } cursor.dispose(); - getAllPictures(); - } + } + + public XWPFFooter(POIXMLDocumentPart parent, PackagePart part, PackageRelationship rel) throws IOException { + super(parent, part, rel); + } - public XWPFFooter(POIXMLDocumentPart parent, PackagePart part, PackageRelationship rel) throws IOException { - super(parent, part, rel); - } - - /** - * save and commit footer - */ - @Override + /** + * save and commit footer + */ + @Override protected void commit() throws IOException { XmlOptions xmlOptions = new XmlOptions(DEFAULT_XML_OPTIONS); xmlOptions.setSaveSyntheticDocumentElement(new QName(CTNumbering.type.getName().getNamespaceURI(), "ftr")); @@ -97,58 +92,44 @@ public class XWPFFooter extends XWPFHeaderFooter { super._getHdrFtr().save(out, xmlOptions); out.close(); } - - @Override - protected void onDocumentRead(){ - bodyElements = new ArrayList(); - paragraphs = new ArrayList(); - tables= new ArrayList(); - FtrDocument ftrDocument = null; - InputStream is; - try { - is = getPackagePart().getInputStream(); - ftrDocument = FtrDocument.Factory.parse(is); - headerFooter = ftrDocument.getFtr(); - // parse the document with cursor and add - // the XmlObject to its lists - XmlCursor cursor = headerFooter.newCursor(); - cursor.selectPath("./*"); - while (cursor.toNextSelection()) { - XmlObject o = cursor.getObject(); - if (o instanceof CTP) { - XWPFParagraph p = new XWPFParagraph((CTP)o, this); - paragraphs.add(p); - bodyElements.add(p); - } - if (o instanceof CTTbl) { - XWPFTable t = new XWPFTable((CTTbl)o, this); - tables.add(t); - bodyElements.add(t); - } - } - cursor.dispose(); - getAllPictures(); - } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (XmlException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } - /** - * returns the Part, to which the body belongs, which you need for adding relationship to other parts - * @see org.apache.poi.xwpf.usermodel.IBody#getPart() - */ - public IBody getPart() { - return this; - } - /** - * get the PartType of the body - * @see org.apache.poi.xwpf.usermodel.IBody#getPartType() - */ - public BodyType getPartType() { - return BodyType.FOOTER; - } + @Override + protected void onDocumentRead() throws IOException{ + super.onDocumentRead(); + FtrDocument ftrDocument = null; + InputStream is; + try { + is = getPackagePart().getInputStream(); + ftrDocument = FtrDocument.Factory.parse(is); + headerFooter = ftrDocument.getFtr(); + // parse the document with cursor and add + // the XmlObject to its lists + XmlCursor cursor = headerFooter.newCursor(); + cursor.selectPath("./*"); + while (cursor.toNextSelection()) { + XmlObject o = cursor.getObject(); + if (o instanceof CTP) { + XWPFParagraph p = new XWPFParagraph((CTP)o, this); + paragraphs.add(p); + bodyElements.add(p); + } + if (o instanceof CTTbl) { + XWPFTable t = new XWPFTable((CTTbl)o, this); + tables.add(t); + bodyElements.add(t); + } + } + cursor.dispose(); + } catch (Exception e) { + throw new POIXMLException(e); + } + } + + /** + * get the PartType of the body + * @see org.apache.poi.xwpf.usermodel.IBody#getPartType() + */ + public BodyType getPartType() { + return BodyType.FOOTER; + } } diff --git a/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFHeader.java b/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFHeader.java index 6ccbf46241..f18e0c7bd2 100644 --- a/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFHeader.java +++ b/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFHeader.java @@ -19,13 +19,13 @@ package org.apache.poi.xwpf.usermodel; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import javax.xml.namespace.QName; import org.apache.poi.POIXMLDocumentPart; +import org.apache.poi.POIXMLException; import org.apache.poi.openxml4j.opc.PackagePart; import org.apache.poi.openxml4j.opc.PackageRelationship; import org.apache.xmlbeans.XmlCursor; @@ -47,40 +47,31 @@ public class XWPFHeader extends XWPFHeaderFooter { } public XWPFHeader(POIXMLDocumentPart parent, PackagePart part, PackageRelationship rel) throws IOException { - super(parent, part, rel); - } - - public XWPFHeader(XWPFDocument doc, CTHdrFtr hdrFtr) throws IOException { - super(doc, hdrFtr); - paragraphs = new ArrayList(); - tables = new ArrayList(); - XmlCursor cursor = headerFooter.newCursor(); - cursor.selectPath("./*"); - while (cursor.toNextSelection()) { - XmlObject o = cursor.getObject(); - if (o instanceof CTP) { - XWPFParagraph p = new XWPFParagraph((CTP) o, this); - paragraphs.add(p); - } - if (o instanceof CTTbl) { - XWPFTable t = new XWPFTable((CTTbl) o, this); - tables.add(t); - } - } + super(parent, part, rel); + } + + public XWPFHeader(XWPFDocument doc, CTHdrFtr hdrFtr) { + super(doc, hdrFtr); + XmlCursor cursor = headerFooter.newCursor(); + cursor.selectPath("./*"); + while (cursor.toNextSelection()) { + XmlObject o = cursor.getObject(); + if (o instanceof CTP) { + XWPFParagraph p = new XWPFParagraph((CTP) o, this); + paragraphs.add(p); + } + if (o instanceof CTTbl) { + XWPFTable t = new XWPFTable((CTTbl) o, this); + tables.add(t); + } + } cursor.dispose(); - getAllPictures(); - } + } /** - public XWPFHeader(PackagePart part, PackageRelationship rel) - throws IOException { - super(part, rel); - } - - /** - * save and commit footer - */ - @Override + * save and commit footer + */ + @Override protected void commit() throws IOException { XmlOptions xmlOptions = new XmlOptions(DEFAULT_XML_OPTIONS); xmlOptions.setSaveSyntheticDocumentElement(new QName(CTNumbering.type.getName().getNamespaceURI(), "hdr")); @@ -101,62 +92,47 @@ public class XWPFHeader extends XWPFHeaderFooter { out.close(); } - /** - * reads the document - */ - @Override - protected void onDocumentRead(){ - bodyElements = new ArrayList(); - paragraphs = new ArrayList(); - tables= new ArrayList(); - HdrDocument hdrDocument = null; - InputStream is; - try { - is = getPackagePart().getInputStream(); - hdrDocument = HdrDocument.Factory.parse(is); - headerFooter = hdrDocument.getHdr(); - // parse the document with cursor and add - // the XmlObject to its lists - XmlCursor cursor = headerFooter.newCursor(); - cursor.selectPath("./*"); - while (cursor.toNextSelection()) { - XmlObject o = cursor.getObject(); - if (o instanceof CTP) { - XWPFParagraph p = new XWPFParagraph((CTP)o, this); - paragraphs.add(p); - bodyElements.add(p); - } - if (o instanceof CTTbl) { - XWPFTable t = new XWPFTable((CTTbl)o, this); - tables.add(t); - bodyElements.add(t); - } - } - cursor.dispose(); - } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (XmlException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } - - /** - * returns the Part, to which the body belongs, which you need for adding relationship to other parts - * @see org.apache.poi.xwpf.usermodel.IBody#getPart() - */ - public IBody getPart() { - return this; - } - - /** - * get the PartType of the body - * @see org.apache.poi.xwpf.usermodel.IBody#getPartType() - */ - public BodyType getPartType() { - return BodyType.HEADER; - } - + /** + * reads the document + * @throws IOException + */ + @Override + protected void onDocumentRead() throws IOException { + super.onDocumentRead(); + HdrDocument hdrDocument = null; + InputStream is; + try { + is = getPackagePart().getInputStream(); + hdrDocument = HdrDocument.Factory.parse(is); + headerFooter = hdrDocument.getHdr(); + // parse the document with cursor and add + // the XmlObject to its lists + XmlCursor cursor = headerFooter.newCursor(); + cursor.selectPath("./*"); + while (cursor.toNextSelection()) { + XmlObject o = cursor.getObject(); + if (o instanceof CTP) { + XWPFParagraph p = new XWPFParagraph((CTP)o, this); + paragraphs.add(p); + bodyElements.add(p); + } + if (o instanceof CTTbl) { + XWPFTable t = new XWPFTable((CTTbl)o, this); + tables.add(t); + bodyElements.add(t); + } + } + cursor.dispose(); + } catch (XmlException e) { + throw new POIXMLException(e); + } + } + /** + * get the PartType of the body + * @see org.apache.poi.xwpf.usermodel.IBody#getPartType() + */ + public BodyType getPartType() { + return BodyType.HEADER; + } }//end class diff --git a/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFHeaderFooter.java b/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFHeaderFooter.java index 96274be21a..482994b128 100644 --- a/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFHeaderFooter.java +++ b/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFHeaderFooter.java @@ -25,11 +25,12 @@ import java.util.List; 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.opc.PackagePart; import org.apache.poi.openxml4j.opc.PackagePartName; import org.apache.poi.openxml4j.opc.PackageRelationship; -import org.apache.poi.openxml4j.opc.PackagingURIHelper; +import org.apache.poi.openxml4j.opc.TargetMode; import org.apache.poi.util.IOUtils; import org.apache.poi.util.Internal; import org.apache.xmlbeans.XmlCursor; @@ -43,254 +44,264 @@ import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTc; /** * Parent of XWPF headers and footers */ -public abstract class XWPFHeaderFooter extends POIXMLDocumentPart implements IBody{ - protected CTHdrFtr headerFooter; - protected List paragraphs; - protected List tables; - protected List pictures; - protected XWPFDocument document; - protected List bodyElements; - - protected XWPFHeaderFooter(XWPFDocument doc, CTHdrFtr hdrFtr){ +public abstract class XWPFHeaderFooter extends POIXMLDocumentPart implements IBody { + List paragraphs = new ArrayList(1); + List tables= new ArrayList(1); + List pictures = new ArrayList(); + List bodyElements = new ArrayList(1); + + CTHdrFtr headerFooter; + XWPFDocument document; + + XWPFHeaderFooter(XWPFDocument doc, CTHdrFtr hdrFtr){ if (doc==null) { throw new NullPointerException(); } document = doc; - headerFooter = hdrFtr; - readHdrFtr(); - } + headerFooter = hdrFtr; + readHdrFtr(); + } protected XWPFHeaderFooter() { headerFooter = CTHdrFtr.Factory.newInstance(); readHdrFtr(); } - public XWPFHeaderFooter(POIXMLDocumentPart parent, PackagePart part, PackageRelationship rel) throws IOException { - super(parent, part, rel); - this.document = (XWPFDocument)getParent(); + super(parent, part, rel); + this.document = (XWPFDocument)getParent(); if (this.document==null) { throw new NullPointerException(); } + } + + @Override + protected void onDocumentRead() throws IOException { + for (POIXMLDocumentPart poixmlDocumentPart : getRelations()){ + if(poixmlDocumentPart instanceof XWPFPictureData){ + XWPFPictureData xwpfPicData = (XWPFPictureData) poixmlDocumentPart; + pictures.add(xwpfPicData); + document.registerPackagePictureData(xwpfPicData); + } + } + } - onDocumentRead(); - } - @Internal - public CTHdrFtr _getHdrFtr() { - return headerFooter; - } + public CTHdrFtr _getHdrFtr() { + return headerFooter; + } - public List getBodyElements(){ + public List getBodyElements(){ return Collections.unmodifiableList(bodyElements); - } - - /** - * Returns the paragraph(s) that holds - * the text of the header or footer. - * Normally there is only the one paragraph, but - * there could be more in certain cases, or - * a table. - */ + } + + /** + * Returns the paragraph(s) that holds + * the text of the header or footer. + * Normally there is only the one paragraph, but + * there could be more in certain cases, or + * a table. + */ public List getParagraphs() { return Collections.unmodifiableList(paragraphs); } - - - /** - * Return the table(s) that holds the text - * of the header or footer, for complex cases - * where a paragraph isn't used. - * Normally there's just one paragraph, but some - * complex headers/footers have a table or two - * in addition. - */ - public List getTables()throws ArrayIndexOutOfBoundsException { - return Collections.unmodifiableList(tables); + + + /** + * Return the table(s) that holds the text + * of the header or footer, for complex cases + * where a paragraph isn't used. + * Normally there's just one paragraph, but some + * complex headers/footers have a table or two + * in addition. + */ + public List getTables()throws ArrayIndexOutOfBoundsException { + return Collections.unmodifiableList(tables); + } + + + + /** + * Returns the textual content of the header/footer, + * by flattening out the text of its paragraph(s) + */ + public String getText() { + StringBuffer t = new StringBuffer(); + + for(int i=0; i 0) { + t.append(text); + t.append('\n'); + } + } + } + + List tables = getTables(); + for(int i=0; i 0) { + t.append(text); + t.append('\n'); + } + } + + return t.toString(); + } + + /** + * set a new headerFooter + */ + public void setHeaderFooter(CTHdrFtr headerFooter){ + this.headerFooter = headerFooter; + readHdrFtr(); + } + + /** + * if there is a corresponding {@link XWPFTable} of the parameter ctTable in the tableList of this header + * the method will return this table + * if there is no corresponding {@link XWPFTable} the method will return null + * @param ctTable + */ + public XWPFTable getTable(CTTbl ctTable){ + for (XWPFTable table : tables) { + if(table==null) + return null; + if(table.getCTTbl().equals(ctTable)) + return table; + } + return null; + } + + /** + * if there is a corresponding {@link XWPFParagraph} of the parameter ctTable in the paragraphList of this header or footer + * the method will return this paragraph + * if there is no corresponding {@link XWPFParagraph} the method will return null + * @param p is instance of CTP and is searching for an XWPFParagraph + * @return null if there is no XWPFParagraph with an corresponding CTPparagraph in the paragraphList of this header or footer + * XWPFParagraph with the correspondig CTP p + */ + public XWPFParagraph getParagraph(CTP p){ + for (XWPFParagraph paragraph : paragraphs) { + if(paragraph.getCTP().equals(p)) + return paragraph; + } + return null; + + } + + /** + * Returns the paragraph that holds + * the text of the header or footer. + */ + public XWPFParagraph getParagraphArray(int pos) { + + return paragraphs.get(pos); + } + + /** + * get a List of all Paragraphs + * @return a list of {@link XWPFParagraph} + */ + public List getListParagraph(){ + return paragraphs; } - - - - /** - * Returns the textual content of the header/footer, - * by flattening out the text of its paragraph(s) - */ - public String getText() { - StringBuffer t = new StringBuffer(); - - for(int i=0; i 0) { - t.append(text); - t.append('\n'); - } - } - } - - List tables = getTables(); - for(int i=0; i 0) { - t.append(text); - t.append('\n'); - } - } - - return t.toString(); - } - - /** - * set a new headerFooter - */ - public void setHeaderFooter(CTHdrFtr headerFooter){ - this.headerFooter = headerFooter; - readHdrFtr(); - } - - /** - * if there is a corresponding {@link XWPFTable} of the parameter ctTable in the tableList of this header - * the method will return this table - * if there is no corresponding {@link XWPFTable} the method will return null - * @param ctTable - */ - public XWPFTable getTable(CTTbl ctTable){ - for (XWPFTable table : tables) { - if(table==null) - return null; - if(table.getCTTbl().equals(ctTable)) - return table; - } - return null; - } - - /** - * if there is a corresponding {@link XWPFParagraph} of the parameter ctTable in the paragraphList of this header or footer - * the method will return this paragraph - * if there is no corresponding {@link XWPFParagraph} the method will return null - * @param p is instance of CTP and is searching for an XWPFParagraph - * @return null if there is no XWPFParagraph with an corresponding CTPparagraph in the paragraphList of this header or footer - * XWPFParagraph with the correspondig CTP p - */ - public XWPFParagraph getParagraph(CTP p){ - for (XWPFParagraph paragraph : paragraphs) { - if(paragraph.getCTP().equals(p)) - return paragraph; - } - return null; - - } - - /** - * Returns the paragraph that holds - * the text of the header or footer. - */ - public XWPFParagraph getParagraphArray(int pos) { - - return paragraphs.get(pos); - } - - /** - * get a List of all Paragraphs - * @return a list of {@link XWPFParagraph} - */ - public List getListParagraph(){ - return paragraphs; - } - + public List getAllPictures() { - if(pictures == null){ - pictures = new ArrayList(); - for (POIXMLDocumentPart poixmlDocumentPart : getRelations()){ - if(poixmlDocumentPart instanceof XWPFPictureData){ - pictures.add((XWPFPictureData)poixmlDocumentPart); - } - } - } - return pictures; + return Collections.unmodifiableList(pictures); } - + /** * get all Pictures in this package * @return all Pictures in this package */ public List getAllPackagePictures(){ - List pkgpictures = new ArrayList(); - pkgpictures.addAll(getAllPictures()); - for (POIXMLDocumentPart poixmlDocumentPart : getRelations()){ - if(poixmlDocumentPart instanceof XWPFHeaderFooter){ - pkgpictures.addAll(((XWPFHeaderFooter)poixmlDocumentPart).getAllPictures()); - } - } - return pkgpictures; + return document.getAllPackagePictures(); + } - - /** + + /** * Adds a picture to the document. * * @param is The stream to read image from * @param format The format of the picture. * * @return the index to this picture (0 based), the added picture can be obtained from {@link #getAllPictures()} . + * @throws InvalidFormatException */ - public int addPicture(InputStream is, int format) throws IOException { - int imageNumber = getNextPicNameNumber(format); - XWPFPictureData img = (XWPFPictureData)createRelationship(XWPFPictureData.RELATIONS[format], XWPFFactory.getInstance(), imageNumber, true); - OutputStream out = img.getPackagePart().getOutputStream(); - IOUtils.copy(is, out); - out.close(); - pictures.add(img); - return getAllPictures().size()-1; - + public String addPictureData(byte[] pictureData,int format) throws InvalidFormatException + { + XWPFPictureData xwpfPicData = document.findPackagePictureData(pictureData, format); + POIXMLRelation relDesc = XWPFPictureData.RELATIONS[format]; + + if (xwpfPicData == null) + { + /* Part doesn't exist, create a new one */ + int idx = document.getNextPicNameNumber(format); + xwpfPicData = (XWPFPictureData) createRelationship(relDesc, XWPFFactory.getInstance(),idx); + /* write bytes to new part */ + PackagePart picDataPart = xwpfPicData.getPackagePart(); + OutputStream out = null; + try { + out = picDataPart.getOutputStream(); + out.write(pictureData); + } catch (IOException e) { + throw new POIXMLException(e); + } finally { + try { + out.close(); + } catch (IOException e) { + // ignore + } + } + + document.registerPackagePictureData(xwpfPicData); + pictures.add(xwpfPicData); + return getRelationId(xwpfPicData); + } + else if (!getRelations().contains(xwpfPicData)) + { + /* + * Part already existed, but was not related so far. Create + * relationship to the already existing part and update + * POIXMLDocumentPart data. + */ + PackagePart picDataPart = xwpfPicData.getPackagePart(); + // TODO add support for TargetMode.EXTERNAL relations. + TargetMode targetMode = TargetMode.INTERNAL; + PackagePartName partName = picDataPart.getPartName(); + String relation = relDesc.getRelation(); + PackageRelationship relShip = getPackagePart().addRelationship(partName,targetMode,relation); + String id = relShip.getId(); + addRelation(id,xwpfPicData); + pictures.add(xwpfPicData); + return id; + } + else + { + /* Part already existed, get relation id and return it */ + return getRelationId(xwpfPicData); + } } - + /** * Adds a picture to the document. * - * @param pictureData The picture bytes + * @param is The stream to read image from * @param format The format of the picture. * * @return the index to this picture (0 based), the added picture can be obtained from {@link #getAllPictures()} . + * @throws InvalidFormatException + * @throws IOException */ - public int addPicture(byte[] pictureData, int format) { - int imageNumber = getNextPicNameNumber(format); - XWPFPictureData img = (XWPFPictureData)createRelationship(XWPFPictureData.RELATIONS[format], XWPFFactory.getInstance(), imageNumber, false); - try { - OutputStream out = img.getPackagePart().getOutputStream(); - out.write(pictureData); - out.close(); - } catch (IOException e){ - throw new POIXMLException(e); - } - - pictures.add(img); - return getAllPictures().size()-1; - } - - /** - * get the next free ImageNumber - * @param format - * @return the next free ImageNumber - */ - public int getNextPicNameNumber(int format){ - int img = getAllPackagePictures().size()+1; - String proposal = XWPFPictureData.RELATIONS[format].getFileName(img); - try { - PackagePartName createPartName = PackagingURIHelper.createPartName(proposal); - while (this.getPackagePart().getPackage().getPart(createPartName)!= null){ - img++; - proposal = XWPFPictureData.RELATIONS[format].getFileName(img); - createPartName = PackagingURIHelper.createPartName(proposal); - } - } catch (InvalidFormatException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - return img; + public String addPictureData(InputStream is, int format) throws InvalidFormatException,IOException { + byte[] data = IOUtils.toByteArray(is); + return addPictureData(data,format); } - + /** * returns the PictureData by blipID * @param blipID @@ -298,246 +309,195 @@ public abstract class XWPFHeaderFooter extends POIXMLDocumentPart implements IBo * @throws Exception */ public XWPFPictureData getPictureDataByID(String blipID) { - for(POIXMLDocumentPart part: getRelations()){ - if(part.getPackageRelationship() != null){ - if(part.getPackageRelationship().getId() != null){ - if(part.getPackageRelationship().getId().equals(blipID)){ - return (XWPFPictureData)part; - } - } - } - } - return null; + POIXMLDocumentPart relatedPart = getRelationById(blipID); + if (relatedPart != null && relatedPart instanceof XWPFPictureData) { + return (XWPFPictureData) relatedPart; + } + return null; } - + /** - * Add the picture to drawing relations - * - * @param pictureData the picture bytes - * @param format the picture format + * add a new paragraph at position of the cursor + * @param cursor + * @return the inserted paragraph + */ + public XWPFParagraph insertNewParagraph(XmlCursor cursor){ + if(isCursorInHdrF(cursor)){ + String uri = CTP.type.getName().getNamespaceURI(); + String localPart = "p"; + cursor.beginElement(localPart,uri); + cursor.toParent(); + CTP p = (CTP)cursor.getObject(); + XWPFParagraph newP = new XWPFParagraph(p, this); + XmlObject o = null; + while(!(o instanceof CTP)&&(cursor.toPrevSibling())){ + o = cursor.getObject(); + } + if((!(o instanceof CTP)) || (CTP)o == p){ + paragraphs.add(0, newP); + } + else{ + int pos = paragraphs.indexOf(getParagraph((CTP)o))+1; + paragraphs.add(pos,newP); + } + int i=0; + cursor.toCursor(p.newCursor()); + while(cursor.toPrevSibling()){ + o =cursor.getObject(); + if(o instanceof CTP || o instanceof CTTbl) + i++; + } + bodyElements.add(i, newP); + cursor.toCursor(p.newCursor()); + cursor.toEndToken(); + return newP; + } + return null; + } + + + /** + * + * @param cursor + * @return the inserted table + */ + public XWPFTable insertNewTbl(XmlCursor cursor) { + if(isCursorInHdrF(cursor)){ + String uri = CTTbl.type.getName().getNamespaceURI(); + String localPart = "tbl"; + cursor.beginElement(localPart,uri); + cursor.toParent(); + CTTbl t = (CTTbl)cursor.getObject(); + XWPFTable newT = new XWPFTable(t, this); + cursor.removeXmlContents(); + XmlObject o = null; + while(!(o instanceof CTTbl)&&(cursor.toPrevSibling())){ + o = cursor.getObject(); + } + if(!(o instanceof CTTbl)){ + tables.add(0, newT); + } + else{ + int pos = tables.indexOf(getTable((CTTbl)o))+1; + tables.add(pos,newT); + } + int i=0; + cursor = t.newCursor(); + while(cursor.toPrevSibling()){ + o =cursor.getObject(); + if(o instanceof CTP || o instanceof CTTbl) + i++; + } + bodyElements.add(i, newT); + cursor = t.newCursor(); + cursor.toEndToken(); + return newT; + } + return null; + } + + /** + * verifies that cursor is on the right position + * @param cursor */ - public PackageRelationship addPictureReference(byte[] pictureData, int format){ - int imageNumber = getNextPicNameNumber(format); - XWPFPictureData img = (XWPFPictureData)createRelationship(XWPFPictureData.RELATIONS[format], XWPFFactory.getInstance(), imageNumber, false); - PackageRelationship rel = null; - try { - OutputStream out = img.getPackagePart().getOutputStream(); - out.write(pictureData); - out.close(); - rel = img.getPackageRelationship(); - pictures.add(img); - } catch (IOException e){ - throw new POIXMLException(e); - } - return rel; + private boolean isCursorInHdrF(XmlCursor cursor) { + XmlCursor verify = cursor.newCursor(); + verify.toParent(); + if(verify.getObject() == this.headerFooter){ + return true; + } + return false; + } + + + public POIXMLDocumentPart getOwner(){ + return this; + } + + /** + * Returns the table at position pos + * @see org.apache.poi.xwpf.usermodel.IBody#getTableArray(int) + */ + public XWPFTable getTableArray(int pos) { + + if(pos > 0 && pos < tables.size()){ + return tables.get(pos); + } + return null; + } + + /** + * inserts an existing XWPFTable to the arrays bodyElements and tables + * @param pos + * @param table + */ + public void insertTable(int pos, XWPFTable table) { + bodyElements.add(pos, table); + int i; + for (i = 0; i < headerFooter.getTblList().size(); i++) { + CTTbl tbl = headerFooter.getTblArray(i); + if(tbl == table.getCTTbl()){ + break; + } + } + tables.add(i, table); + } - - /** - * Add the picture to drawing relations - * - * @param is the stream to read picture data from - */ - public PackageRelationship addPictureReference(InputStream is, int format){ - - PackageRelationship rel = null; - try { - int imageNumber = getNextPicNameNumber(format); - XWPFPictureData img = (XWPFPictureData)createRelationship(XWPFPictureData.RELATIONS[format], XWPFFactory.getInstance(), imageNumber, false); - OutputStream out = img.getPackagePart().getOutputStream(); - IOUtils.copy(is, out); - out.close(); - rel = img.getPackageRelationship(); - pictures.add(img); - } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - return rel; - } - - /** - * add a new paragraph at position of the cursor - * @param cursor - * @return the inserted paragraph - */ - public XWPFParagraph insertNewParagraph(XmlCursor cursor){ - if(isCursorInHdrF(cursor)){ - String uri = CTP.type.getName().getNamespaceURI(); - String localPart = "p"; - cursor.beginElement(localPart,uri); - cursor.toParent(); - CTP p = (CTP)cursor.getObject(); - XWPFParagraph newP = new XWPFParagraph(p, this); - XmlObject o = null; - while(!(o instanceof CTP)&&(cursor.toPrevSibling())){ - o = cursor.getObject(); - } - if((!(o instanceof CTP)) || (CTP)o == p){ - paragraphs.add(0, newP); - } - else{ - int pos = paragraphs.indexOf(getParagraph((CTP)o))+1; - paragraphs.add(pos,newP); - } - int i=0; - cursor.toCursor(p.newCursor()); - while(cursor.toPrevSibling()){ - o =cursor.getObject(); - if(o instanceof CTP || o instanceof CTTbl) - i++; - } - bodyElements.add(i, newP); - cursor.toCursor(p.newCursor()); - cursor.toEndToken(); - return newP; - } - return null; - } - - - /** - * - * @param cursor - * @return the inserted table - */ - public XWPFTable insertNewTbl(XmlCursor cursor) { - if(isCursorInHdrF(cursor)){ - String uri = CTTbl.type.getName().getNamespaceURI(); - String localPart = "tbl"; - cursor.beginElement(localPart,uri); - cursor.toParent(); - CTTbl t = (CTTbl)cursor.getObject(); - XWPFTable newT = new XWPFTable(t, this); - cursor.removeXmlContents(); - XmlObject o = null; - while(!(o instanceof CTTbl)&&(cursor.toPrevSibling())){ - o = cursor.getObject(); - } - if(!(o instanceof CTTbl)){ - tables.add(0, newT); - } - else{ - int pos = tables.indexOf(getTable((CTTbl)o))+1; - tables.add(pos,newT); - } - int i=0; - cursor = t.newCursor(); - while(cursor.toPrevSibling()){ - o =cursor.getObject(); - if(o instanceof CTP || o instanceof CTTbl) - i++; - } - bodyElements.add(i, newT); - cursor = t.newCursor(); - cursor.toEndToken(); - return newT; - } - return null; - } - - /** - * verifies that cursor is on the right position - * @param cursor - */ - private boolean isCursorInHdrF(XmlCursor cursor) { - XmlCursor verify = cursor.newCursor(); - verify.toParent(); - if(verify.getObject() == this.headerFooter){ - return true; - } - return false; - } - - - public POIXMLDocumentPart getOwner(){ - return this; - } - - /** - * Returns the table at position pos - * @see org.apache.poi.xwpf.usermodel.IBody#getTableArray(int) - */ - public XWPFTable getTableArray(int pos) { - - if(pos > 0 && pos < tables.size()){ - return tables.get(pos); - } - return null; - } - - /** - * inserts an existing XWPFTable to the arrays bodyElements and tables - * @param pos - * @param table - */ - public void insertTable(int pos, XWPFTable table) { - bodyElements.add(pos, table); - int i; - for (i = 0; i < headerFooter.getTblList().size(); i++) { - CTTbl tbl = headerFooter.getTblArray(i); - if(tbl == table.getCTTbl()){ - break; - } - } - tables.add(i, table); - - } - - public void readHdrFtr(){ - bodyElements = new ArrayList(); + + public void readHdrFtr(){ + bodyElements = new ArrayList(); paragraphs = new ArrayList(); tables= new ArrayList(); // parse the document with cursor and add // the XmlObject to its lists - XmlCursor cursor = headerFooter.newCursor(); + XmlCursor cursor = headerFooter.newCursor(); cursor.selectPath("./*"); while (cursor.toNextSelection()) { XmlObject o = cursor.getObject(); if (o instanceof CTP) { - XWPFParagraph p = new XWPFParagraph((CTP)o, this); - paragraphs.add(p); - bodyElements.add(p); + XWPFParagraph p = new XWPFParagraph((CTP)o, this); + paragraphs.add(p); + bodyElements.add(p); } if (o instanceof CTTbl) { - XWPFTable t = new XWPFTable((CTTbl)o, this); - tables.add(t); - bodyElements.add(t); + XWPFTable t = new XWPFTable((CTTbl)o, this); + tables.add(t); + bodyElements.add(t); } } cursor.dispose(); - getAllPictures(); - } - - /** - * get the TableCell which belongs to the TableCell - * @param cell - */ - public XWPFTableCell getTableCell(CTTc cell) { - XmlCursor cursor = cell.newCursor(); - cursor.toParent(); - XmlObject o = cursor.getObject(); - if(!(o instanceof CTRow)){ - return null; - } - CTRow row = (CTRow)o; - cursor.toParent(); - o = cursor.getObject(); + } + + /** + * get the TableCell which belongs to the TableCell + * @param cell + */ + public XWPFTableCell getTableCell(CTTc cell) { + XmlCursor cursor = cell.newCursor(); + cursor.toParent(); + XmlObject o = cursor.getObject(); + if(!(o instanceof CTRow)){ + return null; + } + CTRow row = (CTRow)o; + cursor.toParent(); + o = cursor.getObject(); cursor.dispose(); - if(! (o instanceof CTTbl)){ - return null; - } - CTTbl tbl = (CTTbl) o; - XWPFTable table = getTable(tbl); - if(table == null){ - return null; - } - XWPFTableRow tableRow = table.getRow(row); - if(row == null){ - return null; - } - return tableRow.getTableCell(cell); - } - + if(! (o instanceof CTTbl)){ + return null; + } + CTTbl tbl = (CTTbl) o; + XWPFTable table = getTable(tbl); + if(table == null){ + return null; + } + XWPFTableRow tableRow = table.getRow(row); + if(row == null){ + return null; + } + return tableRow.getTableCell(cell); + } + public XWPFDocument getXWPFDocument() { if (document!=null) { return document; @@ -545,4 +505,12 @@ public abstract class XWPFHeaderFooter extends POIXMLDocumentPart implements IBo return (XWPFDocument)getParent(); } } + + /** + * returns the Part, to which the body belongs, which you need for adding relationship to other parts + * @see org.apache.poi.xwpf.usermodel.IBody#getPart() + */ + public POIXMLDocumentPart getPart() { + return this; + } }//end class diff --git a/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFNumbering.java b/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFNumbering.java index 9e4fc7d08b..861b07409b 100644 --- a/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFNumbering.java +++ b/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFNumbering.java @@ -44,10 +44,11 @@ import org.openxmlformats.schemas.wordprocessingml.x2006.main.NumberingDocument; * */ public class XWPFNumbering extends POIXMLDocumentPart { - private CTNumbering ctNumbering; - protected List abstractNums; - protected List nums; - protected boolean isNew; + protected List abstractNums = new ArrayList(); + protected List nums = new ArrayList(); + + private CTNumbering ctNumbering; + boolean isNew; /** *create a new styles object with an existing document @@ -55,7 +56,6 @@ public class XWPFNumbering extends POIXMLDocumentPart { public XWPFNumbering(PackagePart part, PackageRelationship rel) throws IOException, OpenXML4JException{ super(part, rel); isNew = true; - onDocumentRead(); } /** @@ -72,8 +72,6 @@ public class XWPFNumbering extends POIXMLDocumentPart { */ @Override protected void onDocumentRead() throws IOException{ - abstractNums = new ArrayList(); - nums = new ArrayList(); NumberingDocument numberingDoc = null; InputStream is; is = getPackagePart().getInputStream(); @@ -100,7 +98,7 @@ public class XWPFNumbering extends POIXMLDocumentPart { protected void commit() throws IOException { XmlOptions xmlOptions = new XmlOptions(DEFAULT_XML_OPTIONS); xmlOptions.setSaveSyntheticDocumentElement(new QName(CTNumbering.type.getName().getNamespaceURI(), "numbering")); - Map map = new HashMap(); + Map map = new HashMap(); map.put("http://schemas.openxmlformats.org/markup-compatibility/2006", "ve"); map.put("urn:schemas-microsoft-com:office:office", "o"); map.put("http://schemas.openxmlformats.org/officeDocument/2006/relationships", "r"); diff --git a/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFParagraph.java b/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFParagraph.java index c46b8da750..4cbb8223e9 100644 --- a/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFParagraph.java +++ b/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFParagraph.java @@ -21,6 +21,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import org.apache.poi.POIXMLDocumentPart; import org.apache.poi.util.Internal; import org.apache.xmlbeans.XmlCursor; import org.apache.xmlbeans.XmlObject; @@ -50,17 +51,16 @@ import org.openxmlformats.schemas.wordprocessingml.x2006.main.STLineSpacingRule; import org.openxmlformats.schemas.wordprocessingml.x2006.main.STOnOff; import org.openxmlformats.schemas.wordprocessingml.x2006.main.STTextAlignment; - /** * Sketch of XWPF paragraph class */ -public class XWPFParagraph implements IBodyElement{ +public class XWPFParagraph implements IBodyElement { private final CTP paragraph; protected IBody part; /** For access to the document's hyperlink, comments, tables etc */ protected XWPFDocument document; protected List runs; - + private StringBuffer footnoteText = new StringBuffer(); public XWPFParagraph(CTP prgrph, IBody part) { @@ -260,9 +260,9 @@ public class XWPFParagraph implements IBodyElement{ * @return a new text run */ public XWPFRun createRun() { - XWPFRun run = new XWPFRun(paragraph.addNewR(), this); - runs.add(run); - return run; + XWPFRun xwpfRun = new XWPFRun(paragraph.addNewR(), this); + runs.add(xwpfRun); + return xwpfRun; } /** @@ -1233,53 +1233,65 @@ public class XWPFParagraph implements IBodyElement{ return false; } - /** - * returns the type of the BodyElement Paragraph - * @see org.apache.poi.xwpf.usermodel.IBodyElement#getElementType() - */ - public BodyElementType getElementType() { - return BodyElementType.PARAGRAPH; - } - - /** - * returns the part of the bodyElement - * @see org.apache.poi.xwpf.usermodel.IBody#getPart() - */ - public IBody getPart() { - if(part != null){ - return part.getPart(); - } - return null; - } + /** + * returns the type of the BodyElement Paragraph + * @see org.apache.poi.xwpf.usermodel.IBodyElement#getElementType() + */ + public BodyElementType getElementType() { + return BodyElementType.PARAGRAPH; + } - /** - * returns the partType of the bodyPart which owns the bodyElement - * @see org.apache.poi.xwpf.usermodel.IBody#getPartType() - */ - public BodyType getPartType() { - return part.getPartType(); - } - - /** - * adds a new Run to the Paragraph - */ - public void addRun(XWPFRun r){ - runs.add(r); - } - - /** - * return the XWPFRun-Element which owns the CTR run-Element - */ - public XWPFRun getRun(CTR r){ - for(int i=0; i < getRuns().size(); i++){ - if(getRuns().get(i).getCTR() == r) return getRuns().get(i); - } - return null; - } - + @Override + public IBody getBody() + { + return part; + } + /** + * returns the part of the bodyElement + * @see org.apache.poi.xwpf.usermodel.IBody#getPart() + */ + public POIXMLDocumentPart getPart() { + if(part != null){ + return part.getPart(); + } + return null; + } -}//end class + /** + * returns the partType of the bodyPart which owns the bodyElement + * + * @see org.apache.poi.xwpf.usermodel.IBody#getPartType() + */ + public BodyType getPartType() { + return part.getPartType(); + } + /** + * adds a new Run to the Paragraph + * + * @param r + * @return + */ + public void addRun(XWPFRun r) { + if (!runs.contains(r)) { + runs.add(r); + } + } + /** + * return the XWPFRun-Element which owns the CTR run-Element + * + * @param r + * @return + */ + public XWPFRun getRun(CTR r) { + for (int i = 0; i < getRuns().size(); i++) { + if (getRuns().get(i).getCTR() == r) { + return getRuns().get(i); + } + } + return null; + } +}//end class \ No newline at end of file diff --git a/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFPicture.java b/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFPicture.java index af207c651d..d613030f88 100644 --- a/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFPicture.java +++ b/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFPicture.java @@ -25,28 +25,25 @@ import org.openxmlformats.schemas.drawingml.x2006.picture.CTPicture; * @author Philipp Epp */ public class XWPFPicture { - protected XWPFParagraph paragraph; - private CTPicture ctPic; + + private CTPicture ctPic; private String description; + private XWPFRun run; + + public XWPFPicture(CTPicture ctPic, XWPFRun run){ + this.run = run; + this.ctPic = ctPic; + description = ctPic.getNvPicPr().getCNvPr().getDescr(); + } + + /** + * Link Picture with PictureData + * @param rel + */ + public void setPictureReference(PackageRelationship rel){ + ctPic.getBlipFill().getBlip().setEmbed(rel.getId()); + } - public XWPFParagraph getParagraph(){ - return paragraph; - } - - public XWPFPicture(CTPicture ctPic, XWPFParagraph paragraph){ - this.paragraph = paragraph; - this.ctPic = ctPic; - description = ctPic.getNvPicPr().getCNvPr().getDescr(); - } - - /** - * Link Picture with PictureData - * @param rel - */ - public void setPictureReference(PackageRelationship rel){ - ctPic.getBlipFill().getBlip().setEmbed(rel.getId()); - } - /** * Return the underlying CTPicture bean that holds all properties for this picture * @@ -55,19 +52,22 @@ public class XWPFPicture { public CTPicture getCTPicture(){ return ctPic; } - + /** * Get the PictureData of the Picture, if present. * Note - not all kinds of picture have data */ public XWPFPictureData getPictureData(){ - String blipId = ctPic.getBlipFill().getBlip().getEmbed(); - for(POIXMLDocumentPart part: ((POIXMLDocumentPart) paragraph.getPart()).getRelations()){ - if(part.getPackageRelationship().getId().equals(blipId)){ - return (XWPFPictureData)part; - } - } - return null; + String blipId = ctPic.getBlipFill().getBlip().getEmbed(); + POIXMLDocumentPart part = run.getParagraph().getPart(); + if (part != null) + { + POIXMLDocumentPart relatedPart = part.getRelationById(blipId); + if (relatedPart instanceof XWPFPictureData) { + return (XWPFPictureData) relatedPart; + } + } + return null; } public String getDescription() { diff --git a/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFPictureData.java b/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFPictureData.java index aee464e0e3..6671438417 100644 --- a/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFPictureData.java +++ b/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFPictureData.java @@ -18,9 +18,13 @@ package org.apache.poi.xwpf.usermodel; import java.io.IOException; +import java.io.InputStream; +import java.util.Arrays; import org.apache.poi.POIXMLDocumentPart; +import org.apache.poi.POIXMLException; import org.apache.poi.POIXMLRelation; +import org.apache.poi.openxml4j.opc.OPCPackage; import org.apache.poi.openxml4j.opc.PackagePart; import org.apache.poi.openxml4j.opc.PackageRelationship; import org.apache.poi.util.IOUtils; @@ -32,29 +36,31 @@ import org.apache.poi.util.IOUtils; /** * @author Philipp Epp - * */ public class XWPFPictureData extends POIXMLDocumentPart { - + /** * Relationships for each known picture type */ protected static final POIXMLRelation[] RELATIONS; static { RELATIONS = new POIXMLRelation[9]; - RELATIONS[Document.PICTURE_TYPE_EMF] = XWPFRelation.IMAGE_EMF; - RELATIONS[Document.PICTURE_TYPE_WMF] = XWPFRelation.IMAGE_WMF; + RELATIONS[Document.PICTURE_TYPE_EMF] = XWPFRelation.IMAGE_EMF; + RELATIONS[Document.PICTURE_TYPE_WMF] = XWPFRelation.IMAGE_WMF; RELATIONS[Document.PICTURE_TYPE_PICT] = XWPFRelation.IMAGE_PICT; RELATIONS[Document.PICTURE_TYPE_JPEG] = XWPFRelation.IMAGE_JPEG; - RELATIONS[Document.PICTURE_TYPE_PNG] = XWPFRelation.IMAGE_PNG; - RELATIONS[Document.PICTURE_TYPE_DIB] = XWPFRelation.IMAGE_DIB; - RELATIONS[Document.PICTURE_TYPE_GIF] = XWPFRelation.IMAGE_GIF; + RELATIONS[Document.PICTURE_TYPE_PNG] = XWPFRelation.IMAGE_PNG; + RELATIONS[Document.PICTURE_TYPE_DIB] = XWPFRelation.IMAGE_DIB; + RELATIONS[Document.PICTURE_TYPE_GIF] = XWPFRelation.IMAGE_GIF; } + + private Long checksum = null; + /** * Create a new XWPFGraphicData node * */ - protected XWPFPictureData() { + protected XWPFPictureData() { super(); } @@ -65,11 +71,15 @@ public class XWPFPictureData extends POIXMLDocumentPart { * @param rel the package relationship holding this drawing, * the relationship type must be http://schemas.openxmlformats.org/officeDocument/2006/relationships/image */ - - public XWPFPictureData(PackagePart part, PackageRelationship rel) { + public XWPFPictureData(PackagePart part, PackageRelationship rel) { super(part, rel); } - + + @Override + protected void onDocumentRead() throws IOException { + super.onDocumentRead(); + } + /** * Gets the picture data as a byte array. *

@@ -80,44 +90,40 @@ public class XWPFPictureData extends POIXMLDocumentPart { * InputStream is = getPackagePart().getInputStream(); * *

- * * @return the Picture data. */ public byte[] getData() { - try { - return IOUtils.toByteArray(getPackagePart().getInputStream()); - } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - return null; + try { + return IOUtils.toByteArray(getPackagePart().getInputStream()); + } catch (IOException e) { + throw new POIXMLException(e); + } } - + /** - * Returns the file name of the image, eg image7.jpg . - * The original filename isn't always available, but if it - * can be found it's likely to be in the CTDrawing + * Returns the file name of the image, eg image7.jpg . The original filename + * isn't always available, but if it can be found it's likely to be in the + * CTDrawing */ public String getFileName() { - String name = getPackagePart().getPartName().getName(); - if(name == null) - return null; - return name.substring(name.lastIndexOf('/') + 1); + String name = getPackagePart().getPartName().getName(); + if (name == null) + return null; + return name.substring(name.lastIndexOf('/') + 1); } - + /** * Suggests a file extension for this image. - * * @return the file extension. */ public String suggestFileExtension() { return getPackagePart().getPartName().getExtension(); } - + /** * Return an integer constant that specifies type of this picture - * - * @return an integer constant that specifies type of this picture + * + * @return an integer constant that specifies type of this picture * @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 @@ -125,15 +131,103 @@ public class XWPFPictureData extends POIXMLDocumentPart { * @see org.apache.poi.xwpf.usermodel.Document#PICTURE_TYPE_PNG * @see org.apache.poi.xwpf.usermodel.Document#PICTURE_TYPE_DIB */ - public int getPictureType(){ + public int getPictureType() { String contentType = getPackagePart().getContentType(); for (int i = 0; i < RELATIONS.length; i++) { - if(RELATIONS[i] == null) continue; + if (RELATIONS[i] == null) { + continue; + } - if(RELATIONS[i].getContentType().equals(contentType)){ + if (RELATIONS[i].getContentType().equals(contentType)) { return i; } } return 0; } + + public Long getChecksum() { + if (this.checksum == null) { + InputStream is = null; + byte[] data; + try { + is = getPackagePart().getInputStream(); + data = IOUtils.toByteArray(is); + } catch (IOException e) { + throw new POIXMLException(e); + } finally { + try { + is.close(); + } catch (IOException e) { + throw new POIXMLException(e); + } + } + this.checksum = IOUtils.calculateChecksum(data); + } + return this.checksum; + } + + @Override + public boolean equals(Object obj) { + /** + * In case two objects ARE equal, but its not the same instance, this + * implementation will always run through the whole + * byte-array-comparison before returning true. If this will turn into a + * performance issue, two possible approaches are available:
+ * a) Use the checksum only and take the risk that two images might have + * the same CRC32 sum, although they are not the same.
+ * b) Use a second (or third) checksum algorithm to minimise the chance + * that two images have the same checksums but are not equal (e.g. + * CRC32, MD5 and SHA-1 checksums, additionally compare the + * data-byte-array lengths). + */ + if (obj == this) { + return true; + } + + if (obj == null) { + return false; + } + + if (!(obj instanceof XWPFPictureData)) { + return false; + } + + XWPFPictureData picData = (XWPFPictureData) obj; + PackagePart foreignPackagePart = picData.getPackagePart(); + PackagePart ownPackagePart = this.getPackagePart(); + + if ((foreignPackagePart != null && ownPackagePart == null) + || (foreignPackagePart == null && ownPackagePart != null)) { + return false; + } + + if (ownPackagePart != null) { + OPCPackage foreignPackage = foreignPackagePart.getPackage(); + OPCPackage ownPackage = ownPackagePart.getPackage(); + + if ((foreignPackage != null && ownPackage == null) + || (foreignPackage == null && ownPackage != null)) { + return false; + } + if (ownPackage != null) { + + if (!ownPackage.equals(foreignPackage)) { + return false; + } + } + } + + Long foreignChecksum = picData.getChecksum(); + Long localChecksum = getChecksum(); + + if (!(localChecksum.equals(foreignChecksum))) { + return false; + } + return Arrays.equals(this.getData(), picData.getData()); + } + + @Override + public int hashCode() { + return getChecksum().hashCode(); + } } 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 a08823c195..4e905ec6af 100644 --- a/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFRun.java +++ b/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFRun.java @@ -45,6 +45,7 @@ import org.openxmlformats.schemas.drawingml.x2006.main.CTPresetGeometry2D; import org.openxmlformats.schemas.drawingml.x2006.main.CTShapeProperties; import org.openxmlformats.schemas.drawingml.x2006.main.CTTransform2D; import org.openxmlformats.schemas.drawingml.x2006.main.STShapeType; +import org.openxmlformats.schemas.drawingml.x2006.wordprocessingDrawing.CTAnchor; import org.openxmlformats.schemas.drawingml.x2006.wordprocessingDrawing.CTInline; import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTBr; import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTDrawing; @@ -87,56 +88,75 @@ public class XWPFRun { public XWPFRun(CTR r, XWPFParagraph p) { this.run = r; this.paragraph = p; - + + /** + * reserve already occupied drawing ids, so reserving new ids later will + * not corrupt the document + */ + List drawingList = r.getDrawingList(); + for (CTDrawing ctDrawing : drawingList) { + List anchorList = ctDrawing.getAnchorList(); + for (CTAnchor anchor : anchorList) { + if (anchor.getDocPr() != null) { + getDocument().getDrawingIdManager().reserve(anchor.getDocPr().getId()); + } + } + List inlineList = ctDrawing.getInlineList(); + for (CTInline inline : inlineList) { + if (inline.getDocPr() != null) { + getDocument().getDrawingIdManager().reserve(inline.getDocPr().getId()); + } + } + } + // Look for any text in any of our pictures or drawings StringBuffer text = new StringBuffer(); List pictTextObjs = new ArrayList(); pictTextObjs.addAll(r.getPictList()); - pictTextObjs.addAll(r.getDrawingList()); + pictTextObjs.addAll(drawingList); for(XmlObject o : pictTextObjs) { - XmlObject[] t = o - .selectPath("declare namespace w='http://schemas.openxmlformats.org/wordprocessingml/2006/main' .//w:t"); - for (int m = 0; m < t.length; m++) { - NodeList kids = t[m].getDomNode().getChildNodes(); - for (int n = 0; n < kids.getLength(); n++) { - if (kids.item(n) instanceof Text) { - if(text.length() > 0) - text.append("\n"); - text.append(kids.item(n).getNodeValue()); - } - } - } + XmlObject[] t = o.selectPath("declare namespace w='http://schemas.openxmlformats.org/wordprocessingml/2006/main' .//w:t"); + for (int m = 0; m < t.length; m++) { + NodeList kids = t[m].getDomNode().getChildNodes(); + for (int n = 0; n < kids.getLength(); n++) { + if (kids.item(n) instanceof Text) { + if(text.length() > 0) + text.append("\n"); + text.append(kids.item(n).getNodeValue()); + } + } + } } pictureText = text.toString(); - + // Do we have any embedded pictures? // (They're a different CTPicture, under the drawingml namespace) pictures = new ArrayList(); for(XmlObject o : pictTextObjs) { - for(CTPicture pict : getCTPictures(o)) { - XWPFPicture picture = new XWPFPicture( pict, p ); - pictures.add(picture); - } + for(CTPicture pict : getCTPictures(o)) { + XWPFPicture picture = new XWPFPicture(pict, this); + pictures.add(picture); + } } } - + private List getCTPictures(XmlObject o) { - List pictures = new ArrayList(); - XmlObject[] picts = o.selectPath("declare namespace pic='"+CTPicture.type.getName().getNamespaceURI()+"' .//pic:pic"); - for(XmlObject pict : picts) { - if(pict instanceof XmlAnyTypeImpl) { - // Pesky XmlBeans bug - see Bugzilla #49934 - try { - pict = CTPicture.Factory.parse( pict.toString() ); - } catch(XmlException e) { - throw new POIXMLException(e); - } - } - if(pict instanceof CTPicture) { - pictures.add((CTPicture)pict); - } - } - return pictures; + List pictures = new ArrayList(); + XmlObject[] picts = o.selectPath("declare namespace pic='"+CTPicture.type.getName().getNamespaceURI()+"' .//pic:pic"); + for(XmlObject pict : picts) { + if(pict instanceof XmlAnyTypeImpl) { + // Pesky XmlBeans bug - see Bugzilla #49934 + try { + pict = CTPicture.Factory.parse( pict.toString() ); + } catch(XmlException e) { + throw new POIXMLException(e); + } + } + if(pict instanceof CTPicture) { + pictures.add((CTPicture)pict); + } + } + return pictures; } /** @@ -155,18 +175,29 @@ public class XWPFRun { public XWPFParagraph getParagraph() { return paragraph; } - + + /** + * @return The {@link XWPFDocument} instance, this run belongs to, or + * null if parent structure (paragraph > document) is not properly set. + */ + public XWPFDocument getDocument() { + if (paragraph != null) { + return paragraph.getDocument(); + } + return null; + } + /** * For isBold, isItalic etc */ private boolean isCTOnOff(CTOnOff onoff) { - if(! onoff.isSetVal()) - return true; - if(onoff.getVal() == STOnOff.ON) - return true; - if(onoff.getVal() == STOnOff.TRUE) - return true; - return false; + if(! onoff.isSetVal()) + return true; + if(onoff.getVal() == STOnOff.ON) + return true; + if(onoff.getVal() == STOnOff.TRUE) + return true; + return false; } /** @@ -177,8 +208,9 @@ public class XWPFRun { */ public boolean isBold() { CTRPr pr = run.getRPr(); - if(pr == null || !pr.isSetB()) - return false; + if(pr == null || !pr.isSetB()) { + return false; + } return isCTOnOff(pr.getB()); } @@ -221,7 +253,7 @@ public class XWPFRun { return run.sizeOfTArray() == 0 ? null : run.getTArray(pos) .getStringValue(); } - + /** * Returns text embedded in pictures */ @@ -235,7 +267,7 @@ public class XWPFRun { * @param value the literal text which shall be displayed in the document */ public void setText(String value) { - setText(value,run.getTList().size()); + setText(value,run.getTList().size()); } /** @@ -245,13 +277,12 @@ public class XWPFRun { * @param pos - position in the text array (NB: 0 based) */ public void setText(String value, int pos) { - if(pos > run.sizeOfTArray()) throw new ArrayIndexOutOfBoundsException("Value too large for the parameter position in XWPFRun.setText(String value,int pos)"); + if(pos > run.sizeOfTArray()) throw new ArrayIndexOutOfBoundsException("Value too large for the parameter position in XWPFRun.setText(String value,int pos)"); CTText t = (pos < run.sizeOfTArray() && pos >= 0) ? run.getTArray(pos) : run.addNewT(); t.setStringValue(value); preserveSpaces(t); } - /** * Whether the italic property should be applied to all non-complex script * characters in the contents of this run when displayed in a document. @@ -261,7 +292,7 @@ public class XWPFRun { public boolean isItalic() { CTRPr pr = run.getRPr(); if(pr == null || !pr.isSetI()) - return false; + return false; return isCTOnOff(pr.getI()); } @@ -306,7 +337,7 @@ public class XWPFRun { public UnderlinePatterns getUnderline() { CTRPr pr = run.getRPr(); return (pr != null && pr.isSetU()) ? UnderlinePatterns.valueOf(pr - .getU().getVal().intValue()) : UnderlinePatterns.NONE; + .getU().getVal().intValue()) : UnderlinePatterns.NONE; } /** @@ -339,7 +370,7 @@ public class XWPFRun { public boolean isStrike() { CTRPr pr = run.getRPr(); if(pr == null || !pr.isSetStrike()) - return false; + return false; return isCTOnOff(pr.getStrike()); } @@ -384,8 +415,7 @@ public class XWPFRun { */ public VerticalAlign getSubscript() { CTRPr pr = run.getRPr(); - return (pr != null && pr.isSetVertAlign()) ? VerticalAlign.valueOf(pr - .getVertAlign().getVal().intValue()) : VerticalAlign.BASELINE; + return (pr != null && pr.isSetVertAlign()) ? VerticalAlign.valueOf(pr.getVertAlign().getVal().intValue()) : VerticalAlign.BASELINE; } /** @@ -460,7 +490,7 @@ public class XWPFRun { * @param size */ public void setFontSize(int size) { - BigInteger bint=new BigInteger(""+size); + BigInteger bint=new BigInteger(""+size); CTRPr pr = run.isSetRPr() ? run.getRPr() : run.addNewRPr(); CTHpsMeasure ctSize = pr.isSetSz() ? pr.getSz() : pr.addNewSz(); ctSize.setVal(bint.multiply(new BigInteger("2"))); @@ -503,7 +533,7 @@ public class XWPFRun { * @param val */ public void setTextPosition(int val) { - BigInteger bint=new BigInteger(""+val); + BigInteger bint=new BigInteger(""+val); CTRPr pr = run.isSetRPr() ? run.getRPr() : run.addNewRPr(); CTSignedHpsMeasure position = pr.isSetPosition() ? pr.getPosition() : pr.addNewPosition(); position.setVal(bint); @@ -513,7 +543,7 @@ public class XWPFRun { * */ public void removeBreak() { - // TODO + // TODO } /** @@ -525,7 +555,7 @@ public class XWPFRun { * @see #addCarriageReturn() */ public void addBreak() { - run.addNewBr(); + run.addNewBr(); } /** @@ -542,11 +572,10 @@ public class XWPFRun { * @see BreakType */ public void addBreak(BreakType type){ - CTBr br=run.addNewBr(); - br.setType(STBrType.Enum.forInt(type.getValue())); + CTBr br=run.addNewBr(); + br.setType(STBrType.Enum.forInt(type.getValue())); } - /** * Specifies that a break shall be placed at the current location in the run * content. A break is a special character which is used to override the @@ -560,9 +589,9 @@ public class XWPFRun { * @see BreakClear */ public void addBreak(BreakClear clear){ - CTBr br=run.addNewBr(); - br.setType(STBrType.Enum.forInt(BreakType.TEXT_WRAPPING.getValue())); - br.setClear(STBrClear.Enum.forInt(clear.getValue())); + CTBr br=run.addNewBr(); + br.setType(STBrType.Enum.forInt(BreakType.TEXT_WRAPPING.getValue())); + br.setClear(STBrClear.Enum.forInt(clear.getValue())); } /** @@ -578,13 +607,13 @@ public class XWPFRun { * restarted on the next available line in the document. */ public void addCarriageReturn() { - run.addNewCr(); + run.addNewCr(); } public void removeCarriageReturn() { - //TODO - } - + //TODO + } + /** * Adds a picture to the run. This method handles * attaching the picture data to the overall file. @@ -598,100 +627,104 @@ public class XWPFRun { * * @param pictureData The raw picture data * @param pictureType The type of the picture, eg {@link Document#PICTURE_TYPE_JPEG} - * @throws IOException - * @throws org.apache.poi.openxml4j.exceptions.InvalidFormatException - * @throws IOException + * @throws IOException + * @throws org.apache.poi.openxml4j.exceptions.InvalidFormatException + * @throws IOException */ public XWPFPicture addPicture(InputStream pictureData, int pictureType, String filename, int width, int height) throws InvalidFormatException, IOException { - XWPFDocument doc = paragraph.document; - - // Add the picture + relationship - int picNumber = doc.addPicture(pictureData, pictureType); - XWPFPictureData picData = doc.getAllPackagePictures().get(picNumber); - - // Create the drawing entry for it - try { - CTDrawing drawing = run.addNewDrawing(); - CTInline inline = drawing.addNewInline(); - - // Do the fiddly namespace bits on the inline - // (We need full control of what goes where and as what) - String xml = - "" + - "" + - "" + - "" + - ""; - inline.set(XmlToken.Factory.parse(xml)); - - // Setup the inline - inline.setDistT(0); - inline.setDistR(0); - inline.setDistB(0); - inline.setDistL(0); - - CTNonVisualDrawingProps docPr = inline.addNewDocPr(); - docPr.setId(picNumber); - docPr.setName("Picture " + picNumber); - docPr.setDescr(filename); - - CTPositiveSize2D extent = inline.addNewExtent(); - extent.setCx(width); - extent.setCy(height); - - // Grab the picture object - CTGraphicalObject graphic = inline.getGraphic(); - CTGraphicalObjectData graphicData = graphic.getGraphicData(); - CTPicture pic = getCTPictures(graphicData).get(0); - - // Set it up - CTPictureNonVisual nvPicPr = pic.addNewNvPicPr(); - - CTNonVisualDrawingProps cNvPr = nvPicPr.addNewCNvPr(); - cNvPr.setId(picNumber); - cNvPr.setName("Picture " + picNumber); - cNvPr.setDescr(filename); - - CTNonVisualPictureProperties cNvPicPr = nvPicPr.addNewCNvPicPr(); - cNvPicPr.addNewPicLocks().setNoChangeAspect(true); - - CTBlipFillProperties blipFill = pic.addNewBlipFill(); - CTBlip blip = blipFill.addNewBlip(); - blip.setEmbed( picData.getPackageRelationship().getId() ); - blipFill.addNewStretch().addNewFillRect(); - - CTShapeProperties spPr = pic.addNewSpPr(); - CTTransform2D xfrm = spPr.addNewXfrm(); - - CTPoint2D off = xfrm.addNewOff(); - off.setX(0); - off.setY(0); - - CTPositiveSize2D ext = xfrm.addNewExt(); - ext.setCx(width); - ext.setCy(height); - - CTPresetGeometry2D prstGeom = spPr.addNewPrstGeom(); - prstGeom.setPrst(STShapeType.RECT); - prstGeom.addNewAvLst(); - - // Finish up - XWPFPicture xwpfPicture = new XWPFPicture(pic, paragraph); - pictures.add(xwpfPicture); - return xwpfPicture; - } catch(XmlException e) { - throw new IllegalStateException(e); - } - } - + XWPFDocument doc = paragraph.document; + + // Add the picture + relationship + String relationId = doc.addPictureData(pictureData, pictureType); + XWPFPictureData picData = (XWPFPictureData) doc.getRelationById(relationId); + + // Create the drawing entry for it + try { + CTDrawing drawing = run.addNewDrawing(); + CTInline inline = drawing.addNewInline(); + + // Do the fiddly namespace bits on the inline + // (We need full control of what goes where and as what) + String xml = + "" + + "" + + "" + + "" + + ""; + inline.set(XmlToken.Factory.parse(xml)); + + // Setup the inline + inline.setDistT(0); + inline.setDistR(0); + inline.setDistB(0); + inline.setDistL(0); + + CTNonVisualDrawingProps docPr = inline.addNewDocPr(); + long id = getParagraph().document.getDrawingIdManager().reserveNew(); + docPr.setId(id); + /* This name is not visible in Word 2010 anywhere. */ + docPr.setName("Drawing " + id); + docPr.setDescr(filename); + + CTPositiveSize2D extent = inline.addNewExtent(); + extent.setCx(width); + extent.setCy(height); + + // Grab the picture object + CTGraphicalObject graphic = inline.getGraphic(); + CTGraphicalObjectData graphicData = graphic.getGraphicData(); + CTPicture pic = getCTPictures(graphicData).get(0); + + // Set it up + CTPictureNonVisual nvPicPr = pic.addNewNvPicPr(); + + CTNonVisualDrawingProps cNvPr = nvPicPr.addNewCNvPr(); + /* use "0" for the id. See ECM-576, 20.2.2.3 */ + cNvPr.setId(0L); + /* This name is not visible in Word 2010 anywhere */ + cNvPr.setName("Picture " + id); + cNvPr.setDescr(filename); + + CTNonVisualPictureProperties cNvPicPr = nvPicPr.addNewCNvPicPr(); + cNvPicPr.addNewPicLocks().setNoChangeAspect(true); + + CTBlipFillProperties blipFill = pic.addNewBlipFill(); + CTBlip blip = blipFill.addNewBlip(); + blip.setEmbed( picData.getPackageRelationship().getId() ); + blipFill.addNewStretch().addNewFillRect(); + + CTShapeProperties spPr = pic.addNewSpPr(); + CTTransform2D xfrm = spPr.addNewXfrm(); + + CTPoint2D off = xfrm.addNewOff(); + off.setX(0); + off.setY(0); + + CTPositiveSize2D ext = xfrm.addNewExt(); + ext.setCx(width); + ext.setCy(height); + + CTPresetGeometry2D prstGeom = spPr.addNewPrstGeom(); + prstGeom.setPrst(STShapeType.RECT); + prstGeom.addNewAvLst(); + + // Finish up + XWPFPicture xwpfPicture = new XWPFPicture(pic, this); + pictures.add(xwpfPicture); + return xwpfPicture; + } catch(XmlException e) { + throw new IllegalStateException(e); + } + } + /** * Returns the embedded pictures of the run. These * are pictures which reference an external, * embedded picture image such as a .png or .jpg */ public List getEmbeddedPictures() { - return pictures; + return pictures; } /** @@ -714,56 +747,56 @@ public class XWPFRun { * carriage returns in place of their xml equivalents. */ public String toString() { - StringBuffer text = new StringBuffer(); - - // Grab the text and tabs of the text run - // Do so in a way that preserves the ordering - XmlCursor c = run.newCursor(); - c.selectPath("./*"); - while (c.toNextSelection()) { - XmlObject o = c.getObject(); - if (o instanceof CTText) { - String tagName = o.getDomNode().getNodeName(); - // Field Codes (w:instrText, defined in spec sec. 17.16.23) - // come up as instances of CTText, but we don't want them - // in the normal text output - if (!"w:instrText".equals(tagName)) { - text.append(((CTText) o).getStringValue()); - } - } - - if (o instanceof CTPTab) { - text.append("\t"); - } - if (o instanceof CTBr) { - text.append("\n"); - } - if (o instanceof CTEmpty) { - // Some inline text elements get returned not as - // themselves, but as CTEmpty, owing to some odd - // definitions around line 5642 of the XSDs - // This bit works around it, and replicates the above - // rules for that case - String tagName = o.getDomNode().getNodeName(); - if ("w:tab".equals(tagName)) { - text.append("\t"); - } - if ("w:br".equals(tagName)) { - text.append("\n"); - } - if ("w:cr".equals(tagName)) { - text.append("\n"); - } - } - } - - c.dispose(); - - // Any picture text? - if(pictureText != null && pictureText.length() > 0) { - text.append("\n").append(pictureText); - } - - return text.toString(); + StringBuffer text = new StringBuffer(); + + // Grab the text and tabs of the text run + // Do so in a way that preserves the ordering + XmlCursor c = run.newCursor(); + c.selectPath("./*"); + while (c.toNextSelection()) { + XmlObject o = c.getObject(); + if (o instanceof CTText) { + String tagName = o.getDomNode().getNodeName(); + // Field Codes (w:instrText, defined in spec sec. 17.16.23) + // come up as instances of CTText, but we don't want them + // in the normal text output + if (!"w:instrText".equals(tagName)) { + text.append(((CTText) o).getStringValue()); + } + } + + if (o instanceof CTPTab) { + text.append("\t"); + } + if (o instanceof CTBr) { + text.append("\n"); + } + if (o instanceof CTEmpty) { + // Some inline text elements get returned not as + // themselves, but as CTEmpty, owing to some odd + // definitions around line 5642 of the XSDs + // This bit works around it, and replicates the above + // rules for that case + String tagName = o.getDomNode().getNodeName(); + if ("w:tab".equals(tagName)) { + text.append("\t"); + } + if ("w:br".equals(tagName)) { + text.append("\n"); + } + if ("w:cr".equals(tagName)) { + text.append("\n"); + } + } + } + + c.dispose(); + + // Any picture text? + if(pictureText != null && pictureText.length() > 0) { + text.append("\n").append(pictureText); + } + + return text.toString(); } } diff --git a/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFSettings.java b/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFSettings.java index 09ec4cc6fb..18d787504a 100644 --- a/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFSettings.java +++ b/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFSettings.java @@ -42,7 +42,6 @@ public class XWPFSettings extends POIXMLDocumentPart { public XWPFSettings(PackagePart part, PackageRelationship rel) throws IOException { super(part, rel); - readFrom(part.getInputStream()); } public XWPFSettings() { @@ -50,19 +49,26 @@ public class XWPFSettings extends POIXMLDocumentPart { ctSettings = CTSettings.Factory.newInstance(); } + @Override + protected void onDocumentRead() throws IOException + { + super.onDocumentRead(); + readFrom(getPackagePart().getInputStream()); + } + /** * Set zoom.
* In the zoom tag inside settings.xml file
* it sets the value of zoom *
- * sample snippet from settings.xml + * sample snippet from settings.xml *
-     *    <w:zoom w:percent="50" /> 
+     *    <w:zoom w:percent="50" />
      * 
      * @return percentage as an integer of zoom level
      */
     public long getZoomPercent() {
-       CTZoom zoom; 
+       CTZoom zoom;
        if (!ctSettings.isSetZoom()) {
           zoom = ctSettings.addNewZoom();
        } else {
diff --git a/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFStyles.java b/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFStyles.java
index 86895032eb..f45d113ca5 100644
--- a/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFStyles.java
+++ b/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFStyles.java
@@ -24,7 +24,6 @@ import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import java.lang.String;
 
 import javax.xml.namespace.QName;
 
@@ -49,11 +48,12 @@ import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTDocDefaults;
  *
  */
 public class XWPFStyles extends POIXMLDocumentPart{
-	private CTStyles ctStyles;
-	protected XWPFLatentStyles latentStyles;
-	protected List listStyle;
-	
-	/**
+    
+    private List listStyle = new ArrayList();
+    private CTStyles ctStyles;
+    XWPFLatentStyles latentStyles;
+
+    /**
      * Construct XWPFStyles from a package part
      *
      * @param part the package part holding the data of the styles,
@@ -62,14 +62,12 @@ public class XWPFStyles extends POIXMLDocumentPart{
 
 	public XWPFStyles(PackagePart part, PackageRelationship rel) throws IOException, OpenXML4JException{
 		super(part, rel);
-		onDocumentRead();
 	}
 
 	/**
 	 * Construct XWPFStyles from scratch for a new document.
 	 */
 	public XWPFStyles() {
-		listStyle = new ArrayList();
 	}
 
 	/**
@@ -77,7 +75,6 @@ public class XWPFStyles extends POIXMLDocumentPart{
 	 */
 	 @Override
 	protected void onDocumentRead ()throws IOException{
-		listStyle = new ArrayList();
 		StylesDocument stylesDoc;
 		try {
 			InputStream is = getPackagePart().getInputStream();
@@ -98,7 +95,7 @@ public class XWPFStyles extends POIXMLDocumentPart{
 	    protected void commit() throws IOException {
 	        XmlOptions xmlOptions = new XmlOptions(DEFAULT_XML_OPTIONS);
 	        xmlOptions.setSaveSyntheticDocumentElement(new QName(CTStyles.type.getName().getNamespaceURI(), "styles"));
-	        Map map = new HashMap();
+	        Map map = new HashMap();
 	        map.put("http://schemas.openxmlformats.org/officeDocument/2006/relationships", "r");
 	        map.put("http://schemas.openxmlformats.org/wordprocessingml/2006/main", "w");
 	        xmlOptions.setSaveSuggestedPrefixes(map);
diff --git a/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFTable.java b/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFTable.java
index d310b288ae..5c4d2cca58 100644
--- a/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFTable.java
+++ b/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFTable.java
@@ -20,6 +20,7 @@ import java.math.BigInteger;
 import java.util.ArrayList;
 import java.util.List;
 
+import org.apache.poi.POIXMLDocumentPart;
 import org.apache.poi.util.Internal;
 import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTP;
 import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTRow;
@@ -58,7 +59,6 @@ public class XWPFTable implements IBodyElement{
         }
     }
 
-
     public XWPFTable(CTTbl table, IBody part){
     	this.part = part;
         this.ctTbl = table;
@@ -131,17 +131,17 @@ public class XWPFTable implements IBodyElement{
         return text.toString();
     }
 
-
     public void addNewRowBetween(int start, int end) {
         // TODO
     }
 
-
     /**
      * add a new column for each row in this table
      */
     public void addNewCol() {
-        if (ctTbl.sizeOfTrArray() == 0) createRow();
+        if (ctTbl.sizeOfTrArray() == 0) {
+            createRow();
+        }
         for (int i = 0; i < ctTbl.sizeOfTrArray(); i++) {
             XWPFTableRow tabRow = new XWPFTableRow(ctTbl.getTrArray(i), this);
             tabRow.createCell();
@@ -268,12 +268,12 @@ public class XWPFTable implements IBodyElement{
      * @param pos	position the Row in the Table
      */
     public boolean removeRow(int pos) throws IndexOutOfBoundsException {
-    	if(pos > 0 && pos < tableRows.size()){
-    		ctTbl.removeTr(pos);
-    		tableRows.remove(pos);
-    		return true;
-    	}
-    	return false;
+        if (pos >= 0 && pos < tableRows.size()) {
+            ctTbl.removeTr(pos);
+            tableRows.remove(pos);
+            return true;
+        }
+        return false;
     }
 	
     public List getRows() {
@@ -289,26 +289,30 @@ public class XWPFTable implements IBodyElement{
 		return BodyElementType.TABLE;
 	}
 
+    @Override
+    public IBody getBody()
+    {
+        return part;
+    }
 
-	/**
-	 * returns the part of the bodyElement
-	 * @see org.apache.poi.xwpf.usermodel.IBody#getPart()
-	 */
-	public IBody getPart() {
-		if(part != null){
-			return part.getPart();
-		}
-		return null;
-	}
-
+    /**
+     * returns the part of the bodyElement
+     * @see org.apache.poi.xwpf.usermodel.IBody#getPart()
+     */
+    public POIXMLDocumentPart getPart() {
+        if(part != null){
+            return part.getPart();
+        }
+        return null;
+    }
 
-	/**
-	 * returns the partType of the bodyPart which owns the bodyElement
-	 * @see org.apache.poi.xwpf.usermodel.IBody#getPartType()
-	 */
-	public BodyType getPartType() {
-		return ((IBody)part).getPartType();
-	}
+    /**
+     * returns the partType of the bodyPart which owns the bodyElement
+     * @see org.apache.poi.xwpf.usermodel.IBody#getPartType()
+     */
+    public BodyType getPartType() {
+        return part.getPartType();
+    }
 
 	/**
 	 * returns the XWPFRow which belongs to the CTRow row
diff --git a/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFTableCell.java b/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFTableCell.java
index 9ffb65a340..a28a963db9 100644
--- a/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFTableCell.java
+++ b/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFTableCell.java
@@ -20,6 +20,7 @@ import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 
+import org.apache.poi.POIXMLDocumentPart;
 import org.apache.poi.util.Internal;
 import org.apache.xmlbeans.XmlCursor;
 import org.apache.xmlbeans.XmlObject;
@@ -248,16 +249,14 @@ public class XWPFTableCell implements IBody {
 		return null;
 	}
 
-
-
-
-	/**
-	 * get the to which the TableCell belongs 
-	 * @see org.apache.poi.xwpf.usermodel.IBody#getPart()
-	 */
-	public IBody getPart() {
-		return tableRow.getTable().getPart();
-	}
+    /**
+     * get the to which the TableCell belongs
+     * 
+     * @see org.apache.poi.xwpf.usermodel.IBody#getPart()
+     */
+    public POIXMLDocumentPart getPart() {
+        return tableRow.getTable().getPart();
+    }
 
 
 	/** 
diff --git a/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFTableRow.java b/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFTableRow.java
index ebdbe04ba2..a717d7f374 100644
--- a/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFTableRow.java
+++ b/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFTableRow.java
@@ -52,7 +52,7 @@ public class XWPFTableRow {
      * @return the newly created XWPFTableCell
      */
     public XWPFTableCell createCell() {
-        XWPFTableCell tableCell = new XWPFTableCell(ctRow.addNewTc(), this, table.getPart());
+        XWPFTableCell tableCell = new XWPFTableCell(ctRow.addNewTc(), this, table.getBody());
         tableCells.add(tableCell);
         return tableCell;
     }
@@ -69,7 +69,7 @@ public class XWPFTableRow {
      */
     public XWPFTableCell addNewTableCell(){
     	CTTc cell = ctRow.addNewTc();
-    	XWPFTableCell tableCell = new XWPFTableCell(cell, this, table.getPart());
+    	XWPFTableCell tableCell = new XWPFTableCell(cell, this, table.getBody());
     	tableCells.add(tableCell);
     	return tableCell;
     }
@@ -123,7 +123,7 @@ public class XWPFTableRow {
     	if(tableCells == null){
     		List cells = new ArrayList();
     		for (CTTc tableCell : ctRow.getTcList()) {
-    			cells.add(new XWPFTableCell(tableCell, this, table.getPart()));
+    			cells.add(new XWPFTableCell(tableCell, this, table.getBody()));
     		}
     		this.tableCells = cells;
     	}
diff --git a/src/ooxml/testcases/org/apache/poi/TestPOIXMLDocument.java b/src/ooxml/testcases/org/apache/poi/TestPOIXMLDocument.java
index 4e7f788e67..e27708b1ce 100644
--- a/src/ooxml/testcases/org/apache/poi/TestPOIXMLDocument.java
+++ b/src/ooxml/testcases/org/apache/poi/TestPOIXMLDocument.java
@@ -141,4 +141,15 @@ public final class TestPOIXMLDocument extends TestCase {
                 PackageHelper.open(POIDataSamples.getDocumentInstance().openResourceAsStream("WordWithAttachments.docx"))
                 );
     }
+
+    public void testRelationOrder() throws Exception {
+        OPCPackage pkg = PackageHelper.open(POIDataSamples.getDocumentInstance().openResourceAsStream("WordWithAttachments.docx"));
+        OPCParser doc = new OPCParser(pkg);
+        doc.parse(new TestFactory());
+
+        for(POIXMLDocumentPart rel : doc.getRelations()){
+            System.out.println(rel);
+        }
+
+    }
 }
diff --git a/src/ooxml/testcases/org/apache/poi/TestPOIXMLDocumentPart.java b/src/ooxml/testcases/org/apache/poi/TestPOIXMLDocumentPart.java
new file mode 100644
index 0000000000..7aecb424de
--- /dev/null
+++ b/src/ooxml/testcases/org/apache/poi/TestPOIXMLDocumentPart.java
@@ -0,0 +1,144 @@
+/* ====================================================================
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+==================================================================== */
+
+package org.apache.poi;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+import org.apache.poi.openxml4j.opc.OPCPackage;
+import org.apache.poi.openxml4j.opc.PackagePart;
+import org.apache.poi.openxml4j.opc.PackageRelationship;
+import org.apache.poi.util.TempFile;
+import org.apache.poi.util.PackageHelper;
+
+/**
+ * Test recursive read and write of OPC packages
+ */
+public final class TestPOIXMLDocumentPart extends TestCase {
+
+    private static class OPCParser extends POIXMLDocument {
+
+        public OPCParser(OPCPackage pkg) {
+            super(pkg);
+        }
+
+        public List getAllEmbedds() {
+            throw new RuntimeException("not supported");
+        }
+
+        public void parse(POIXMLFactory factory) throws IOException{
+            load(factory);
+        }
+    }
+
+    private static final class TestFactory extends POIXMLFactory {
+
+        public TestFactory() {
+            //
+        }
+        public POIXMLDocumentPart createDocumentPart(POIXMLDocumentPart parent, PackageRelationship rel, PackagePart part){
+            return new POIXMLDocumentPart(part, rel);
+        }
+
+        public POIXMLDocumentPart newDocumentPart(POIXMLRelation descriptor){
+            throw new RuntimeException("not supported");
+        }
+
+    }
+
+    /**
+     * Recursively traverse a OOXML document and assert that same logical parts have the same physical instances
+     */
+    private static void traverse(POIXMLDocumentPart part, HashMap context) throws IOException{
+        context.put(part.getPackageRelationship().getTargetURI().toString(), part);
+        for(POIXMLDocumentPart p : part.getRelations()){
+            String uri = p.getPackageRelationship().getTargetURI().toString();
+            if (!context.containsKey(uri)) {
+                traverse(p, context);
+            } else {
+                POIXMLDocumentPart prev = context.get(uri);
+                assertSame("Duplicate POIXMLDocumentPart instance for targetURI=" + uri, prev, p);
+            }
+        }
+    }
+
+    public void assertReadWrite(OPCPackage pkg1) throws Exception {
+
+        OPCParser doc = new OPCParser(pkg1);
+        doc.parse(new TestFactory());
+
+        HashMap context = new HashMap();
+        traverse(doc, context);
+        context.clear();
+
+        File tmp = TempFile.createTempFile("poi-ooxml", ".tmp");
+        FileOutputStream out = new FileOutputStream(tmp);
+        doc.write(out);
+        out.close();
+
+        OPCPackage pkg2 = OPCPackage.open(tmp.getAbsolutePath());
+
+        doc = new OPCParser(pkg1);
+        doc.parse(new TestFactory());
+        context = new HashMap();
+        traverse(doc, context);
+        context.clear();
+
+        assertEquals(pkg1.getRelationships().size(), pkg2.getRelationships().size());
+
+        ArrayList l1 = pkg1.getParts();
+        ArrayList l2 = pkg2.getParts();
+
+        assertEquals(l1.size(), l2.size());
+        for (int i=0; i < l1.size(); i++){
+            PackagePart p1 = l1.get(i);
+            PackagePart p2 = l2.get(i);
+
+            assertEquals(p1.getContentType(), p2.getContentType());
+            assertEquals(p1.hasRelationships(), p2.hasRelationships());
+            if(p1.hasRelationships()){
+                assertEquals(p1.getRelationships().size(), p2.getRelationships().size());
+            }
+            assertEquals(p1.getPartName(), p2.getPartName());
+        }
+    }
+
+    public void testPPTX() throws Exception {
+        assertReadWrite(
+                PackageHelper.open(POIDataSamples.getSlideShowInstance().openResourceAsStream("PPTWithAttachments.pptm"))
+        );
+    }
+
+    public void testXLSX() throws Exception {
+        assertReadWrite(
+                PackageHelper.open(POIDataSamples.getSpreadSheetInstance().openResourceAsStream("ExcelWithAttachments.xlsm"))
+                );
+    }
+
+    public void testDOCX() throws Exception {
+        assertReadWrite(
+                PackageHelper.open(POIDataSamples.getDocumentInstance().openResourceAsStream("WordWithAttachments.docx"))
+                );
+    }
+}
diff --git a/src/ooxml/testcases/org/apache/poi/TestPOIXMLProperties.java b/src/ooxml/testcases/org/apache/poi/TestPOIXMLProperties.java
index 56f1db4613..c7215a4061 100644
--- a/src/ooxml/testcases/org/apache/poi/TestPOIXMLProperties.java
+++ b/src/ooxml/testcases/org/apache/poi/TestPOIXMLProperties.java
@@ -17,16 +17,21 @@
 
 package org.apache.poi;
 
-import java.util.*;
+import java.io.IOException;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.Locale;
+import java.util.TimeZone;
 
 import junit.framework.TestCase;
 
 import org.apache.poi.POIXMLProperties.CoreProperties;
+import org.apache.poi.openxml4j.util.Nullable;
 import org.apache.poi.xssf.XSSFTestDataSamples;
 import org.apache.poi.xssf.usermodel.XSSFWorkbook;
 import org.apache.poi.xwpf.XWPFTestDataSamples;
 import org.apache.poi.xwpf.usermodel.XWPFDocument;
-import org.apache.poi.openxml4j.util.Nullable;
 
 /**
  * Test setting extended and custom OOXML properties
@@ -35,7 +40,7 @@ public final class TestPOIXMLProperties extends TestCase {
 	private POIXMLProperties _props;
 	private CoreProperties _coreProperties;
 
-	public void setUp() {
+	public void setUp() throws IOException {
 		XWPFDocument sampleDoc = XWPFTestDataSamples.openSampleDocument("documentProperties.docx");
 		_props = sampleDoc.getProperties();
 		_coreProperties = _props.getCoreProperties();
@@ -152,7 +157,7 @@ public final class TestPOIXMLProperties extends TestCase {
 		assertEquals("Hello World", title);
 	}
 
-    public void testTransitiveSetters() {
+    public void testTransitiveSetters() throws IOException {
 		XWPFDocument doc = new XWPFDocument();
         CoreProperties cp = doc.getProperties().getCoreProperties();
 
diff --git a/src/ooxml/testcases/org/apache/poi/openxml4j/opc/TestPackage.java b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/TestPackage.java
index 287bcdae67..35e87ec00d 100644
--- a/src/ooxml/testcases/org/apache/poi/openxml4j/opc/TestPackage.java
+++ b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/TestPackage.java
@@ -25,8 +25,8 @@ import java.io.IOException;
 import java.io.OutputStream;
 import java.lang.reflect.Field;
 import java.net.URI;
-import java.util.TreeMap;
-import java.util.Iterator;
+import java.util.*;
+import java.util.regex.Pattern;
 
 import junit.framework.TestCase;
 
@@ -509,4 +509,23 @@ public final class TestPackage extends TestCase {
 		f.setAccessible(true);
 		return (ContentTypeManager)f.get(pkg);
 	}
+
+    public void testGetPartsByName() throws Exception {
+        String filepath =  OpenXML4JTestDataSamples.getSampleFileName("sample.docx");
+
+        OPCPackage pkg = OPCPackage.open(filepath, PackageAccess.READ_WRITE);
+        List rs =  pkg.getPartsByName(Pattern.compile("/word/.*?\\.xml"));
+        HashMap  selected = new HashMap();
+
+        for(PackagePart p : rs)
+            selected.put(p.getPartName().getName(), p);
+
+        assertEquals(6, selected.size());
+        assertTrue(selected.containsKey("/word/document.xml"));
+        assertTrue(selected.containsKey("/word/fontTable.xml"));
+        assertTrue(selected.containsKey("/word/settings.xml"));
+        assertTrue(selected.containsKey("/word/styles.xml"));
+        assertTrue(selected.containsKey("/word/theme/theme1.xml"));
+        assertTrue(selected.containsKey("/word/webSettings.xml"));
+    }
 }
diff --git a/src/ooxml/testcases/org/apache/poi/util/TestIdentifierManager.java b/src/ooxml/testcases/org/apache/poi/util/TestIdentifierManager.java
new file mode 100644
index 0000000000..35e213fabf
--- /dev/null
+++ b/src/ooxml/testcases/org/apache/poi/util/TestIdentifierManager.java
@@ -0,0 +1,113 @@
+package org.apache.poi.util;
+
+import junit.framework.TestCase;
+
+public class TestIdentifierManager extends TestCase
+{
+    public void testBasic()
+    {
+        IdentifierManager manager = new IdentifierManager(0L,100L);
+        assertEquals(101L,manager.getRemainingIdentifiers());
+        assertEquals(0L,manager.reserveNew());
+        assertEquals(100L,manager.getRemainingIdentifiers());
+        assertEquals(1L,manager.reserve(0L));
+        assertEquals(99L,manager.getRemainingIdentifiers());
+    }
+
+    public void testLongLimits()
+    {
+        long min = IdentifierManager.MIN_ID;
+        long max = IdentifierManager.MAX_ID;
+        IdentifierManager manager = new IdentifierManager(min,max);
+        assertTrue("Limits lead to a long variable overflow", max - min + 1 > 0);
+        assertTrue("Limits lead to a long variable overflow", manager.getRemainingIdentifiers() > 0);
+        assertEquals(min,manager.reserveNew());
+        assertEquals(max,manager.reserve(max));
+        assertEquals(max - min -1, manager.getRemainingIdentifiers());
+        manager.release(max);
+        manager.release(min);
+    }
+    
+    public void testReserve()
+    {
+        IdentifierManager manager = new IdentifierManager(10L,30L);
+        assertEquals(12L,manager.reserve(12L));
+        long reserve = manager.reserve(12L);
+        assertFalse("Same id must be reserved twice!",reserve == 12L);
+        assertTrue(manager.release(12L));
+        assertTrue(manager.release(reserve));
+        assertFalse(manager.release(12L));
+        assertFalse(manager.release(reserve));
+        
+        manager = new IdentifierManager(0L,2L);
+        assertEquals(0L,manager.reserve(0L));
+        assertEquals(1L,manager.reserve(1L));
+        assertEquals(2L,manager.reserve(2L));
+        try
+        {
+            manager.reserve(0L);
+            fail("Exception expected");
+        }
+        catch(IllegalStateException e)
+        {
+            // expected
+        }
+        try
+        {
+            manager.reserve(1L);
+            fail("Exception expected");
+        }
+        catch(IllegalStateException e)
+        {
+            // expected
+        }
+        try
+        {
+            manager.reserve(2L);
+            fail("Exception expected");
+        }
+        catch(IllegalStateException e)
+        {
+            // expected
+        }
+    }
+
+    public void testReserveNew()
+    {
+        IdentifierManager manager = new IdentifierManager(10L,12L);
+        assertSame(10L,manager.reserveNew());
+        assertSame(11L,manager.reserveNew());
+        assertSame(12L,manager.reserveNew());
+        try {
+            manager.reserveNew();
+            fail("IllegalStateException expected");
+        }
+        catch (IllegalStateException e)
+        {
+            // expected
+        }
+    }
+    
+    public void testRelease() {
+        IdentifierManager manager = new IdentifierManager(10L,20L);
+        assertEquals(10L,manager.reserve(10L));
+        assertEquals(11L,manager.reserve(11L));
+        assertEquals(12L,manager.reserve(12L));
+        assertEquals(13L,manager.reserve(13L));
+        assertEquals(14L,manager.reserve(14L));
+        
+        assertTrue(manager.release(10L));
+        assertEquals(10L,manager.reserve(10L));
+        assertTrue(manager.release(10L));
+        
+        assertTrue(manager.release(11L));
+        assertEquals(11L,manager.reserve(11L));
+        assertTrue(manager.release(11L));
+        assertFalse(manager.release(11L));
+        assertFalse(manager.release(10L));
+
+        assertEquals(10L,manager.reserve(10L));
+        assertEquals(11L,manager.reserve(11L));
+        assertTrue(manager.release(12L));
+    }
+}
diff --git a/src/ooxml/testcases/org/apache/poi/xwpf/AllXWPFTests.java b/src/ooxml/testcases/org/apache/poi/xwpf/AllXWPFTests.java
index b0d9513be6..b1a9983f75 100644
--- a/src/ooxml/testcases/org/apache/poi/xwpf/AllXWPFTests.java
+++ b/src/ooxml/testcases/org/apache/poi/xwpf/AllXWPFTests.java
@@ -22,6 +22,7 @@ import junit.framework.TestSuite;
 
 import org.apache.poi.xwpf.extractor.TestXWPFWordExtractor;
 import org.apache.poi.xwpf.model.TestXWPFHeaderFooterPolicy;
+import org.apache.poi.xwpf.usermodel.TestXWPFDocument;
 import org.apache.poi.xwpf.usermodel.TestXWPFHeader;
 import org.apache.poi.xwpf.usermodel.TestXWPFHeadings;
 import org.apache.poi.xwpf.usermodel.TestXWPFNumbering;
diff --git a/src/ooxml/testcases/org/apache/poi/xwpf/TestAllExtendedProperties.java b/src/ooxml/testcases/org/apache/poi/xwpf/TestAllExtendedProperties.java
index 8b7b08b7c9..c04f73c934 100644
--- a/src/ooxml/testcases/org/apache/poi/xwpf/TestAllExtendedProperties.java
+++ b/src/ooxml/testcases/org/apache/poi/xwpf/TestAllExtendedProperties.java
@@ -17,6 +17,8 @@
 
 package org.apache.poi.xwpf;
 
+import java.io.IOException;
+
 import junit.framework.TestCase;
 
 import org.apache.poi.POIXMLProperties.CoreProperties;
@@ -41,7 +43,7 @@ import org.openxmlformats.schemas.officeDocument.x2006.extendedProperties.CTVect
  * 
  */
 public final class TestAllExtendedProperties extends TestCase {
-	public void testGetAllExtendedProperties() {
+	public void testGetAllExtendedProperties() throws IOException {
 		XWPFDocument doc = XWPFTestDataSamples.openSampleDocument("TestPoiXMLDocumentCorePropertiesGetKeywords.docx");
 		CTProperties ctProps = doc.getProperties().getExtendedProperties().getUnderlyingProperties();
 		assertEquals("Microsoft Office Word",ctProps.getApplication());
diff --git a/src/ooxml/testcases/org/apache/poi/xwpf/TestPackageCorePropertiesGetKeywords.java b/src/ooxml/testcases/org/apache/poi/xwpf/TestPackageCorePropertiesGetKeywords.java
index 5c150bdf1d..4b37623df1 100644
--- a/src/ooxml/testcases/org/apache/poi/xwpf/TestPackageCorePropertiesGetKeywords.java
+++ b/src/ooxml/testcases/org/apache/poi/xwpf/TestPackageCorePropertiesGetKeywords.java
@@ -17,6 +17,8 @@
 
 package org.apache.poi.xwpf;
 
+import java.io.IOException;
+
 import junit.framework.TestCase;
 
 import org.apache.poi.POIXMLProperties.CoreProperties;
@@ -37,7 +39,7 @@ import org.apache.poi.xwpf.usermodel.XWPFDocument;
  * 
  */
 public final class TestPackageCorePropertiesGetKeywords extends TestCase {
-	public void testGetSetKeywords() {
+	public void testGetSetKeywords() throws IOException {
 		XWPFDocument doc = XWPFTestDataSamples.openSampleDocument("TestPoiXMLDocumentCorePropertiesGetKeywords.docx");
 		String keywords = doc.getProperties().getCoreProperties().getKeywords();
 		assertEquals("extractor, test, rdf", keywords);
diff --git a/src/ooxml/testcases/org/apache/poi/xwpf/TestXWPFDocument.java b/src/ooxml/testcases/org/apache/poi/xwpf/TestXWPFDocument.java
deleted file mode 100644
index 7b6af4ee94..0000000000
--- a/src/ooxml/testcases/org/apache/poi/xwpf/TestXWPFDocument.java
+++ /dev/null
@@ -1,245 +0,0 @@
-/* ====================================================================
-   Licensed to the Apache Software Foundation (ASF) under one or more
-   contributor license agreements.  See the NOTICE file distributed with
-   this work for additional information regarding copyright ownership.
-   The ASF licenses this file to You under the Apache License, Version 2.0
-   (the "License"); you may not use this file except in compliance with
-   the License.  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-   Unless required by applicable law or agreed to in writing, software
-   distributed under the License is distributed on an "AS IS" BASIS,
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-   See the License for the specific language governing permissions and
-   limitations under the License.
-==================================================================== */
-
-package org.apache.poi.xwpf;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import junit.framework.TestCase;
-
-import org.apache.poi.POIXMLDocumentPart;
-import org.apache.poi.POIXMLProperties;
-import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
-import org.apache.poi.openxml4j.opc.OPCPackage;
-import org.apache.poi.openxml4j.opc.PackagePart;
-import org.apache.poi.xwpf.usermodel.XWPFDocument;
-import org.apache.poi.xwpf.usermodel.XWPFParagraph;
-import org.apache.poi.xwpf.usermodel.XWPFPictureData;
-import org.apache.poi.xwpf.usermodel.XWPFRelation;
-import org.apache.xmlbeans.XmlCursor;
-import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTP;
-
-public final class TestXWPFDocument extends TestCase {
-
-	public void testContainsMainContentType() throws Exception {
-		XWPFDocument doc = XWPFTestDataSamples.openSampleDocument("sample.docx");
-		OPCPackage pack = doc.getPackage();
-
-		boolean found = false;
-		for(PackagePart part : pack.getParts()) {
-			if(part.getContentType().equals(XWPFRelation.DOCUMENT.getContentType())) {
-				found = true;
-			}
-			if (false) {
-				// successful tests should be silent
-				System.out.println(part);
-			}
-		}
-		assertTrue(found);
-	}
-
-	public void testOpen() throws Exception {
-		XWPFDocument xml;
-
-		// Simple file
-		xml = XWPFTestDataSamples.openSampleDocument("sample.docx");
-		// Check it has key parts
-		assertNotNull(xml.getDocument());
-		assertNotNull(xml.getDocument().getBody());
-		assertNotNull(xml.getStyle());
-
-		// Complex file
-		xml = XWPFTestDataSamples.openSampleDocument("IllustrativeCases.docx");
-		assertNotNull(xml.getDocument());
-		assertNotNull(xml.getDocument().getBody());
-		assertNotNull(xml.getStyle());
-	}
-
-	public void testMetadataBasics() {
-		XWPFDocument xml = XWPFTestDataSamples.openSampleDocument("sample.docx");
-		assertNotNull(xml.getProperties().getCoreProperties());
-		assertNotNull(xml.getProperties().getExtendedProperties());
-
-		assertEquals("Microsoft Office Word", xml.getProperties().getExtendedProperties().getUnderlyingProperties().getApplication());
-		assertEquals(1315, xml.getProperties().getExtendedProperties().getUnderlyingProperties().getCharacters());
-		assertEquals(10, xml.getProperties().getExtendedProperties().getUnderlyingProperties().getLines());
-
-		assertEquals(null, xml.getProperties().getCoreProperties().getTitle());
-		assertEquals(null, xml.getProperties().getCoreProperties().getUnderlyingProperties().getSubjectProperty().getValue());
-	}
-
-	public void testMetadataComplex() {
-		XWPFDocument xml = XWPFTestDataSamples.openSampleDocument("IllustrativeCases.docx");
-		assertNotNull(xml.getProperties().getCoreProperties());
-		assertNotNull(xml.getProperties().getExtendedProperties());
-
-		assertEquals("Microsoft Office Outlook", xml.getProperties().getExtendedProperties().getUnderlyingProperties().getApplication());
-		assertEquals(5184, xml.getProperties().getExtendedProperties().getUnderlyingProperties().getCharacters());
-		assertEquals(0, xml.getProperties().getExtendedProperties().getUnderlyingProperties().getLines());
-
-		assertEquals(" ", xml.getProperties().getCoreProperties().getTitle());
-		assertEquals(" ", xml.getProperties().getCoreProperties().getUnderlyingProperties().getSubjectProperty().getValue());
-	}
-
-	public void testWorkbookProperties() {
-		XWPFDocument doc = new XWPFDocument();
-		POIXMLProperties props = doc.getProperties();
-		assertNotNull(props);
-		assertEquals("Apache POI", props.getExtendedProperties().getUnderlyingProperties().getApplication());
-	}
-	
-	public void testAddParagraph(){
-	   XWPFDocument doc = XWPFTestDataSamples.openSampleDocument("sample.docx");
-	   assertEquals(3, doc.getParagraphs().size());
-
-	   XWPFParagraph p = doc.createParagraph();
-	   assertEquals(p, doc.getParagraphs().get(3));
-	   assertEquals(4, doc.getParagraphs().size());
-	   
-	   assertEquals(3, doc.getParagraphPos(3));
-      assertEquals(3, doc.getPosOfParagraph(p));
-
-	   CTP ctp = p.getCTP();
-	   XWPFParagraph newP = doc.getParagraph(ctp);
-	   assertSame(p, newP);
-	   XmlCursor cursor = doc.getDocument().getBody().getPArray(0).newCursor();
-	   XWPFParagraph cP = doc.insertNewParagraph(cursor);
-	   assertSame(cP, doc.getParagraphs().get(0));
-	   assertEquals(5, doc.getParagraphs().size());
-	}
-	
-	public void testAddPicture(){
-		XWPFDocument doc = XWPFTestDataSamples.openSampleDocument("sample.docx");
-		byte[] jpeg = "This is a jpeg".getBytes();
-		try {
-			int jpegNum = doc.addPicture(jpeg, XWPFDocument.PICTURE_TYPE_JPEG);
-			byte[] newJpeg = doc.getAllPictures().get(jpegNum).getData();
-			assertEquals(newJpeg.length, jpeg.length);
-			for(int i = 0 ; i < jpeg.length; i++){
-				assertEquals(newJpeg[i], jpeg[i]); 
-			}
-		} catch (InvalidFormatException e) {
-			// TODO Auto-generated catch block
-			e.printStackTrace();
-		}
-	}
-	
-	public void testRemoveBodyElement() {
-	   XWPFDocument doc = XWPFTestDataSamples.openSampleDocument("sample.docx");
-	   assertEquals(3, doc.getParagraphs().size());
-      assertEquals(3, doc.getBodyElements().size());
-      
-      XWPFParagraph p1 = doc.getParagraphs().get(0);
-      XWPFParagraph p2 = doc.getParagraphs().get(1);
-      XWPFParagraph p3 = doc.getParagraphs().get(2);
-      
-      assertEquals(p1, doc.getBodyElements().get(0));
-      assertEquals(p1, doc.getParagraphs().get(0));
-      assertEquals(p2, doc.getBodyElements().get(1));
-      assertEquals(p2, doc.getParagraphs().get(1));
-      assertEquals(p3, doc.getBodyElements().get(2));
-      assertEquals(p3, doc.getParagraphs().get(2));
-      
-      // Add another
-      XWPFParagraph p4 = doc.createParagraph();
-      
-      assertEquals(4, doc.getParagraphs().size());
-      assertEquals(4, doc.getBodyElements().size());
-      assertEquals(p1, doc.getBodyElements().get(0));
-      assertEquals(p1, doc.getParagraphs().get(0));
-      assertEquals(p2, doc.getBodyElements().get(1));
-      assertEquals(p2, doc.getParagraphs().get(1));
-      assertEquals(p3, doc.getBodyElements().get(2));
-      assertEquals(p3, doc.getParagraphs().get(2));
-      assertEquals(p4, doc.getBodyElements().get(3));
-      assertEquals(p4, doc.getParagraphs().get(3));
-      
-      // Remove the 2nd
-      assertEquals(true, doc.removeBodyElement(1));
-      assertEquals(3, doc.getParagraphs().size());
-      assertEquals(3, doc.getBodyElements().size());
-      
-      assertEquals(p1, doc.getBodyElements().get(0));
-      assertEquals(p1, doc.getParagraphs().get(0));
-      assertEquals(p3, doc.getBodyElements().get(1));
-      assertEquals(p3, doc.getParagraphs().get(1));
-      assertEquals(p4, doc.getBodyElements().get(2));
-      assertEquals(p4, doc.getParagraphs().get(2));
-      
-      // Remove the 1st
-      assertEquals(true, doc.removeBodyElement(0));
-      assertEquals(2, doc.getParagraphs().size());
-      assertEquals(2, doc.getBodyElements().size());
-      
-      assertEquals(p3, doc.getBodyElements().get(0));
-      assertEquals(p3, doc.getParagraphs().get(0));
-      assertEquals(p4, doc.getBodyElements().get(1));
-      assertEquals(p4, doc.getParagraphs().get(1));
-      
-      // Remove the last
-      assertEquals(true, doc.removeBodyElement(1));
-      assertEquals(1, doc.getParagraphs().size());
-      assertEquals(1, doc.getBodyElements().size());
-      
-      assertEquals(p3, doc.getBodyElements().get(0));
-      assertEquals(p3, doc.getParagraphs().get(0));
-	}
-	
-	public void testSettings() throws Exception {
-	   XWPFDocument doc = XWPFTestDataSamples.openSampleDocument("WithGIF.docx");
-	   assertEquals(120, doc.getZoomPercent());
-	   assertEquals(false, doc.isEnforcedCommentsProtection());
-      assertEquals(false, doc.isEnforcedFillingFormsProtection());
-      assertEquals(false, doc.isEnforcedReadonlyProtection());
-      assertEquals(false, doc.isEnforcedTrackedChangesProtection());
-      
-      doc.setZoomPercent(124);
-      
-      // Only one enforcement allowed, last one wins!
-      doc.enforceFillingFormsProtection();
-      doc.enforceReadonlyProtection();
-      
-      doc = XWPFTestDataSamples.writeOutAndReadBack(doc);
-      
-      assertEquals(124, doc.getZoomPercent());
-      assertEquals(false, doc.isEnforcedCommentsProtection());
-      assertEquals(false, doc.isEnforcedFillingFormsProtection());
-      assertEquals(true, doc.isEnforcedReadonlyProtection());
-      assertEquals(false, doc.isEnforcedTrackedChangesProtection());
-	}
-	
-	public void testGIFSupport() throws Exception {
-	    XWPFDocument doc = XWPFTestDataSamples.openSampleDocument("WithGIF.docx");
-	    ArrayList gifParts = doc.getPackage().getPartsByContentType(XWPFRelation.IMAGE_GIF.getContentType());
-	    assertEquals("Expected exactly one GIF part in package.",1,gifParts.size());
-	    PackagePart gifPart = gifParts.get(0);
-	    
-	    List relations = doc.getRelations();
-	    POIXMLDocumentPart gifDocPart = null;
-	    for (POIXMLDocumentPart docPart : relations)
-        {
-            if (gifPart == docPart.getPackagePart())
-            {
-                assertNull("More than one POIXMLDocumentPart for GIF PackagePart.",gifDocPart);
-                gifDocPart = docPart;
-            }
-        }
-	    assertNotNull("GIF part not related to document.xml PackagePart",gifDocPart);
-	    assertTrue("XWPFRelation for GIF image was not recognized properly, as the POIXMLDocumentPart created was of a wrong type.",XWPFRelation.IMAGE_GIF.getRelationClass().isInstance(gifDocPart));
-	}
-}
diff --git a/src/ooxml/testcases/org/apache/poi/xwpf/XWPFTestDataSamples.java b/src/ooxml/testcases/org/apache/poi/xwpf/XWPFTestDataSamples.java
index d88c029cdb..a6028eb49a 100644
--- a/src/ooxml/testcases/org/apache/poi/xwpf/XWPFTestDataSamples.java
+++ b/src/ooxml/testcases/org/apache/poi/xwpf/XWPFTestDataSamples.java
@@ -22,6 +22,8 @@ import java.io.IOException;
 import java.io.InputStream;
 
 import org.apache.poi.POIDataSamples;
+import org.apache.poi.util.IOUtils;
+import org.apache.poi.util.PackageHelper;
 import org.apache.poi.xwpf.usermodel.XWPFDocument;
 
 /**
@@ -29,23 +31,21 @@ import org.apache.poi.xwpf.usermodel.XWPFDocument;
  */
 public class XWPFTestDataSamples {
 
-    public static XWPFDocument openSampleDocument(String sampleName) {
+    public static XWPFDocument openSampleDocument(String sampleName) throws IOException {
         InputStream is = POIDataSamples.getDocumentInstance().openResourceAsStream(sampleName);
-        try {
-            return new XWPFDocument(is);
-        } catch (IOException e) {
-            throw new RuntimeException(e);
-        }
+        return new XWPFDocument(is);
     }
 
-    public static XWPFDocument writeOutAndReadBack(XWPFDocument doc) {
-        try {
-            ByteArrayOutputStream baos = new ByteArrayOutputStream(4096);
-            doc.write(baos);
-            ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
-            return new XWPFDocument(bais);
-        } catch (IOException e) {
-            throw new RuntimeException(e);
-        }
+    public static XWPFDocument writeOutAndReadBack(XWPFDocument doc) throws IOException {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream(4096);
+        doc.write(baos);
+        ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
+        return new XWPFDocument(bais);
+    }
+
+    public static byte[] getImage(String filename) throws IOException {
+        InputStream is = POIDataSamples.getDocumentInstance().openResourceAsStream(filename);
+        byte[] result = IOUtils.toByteArray(is);
+        return result;
     }
 }
diff --git a/src/ooxml/testcases/org/apache/poi/xwpf/extractor/TestXWPFWordExtractor.java b/src/ooxml/testcases/org/apache/poi/xwpf/extractor/TestXWPFWordExtractor.java
index d1cc2eea53..57a67eab6b 100644
--- a/src/ooxml/testcases/org/apache/poi/xwpf/extractor/TestXWPFWordExtractor.java
+++ b/src/ooxml/testcases/org/apache/poi/xwpf/extractor/TestXWPFWordExtractor.java
@@ -17,6 +17,8 @@
 
 package org.apache.poi.xwpf.extractor;
 
+import java.io.IOException;
+
 import junit.framework.TestCase;
 
 import org.apache.poi.xwpf.XWPFTestDataSamples;
@@ -29,8 +31,9 @@ public class TestXWPFWordExtractor extends TestCase {
 
     /**
      * Get text out of the simple file
+     * @throws IOException 
      */
-    public void testGetSimpleText() {
+    public void testGetSimpleText() throws IOException {
         XWPFDocument doc = XWPFTestDataSamples.openSampleDocument("sample.docx");
         XWPFWordExtractor extractor = new XWPFWordExtractor(doc);
 
@@ -58,8 +61,9 @@ public class TestXWPFWordExtractor extends TestCase {
 
     /**
      * Tests getting the text out of a complex file
+     * @throws IOException 
      */
-    public void testGetComplexText() {
+    public void testGetComplexText() throws IOException {
         XWPFDocument doc = XWPFTestDataSamples.openSampleDocument("IllustrativeCases.docx");
         XWPFWordExtractor extractor = new XWPFWordExtractor(doc);
 
@@ -91,7 +95,7 @@ public class TestXWPFWordExtractor extends TestCase {
         assertEquals(134, ps);
     }
 
-    public void testGetWithHyperlinks() {
+    public void testGetWithHyperlinks() throws IOException {
         XWPFDocument doc = XWPFTestDataSamples.openSampleDocument("TestDocument.docx");
         XWPFWordExtractor extractor = new XWPFWordExtractor(doc);
 
@@ -116,7 +120,7 @@ public class TestXWPFWordExtractor extends TestCase {
         );
     }
 
-    public void testHeadersFooters() {
+    public void testHeadersFooters() throws IOException {
         XWPFDocument doc = XWPFTestDataSamples.openSampleDocument("ThreeColHeadFoot.docx");
         XWPFWordExtractor extractor = new XWPFWordExtractor(doc);
 
@@ -159,7 +163,7 @@ public class TestXWPFWordExtractor extends TestCase {
         );
     }
 
-    public void testFootnotes() {
+    public void testFootnotes() throws IOException {
         XWPFDocument doc = XWPFTestDataSamples.openSampleDocument("footnotes.docx");
         XWPFWordExtractor extractor = new XWPFWordExtractor(doc);
 
@@ -167,14 +171,14 @@ public class TestXWPFWordExtractor extends TestCase {
     }
 
 
-    public void testTableFootnotes() {
+    public void testTableFootnotes() throws IOException {
         XWPFDocument doc = XWPFTestDataSamples.openSampleDocument("table_footnotes.docx");
         XWPFWordExtractor extractor = new XWPFWordExtractor(doc);
 
         assertTrue(extractor.getText().contains("snoska"));
     }
 
-    public void testFormFootnotes() {
+    public void testFormFootnotes() throws IOException {
         XWPFDocument doc = XWPFTestDataSamples.openSampleDocument("form_footnotes.docx");
         XWPFWordExtractor extractor = new XWPFWordExtractor(doc);
 
@@ -183,14 +187,14 @@ public class TestXWPFWordExtractor extends TestCase {
         assertTrue("Unable to find expected word in text\n" + text, text.contains("test phrase"));
     }
 
-    public void testEndnotes() {
+    public void testEndnotes() throws IOException {
         XWPFDocument doc = XWPFTestDataSamples.openSampleDocument("endnotes.docx");
         XWPFWordExtractor extractor = new XWPFWordExtractor(doc);
 
         assertTrue(extractor.getText().contains("XXX"));
     }
 
-    public void testInsertedDeletedText() {
+    public void testInsertedDeletedText() throws IOException {
         XWPFDocument doc = XWPFTestDataSamples.openSampleDocument("delins.docx");
         XWPFWordExtractor extractor = new XWPFWordExtractor(doc);
 
@@ -198,7 +202,7 @@ public class TestXWPFWordExtractor extends TestCase {
         assertTrue(extractor.getText().contains("extremely well"));
     }
 
-    public void testParagraphHeader() {
+    public void testParagraphHeader() throws IOException {
         XWPFDocument doc = XWPFTestDataSamples.openSampleDocument("Headers.docx");
         XWPFWordExtractor extractor = new XWPFWordExtractor(doc);
 
@@ -210,8 +214,9 @@ public class TestXWPFWordExtractor extends TestCase {
     /**
      * Test that we can open and process .docm
      *  (macro enabled) docx files (bug #45690)
+     * @throws IOException 
      */
-    public void testDOCMFiles() {
+    public void testDOCMFiles() throws IOException {
         XWPFDocument doc = XWPFTestDataSamples.openSampleDocument("45690.docm");
         XWPFWordExtractor extractor = new XWPFWordExtractor(doc);
 
@@ -224,8 +229,9 @@ public class TestXWPFWordExtractor extends TestCase {
      * Test that we handle things like tabs and
      *  carriage returns properly in the text that
      *  we're extracting (bug #49189)
+     * @throws IOException 
      */
-    public void testDocTabs() {
+    public void testDocTabs() throws IOException {
        XWPFDocument doc = XWPFTestDataSamples.openSampleDocument("WithTabs.docx");
        XWPFWordExtractor extractor = new XWPFWordExtractor(doc);
  
@@ -241,8 +247,9 @@ public class TestXWPFWordExtractor extends TestCase {
     /**
      * The output should not contain field codes, e.g. those specified in the
      * w:instrText tag (spec sec. 17.16.23)
+     * @throws IOException 
      */
-    public void testNoFieldCodes() {
+    public void testNoFieldCodes() throws IOException {
         XWPFDocument doc = XWPFTestDataSamples.openSampleDocument("FieldCodes.docx");
         XWPFWordExtractor extractor = new XWPFWordExtractor(doc);
         String text = extractor.getText();
@@ -254,8 +261,9 @@ public class TestXWPFWordExtractor extends TestCase {
     /**
      * The output should contain the values of simple fields, those specified
      * with the fldSimple element (spec sec. 17.16.19)
+     * @throws IOException 
      */
-    public void testFldSimpleContent() {
+    public void testFldSimpleContent() throws IOException {
         XWPFDocument doc = XWPFTestDataSamples.openSampleDocument("FldSimple.docx");
         XWPFWordExtractor extractor = new XWPFWordExtractor(doc);
         String text = extractor.getText();
diff --git a/src/ooxml/testcases/org/apache/poi/xwpf/model/TestXWPFDecorators.java b/src/ooxml/testcases/org/apache/poi/xwpf/model/TestXWPFDecorators.java
index 3f51403898..7c96a94660 100644
--- a/src/ooxml/testcases/org/apache/poi/xwpf/model/TestXWPFDecorators.java
+++ b/src/ooxml/testcases/org/apache/poi/xwpf/model/TestXWPFDecorators.java
@@ -17,6 +17,8 @@
 
 package org.apache.poi.xwpf.model;
 
+import java.io.IOException;
+
 import junit.framework.TestCase;
 
 import org.apache.poi.xwpf.XWPFTestDataSamples;
@@ -32,10 +34,10 @@ public class TestXWPFDecorators extends TestCase {
    private XWPFDocument hyperlink;
    private XWPFDocument comments;
 
-   protected void setUp() {
-      simple = XWPFTestDataSamples.openSampleDocument("SampleDoc.docx");
-      hyperlink = XWPFTestDataSamples.openSampleDocument("TestDocument.docx");
-      comments = XWPFTestDataSamples.openSampleDocument("WordWithAttachments.docx");
+   protected void setUp() throws IOException {
+        simple = XWPFTestDataSamples.openSampleDocument("SampleDoc.docx");
+        hyperlink = XWPFTestDataSamples.openSampleDocument("TestDocument.docx");
+        comments = XWPFTestDataSamples.openSampleDocument("WordWithAttachments.docx");
    }
 
    public void testHyperlink() {
diff --git a/src/ooxml/testcases/org/apache/poi/xwpf/model/TestXWPFHeaderFooterPolicy.java b/src/ooxml/testcases/org/apache/poi/xwpf/model/TestXWPFHeaderFooterPolicy.java
index 86b22466bc..7d70866e89 100644
--- a/src/ooxml/testcases/org/apache/poi/xwpf/model/TestXWPFHeaderFooterPolicy.java
+++ b/src/ooxml/testcases/org/apache/poi/xwpf/model/TestXWPFHeaderFooterPolicy.java
@@ -17,6 +17,8 @@
 
 package org.apache.poi.xwpf.model;
 
+import java.io.IOException;
+
 import junit.framework.TestCase;
 
 import org.apache.poi.xwpf.XWPFTestDataSamples;
@@ -33,9 +35,9 @@ public class TestXWPFHeaderFooterPolicy extends TestCase {
 	private XWPFDocument oddEven;
 	private XWPFDocument diffFirst;
 
-	protected void setUp() {
+	protected void setUp() throws IOException {
 
-		noHeader = XWPFTestDataSamples.openSampleDocument("NoHeadFoot.docx");
+	    noHeader = XWPFTestDataSamples.openSampleDocument("NoHeadFoot.docx");
 		header = XWPFTestDataSamples.openSampleDocument("ThreeColHead.docx");
 		headerFooter = XWPFTestDataSamples.openSampleDocument("SimpleHeadThreeColFoot.docx");
 		footer = XWPFTestDataSamples.openSampleDocument("FancyFoot.docx");
diff --git a/src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFDocument.java b/src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFDocument.java
new file mode 100644
index 0000000000..9828af1bb8
--- /dev/null
+++ b/src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFDocument.java
@@ -0,0 +1,319 @@
+/* ====================================================================
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+==================================================================== */
+
+package org.apache.poi.xwpf.usermodel;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Arrays;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+import org.apache.poi.POIXMLDocumentPart;
+import org.apache.poi.POIXMLProperties;
+import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
+import org.apache.poi.openxml4j.opc.OPCPackage;
+import org.apache.poi.openxml4j.opc.PackagePart;
+import org.apache.poi.openxml4j.opc.PackagePartName;
+import org.apache.poi.openxml4j.opc.PackageRelationship;
+import org.apache.poi.openxml4j.opc.PackagingURIHelper;
+import org.apache.poi.openxml4j.opc.TargetMode;
+import org.apache.poi.xwpf.XWPFTestDataSamples;
+import org.apache.xmlbeans.XmlCursor;
+import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTP;
+
+public final class TestXWPFDocument extends TestCase {
+
+	public void testContainsMainContentType() throws Exception {
+		XWPFDocument doc = XWPFTestDataSamples.openSampleDocument("sample.docx");
+		OPCPackage pack = doc.getPackage();
+
+		boolean found = false;
+		for(PackagePart part : pack.getParts()) {
+			if(part.getContentType().equals(XWPFRelation.DOCUMENT.getContentType())) {
+				found = true;
+			}
+			if (false) {
+				// successful tests should be silent
+				System.out.println(part);
+			}
+		}
+		assertTrue(found);
+	}
+
+	public void testOpen() throws Exception {
+		XWPFDocument xml;
+
+		// Simple file
+		xml = XWPFTestDataSamples.openSampleDocument("sample.docx");
+		// Check it has key parts
+		assertNotNull(xml.getDocument());
+		assertNotNull(xml.getDocument().getBody());
+		assertNotNull(xml.getStyle());
+
+		// Complex file
+		xml = XWPFTestDataSamples.openSampleDocument("IllustrativeCases.docx");
+		assertNotNull(xml.getDocument());
+		assertNotNull(xml.getDocument().getBody());
+		assertNotNull(xml.getStyle());
+	}
+
+	public void testMetadataBasics() throws IOException {
+		XWPFDocument xml = XWPFTestDataSamples.openSampleDocument("sample.docx");
+		assertNotNull(xml.getProperties().getCoreProperties());
+		assertNotNull(xml.getProperties().getExtendedProperties());
+
+		assertEquals("Microsoft Office Word", xml.getProperties().getExtendedProperties().getUnderlyingProperties().getApplication());
+		assertEquals(1315, xml.getProperties().getExtendedProperties().getUnderlyingProperties().getCharacters());
+		assertEquals(10, xml.getProperties().getExtendedProperties().getUnderlyingProperties().getLines());
+
+		assertEquals(null, xml.getProperties().getCoreProperties().getTitle());
+		assertEquals(null, xml.getProperties().getCoreProperties().getUnderlyingProperties().getSubjectProperty().getValue());
+	}
+
+	public void testMetadataComplex() throws IOException {
+		XWPFDocument xml = XWPFTestDataSamples.openSampleDocument("IllustrativeCases.docx");
+		assertNotNull(xml.getProperties().getCoreProperties());
+		assertNotNull(xml.getProperties().getExtendedProperties());
+
+		assertEquals("Microsoft Office Outlook", xml.getProperties().getExtendedProperties().getUnderlyingProperties().getApplication());
+		assertEquals(5184, xml.getProperties().getExtendedProperties().getUnderlyingProperties().getCharacters());
+		assertEquals(0, xml.getProperties().getExtendedProperties().getUnderlyingProperties().getLines());
+
+		assertEquals(" ", xml.getProperties().getCoreProperties().getTitle());
+		assertEquals(" ", xml.getProperties().getCoreProperties().getUnderlyingProperties().getSubjectProperty().getValue());
+	}
+
+	public void testWorkbookProperties() {
+		XWPFDocument doc = new XWPFDocument();
+		POIXMLProperties props = doc.getProperties();
+		assertNotNull(props);
+		assertEquals("Apache POI", props.getExtendedProperties().getUnderlyingProperties().getApplication());
+	}
+	
+	public void testAddParagraph() throws IOException{
+	   XWPFDocument doc = XWPFTestDataSamples.openSampleDocument("sample.docx");
+	   assertEquals(3, doc.getParagraphs().size());
+
+	   XWPFParagraph p = doc.createParagraph();
+	   assertEquals(p, doc.getParagraphs().get(3));
+	   assertEquals(4, doc.getParagraphs().size());
+	   
+	   assertEquals(3, doc.getParagraphPos(3));
+      assertEquals(3, doc.getPosOfParagraph(p));
+
+	   CTP ctp = p.getCTP();
+	   XWPFParagraph newP = doc.getParagraph(ctp);
+	   assertSame(p, newP);
+	   XmlCursor cursor = doc.getDocument().getBody().getPArray(0).newCursor();
+	   XWPFParagraph cP = doc.insertNewParagraph(cursor);
+	   assertSame(cP, doc.getParagraphs().get(0));
+	   assertEquals(5, doc.getParagraphs().size());
+	}
+
+    public void testAddPicture() throws IOException, InvalidFormatException
+    {
+        XWPFDocument doc = XWPFTestDataSamples.openSampleDocument("sample.docx");
+        byte[] jpeg = XWPFTestDataSamples.getImage("nature1.jpg");
+        String relationId = doc.addPictureData(jpeg,XWPFDocument.PICTURE_TYPE_JPEG);
+        
+        byte[] newJpeg = ((XWPFPictureData) doc.getRelationById(relationId)).getData();
+        assertEquals(newJpeg.length,jpeg.length);
+        for (int i = 0 ; i < jpeg.length ; i++)
+        {
+            assertEquals(newJpeg[i],jpeg[i]);
+        }
+    }
+
+	public void testRemoveBodyElement() throws IOException {
+	   XWPFDocument doc = XWPFTestDataSamples.openSampleDocument("sample.docx");
+	   assertEquals(3, doc.getParagraphs().size());
+      assertEquals(3, doc.getBodyElements().size());
+      
+      XWPFParagraph p1 = doc.getParagraphs().get(0);
+      XWPFParagraph p2 = doc.getParagraphs().get(1);
+      XWPFParagraph p3 = doc.getParagraphs().get(2);
+      
+      assertEquals(p1, doc.getBodyElements().get(0));
+      assertEquals(p1, doc.getParagraphs().get(0));
+      assertEquals(p2, doc.getBodyElements().get(1));
+      assertEquals(p2, doc.getParagraphs().get(1));
+      assertEquals(p3, doc.getBodyElements().get(2));
+      assertEquals(p3, doc.getParagraphs().get(2));
+      
+      // Add another
+      XWPFParagraph p4 = doc.createParagraph();
+      
+      assertEquals(4, doc.getParagraphs().size());
+      assertEquals(4, doc.getBodyElements().size());
+      assertEquals(p1, doc.getBodyElements().get(0));
+      assertEquals(p1, doc.getParagraphs().get(0));
+      assertEquals(p2, doc.getBodyElements().get(1));
+      assertEquals(p2, doc.getParagraphs().get(1));
+      assertEquals(p3, doc.getBodyElements().get(2));
+      assertEquals(p3, doc.getParagraphs().get(2));
+      assertEquals(p4, doc.getBodyElements().get(3));
+      assertEquals(p4, doc.getParagraphs().get(3));
+      
+      // Remove the 2nd
+      assertEquals(true, doc.removeBodyElement(1));
+      assertEquals(3, doc.getParagraphs().size());
+      assertEquals(3, doc.getBodyElements().size());
+      
+      assertEquals(p1, doc.getBodyElements().get(0));
+      assertEquals(p1, doc.getParagraphs().get(0));
+      assertEquals(p3, doc.getBodyElements().get(1));
+      assertEquals(p3, doc.getParagraphs().get(1));
+      assertEquals(p4, doc.getBodyElements().get(2));
+      assertEquals(p4, doc.getParagraphs().get(2));
+      
+      // Remove the 1st
+      assertEquals(true, doc.removeBodyElement(0));
+      assertEquals(2, doc.getParagraphs().size());
+      assertEquals(2, doc.getBodyElements().size());
+      
+      assertEquals(p3, doc.getBodyElements().get(0));
+      assertEquals(p3, doc.getParagraphs().get(0));
+      assertEquals(p4, doc.getBodyElements().get(1));
+      assertEquals(p4, doc.getParagraphs().get(1));
+      
+      // Remove the last
+      assertEquals(true, doc.removeBodyElement(1));
+      assertEquals(1, doc.getParagraphs().size());
+      assertEquals(1, doc.getBodyElements().size());
+      
+      assertEquals(p3, doc.getBodyElements().get(0));
+      assertEquals(p3, doc.getParagraphs().get(0));
+	}
+	
+	public void testRegisterPackagePictureData() throws IOException, InvalidFormatException {
+	    XWPFDocument doc = XWPFTestDataSamples.openSampleDocument("issue_51265_1.docx");
+	    
+	    /* manually assemble a new image package part*/
+	    OPCPackage opcPckg = doc.getPackage();
+	    XWPFRelation jpgRelation = XWPFRelation.IMAGE_JPEG;
+	    PackagePartName partName = PackagingURIHelper.createPartName(jpgRelation.getDefaultFileName().replace('#', '2'));
+        PackagePart newImagePart = opcPckg.createPart(partName, jpgRelation.getContentType());
+        byte[] nature1 = XWPFTestDataSamples.getImage("abstract4.jpg");
+        OutputStream os = newImagePart.getOutputStream();
+        os.write(nature1);
+	    os.close();
+	    XWPFHeader xwpfHeader = doc.getHeaderList().get(0);
+	    PackageRelationship relationship = xwpfHeader.getPackagePart().addRelationship(partName, TargetMode.INTERNAL, jpgRelation.getRelation());
+	    XWPFPictureData newPicData = new XWPFPictureData(newImagePart,relationship);
+	    /* new part is now ready to rumble */
+	    
+	    assertFalse(xwpfHeader.getAllPictures().contains(newPicData));
+	    assertFalse(doc.getAllPictures().contains(newPicData));
+	    assertFalse(doc.getAllPackagePictures().contains(newPicData));
+
+	    doc.registerPackagePictureData(newPicData);
+	    
+	    assertFalse(xwpfHeader.getAllPictures().contains(newPicData));
+	    assertFalse(doc.getAllPictures().contains(newPicData));
+	    assertTrue(doc.getAllPackagePictures().contains(newPicData));
+	    
+	    doc.getPackage().revert();
+	}
+
+	public void testFindPackagePictureData() throws IOException {
+	    XWPFDocument doc = XWPFTestDataSamples.openSampleDocument("issue_51265_1.docx");
+	    byte[] nature1 = XWPFTestDataSamples.getImage("nature1.gif");
+	    XWPFPictureData part = doc.findPackagePictureData(nature1, Document.PICTURE_TYPE_GIF);
+	    assertNotNull(part);
+	    assertTrue(doc.getAllPictures().contains(part));
+	    assertTrue(doc.getAllPackagePictures().contains(part));
+	    doc.getPackage().revert();
+	}
+	
+	public void testGetAllPictures() throws IOException {
+	    XWPFDocument doc = XWPFTestDataSamples.openSampleDocument("issue_51265_3.docx");
+	    List allPictures = doc.getAllPictures();
+	    List allPackagePictures = doc.getAllPackagePictures();
+	    
+	    assertNotNull(allPictures);
+	    assertEquals(3,allPictures.size());
+	    for (XWPFPictureData xwpfPictureData : allPictures) {
+	        assertTrue(allPackagePictures.contains(xwpfPictureData));
+        }
+
+	    try {
+            allPictures.add(allPictures.get(0));
+            fail("This list must be unmodifiable!");
+        } catch (UnsupportedOperationException e) {
+            // all ok
+        }
+	    
+	    doc.getPackage().revert();
+	}
+
+	public void testGetAllPackagePictures() throws IOException {
+	       XWPFDocument doc = XWPFTestDataSamples.openSampleDocument("issue_51265_3.docx");
+	        List allPackagePictures = doc.getAllPackagePictures();
+	        
+	        assertNotNull(allPackagePictures);
+	        assertEquals(5,allPackagePictures.size());
+
+	        try {
+	            allPackagePictures.add(allPackagePictures.get(0));
+	            fail("This list must be unmodifiable!");
+	        } catch (UnsupportedOperationException e) {
+	            // all ok
+	        }
+	        
+	        doc.getPackage().revert();
+	}
+	
+	public void testPictureHandlingSimpleFile() throws IOException, InvalidFormatException {
+	    XWPFDocument doc = XWPFTestDataSamples.openSampleDocument("issue_51265_1.docx");
+	    assertEquals(1,doc.getAllPackagePictures().size());
+	    byte[] newPic = XWPFTestDataSamples.getImage("abstract4.jpg");
+	    String id1 = doc.addPictureData(newPic, Document.PICTURE_TYPE_JPEG);
+	    assertEquals(2,doc.getAllPackagePictures().size());
+	    /* copy data, to avoid instance-equality */
+	    byte[] newPicCopy = Arrays.copyOf(newPic, newPic.length);
+	    String id2 = doc.addPictureData(newPicCopy, Document.PICTURE_TYPE_JPEG);
+	    assertEquals(id1,id2);
+	    doc.getPackage().revert();
+	}
+	
+	public void testPictureHandlingHeaderDocumentImages() throws IOException {
+	    XWPFDocument doc = XWPFTestDataSamples.openSampleDocument("issue_51265_2.docx");
+	    assertEquals(1,doc.getAllPictures().size());
+	    assertEquals(1,doc.getAllPackagePictures().size());
+	    assertEquals(1,doc.getHeaderList().get(0).getAllPictures().size());
+	    doc.getPackage().revert();
+	}
+	
+	public void testPictureHandlingComplex() throws IOException, InvalidFormatException {
+	    XWPFDocument doc = XWPFTestDataSamples.openSampleDocument("issue_51265_3.docx");
+	    XWPFHeader xwpfHeader = doc.getHeaderList().get(0);
+
+	    assertEquals(3,doc.getAllPictures().size());
+        assertEquals(3,xwpfHeader.getAllPictures().size());
+	    assertEquals(5,doc.getAllPackagePictures().size());
+	    
+	    byte[] nature1 = XWPFTestDataSamples.getImage("nature1.jpg");
+	    String id = doc.addPictureData(nature1, Document.PICTURE_TYPE_JPEG);
+	    POIXMLDocumentPart part1 = xwpfHeader.getRelationById("rId1");
+	    XWPFPictureData part2 = (XWPFPictureData) doc.getRelationById(id);
+	    assertSame(part1,part2);
+	    
+	    doc.getPackage().revert();
+	}
+}
diff --git a/src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFHeader.java b/src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFHeader.java
index 738ca7af13..2a65e8e560 100644
--- a/src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFHeader.java
+++ b/src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFHeader.java
@@ -29,7 +29,7 @@ import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTText;
 
 public final class TestXWPFHeader extends TestCase {
 
-	public void testSimpleHeader() {
+	public void testSimpleHeader() throws IOException {
 		XWPFDocument sampleDoc = XWPFTestDataSamples.openSampleDocument("headerFooter.docx");
 
 		XWPFHeaderFooterPolicy policy = sampleDoc.getHeaderFooterPolicy();
@@ -40,7 +40,7 @@ public final class TestXWPFHeader extends TestCase {
 		assertNotNull(footer);
 	}
 
-    public void testImageInHeader() {
+    public void testImageInHeader() throws IOException {
         XWPFDocument sampleDoc = XWPFTestDataSamples.openSampleDocument("headerPic.docx");
 
         XWPFHeaderFooterPolicy policy = sampleDoc.getHeaderFooterPolicy();
@@ -110,7 +110,7 @@ public final class TestXWPFHeader extends TestCase {
 		// make sure that it contains two paragraphs of text and that
 		// both do hold what is expected.
 		footer = policy.getDefaultFooter();
-
+		
 		XWPFParagraph[] paras = new XWPFParagraph[footer.getParagraphs().size()];
 		int i=0;
 		for(XWPFParagraph p : footer.getParagraphs()) {
@@ -122,7 +122,7 @@ public final class TestXWPFHeader extends TestCase {
 		assertEquals("Second paragraph for the footer", paras[1].getText());
 	}
 
-	public void testSetWatermark() {
+	public void testSetWatermark() throws IOException {
 		XWPFDocument sampleDoc = XWPFTestDataSamples.openSampleDocument("SampleDoc.docx");
 		// no header is set (yet)
 		XWPFHeaderFooterPolicy policy = sampleDoc.getHeaderFooterPolicy();
@@ -136,4 +136,20 @@ public final class TestXWPFHeader extends TestCase {
 		assertNotNull(policy.getFirstPageHeader());
 		assertNotNull(policy.getEvenPageHeader());
 	}
+	
+	public void testAddPictureData() {
+	    
+	}
+	
+	public void testGetAllPictures() {
+	    
+	}
+	
+	public void testGetAllPackagePictures() {
+	    
+	}
+	
+	public void testGetPictureDataById() {
+	    
+	}
 }
diff --git a/src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFNumbering.java b/src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFNumbering.java
index 7990cd70d6..7ee5637394 100644
--- a/src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFNumbering.java
+++ b/src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFNumbering.java
@@ -17,6 +17,7 @@
 
 package org.apache.poi.xwpf.usermodel;
 
+import java.io.IOException;
 import java.math.BigInteger;
 
 import junit.framework.TestCase;
@@ -25,7 +26,7 @@ import org.apache.poi.xwpf.XWPFTestDataSamples;
 
 public class TestXWPFNumbering extends TestCase {
 	
-	public void testCompareAbstractNum(){
+	public void testCompareAbstractNum() throws IOException{
 		XWPFDocument doc = XWPFTestDataSamples.openSampleDocument("Numbering.docx");
 		XWPFNumbering numbering = doc.getNumbering();
 		BigInteger numId = BigInteger.valueOf(1);
diff --git a/src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFParagraph.java b/src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFParagraph.java
index bf1c430f25..cb414168fb 100644
--- a/src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFParagraph.java
+++ b/src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFParagraph.java
@@ -17,6 +17,7 @@
 
 package org.apache.poi.xwpf.usermodel;
 
+import java.io.IOException;
 import java.math.BigInteger;
 import java.util.List;
 
@@ -49,8 +50,9 @@ public final class TestXWPFParagraph extends TestCase {
 
     /**
      * Check that we get the right paragraph from the header
+     * @throws IOException 
      */
-    public void disabled_testHeaderParagraph() {
+    public void disabled_testHeaderParagraph() throws IOException {
         XWPFDocument xml = XWPFTestDataSamples.openSampleDocument("ThreeColHead.docx");
 
         XWPFHeader hdr = xml.getHeaderFooterPolicy().getDefaultHeader();
@@ -67,8 +69,9 @@ public final class TestXWPFParagraph extends TestCase {
 
     /**
      * Check that we get the right paragraphs from the document
+     * @throws IOException 
      */
-    public void disabled_testDocumentParagraph() {
+    public void disabled_testDocumentParagraph() throws IOException {
         XWPFDocument xml = XWPFTestDataSamples.openSampleDocument("ThreeColHead.docx");
         List ps = xml.getParagraphs();
         assertEquals(10, ps.size());
@@ -231,7 +234,7 @@ public final class TestXWPFParagraph extends TestCase {
         assertEquals(STOnOff.TRUE, ppr.getPageBreakBefore().getVal());
     }
 
-    public void testBookmarks() {
+    public void testBookmarks() throws IOException {
         XWPFDocument doc = XWPFTestDataSamples.openSampleDocument("bookmarks.docx");
         XWPFParagraph paragraph = doc.getParagraphs().get(0);
         assertEquals("Sample Word Document", paragraph.getText());
diff --git a/src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFPictureData.java b/src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFPictureData.java
index c05e6201e1..72aa4c115e 100644
--- a/src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFPictureData.java
+++ b/src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFPictureData.java
@@ -17,6 +17,7 @@
 
 package org.apache.poi.xwpf.usermodel;
 
+import java.io.IOException;
 import java.util.Arrays;
 import java.util.List;
 
@@ -29,98 +30,103 @@ import org.apache.poi.xwpf.XWPFTestDataSamples;
 import org.apache.poi.xwpf.model.XWPFHeaderFooterPolicy;
 
 public class TestXWPFPictureData extends TestCase {
-	   public void testRead(){
-		   XWPFDocument sampleDoc = XWPFTestDataSamples.openSampleDocument("VariousPictures.docx");
-	        List pictures = sampleDoc.getAllPictures();
-	        assertSame(pictures, sampleDoc.getAllPictures());
-
-	        assertEquals(5, pictures.size());
-	        String[] ext = {"wmf", "png", "emf", "emf", "jpeg"};
-	        for (int i = 0; i < pictures.size(); i++) {
-	            assertEquals(ext[i], pictures.get(i).suggestFileExtension());
-	        }
-
-	        int num = pictures.size();
-
-	        byte[] pictureData = {0xA, 0xB, 0XC, 0xD, 0xE, 0xF};
-
-	        int idx;
-			try {
-				idx = sampleDoc.addPicture(pictureData, XWPFDocument.PICTURE_TYPE_JPEG);
-				assertEquals(num + 1, pictures.size());
-				//idx is 0-based index in the #pictures array
-				assertEquals(pictures.size() - 1, idx);
-				XWPFPictureData pict = pictures.get(idx);
-				assertEquals("jpeg", pict.suggestFileExtension());
-				assertTrue(Arrays.equals(pictureData, pict.getData()));
-			} catch (InvalidFormatException e) {
-				// TODO Auto-generated catch block
-				e.printStackTrace();
-			}
-	    }
-
-        public void testPictureInHeader() {
-            XWPFDocument sampleDoc = XWPFTestDataSamples.openSampleDocument("headerPic.docx");
-            XWPFHeaderFooterPolicy policy = sampleDoc.getHeaderFooterPolicy();
-
-            XWPFHeader header = policy.getDefaultHeader();
-
-            List pictures = header.getAllPictures();
-            assertEquals(1, pictures.size());
+    
+    public void testRead() throws InvalidFormatException, IOException
+    {
+        XWPFDocument sampleDoc = XWPFTestDataSamples.openSampleDocument("VariousPictures.docx");
+        List pictures = sampleDoc.getAllPictures();
+
+        assertEquals(5,pictures.size());
+        String[] ext = {"wmf","png","emf","emf","jpeg"};
+        for (int i = 0 ; i < pictures.size() ; i++)
+        {
+            assertEquals(ext[i],pictures.get(i).suggestFileExtension());
         }
 
-	    public void testNew() throws Exception {
-	        XWPFDocument doc = XWPFTestDataSamples.openSampleDocument("EmptyDocumentWithHeaderFooter.docx");
-	        byte[] jpegData = "test jpeg data".getBytes();
-	        byte[] wmfData =  "test wmf data".getBytes();
-	        byte[] pngData =  "test png data".getBytes();
-	        
-	        List pictures = doc.getAllPictures();
-	        assertEquals(0, pictures.size());
-	        
-	        // Document shouldn't have any image relationships
-	        assertEquals(13, doc.getPackagePart().getRelationships().size());
-	        for(PackageRelationship rel : doc.getPackagePart().getRelationships()) {
-	           if(rel.getRelationshipType().equals(XSSFRelation.IMAGE_JPEG.getRelation())) {
-	              fail("Shouldn't have JPEG yet");
-	           }
-	        }
-	        
-	        // Add the image
-	        int jpegIdx;
-	        
-	        jpegIdx = doc.addPicture(jpegData, XWPFDocument.PICTURE_TYPE_JPEG);
-	        assertEquals(1, pictures.size());
-	        assertEquals("jpeg", pictures.get(jpegIdx).suggestFileExtension());
-	        assertTrue(Arrays.equals(jpegData, pictures.get(jpegIdx).getData()));
-	        
-	        // Ensure it now has one
-           assertEquals(14, doc.getPackagePart().getRelationships().size());
-           PackageRelationship jpegRel = null;
-           for(PackageRelationship rel : doc.getPackagePart().getRelationships()) {
-              if(rel.getRelationshipType().equals(XWPFRelation.IMAGE_JPEG.getRelation())) {
-                 if(jpegRel != null)
+        int num = pictures.size();
+
+        byte[] pictureData = XWPFTestDataSamples.getImage("nature1.jpg");
+
+        String relationId = sampleDoc.addPictureData(pictureData,XWPFDocument.PICTURE_TYPE_JPEG);
+        // picture list was updated
+        assertEquals(num + 1,pictures.size());
+        XWPFPictureData pict = (XWPFPictureData) sampleDoc.getRelationById(relationId);
+        assertEquals("jpeg",pict.suggestFileExtension());
+        assertTrue(Arrays.equals(pictureData,pict.getData()));
+    }
+
+    public void testPictureInHeader() throws IOException
+    {
+        XWPFDocument sampleDoc = XWPFTestDataSamples.openSampleDocument("headerPic.docx");
+        XWPFHeaderFooterPolicy policy = sampleDoc.getHeaderFooterPolicy();
+
+        XWPFHeader header = policy.getDefaultHeader();
+
+        List pictures = header.getAllPictures();
+        assertEquals(1,pictures.size());
+    }
+
+    public void testNew() throws InvalidFormatException, IOException 
+    {
+        XWPFDocument doc = XWPFTestDataSamples.openSampleDocument("EmptyDocumentWithHeaderFooter.docx");
+        byte[] jpegData = XWPFTestDataSamples.getImage("nature1.jpg");
+        byte[] gifData = XWPFTestDataSamples.getImage("nature1.gif");
+        byte[] pngData = XWPFTestDataSamples.getImage("nature1.png");
+
+        List pictures = doc.getAllPictures();
+        assertEquals(0,pictures.size());
+
+        // Document shouldn't have any image relationships
+        assertEquals(13,doc.getPackagePart().getRelationships().size());
+        for (PackageRelationship rel : doc.getPackagePart().getRelationships())
+        {
+            if (rel.getRelationshipType().equals(XSSFRelation.IMAGE_JPEG.getRelation()))
+            {
+                fail("Shouldn't have JPEG yet");
+            }
+        }
+
+        // Add the image
+        String relationId = doc.addPictureData(jpegData,XWPFDocument.PICTURE_TYPE_JPEG);
+        assertEquals(1,pictures.size());
+        XWPFPictureData jpgPicData = (XWPFPictureData) doc.getRelationById(relationId);
+        assertEquals("jpeg",jpgPicData.suggestFileExtension());
+        assertTrue(Arrays.equals(jpegData,jpgPicData.getData()));
+
+        // Ensure it now has one
+        assertEquals(14,doc.getPackagePart().getRelationships().size());
+        PackageRelationship jpegRel = null;
+        for (PackageRelationship rel : doc.getPackagePart().getRelationships())
+        {
+            if (rel.getRelationshipType().equals(XWPFRelation.IMAGE_JPEG.getRelation()))
+            {
+                if (jpegRel != null)
                     fail("Found 2 jpegs!");
-                 jpegRel = rel;
-              }
-           }
-           assertNotNull("JPEG Relationship not found", jpegRel);
-           
-           // Check the details
-           assertEquals(XWPFRelation.IMAGE_JPEG.getRelation(), jpegRel.getRelationshipType());
-           assertEquals("/word/document.xml", jpegRel.getSource().getPartName().toString());
-           assertEquals("/word/media/image1.jpeg", jpegRel.getTargetURI().getPath());
-
-	        XWPFPictureData pictureDataByID = doc.getPictureDataByID(jpegRel.getId());
-	        byte [] newJPEGData = pictureDataByID.getData();
-	        assertEquals(newJPEGData.length, jpegData.length);
-	        for(int i = 0; i < newJPEGData.length; i++){
-	           assertEquals(newJPEGData[i], jpegData[i]);	
-	        }
-	        
-	        // Save an re-load, check it appears
-	        doc = XWPFTestDataSamples.writeOutAndReadBack(doc);
-	        assertEquals(1, doc.getAllPictures().size());
-           assertEquals(1, doc.getAllPackagePictures().size());
-	    }
+                jpegRel = rel;
+            }
+        }
+        assertNotNull("JPEG Relationship not found",jpegRel);
+
+        // Check the details
+        assertEquals(XWPFRelation.IMAGE_JPEG.getRelation(),jpegRel.getRelationshipType());
+        assertEquals("/word/document.xml",jpegRel.getSource().getPartName().toString());
+        assertEquals("/word/media/image1.jpeg",jpegRel.getTargetURI().getPath());
+
+        XWPFPictureData pictureDataByID = doc.getPictureDataByID(jpegRel.getId());
+        byte[] newJPEGData = pictureDataByID.getData();
+        assertEquals(newJPEGData.length,jpegData.length);
+        for (int i = 0 ; i < newJPEGData.length ; i++)
+        {
+            assertEquals(newJPEGData[i],jpegData[i]);
+        }
+
+        // Save an re-load, check it appears
+        doc = XWPFTestDataSamples.writeOutAndReadBack(doc);
+        assertEquals(1,doc.getAllPictures().size());
+        assertEquals(1,doc.getAllPackagePictures().size());
+    }
+    
+    public void testGetChecksum() {
+        
+    }
 }
diff --git a/src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFRun.java b/src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFRun.java
index b12932c2ec..88b44975f6 100644
--- a/src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFRun.java
+++ b/src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFRun.java
@@ -17,6 +17,7 @@
 package org.apache.poi.xwpf.usermodel;
 
 import java.io.ByteArrayInputStream;
+import java.io.IOException;
 import java.math.BigInteger;
 import java.util.List;
 
@@ -198,8 +199,9 @@ public class TestXWPFRun extends TestCase {
     /**
      * Test that on an existing document, we do the
      *  right thing with it
+     * @throws IOException 
      */
-    public void testExisting() {
+    public void testExisting() throws IOException {
        XWPFDocument doc = XWPFTestDataSamples.openSampleDocument("TestDocument.docx");
        XWPFParagraph p;
        XWPFRun run;
@@ -330,7 +332,7 @@ public class TestXWPFRun extends TestCase {
        assertEquals(null, run.getCTR().getRPr());
     }
 
-    public void testPictureInHeader() {
+    public void testPictureInHeader() throws IOException {
         XWPFDocument sampleDoc = XWPFTestDataSamples.openSampleDocument("headerPic.docx");
         XWPFHeaderFooterPolicy policy = sampleDoc.getHeaderFooterPolicy();
 
diff --git a/src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFStyles.java b/src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFStyles.java
index 646103f905..704b02afc5 100644
--- a/src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFStyles.java
+++ b/src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFStyles.java
@@ -17,6 +17,7 @@
 
 package org.apache.poi.xwpf.usermodel;
 
+import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -30,7 +31,7 @@ public class TestXWPFStyles extends TestCase {
 //		super.setUp();
 //	}
 	
-	public void testGetUsedStyles(){
+	public void testGetUsedStyles() throws IOException{
 		XWPFDocument sampleDoc = XWPFTestDataSamples.openSampleDocument("Styles.docx");
 		List testUsedStyleList = new ArrayList();
 		XWPFStyles styles = sampleDoc.getStyles();
diff --git a/test-data/document/abstract1.jpg b/test-data/document/abstract1.jpg
new file mode 100755
index 0000000000..2c61df9d66
Binary files /dev/null and b/test-data/document/abstract1.jpg differ
diff --git a/test-data/document/abstract2.jpg b/test-data/document/abstract2.jpg
new file mode 100755
index 0000000000..598fbf6850
Binary files /dev/null and b/test-data/document/abstract2.jpg differ
diff --git a/test-data/document/abstract3.jpg b/test-data/document/abstract3.jpg
new file mode 100755
index 0000000000..178af68a6c
Binary files /dev/null and b/test-data/document/abstract3.jpg differ
diff --git a/test-data/document/abstract4.jpg b/test-data/document/abstract4.jpg
new file mode 100755
index 0000000000..bb34774f6d
Binary files /dev/null and b/test-data/document/abstract4.jpg differ
diff --git a/test-data/document/issue_51265_1.docx b/test-data/document/issue_51265_1.docx
new file mode 100755
index 0000000000..a18f6fb672
Binary files /dev/null and b/test-data/document/issue_51265_1.docx differ
diff --git a/test-data/document/issue_51265_2.docx b/test-data/document/issue_51265_2.docx
new file mode 100755
index 0000000000..b528d7d2b8
Binary files /dev/null and b/test-data/document/issue_51265_2.docx differ
diff --git a/test-data/document/issue_51265_3.docx b/test-data/document/issue_51265_3.docx
new file mode 100755
index 0000000000..721d9d4aa3
Binary files /dev/null and b/test-data/document/issue_51265_3.docx differ
diff --git a/test-data/document/nature1.gif b/test-data/document/nature1.gif
new file mode 100755
index 0000000000..838e05199b
Binary files /dev/null and b/test-data/document/nature1.gif differ
diff --git a/test-data/document/nature1.jpg b/test-data/document/nature1.jpg
new file mode 100755
index 0000000000..e461fd91b3
Binary files /dev/null and b/test-data/document/nature1.jpg differ
diff --git a/test-data/document/nature1.png b/test-data/document/nature1.png
new file mode 100755
index 0000000000..ba5b10ac54
Binary files /dev/null and b/test-data/document/nature1.png differ
diff --git a/test-data/document/nature2.jpg b/test-data/document/nature2.jpg
new file mode 100755
index 0000000000..9fcbf4d2d6
Binary files /dev/null and b/test-data/document/nature2.jpg differ
diff --git a/test-data/document/nature3.jpg b/test-data/document/nature3.jpg
new file mode 100755
index 0000000000..034799830a
Binary files /dev/null and b/test-data/document/nature3.jpg differ
diff --git a/test-data/document/nature4.jpg b/test-data/document/nature4.jpg
new file mode 100755
index 0000000000..5d344cc9dd
Binary files /dev/null and b/test-data/document/nature4.jpg differ