/** * Create an image part from the provided byte array, attach it to the source part (eg the main * document part, a header part etc), and return it. * * <p>Works for both docx and pptx. * * <p>Knowing the MIME type allows you to avoid ImageInfo, but you'll probably also need to know * the image dimensions * * @param opcPackage * @param sourcePart * @param bytes * @param mime MIME type eg image/png * @return * @throws Exception @Since 3.0.1 */ public static BinaryPartAbstractImage createImagePart( OpcPackage opcPackage, Part sourcePart, byte[] bytes, String mime) throws Exception { String ext = mimeToExt(mime); if (mime == null || ext == null) { log.warn("Null or unknown mime type; image introspection required!"); return createImagePart(opcPackage, sourcePart, bytes); } ContentTypeManager ctm = opcPackage.getContentTypeManager(); // Ensure the relationships part exists if (sourcePart.getRelationshipsPart() == null) { RelationshipsPart.createRelationshipsPartForPart(sourcePart); } String proposedRelId = sourcePart.getRelationshipsPart().getNextId(); BinaryPartAbstractImage imagePart = (BinaryPartAbstractImage) ctm.newPartForContentType( mime, createImageName(opcPackage, sourcePart, proposedRelId, ext), null); log.debug( "created part " + imagePart.getClass().getName() + " with name " + imagePart.getPartName().toString()); imagePart.setBinaryData(bytes); imagePart.rels.add(sourcePart.addTargetPart(imagePart, proposedRelId)); return imagePart; }
/** * Convenience method, given a Graphic in a document, to get the byte[] representing the * associated image * * @param wmlPkg * @param graphic * @return */ public static byte[] getImage(WordprocessingMLPackage wmlPkg, org.docx4j.dml.Graphic graphic) { if (wmlPkg == null || wmlPkg.getMainDocumentPart() == null || wmlPkg.getMainDocumentPart().getRelationshipsPart() == null) { return null; } Pic pic = graphic.getGraphicData().getPic(); String rId = pic.getBlipFill().getBlip().getEmbed(); if (rId.equals("")) { rId = pic.getBlipFill().getBlip().getLink(); } log.debug("Image rel id: " + rId); org.docx4j.relationships.Relationship rel = wmlPkg.getMainDocumentPart().getRelationshipsPart().getRelationshipByID(rId); if (rel != null) { org.docx4j.openpackaging.parts.Part part = wmlPkg.getMainDocumentPart().getRelationshipsPart().getPart(rel); if (part == null) { log.error("Couldn't get Part!"); } else if (part instanceof BinaryPart) { log.debug("getting bytes..."); return ((BinaryPart) part).getBytes(); } else { log.error("Part was a " + part.getClass().getName()); } } else { log.error("Couldn't find rel " + rId); } return null; }
// public static Part getBinaryPart(ZipFile zf, ContentTypeManager ctm, String resolvedPartUri) public static Part getBinaryPart( HashMap<String, ByteArray> partByteArrays, ContentTypeManager ctm, String resolvedPartUri) throws Docx4JException { Part part = null; InputStream in = null; try { // in = zf.getInputStream( zf.getEntry(resolvedPartUri ) ); in = partByteArrays.get(resolvedPartUri).getInputStream(); part = new BinaryPart(new PartName("/" + resolvedPartUri)); // Set content type part.setContentType(new ContentType(ctm.getContentType(new PartName("/" + resolvedPartUri)))); ((BinaryPart) part).setBinaryData(in); log.info("Stored as BinaryData"); } catch (Exception ioe) { ioe.printStackTrace(); } finally { if (in != null) { try { in.close(); } catch (IOException exc) { exc.printStackTrace(); } } } return part; }
protected static void removeDefinedCustomXmlParts( WordprocessingMLPackage wmlPackage, String xpathStorageItemId) { List<PartName> partsToRemove = new ArrayList<PartName>(); RelationshipsPart relationshipsPart = wmlPackage.getMainDocumentPart().getRelationshipsPart(); List<Relationship> relationshipsList = ((relationshipsPart != null) && (relationshipsPart.getRelationships() != null) ? relationshipsPart.getRelationships().getRelationship() : null); Part part = null; CustomXmlDataStoragePart dataPart = null; if (relationshipsList != null) { for (Relationship relationship : relationshipsList) { if (Namespaces.CUSTOM_XML_DATA_STORAGE.equals(relationship.getType())) { part = relationshipsPart.getPart(relationship); if (IsPartToRemove(part, xpathStorageItemId)) { partsToRemove.add(part.getPartName()); } } } } if (!partsToRemove.isEmpty()) { for (int i = 0; i < partsToRemove.size(); i++) { relationshipsPart.removePart(partsToRemove.get(i)); } } }
protected void saveRawBinaryPart(ZipOutputStream out, Part part) throws Docx4JException { // Drop the leading '/' String resolvedPartUri = part.getPartName().getName().substring(1); try { // Add ZIP entry to output stream. out.putNextEntry(new ZipEntry(resolvedPartUri)); java.nio.ByteBuffer bb = ((BinaryPart) part).getBuffer(); byte[] bytes = null; bytes = new byte[bb.limit()]; bb.get(bytes); out.write(bytes); // Complete the entry out.closeEntry(); } catch (Exception e) { throw new Docx4JException("Failed to put binary part", e); } log.info("success writing part: " + resolvedPartUri); }
/** * Create a linked image part, and attach it as a rel of the specified source part (eg a header * part). * * <p>The current behaviour is that the part is added to the package, but since the target mode of * the rel is external, the part is redundant. * * @param wordMLPackage * @param sourcePart * @param url * @return * @throws Exception */ public static BinaryPartAbstractImage createLinkedImagePart( OpcPackage opcPackage, Part sourcePart, URL url) throws Exception { log.debug("Incoming url for linked image: " + url.toString()); ImageInfo info = ensureFormatIsSupported(url, null, null, false); // final param doesn't matter in this case ContentTypeManager ctm = opcPackage.getContentTypeManager(); String proposedRelId = sourcePart.getRelationshipsPart().getNextId(); // In order to ensure unique part name, // idea is to use the relId, which ought to be unique String ext = info.getMimeType().substring(info.getMimeType().indexOf("/") + 1); BinaryPartAbstractImage imagePart = (BinaryPartAbstractImage) ctm.newPartForContentType( info.getMimeType(), createImageName(opcPackage, sourcePart, proposedRelId, ext), null); // NB: contents never populated log.debug( "created part " + imagePart.getClass().getName() + " with name " + imagePart.getPartName().toString()); imagePart.rels.add( sourcePart.addTargetPart( imagePart)); // want to create rel with suitable name; side effect is to add part imagePart.getRelLast().setTargetMode("External"); opcPackage.getExternalResources().put(imagePart.getExternalTarget(), imagePart); // if (!url.getProtocol().equals("file") && new File(url.toString() ).isFile()) { // imagePart.rel.setTarget("file:///" + url); // } else { // imagePart.rel.setTarget(url.toString()); // } imagePart.getRelLast().setTarget(url.toString()); imagePart.setImageInfo(info); return imagePart; }
public void saveRawXmlPart(ZipOutputStream out, Part part) throws Docx4JException { // This is a neater signature and should be used where possible! String partName = part.getPartName().getName().substring(1); saveRawXmlPart(out, part, partName); }
/** * Get the Relationships Part (if there is one) for a given Part. Otherwise return null. * * @param zf * @param part * @return * @throws InvalidFormatException */ public RelationshipsPart getRelationshipsPart(ZipFile zf, Part part) throws Docx4JException, InvalidFormatException { RelationshipsPart rrp = null; // recurse via this parts relationships, if it has any // String relPart = PartName.getRelationshipsPartName(target); String relPart = PartName.getRelationshipsPartName(part.getPartName().getName().substring(1)); if (zf.getEntry(relPart) != null) { log.info("Found relationships " + relPart); log.info("Recursing ... "); rrp = getRelationshipsPartFromZip(part, zf, relPart); part.setRelationships(rrp); } else { log.info("No relationships " + relPart); return null; } return rrp; }
// public RelationshipsPart getRelationshipsPart(ZipFile zf, Part part) public RelationshipsPart getRelationshipsPart( HashMap<String, ByteArray> partByteArrays, Part part) throws Docx4JException, InvalidFormatException { RelationshipsPart rrp = null; // recurse via this parts relationships, if it has any // String relPart = PartName.getRelationshipsPartName(target); String relPart = PartName.getRelationshipsPartName(part.getPartName().getName().substring(1)); if (partByteArrays.get(relPart) != null) { log.debug("Found relationships " + relPart); rrp = getRelationshipsPartFromZip(part, partByteArrays, relPart); part.setRelationships(rrp); } else { log.debug("No relationships " + relPart); return null; } return rrp; }
/** * @param out * @param resolvedPartUri * @param part * @throws Docx4JException * @throws IOException */ public void savePart(ZipOutputStream out, Part part) throws Docx4JException, IOException { // Drop the leading '/' String resolvedPartUri = part.getPartName().getName().substring(1); if (handled.get(resolvedPartUri) != null) { log.debug(".. duplicate save avoided .."); return; } if (part instanceof BinaryPart) { log.debug(".. saving binary stuff"); saveRawBinaryPart(out, part); } else { log.debug(".. saving "); saveRawXmlPart(out, part); } handled.put(resolvedPartUri, resolvedPartUri); // recurse via this parts relationships, if it has any RelationshipsPart rrp = part.getRelationshipsPart(false); // don't create if (rrp != null) { // log.debug("Found relationships " + rrp.getPartName() ); // Only save it if it actually has rels in it if (rrp.getRelationships().getRelationship().size() > 0) { String relPart = PartName.getRelationshipsPartName(resolvedPartUri); // log.debug("Cf constructed name " + relPart ); saveRawXmlPart(out, rrp, relPart); addPartsFromRelationships(out, rrp); } } // else { // log.debug("No relationships for " + resolvedPartUri ); // } }
/** * Create an image part from the provided filePath image, attach it to the source part (eg the * main document part, a header part etc), and return it. * * <p>Works for both docx and pptx. * * @param opcPackage * @param sourcePart * @param filePath * @return * @throws Exception */ public static BinaryPartAbstractImage createImagePart( OpcPackage opcPackage, Part sourcePart, File imageFile) throws Exception { final byte[] locByte = new byte[1]; // We are in the case that image is not load (no byte Array) so isLoad is false ImageInfo info = ensureFormatIsSupported(imageFile, locByte, false); ContentTypeManager ctm = opcPackage.getContentTypeManager(); // Ensure the relationships part exists if (sourcePart.getRelationshipsPart() == null) { RelationshipsPart.createRelationshipsPartForPart(sourcePart); } String proposedRelId = sourcePart.getRelationshipsPart().getNextId(); String ext = info.getMimeType().substring(info.getMimeType().indexOf("/") + 1); BinaryPartAbstractImage imagePart = (BinaryPartAbstractImage) ctm.newPartForContentType( info.getMimeType(), createImageName(opcPackage, sourcePart, proposedRelId, ext), null); log.debug( "created part " + imagePart.getClass().getName() + " with name " + imagePart.getPartName().toString()); FileInputStream fis = new FileInputStream(imageFile); imagePart.setBinaryData(fis); imagePart.rels.add(sourcePart.addTargetPart(imagePart, proposedRelId)); imagePart.setImageInfo(info); return imagePart; }
/** * Get the Relationships Part (if there is one) for a given Part. Otherwise return null. * * @param zf * @param part * @return * @throws InvalidFormatException */ public RelationshipsPart getRelationshipsPart(Part part) throws Docx4JException, InvalidFormatException { RelationshipsPart rrp = null; // recurse via this parts relationships, if it has any // String relPart = PartName.getRelationshipsPartName(target); String relPart = PartName.getRelationshipsPartName(part.getPartName().getName().substring(1)); rrp = getRelationshipsPartFromZip(part, relPart); part.setRelationships(rrp); // if (partStore.partExists(relPart)) { // //if (partByteArrays.get(relPart) !=null ) { // log.debug("Found relationships " + relPart ); // rrp = getRelationshipsPartFromZip(part, relPart); // part.setRelationships(rrp); // } else { // log.debug("No relationships " + relPart ); // return null; // } return rrp; }
public Part getBinaryPart(ContentTypeManager ctm, String resolvedPartUri) throws Docx4JException { Part part = null; InputStream is = null; try { is = partStore.loadPart(resolvedPartUri); // in = partByteArrays.get(resolvedPartUri).getInputStream(); part = new BinaryPart(new PartName("/" + resolvedPartUri)); // Set content type part.setContentType(new ContentType(ctm.getContentType(new PartName("/" + resolvedPartUri)))); ((BinaryPart) part).setBinaryData(is); log.info("Stored as BinaryData"); } catch (Exception ioe) { ioe.printStackTrace(); } finally { IOUtils.closeQuietly(is); } return part; }
/** * @param alteredParts * @param theseRels * @throws Docx4JException */ private static void addTree(List<Alteration> list, RelationshipsPart rp) throws Docx4JException { if (rp == null) return; for (Relationship r : rp.getJaxbElement().getRelationship()) { if (r.getTargetMode() != null && r.getTargetMode().equals("External")) { log.debug(r.getTarget() + " is external"); // Have everything we need info wise in transmitting the rels part } else { list.add(new Alteration(rp, toStorageFormat(rp.getPart(r)))); log.debug("add tree: " + r.getTarget()); // recurse Part nextSourcePart = rp.getPart(r); RelationshipsPart nextRP = nextSourcePart.getRelationshipsPart(); if (nextRP != null) { list.add(new Alteration(nextSourcePart.getPartName(), toStorageFormat(nextRP))); addTree(list, nextRP); } } } }
public static CustomXmlDataStoragePart injectCustomXmlDataStoragePart(Part parent) throws Exception { org.docx4j.openpackaging.parts.CustomXmlDataStoragePart customXmlDataStoragePart = new org.docx4j.openpackaging.parts.CustomXmlDataStoragePart(); // Defaults to /customXml/item1.xml CustomXmlDataStorage data = new CustomXmlDataStorageImpl(); data.setDocument(createCustomXmlDocument()); customXmlDataStoragePart.setData(data); parent.addTargetPart(customXmlDataStoragePart, AddPartBehaviour.RENAME_IF_NAME_EXISTS); return customXmlDataStoragePart; }
protected static boolean IsPartToRemove(Part part, String xpathStorageItemId) { boolean ret = false; RelationshipsPart relationshipsPart = part.getRelationshipsPart(); List<Relationship> relationshipsList = ((relationshipsPart != null) && (relationshipsPart.getRelationships() != null) ? relationshipsPart.getRelationships().getRelationship() : null); CustomXmlDataStoragePropertiesPart propertiesPart = null; DatastoreItem datastoreItem = null; if ((relationshipsList != null) && (!relationshipsList.isEmpty())) { for (Relationship relationship : relationshipsList) { if (Namespaces.CUSTOM_XML_DATA_STORAGE_PROPERTIES.equals(relationship.getType())) { propertiesPart = (CustomXmlDataStoragePropertiesPart) relationshipsPart.getPart(relationship); break; } } } if (propertiesPart != null) { datastoreItem = propertiesPart.getJaxbElement(); } if (datastoreItem != null) { if ((datastoreItem.getItemID() != null) && (datastoreItem.getItemID().length() > 0)) { ret = datastoreItem.getItemID().equals(xpathStorageItemId); } if ((!ret) && (datastoreItem.getSchemaRefs() != null) && (datastoreItem.getSchemaRefs().getSchemaRef() != null) && (!datastoreItem.getSchemaRefs().getSchemaRef().isEmpty())) { for (SchemaRef ref : datastoreItem.getSchemaRefs().getSchemaRef()) { if (PART_TO_REMOVE_SCHEMA_TYPES.contains(ref.getUri())) { ret = true; break; } } } } return ret; }
/** * Create an image part from the provided byte array, attach it to the source part (eg the main * document part, a header part etc), and return it. * * <p>Works for both docx and pptx. * * <p>Note: this method creates a temp file (and attempts to delete it). That's because it uses * org.apache.xmlgraphics * * @param opcPackage * @param sourcePart * @param bytes * @return * @throws Exception */ public static BinaryPartAbstractImage createImagePart( OpcPackage opcPackage, Part sourcePart, byte[] bytes) throws Exception { // Whatever image type this is, we're going to need // to know its dimensions. // For that we use ImageInfo, which can only // load an image from a URI. // So first, write the bytes to a temp file File tmpImageFile = File.createTempFile("img", ".img"); FileOutputStream fos = new FileOutputStream(tmpImageFile); fos.write(bytes); fos.close(); log.debug("created tmp file: " + tmpImageFile.getAbsolutePath()); ImageInfo info = ensureFormatIsSupported(tmpImageFile, bytes, true); // In the absence of an exception, tmpImageFile now contains an image // Word will accept ContentTypeManager ctm = opcPackage.getContentTypeManager(); // Ensure the relationships part exists if (sourcePart.getRelationshipsPart() == null) { RelationshipsPart.createRelationshipsPartForPart(sourcePart); } String proposedRelId = sourcePart.getRelationshipsPart().getNextId(); String ext = info.getMimeType().substring(info.getMimeType().indexOf("/") + 1); // System.out.println(ext); BinaryPartAbstractImage imagePart = (BinaryPartAbstractImage) ctm.newPartForContentType( info.getMimeType(), createImageName(opcPackage, sourcePart, proposedRelId, ext), null); log.debug( "created part " + imagePart.getClass().getName() + " with name " + imagePart.getPartName().toString()); FileInputStream fis = new FileInputStream(tmpImageFile); imagePart.setBinaryData(fis); imagePart.rels.add(sourcePart.addTargetPart(imagePart, proposedRelId)); imagePart.setImageInfo(info); // Delete the tmp file // As per http://stackoverflow.com/questions/991489/i-cant-delete-a-file-in-java // the following 3 lines are necessary, at least on Win 7 x64 // Also reported on Win XP, but in my testing, the files were deleting OK anyway. fos = null; fis = null; if (Docx4jProperties.getProperty( "docx4j.openpackaging.parts.WordprocessingML.BinaryPartAbstractImage.TempFiles.ForceGC", true)) { System.gc(); } if (tmpImageFile.delete()) { log.debug(".. deleted " + tmpImageFile.getAbsolutePath()); } else { log.warn("Couldn't delete tmp file " + tmpImageFile.getAbsolutePath()); tmpImageFile.deleteOnExit(); // If that doesn't work, see "Clean Up Your Mess: Managing Temp Files in Java Apps" // at devx.com } return imagePart; }
/* recursively (i) get each Part listed in the relationships (ii) add the Part to the zip file (iii) traverse its relationship */ public void addPartsFromRelationships(ZipOutputStream out, RelationshipsPart rp) throws Docx4JException { // for (Iterator it = rp.iterator(); it.hasNext(); ) { // Relationship r = (Relationship)it.next(); // log.info("For Relationship Id=" + r.getId() + " Source is " + r.getSource().getPartName() + // ", Target is " + r.getTargetURI() ); for (Relationship r : rp.getRelationships().getRelationship()) { log.debug( "For Relationship Id=" + r.getId() + " Source is " + rp.getSourceP().getPartName() + ", Target is " + r.getTarget()); if (r.getType().equals(Namespaces.HYPERLINK)) { continue; // whether internal or external } if (r.getTargetMode() != null && r.getTargetMode().equals("External")) { // ie its EXTERNAL // As at 1 May 2008, we don't have a Part for these; // there is just the relationship. log.warn("Encountered external resource " + r.getTarget() + " of type " + r.getType()); // So continue; } try { // String resolvedPartUri = URIHelper.resolvePartUri(r.getSourceURI(), r.getTargetURI() // ).toString(); String resolvedPartUri = URIHelper.resolvePartUri(rp.getSourceURI(), new URI(r.getTarget())).toString(); // Now drop leading "/' resolvedPartUri = resolvedPartUri.substring(1); // Now normalise it .. ie abc/def/../ghi // becomes abc/ghi // Maybe this isn't necessary with a zip file, // - ZipFile class may be smart enough to do it. // But it is certainly necessary in the JCR case. // target = (new java.net.URI(target)).normalize().toString(); // log.info("Normalised, it is " + target ); // Document contents = getDocumentFromZippedPart( zf, target); if (!false) { log.debug("Getting part /" + resolvedPartUri); // Part part = p.getParts().get(new PartName("/" + resolvedPartUri)); Part part = rp.getPart(r); // 2012 09 26: If the part is actually attached to // a different package, using this you can still get it. // Use this 'feature' at your own risk! if (part == null) { log.error("Part " + resolvedPartUri + " not found!"); } else { log.debug(part.getClass().getName()); } if (!part.getPackage().equals(p)) { log.warn("Part " + resolvedPartUri + " is attached to some other package"); } savePart(out, part); } } catch (Exception e) { throw new Docx4JException( "Failed to add parts from relationships of " + rp.getSourceP().getPartName(), e); } } }
/** * Get a Part (except a relationships part), but not its relationships part or related parts. * Useful if you need quick access to just this part. This can be called directly from outside the * library, in which case the Part will not be owned by a Package until the calling code makes it * so. * * @see To get a Part and all its related parts, and add all to a package, use getPart. * @param partByteArrays * @param ctm * @param resolvedPartUri * @param rel * @return * @throws Docx4JException including if result is null */ public Part getRawPart(ContentTypeManager ctm, String resolvedPartUri, Relationship rel) throws Docx4JException { Part part = null; InputStream is = null; try { try { log.debug("resolved uri: " + resolvedPartUri); // Get a subclass of Part appropriate for this content type // This will throw UnrecognisedPartException in the absence of // specific knowledge. Hence it is important to get the is // first, as we do above. part = ctm.getPart("/" + resolvedPartUri, rel); log.info("ctm returned " + part.getClass().getName()); if (part instanceof org.docx4j.openpackaging.parts.ThemePart || part instanceof org.docx4j.openpackaging.parts.DocPropsCorePart || part instanceof org.docx4j.openpackaging.parts.DocPropsCustomPart || part instanceof org.docx4j.openpackaging.parts.DocPropsExtendedPart || part instanceof org.docx4j.openpackaging.parts.CustomXmlDataStoragePropertiesPart || part instanceof org.docx4j.openpackaging.parts.digitalsignature.XmlSignaturePart || part instanceof org.docx4j.openpackaging.parts.JaxbXmlPart) { // Nothing to do here } else if (part instanceof org.docx4j.openpackaging.parts.WordprocessingML.BinaryPart) { log.debug("Detected BinaryPart " + part.getClass().getName()); // is = partStore.loadPart( resolvedPartUri); // ((BinaryPart)part).setBinaryData(is); } else if (part instanceof org.docx4j.openpackaging.parts.CustomXmlDataStoragePart) { // ContentTypeManager initially detects them as CustomXmlDataStoragePart; // the below changes as necessary // Is it a part we know? is = partStore.loadPart(resolvedPartUri); try { Unmarshaller u = Context.jc.createUnmarshaller(); Object o = u.unmarshal(is); log.debug(o.getClass().getName()); PartName name = part.getPartName(); if (o instanceof CoverPageProperties) { part = new DocPropsCoverPagePart(name); ((DocPropsCoverPagePart) part).setJaxbElement((CoverPageProperties) o); } else if (o instanceof org.opendope.conditions.Conditions) { part = new ConditionsPart(name); ((ConditionsPart) part).setJaxbElement((org.opendope.conditions.Conditions) o); } else if (o instanceof org.opendope.xpaths.Xpaths) { part = new XPathsPart(name); ((XPathsPart) part).setJaxbElement((org.opendope.xpaths.Xpaths) o); } else if (o instanceof org.opendope.questions.Questionnaire) { part = new QuestionsPart(name); ((QuestionsPart) part).setJaxbElement((org.opendope.questions.Questionnaire) o); } else if (o instanceof org.opendope.answers.Answers) { part = new StandardisedAnswersPart(name); ((StandardisedAnswersPart) part).setJaxbElement((org.opendope.answers.Answers) o); } else if (o instanceof org.opendope.components.Components) { part = new ComponentsPart(name); ((ComponentsPart) part).setJaxbElement((org.opendope.components.Components) o); } else if (o instanceof JAXBElement<?> && XmlUtils.unwrap(o) instanceof org.docx4j.bibliography.CTSources) { part = new BibliographyPart(name); ((BibliographyPart) part) .setJaxbElement((JAXBElement<org.docx4j.bibliography.CTSources>) o); } else { log.error("TODO: handle known CustomXmlPart part " + o.getClass().getName()); CustomXmlDataStorage data = getCustomXmlDataStorageClass().factory(); is.reset(); data.setDocument(is); // Not necessarily JAXB, that's just our method name ((org.docx4j.openpackaging.parts.CustomXmlDataStoragePart) part).setData(data); } } catch (javax.xml.bind.UnmarshalException ue) { log.warn("No JAXB model for this CustomXmlDataStorage part; " + ue.getMessage()); CustomXmlDataStorage data = getCustomXmlDataStorageClass().factory(); is.reset(); data.setDocument(is); // Not necessarily JAXB, that's just our method name ((org.docx4j.openpackaging.parts.CustomXmlDataStoragePart) part).setData(data); } } else if (part instanceof org.docx4j.openpackaging.parts.XmlPart) { is = partStore.loadPart(resolvedPartUri); // try { ((XmlPart) part).setDocument(is); // Experimental 22/6/2011; don't fall back to binary (which we used to) // } catch (Docx4JException d) { // // This isn't an XML part after all, // // even though ContentTypeManager detected it as such // // So get it as a binary part // part = getBinaryPart(partByteArrays, ctm, resolvedPartUri); // log.warn("Could not parse as XML, so using BinaryPart for " // + resolvedPartUri); // ((BinaryPart)part).setBinaryData(is); // } } else { // Shouldn't happen, since ContentTypeManagerImpl should // return an instance of one of the above, or throw an // Exception. log.error("No suitable part found for: " + resolvedPartUri); part = null; } } catch (PartUnrecognisedException e) { log.error("PartUnrecognisedException shouldn't happen anymore!", e); // Try to get it as a binary part part = getBinaryPart(ctm, resolvedPartUri); log.warn("Using BinaryPart for " + resolvedPartUri); // is = partStore.loadPart( resolvedPartUri); // ((BinaryPart)part).setBinaryData(is); } } catch (Exception ex) { // IOException, URISyntaxException ex.printStackTrace(); throw new Docx4JException("Failed to getPart", ex); } finally { IOUtils.closeQuietly(is); } if (part == null) { throw new Docx4JException( "cannot find part " + resolvedPartUri + " from rel " + rel.getId() + "=" + rel.getTarget()); } return part; }
public static void recurse( Alterations alterations, RelationshipsPart thisRP, RelationshipsPart otherRP) throws Docx4JException { log.info("######### @" + thisRP.partName.getName() + "#########"); log.info("uniques -------"); List<Relationship> uniques = thisRP.uniqueToThis(otherRP); addPartsForRels(alterations.getPartsAdded(), uniques, thisRP); List<Relationship> missings = thisRP.uniqueToOther(otherRP); addPartsForRels(alterations.getPartsDeleted(), missings, otherRP); // is this rels part itself altered? if (!thisRP.isContentEqual(otherRP)) { alterations.getPartsModified().add(new Alteration(thisRP, toStorageFormat(thisRP))); } log.info("content -------"); List<Relationship> altered = thisRP.differingContent(otherRP); addPartsForRels(alterations.getPartsModified(), altered, thisRP); // Now recurse all rels log.info("recurse ------- "); for (Relationship r : thisRP.getJaxbElement().getRelationship()) { if (r.getTargetMode() != null && r.getTargetMode().equals("External")) { // do nothing } else { if (uniques.contains(r)) { // add tree, including any external parts // (we already have the part itself) addTree(alterations.getPartsAdded(), thisRP.getPart(r).getRelationshipsPart()); } else if (missings.contains(r)) { addTree(alterations.getPartsDeleted(), thisRP.getPart(r).getRelationshipsPart()); } else { // its present in both trees. // irrespective of whether content of part is the same, content of a rel could still have // changed Part thisPart = thisRP.getPart(r); Part otherPart = otherRP.getPart(RelationshipsPart.getRelationshipByTarget(otherRP, r.getTarget())); if (thisPart.getRelationshipsPart() == null) { if (otherPart.getRelationshipsPart() != null) { // add tree, including any external parts alterations .getPartsDeleted() .add( new Alteration( thisPart.getPartName(), toStorageFormat(thisPart.getRelationshipsPart()))); addTree(alterations.getPartsDeleted(), thisPart.getRelationshipsPart()); } } else { if (otherPart.getRelationshipsPart() == null) { // add tree, including any external parts alterations .getPartsAdded() .add( new Alteration( thisPart.getPartName(), toStorageFormat(thisPart.getRelationshipsPart()))); addTree(alterations.getPartsAdded(), thisPart.getRelationshipsPart()); } else { recurse( alterations, thisPart.getRelationshipsPart(), otherPart.getRelationshipsPart()); } } } } } }
/** * Get a Part (except a relationships part), and all its related parts. This can be called * directly from outside the library, in which case the Part will not be owned by a Package until * the calling code makes it so. * * @param zf * @param source * @param unusedZipEntries * @param pkg * @param r * @param resolvedPartUri * @throws Docx4JException * @throws InvalidFormatException */ private void getPart(OpcPackage pkg, RelationshipsPart rp, Relationship r, ContentTypeManager ctm) throws Docx4JException, InvalidFormatException, URISyntaxException { Base source = null; String resolvedPartUri = null; if (r.getType().equals(Namespaces.HYPERLINK)) { // Could be Internal or External // Example of Internal is w:drawing/wp:inline/wp:docPr/a:hlinkClick log.info("Encountered (but not loading) hyperlink " + r.getTarget()); return; } else if (r.getTargetMode() == null || !r.getTargetMode().equals("External")) { // Usual case source = rp.getSourceP(); resolvedPartUri = URIHelper.resolvePartUri(rp.getSourceURI(), new URI(r.getTarget())).toString(); // Now drop leading "/' resolvedPartUri = resolvedPartUri.substring(1); // Now normalise it .. ie abc/def/../ghi // becomes abc/ghi // Maybe this isn't necessary with a zip file, // - ZipFile class may be smart enough to do it. // But it is certainly necessary in the JCR case. // resolvedPartUri = (new java.net.URI(resolvedPartUri)).normalize().toString(); // log.info("Normalised, it is " + resolvedPartUri ); } else { // EXTERNAL if (loadExternalTargets && r.getType().equals(Namespaces.IMAGE)) { // It could instead be, for example, of type hyperlink, // and we don't want to try to fetch that log.info("Loading external resource " + r.getTarget() + " of type " + r.getType()); BinaryPart bp = ExternalResourceUtils.getExternalResource(r.getTarget()); pkg.getExternalResources().put(bp.getExternalTarget(), bp); } else { log.info( "Encountered (but not loading) external resource " + r.getTarget() + " of type " + r.getType()); } return; } String relationshipType = r.getType(); Part part; if (pkg.handled.get(resolvedPartUri) != null) { // The source Part (or Package) might have a convenience // method for this part = pkg.getParts().getParts().get(new PartName("/" + resolvedPartUri)); if (source.setPartShortcut(part, relationshipType)) { log.debug( "Convenience method established from " + source.getPartName() + " to " + part.getPartName()); } return; } part = getRawPart(ctm, resolvedPartUri, r); // will throw exception if null // The source Part (or Package) might have a convenience // method for this if (source.setPartShortcut(part, relationshipType)) { log.debug( "Convenience method established from " + source.getPartName() + " to " + part.getPartName()); } if (part instanceof BinaryPart || part instanceof DefaultXmlPart) { // The constructors of other parts should take care of this... part.setRelationshipType(relationshipType); } rp.loadPart(part, r); pkg.handled.put(resolvedPartUri, resolvedPartUri); // unusedZipEntries.put(resolvedPartUri, new Boolean(false)); RelationshipsPart rrp = getRelationshipsPart(part); if (rrp != null) { // recurse via this parts relationships, if it has any addPartsFromRelationships(part, rrp, ctm); String relPart = PartName.getRelationshipsPartName(part.getPartName().getName().substring(1)); // unusedZipEntries.put(relPart, new Boolean(false)); } }
/** * Get a Part (except a relationships part), but not its relationships part or related parts. * Useful if you need quick access to just this part. This can be called directly from outside the * library, in which case the Part will not be owned by a Package until the calling code makes it * so. * * @see To get a Part and all its related parts, and add all to a package, use getPart. * @param zf * @param resolvedPartUri * @return * @throws URISyntaxException * @throws InvalidFormatException */ public static Part getRawPart( ZipFile zf, ContentTypeManager ctm, String resolvedPartUri, Relationship rel) throws Docx4JException { Part part = null; InputStream is = null; try { try { log.debug("resolved uri: " + resolvedPartUri); is = getInputStreamFromZippedPart(zf, resolvedPartUri); // Get a subclass of Part appropriate for this content type // This will throw UnrecognisedPartException in the absence of // specific knowledge. Hence it is important to get the is // first, as we do above. part = ctm.getPart("/" + resolvedPartUri, rel); if (part instanceof org.docx4j.openpackaging.parts.ThemePart) { ((org.docx4j.openpackaging.parts.JaxbXmlPart) part).setJAXBContext(Context.jcThemePart); ((org.docx4j.openpackaging.parts.JaxbXmlPart) part).unmarshal(is); } else if (part instanceof org.docx4j.openpackaging.parts.DocPropsCorePart) { ((org.docx4j.openpackaging.parts.JaxbXmlPart) part) .setJAXBContext(Context.jcDocPropsCore); ((org.docx4j.openpackaging.parts.JaxbXmlPart) part).unmarshal(is); } else if (part instanceof org.docx4j.openpackaging.parts.DocPropsCustomPart) { ((org.docx4j.openpackaging.parts.JaxbXmlPart) part) .setJAXBContext(Context.jcDocPropsCustom); ((org.docx4j.openpackaging.parts.JaxbXmlPart) part).unmarshal(is); } else if (part instanceof org.docx4j.openpackaging.parts.DocPropsExtendedPart) { ((org.docx4j.openpackaging.parts.JaxbXmlPart) part) .setJAXBContext(Context.jcDocPropsExtended); ((org.docx4j.openpackaging.parts.JaxbXmlPart) part).unmarshal(is); } else if (part instanceof org.docx4j.openpackaging.parts.CustomXmlDataStoragePropertiesPart) { ((org.docx4j.openpackaging.parts.JaxbXmlPart) part) .setJAXBContext(Context.jcCustomXmlProperties); ((org.docx4j.openpackaging.parts.JaxbXmlPart) part).unmarshal(is); } else if (part instanceof org.docx4j.openpackaging.parts.digitalsignature.XmlSignaturePart) { ((org.docx4j.openpackaging.parts.JaxbXmlPart) part).setJAXBContext(Context.jcXmlDSig); ((org.docx4j.openpackaging.parts.JaxbXmlPart) part).unmarshal(is); } else if (part instanceof org.docx4j.openpackaging.parts.JaxbXmlPart) { // MainDocument part, Styles part, Font part etc ((org.docx4j.openpackaging.parts.JaxbXmlPart) part).setJAXBContext(Context.jc); ((org.docx4j.openpackaging.parts.JaxbXmlPart) part).unmarshal(is); } else if (part instanceof org.docx4j.openpackaging.parts.WordprocessingML.BinaryPart) { log.debug("Detected BinaryPart " + part.getClass().getName()); if (conserveMemory) { ((BinaryPart) part).setBinaryDataRef(zf.getName(), resolvedPartUri); } else { ((BinaryPart) part).setBinaryData(is); } } else if (part instanceof org.docx4j.openpackaging.parts.CustomXmlDataStoragePart) { // Is it a part we know? try { Unmarshaller u = Context.jc.createUnmarshaller(); Object o = u.unmarshal(is); log.debug(o.getClass().getName()); PartName name = part.getPartName(); if (o instanceof org.opendope.conditions.Conditions) { part = new ConditionsPart(name); ((ConditionsPart) part).setJaxbElement((org.opendope.conditions.Conditions) o); } else if (o instanceof org.opendope.xpaths.Xpaths) { part = new XPathsPart(name); ((XPathsPart) part).setJaxbElement((org.opendope.xpaths.Xpaths) o); } else if (o instanceof org.opendope.questions.Questionnaire) { part = new QuestionsPart(name); ((QuestionsPart) part).setJaxbElement((org.opendope.questions.Questionnaire) o); } else if (o instanceof org.opendope.answers.Answers) { part = new StandardisedAnswersPart(name); ((StandardisedAnswersPart) part).setJaxbElement((org.opendope.answers.Answers) o); } else if (o instanceof org.opendope.components.Components) { part = new ComponentsPart(name); ((ComponentsPart) part).setJaxbElement((org.opendope.components.Components) o); } else if (o instanceof JAXBElement<?> && XmlUtils.unwrap(o) instanceof org.docx4j.bibliography.CTSources) { part = new BibliographyPart(name); ((BibliographyPart) part) .setJaxbElement((JAXBElement<org.docx4j.bibliography.CTSources>) o); } else { log.warn("No known part after all for CustomXmlPart " + o.getClass().getName()); CustomXmlDataStorage data = getCustomXmlDataStorageClass().factory(); is.reset(); data.setDocument(is); // Not necessarily JAXB, that's just our method name ((org.docx4j.openpackaging.parts.CustomXmlDataStoragePart) part).setData(data); } } catch (javax.xml.bind.UnmarshalException ue) { // No ... CustomXmlDataStorage data = getCustomXmlDataStorageClass().factory(); is.reset(); data.setDocument(is); // Not necessarily JAXB, that's just our method name ((org.docx4j.openpackaging.parts.CustomXmlDataStoragePart) part).setData(data); } } else if (part instanceof org.docx4j.openpackaging.parts.XmlPart) { try { ((XmlPart) part).setDocument(is); } catch (Docx4JException d) { // This isn't an XML part after all, // even though ContentTypeManager detected it as such // So get it as a binary part part = getBinaryPart(zf, ctm, resolvedPartUri); if (conserveMemory) { ((BinaryPart) part).setBinaryDataRef(zf.getName(), resolvedPartUri); } else { ((BinaryPart) part).setBinaryData(is); } } } else { // Shouldn't happen, since ContentTypeManagerImpl should // return an instance of one of the above, or throw an // Exception. log.error("No suitable part found for: " + resolvedPartUri); part = null; } } catch (PartUnrecognisedException e) { log.warn("PartUnrecognisedException shouldn't happen anymore!"); // Try to get it as a binary part part = getBinaryPart(zf, ctm, resolvedPartUri); if (conserveMemory) { ((BinaryPart) part).setBinaryDataRef(zf.getName(), resolvedPartUri); } else { ((BinaryPart) part).setBinaryData(is); } } } catch (Exception ex) { // IOException, URISyntaxException ex.printStackTrace(); throw new Docx4JException("Failed to getPart", ex); } finally { if (is != null) { try { is.close(); } catch (IOException exc) { exc.printStackTrace(); } } } return part; }
public static void apply(WordprocessingMLPackage otherPackage, Alterations alterations) throws Docx4JException, JAXBException { if (alterations.getContentTypes() != null) { log.info("replacing [Content_Types].xml"); ContentTypeManager newCTM = new ContentTypeManager(); newCTM.parseContentTypesFile(new ByteArrayInputStream(alterations.getContentTypes())); otherPackage.setContentTypeManager(newCTM); } if (alterations.isEmpty()) return; // -- Deletions List<PartName> removedParts = new ArrayList<PartName>(); for (Alteration a : alterations.getPartsDeleted()) { org.docx4j.xmlPackage.Part xmlpart = a.getPart(); // These deleted parts are likely to be attached to // to otherPackage, but there is no requirement for // that to be so. In other words, you could try // to apply the alterations to some third docx. // It might have already been deleted as a consequence // of recursive deletion... if (removedParts.contains(xmlpart.getName())) continue; // Design decision: how to find owning rels part? // Since part might not be from this package, can't do: // part.getOwningRelationshipPart().removePart(p.getPartName()) // We could store the info when AlteredParts is run, // but lets try to get away without that... // If a part has been deleted, we know its owning rels will // have been modified or deleted. So look for that. // BUT IT WON'T BE THERE IF ITS BEEN DELETED! DOH! // So we have to store the info when AlteredParts is run // after all. Part parentPart = otherPackage.getParts().get(a.getSourcePartName()); if (a.getPart().getContentType().equals(ContentTypes.RELATIONSHIPS_PART)) { parentPart.setRelationships(null); } else { removedParts.addAll( parentPart.getRelationshipsPart().removePart(new PartName(xmlpart.getName()))); } } // -- Modifications for (Alteration a : alterations.getPartsModified()) { log.info("Applying modifications to part: " + a.getPart().getName()); if (a.getPart().getContentType().equals(ContentTypes.RELATIONSHIPS_PART)) { RelationshipsPart newRP = null; // FlatOpcXmlImporter.createRelationshipsPart(a.getPart()); if (a.getSourcePartName().equals("/")) { newRP = otherPackage.getRelationshipsPart(true); // otherPackage.setRelationships(newRP); // newRP.setSourceP(otherPackage); } else { Part parentPart = otherPackage.getParts().get(a.getSourcePartName()); newRP = parentPart.getRelationshipsPart(true); // parentPart.setRelationships(newRP); // newRP.setSourceP(parentPart); } FlatOpcXmlImporter.populateRelationshipsPart(newRP, a.getPart().getXmlData().getAny()); } else { Part targetPart = otherPackage.getParts().get(new PartName(a.getPart().getName())); if (targetPart == null) { log.error( "Couldn't find " + a.getPart().getName() + " @ " + a.getSourcePartName().getName()); continue; } Part tmpPart = FlatOpcXmlImporter.getRawPart(otherPackage.getContentTypeManager(), a.getPart(), null); if (targetPart instanceof JaxbXmlPart) { ((JaxbXmlPart) targetPart).setJaxbElement(((JaxbXmlPart) tmpPart).getJaxbElement()); } else if (targetPart instanceof XmlPart) { ((XmlPart) targetPart).setDocument(((XmlPart) tmpPart).getDocument()); } else if (targetPart instanceof CustomXmlDataStoragePart) { ((CustomXmlDataStoragePart) targetPart) .setData(((CustomXmlDataStoragePart) tmpPart).getData()); // TODO: check that } else if (targetPart instanceof BinaryPart) { ((BinaryPart) targetPart).setBinaryData(((BinaryPart) tmpPart).getBuffer()); } else { log.error("TODO: handle " + targetPart.getClass().getName()); } } } // -- Additions for (Alteration a : alterations.getPartsAdded()) { log.info("Adding part: " + a.getPart().getName()); if (a.getPart().getContentType().equals(ContentTypes.RELATIONSHIPS_PART)) { RelationshipsPart newRP = null; // FlatOpcXmlImporter.createRelationshipsPart(a.getPart()); if (a.getSourcePartName().equals("/")) { newRP = otherPackage.getRelationshipsPart(true); // otherPackage.setRelationships(newRP); // newRP.setSourceP(otherPackage); } else { Part parentPart = otherPackage.getParts().get(a.getSourcePartName()); newRP = parentPart.getRelationshipsPart(true); // parentPart.setRelationships(newRP); // newRP.setSourceP(parentPart); } FlatOpcXmlImporter.populateRelationshipsPart(newRP, a.getPart().getXmlData().getAny()); } else { Part parentPart = otherPackage.getParts().get(a.getSourcePartName()); Part newPart = FlatOpcXmlImporter.getRawPart(otherPackage.getContentTypeManager(), a.getPart(), null); // There will already be a rel for the new part, // since we will already have modified or added the rels part // so don't do AddTargetPart (which will probably create a new rel id, // which will cause problems) newPart.setOwningRelationshipPart(parentPart.getRelationshipsPart()); newPart .getSourceRelationships() .add(parentPart.getRelationshipsPart().getRel(new PartName(a.getPart().getName()))); otherPackage.getParts().put(newPart); newPart.setPackage(otherPackage); // TODO: add content type if necessary } } }