rtmc
rtmc

Reputation: 109

Merge word(docx) documents with DOCX4J: how to copy images?

I need to merge two (or more, but let's stick to two) word documents (docx) with docx4j. My approach to merge is copy all body children from one document and append to another. Then, I just rearrange some stuff. I have been using it for two years, and it is fine for my purpose.

Here is a simple example:

first.docx = simple text
second.docx = simple text + image

    File first = new File("first.docx");
    File second = new File("second.docx");

    WordprocessingMLPackage f = WordprocessingMLPackage.load(first);
    WordprocessingMLPackage s = WordprocessingMLPackage.load(second);

    List body = s.getMainDocumentPart().getJAXBNodesViaXPath("//w:body", false);
    for(Object b : body){
        List filhos = ((org.docx4j.wml.Body)b).getContent();
        for(Object k : filhos)
            f.getMainDocumentPart().addObject(k);
    }

    List blips = s.getMainDocumentPart().getJAXBNodesViaXPath("//a:blip", false);
    for(Object el : blips){
        try {
            CTBlip blip = (CTBlip) el;

            RelationshipsPart parts = s.getMainDocumentPart().getRelationshipsPart();
            Relationship rel = parts.getRelationshipByID(blip.getEmbed());

            RelationshipsPart docRels = f.getMainDocumentPart().getRelationshipsPart();

            rel.setId(null);
            docRels.addRelationship(rel);
            blip.setEmbed(rel.getId());

            f.getMainDocumentPart().addTargetPart(s.getParts().getParts().get(new PartName("/word/"+rel.getTarget())));

        } catch (Exception ex){}
    }

    File saved = new File("saved.docx");
    f.save(saved);

    Desktop.getDesktop().open(saved);

The problem is when I save. This errors come out:

    org.docx4j.openpackaging.exceptions.Docx4JException: Failed to add parts from relationships of /
at org.docx4j.openpackaging.io3.Save.addPartsFromRelationships(Save.java:390)
at org.docx4j.openpackaging.io3.Save.save(Save.java:192)
at org.docx4j.openpackaging.packages.OpcPackage.save(OpcPackage.java:441)
at org.docx4j.openpackaging.packages.OpcPackage.save(OpcPackage.java:406)

Caused by: org.docx4j.openpackaging.exceptions.Docx4JException: Failed to add parts from relationships of /word/document.xml
at org.docx4j.openpackaging.io3.Save.addPartsFromRelationships(Save.java:390)
at org.docx4j.openpackaging.io3.Save.savePart(Save.java:442)
at org.docx4j.openpackaging.io3.Save.addPartsFromRelationships(Save.java:385)
... 4 more

Caused by: org.docx4j.openpackaging.exceptions.Docx4JException: Failed to put binary part
at
org.docx4j.openpackaging.io3.stores.ZipPartStore.saveBinaryPart(ZipPartStore.java:398)
at org.docx4j.openpackaging.io3.Save.savePart(Save.java:418)
at org.docx4j.openpackaging.io3.Save.addPartsFromRelationships(Save.java:385)
... 6 more

Caused by: java.io.IOException: part '/word/media/image1.jpg' not found
at
org.docx4j.openpackaging.io3.stores.ZipPartStore.saveBinaryPart(ZipPartStore.java:361)
... 8 more

Exception in thread "main" org.docx4j.openpackaging.exceptions.Docx4JException: Failed to add parts from relationships of /
at org.docx4j.openpackaging.io3.Save.addPartsFromRelationships(Save.java:390)
at org.docx4j.openpackaging.io3.Save.save(Save.java:192)
at org.docx4j.openpackaging.packages.OpcPackage.save(OpcPackage.java:441)
at org.docx4j.openpackaging.packages.OpcPackage.save(OpcPackage.java:406)

Caused by: org.docx4j.openpackaging.exceptions.Docx4JException: Failed to add parts from relationships of /word/document.xml
at org.docx4j.openpackaging.io3.Save.addPartsFromRelationships(Save.java:390)
at org.docx4j.openpackaging.io3.Save.savePart(Save.java:442)
at org.docx4j.openpackaging.io3.Save.addPartsFromRelationships(Save.java:385)
... 4 more

Caused by: org.docx4j.openpackaging.exceptions.Docx4JException: Failed to put binary part
at
org.docx4j.openpackaging.io3.stores.ZipPartStore.saveBinaryPart(ZipPartStore.java:398)
at org.docx4j.openpackaging.io3.Save.savePart(Save.java:418)
at org.docx4j.openpackaging.io3.Save.addPartsFromRelationships(Save.java:385)
... 6 more

Caused by: java.io.IOException: part '/word/media/image1.jpg' not found
at
org.docx4j.openpackaging.io3.stores.ZipPartStore.saveBinaryPart(ZipPartStore.java:361)
... 8 more

Any light here to solve it?

1) I dont want altchunk, it sucks. 2) The commercial (Enterprise) version of docx4j can do it, but I'm looking for FOSS.

Thanks

Upvotes: 1

Views: 9147

Answers (2)

hakanmoral
hakanmoral

Reputation: 51

Here we combined together from the posts a real working code:

List<Object> blips = s.getMainDocumentPart().getJAXBNodesViaXPath("//a:blip", false);
for (Object el : blips) {
    try {

        CTBlip blip = (CTBlip) el;
        RelationshipsPart parts = s.getMainDocumentPart().getRelationshipsPart();
        Relationship rel = parts.getRelationshipByID(blip.getEmbed());
        Part part = parts.getPart(rel);

        if (part instanceof ImagePngPart)
            System.out.println(((ImagePngPart) part).getBytes());
        if (part instanceof ImageJpegPart)
            System.out.println(((ImageJpegPart) part).getBytes());
        if (part instanceof ImageBmpPart)
            System.out.println(((ImageBmpPart) part).getBytes());
        if (part instanceof ImageGifPart)
            System.out.println(((ImageGifPart) part).getBytes());
        if (part instanceof ImageEpsPart)
            System.out.println(((ImageEpsPart) part).getBytes());
        if (part instanceof ImageTiffPart)
            System.out.println(((ImageTiffPart) part).getBytes());

        Relationship newrel = f.getMainDocumentPart().addTargetPart(part, AddPartBehaviour.RENAME_IF_NAME_EXISTS);

        blip.setEmbed(newrel.getId());
        f.getMainDocumentPart().addTargetPart(s.getParts().getParts().get(new PartName("/word/" + rel.getTarget())));

    } catch (Exception ex) {
        ex.printStackTrace();
    }
}

this snippet takes images with decoration in docx from s document to f document. Sysouts needed because of a reason I have forgotten but without it lib can not determine the mime of the image.

Upvotes: 3

JasonPlutext
JasonPlutext

Reputation: 15878

Manipulate the blips in s before adding them to f. In other words, swap the order of your for loops.

Then in your blip manipulation, what you need to do is:

  • get the part of interest
  • Rel rel = f.getMainDocumentPart().addTargetPart
  • update the relId in the blip from rel.getId

Now add the content of s to f. You could just use addAll to do the job without the nested loop. Also there's only one body object, so you don't need the outer loop.

Obviously this answer is limited to handling only CTBlip, and then only embedded ones. There's a lot more to a complete solution to merging docx files...

Note: I wrote the code for merging documents in docx4j Enterprise

Upvotes: 3

Related Questions