/**
   * 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;
  }
  /**
   * 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;
  }
  /**
   * 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;
  }
예제 #4
0
  /**
   * @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 );
    //		}
  }
  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;
  }
예제 #6
0
  /**
   * @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);
        }
      }
    }
  }
예제 #7
0
  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
      }
    }
  }
예제 #8
0
  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());
            }
          }
        }
      }
    }
  }
  /**
   * 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;
  }