Page 1 of 1

org.docx4j.Docx4J part xxx not found

PostPosted: Thu Feb 08, 2018 5:54 am
by Zelarith
Hi,

I'm trying to merge two docx documents in one.
I've found a lot of forums posts about this in various forums, but none seemed to have been dealing with my problem :
Some of my documents have headers and/or footers while others don't.

As a result, merging the documents can get really messy with headers of one document 'overflowing" on the others.

What I want is to append documents in a new page.

Looking into the resulting document (opened as a zip), modifying it manually to get it right so I would understand what I needed to do programatically, I figured I needed to remove the sectPr from the body of my documents, put it in a P > pPr I would add to the body, and then create a sectPr for my Body with an empty Header and Footer (if I'm wrong, do correct me !)

Anyway, my problem is that, when I programatically create these new Header and Footer, it seems they don't physically exist (i.e. as a File inside the docx archive), wich makes my code crash whenever I want to Save my document to a byte array. I get a "part 'word/header.xml' not found".

Closest issue I could find was this one : docx-java-f6/merge-document-error-t2584.html but the suggested solution didn't seem to solve my issue.

Here's what my code look like (I don't have my code available, so I'm just trying to recreate it from memory)

Code: Select all
private static void stopHeaderAndFooterOverflow(WordprocessingMLPackage doc) {
    SectPr bodySectPr = doc.getMainDocumentPart().getContent().getBody().getSectPr();
    if(bodySectPr != null) {
        //Sets the (hidden) SectPr of the body inside a paragraph object (results in a section / page break)
        P p = factory.createP();
        PPr pPr = factory.createPPr();
        p.setPPr(pPr);
        p.getPPr().setSectPr(bodySectPr);

        //Changes the body's SectPr to a clean SectPr with an empty Header and Footer
       
        //First, creates the Header and the Footer programatically
        CTRel footer = null, header = null;

        footer = factory.createFooterReference();
        footer.setType(Type.DEFAULT);

        header = factory.createHeaderReference();
        header.setType(Type.DEFAULT);

        //Empties the header / footer info, but it seems it's not enough : if I don't define a new header / footer, it will overflow from the P > pPr > SectPr we defined above !
        bodySectPr.getEgHdrFtrReferences.clear();

        //Sets the header / footer info to the ones we programatically created to be empty
        bodySectPr.getEgHdrFtrReferences.add(footer);
        bodySectPr.getEgHdrFtrReferences.add(header);
    }
}

private static void mergeDocuments(WordprocessingMLPackage main, WordprocessingMLPackage chunk) {
    //Transforms the docx "chunk" to a byte array so I could inject it in the docx "main"
    Save saver = new Save(chunk);
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    saver.save(baos); // <<<<<<<<<< Program crashs here

    main.getMainDocumentPart().addAltChunk(AltChunkType.WordprocessingML, baos.toByteArray());
}


I hope I could recreate my code as closely as I needed to, if further informations are required, ask away.

Thanks by advance for any help / insight provided !

Re: org.docx4j.Docx4J part xxx not found

PostPosted: Thu Feb 08, 2018 7:21 am
by jason
Basically, you need to copy the corresponding header/footer parts over (addTargetPart), and be sure to use the new relIds.

That much is easy. But unless your documents are simple and predictable, there are a lot of other rels you'll need to manage, not to mention styles, numbering etc. That's why "MergeDocx" is offered as part of Plutext's commercial Docx4j Enterprise.

Re: org.docx4j.Docx4J part xxx not found

PostPosted: Thu Feb 08, 2018 7:09 pm
by Zelarith
Sorry, I had forgotten a couple lines of my code, now that I have it on my computer, my stopHeaderAndFooterOverflow was actually like this :

Code: Select all
   private static void stopHeaderAndFooterOverflow(WordprocessingMLPackage document) throws Exception {
      if(document.getMainDocumentPart().getContents().getBody().getSectPr() != null) {
         SectPr originalSectPr = document.getMainDocumentPart().getContents().getBody().getSectPr();
         SectPr newBodySectPr = factory.createSectPr();
         CTRel blankHeader = null, blankFooter = null;
         
         FooterPart footerPart = new FooterPart();
         footerPart.setPartName(new PartName("/word/customBlankFooter.xml"));
         Relationship footerRel = document.getMainDocumentPart().addTargetPart(footerPart, AddPartBehaviour.REUSE_EXISTING);
         
         HeaderPart headerPart = new HeaderPart();
         headerPart.setPartName(new PartName("/word/customBlankHeader.xml"));
         Relationship headerRel = document.getMainDocumentPart().addTargetPart(headerPart, AddPartBehaviour.REUSE_EXISTING);
         
         blankFooter = factory.createFooterReference();
         blankFooter.setId(footerRel.getId());
         ((FooterReference) blankFooter).setType(HdrFtrRef.DEFAULT);
         
         blankHeader = factory.createHeaderReference();
         blankHeader.setId(headerRel.getId());
         ((HeaderReference) blankHeader).setType(HdrFtrRef.DEFAULT);
            
         newBodySectPr.getEGHdrFtrReferences().add(blankFooter);
         newBodySectPr.getEGHdrFtrReferences().add(blankHeader);
         document.getMainDocumentPart().getContents().getBody().setSectPr(newBodySectPr);
         P paragraphe = factory.createP();
         paragraphe.setPPr(factory.createPPr());
         paragraphe.getPPr().setSectPr(originalSectPr);
         document.getMainDocumentPart().getContent().add(paragraphe);
      }
   }


So I was already using addTargetPart and using the relationship ID. I'm not sure I would be getting this error otherwise.
EDIT : yeah, I tried my incomplete code up there, it only gives word errors (when opening the docx), no runtime exception on my server, it's actually when adding the call to function addTargetPart that it ends up giving this specific "part " + part.partName() + " not found" server exception. Seems like I'm missing something.

I also tried calling addTargetPart from document instead of document.getMainDocumentPart, but it didn't change my issue.

I'm kinda new to docx4j, I'm just trying to fix an already existing code I didn't write with this "stopHeaderAndFooterOverflow" function.

EDIT :
Well, the Exception was thrown because the byte array representing my part was null. I thought it would be just empty, but it seems I needed to add something in my Header / Footer parts, but now I'm left with Word errors.
From here on, I should be able to fall back on my feet. Thanks :)