Page 1 of 1

Replacing/copying header/footer

PostPosted: Wed Feb 23, 2011 6:15 am
by kjetilhp
Hi, main question here is how do I go about replacing headers and footers in a document.. I've tried a couple of different options based on the examples.

First, copying header and footer from a document with header/footer to a document with none of these
Code: Select all
public static void copyHeaderAndFooter(String sourcefile, String targetfile) throws Exception {
        WordprocessingMLPackage source;
        WordprocessingMLPackage target;
        if (sourcefile.endsWith(".docx")) {
            try {
                source = WordprocessingMLPackage.load(new File(sourcefile));
                target = WordprocessingMLPackage.load(new File(targetfile));

                // Find the part we want to copy
                RelationshipsPart rp = source.getMainDocumentPart().getRelationshipsPart();
                Relationship rel = rp.getRelationshipByType(Namespaces.HEADER);
                Part p = rp.getPart(rel);
                log.debug(p.getPartName().getName());
                p.setPartName(new PartName("/word/header1.xml"));
                // Now try adding it
                target.getMainDocumentPart().addTargetPart(p);

                rp = source.getMainDocumentPart().getRelationshipsPart();
                rel = rp.getRelationshipByType(Namespaces.FOOTER);
                p = rp.getPart(rel);
                log.debug(p.getPartName().getName());
                p.setPartName(new PartName("/word/footer1.xml"));
                // Now try adding it
                target.getMainDocumentPart().addTargetPart(p);

                target.save(new File(targetfile));
            } catch (Docx4JException e) {
                log.error(e);
            }
        }

    }


results in nothing at all.. no headers & footers in target document (based on the CopyPart sample)


Then, I tried getting the raw xml files, based on the ImportForeignPart sample
Code: Select all
public static void replaceHeader(String file, String baseDir, String expandedDir) throws Exception {
        WordprocessingMLPackage wordMLPackage = null;
        if (file.endsWith(".docx")) {
            try {
                wordMLPackage = WordprocessingMLPackage.load(new File(baseDir + file));

                InputStream in = new FileInputStream(baseDir + expandedDir + "/[Content_Types].xml");
                ContentTypeManager externalCtm = new ContentTypeManager();

                externalCtm.parseContentTypesFile(in);

                // Example of a part which become a rel of the word document
                in = new FileInputStream(baseDir + expandedDir + "/word/header1.xml");
                attachForeignPart(wordMLPackage,
                        wordMLPackage.getMainDocumentPart(),
                        externalCtm,
                        "word/header1.xml",
                        in);

                // Example of a part which become a rel of the package
                in = new FileInputStream(baseDir + expandedDir + "/docProps/app.xml");
                attachForeignPart(wordMLPackage, wordMLPackage,
                        externalCtm, "docProps/app.xml", in);


                wordMLPackage.save(new File(baseDir + "header-added.docx"));
            } catch (Docx4JException e) {
                log.error(e);
            }
        }
    }

public static void attachForeignPart(WordprocessingMLPackage wordMLPackage,
                                         Base attachmentPoint,
                                         ContentTypeManager foreignCtm,
                                         String resolvedPartUri, InputStream is) throws Exception {
        Part foreignPart = Load.getRawPart(is, foreignCtm, resolvedPartUri, null);
        // the null means this won't work for an AlternativeFormatInputPart
        attachmentPoint.addTargetPart(foreignPart);
        // Add content type
        ContentTypeManager packageCtm = wordMLPackage.getContentTypeManager();
        packageCtm.addOverrideContentType(foreignPart.getPartName().getURI(), foreignPart.getContentType());
        log.debug("Attached foreign part: " + resolvedPartUri);
    }



this one results in a document without a header, however, if I unzip the produced document I find the correct header in /word/header1.xml...

going further... I managed to import a header and add it to my document based on the HeaderFooterCreate sample, but this only adds to the existing header in the document, so I thought I'd use code from StripParts sample to remove headers/footers first...

so I added this If statement to the sample
Code: Select all
                   } else if (stripHeaderFooter &&
                            part instanceof org.docx4j.openpackaging.parts.WordprocessingML.FooterPart
                            || part instanceof org.docx4j.openpackaging.parts.WordprocessingML.HeaderPart) {
                        deletions.add(r);
                        sb.append(".. DELETED");
                        if (part.getRelationshipsPart() == null) {
                            sb.append(".. no rels");
                        } else {
                            traverseRelationships(wordMLPackage, part.getRelationshipsPart(), sb, indent + "    ");
                        }
                    }


but this only results in a document with broken relationships/references to header and footer when opening in Word...

long post and a little sidetracked but individually I would think that all of these would work, though I'm basically looking for the right code to open a document and replace it's header and footer with my own content...

cheers,
Kjetil

Re: Replacing/copying header/footer

PostPosted: Wed Feb 23, 2011 7:46 pm
by jason
kjetilhp wrote:
Code: Select all
public static void copyHeaderAndFooter(String sourcefile, String targetfile) throws Exception {
        WordprocessingMLPackage source;
        WordprocessingMLPackage target;
        if (sourcefile.endsWith(".docx")) {
            try {
                source = WordprocessingMLPackage.load(new File(sourcefile));
                target = WordprocessingMLPackage.load(new File(targetfile));

                // Find the part we want to copy
                RelationshipsPart rp = source.getMainDocumentPart().getRelationshipsPart();
                Relationship rel = rp.getRelationshipByType(Namespaces.HEADER);
                Part p = rp.getPart(rel);
                log.debug(p.getPartName().getName());
                p.setPartName(new PartName("/word/header1.xml"));
                // Now try adding it
                target.getMainDocumentPart().addTargetPart(p);

                rp = source.getMainDocumentPart().getRelationshipsPart();
                rel = rp.getRelationshipByType(Namespaces.FOOTER);
                p = rp.getPart(rel);
                log.debug(p.getPartName().getName());
                p.setPartName(new PartName("/word/footer1.xml"));
                // Now try adding it
                target.getMainDocumentPart().addTargetPart(p);

                target.save(new File(targetfile));
            } catch (Docx4JException e) {
                log.error(e);
            }
        }

    }


results in nothing at all.. no headers & footers in target document (based on the CopyPart sample)


At a glance I would have expected this to add the parts to the target docx.

But you aren't doing anything to add header/footer refs to sectPr. See my third reply below for how to do this.

Re: Replacing/copying header/footer

PostPosted: Wed Feb 23, 2011 7:50 pm
by jason
kjetilhp wrote:Then, I tried getting the raw xml files, based on the ImportForeignPart sample
Code: Select all
public static void replaceHeader(String file, String baseDir, String expandedDir) throws Exception {
        WordprocessingMLPackage wordMLPackage = null;
        if (file.endsWith(".docx")) {
            try {
                wordMLPackage = WordprocessingMLPackage.load(new File(baseDir + file));

                InputStream in = new FileInputStream(baseDir + expandedDir + "/[Content_Types].xml");
                ContentTypeManager externalCtm = new ContentTypeManager();

                externalCtm.parseContentTypesFile(in);

                // Example of a part which become a rel of the word document
                in = new FileInputStream(baseDir + expandedDir + "/word/header1.xml");
                attachForeignPart(wordMLPackage,
                        wordMLPackage.getMainDocumentPart(),
                        externalCtm,
                        "word/header1.xml",
                        in);

                // Example of a part which become a rel of the package
                in = new FileInputStream(baseDir + expandedDir + "/docProps/app.xml");
                attachForeignPart(wordMLPackage, wordMLPackage,
                        externalCtm, "docProps/app.xml", in);


                wordMLPackage.save(new File(baseDir + "header-added.docx"));
            } catch (Docx4JException e) {
                log.error(e);
            }
        }
    }

public static void attachForeignPart(WordprocessingMLPackage wordMLPackage,
                                         Base attachmentPoint,
                                         ContentTypeManager foreignCtm,
                                         String resolvedPartUri, InputStream is) throws Exception {
        Part foreignPart = Load.getRawPart(is, foreignCtm, resolvedPartUri, null);
        // the null means this won't work for an AlternativeFormatInputPart
        attachmentPoint.addTargetPart(foreignPart);
        // Add content type
        ContentTypeManager packageCtm = wordMLPackage.getContentTypeManager();
        packageCtm.addOverrideContentType(foreignPart.getPartName().getURI(), foreignPart.getContentType());
        log.debug("Attached foreign part: " + resolvedPartUri);
    }



this one results in a document without a header, however, if I unzip the produced document I find the correct header in /word/header1.xml...


Same issue .. you need to add header/footer refs to sectPr.

Re: Replacing/copying header/footer

PostPosted: Wed Feb 23, 2011 7:57 pm
by jason
kjetilhp wrote:going further... I managed to import a header and add it to my document based on the HeaderFooterCreate sample, but this only adds to the existing header in the document, so I thought I'd use code from StripParts sample to remove headers/footers first...

so I added this If statement to the sample
Code: Select all
                   } else if (stripHeaderFooter &&
                            part instanceof org.docx4j.openpackaging.parts.WordprocessingML.FooterPart
                            || part instanceof org.docx4j.openpackaging.parts.WordprocessingML.HeaderPart) {
                        deletions.add(r);
                        sb.append(".. DELETED");
                        if (part.getRelationshipsPart() == null) {
                            sb.append(".. no rels");
                        } else {
                            traverseRelationships(wordMLPackage, part.getRelationshipsPart(), sb, indent + "    ");
                        }
                    }


but this only results in a document with broken relationships/references to header and footer when opening in Word...


Same issue .. here is the code in the HeaderFooterCreate sample which adds the rel to the main document part:

Code: Select all
   public static void createHeaderReference(
         WordprocessingMLPackage wordprocessingMLPackage,
         Relationship relationship )
         throws InvalidFormatException {

      List<SectionWrapper> sections = wordprocessingMLPackage.getDocumentModel().getSections();
         
      SectPr sectPr = sections.get(sections.size() - 1).getSectPr();
      // There is always a section wrapper, but it might not contain a sectPr
      if (sectPr==null ) {
         sectPr = objectFactory.createSectPr();
         wordprocessingMLPackage.getMainDocumentPart().addObject(sectPr);
         sections.get(sections.size() - 1).setSectPr(sectPr);
      }

      HeaderReference headerReference = objectFactory.createHeaderReference();
      headerReference.setId(relationship.getId());
      headerReference.setType(HdrFtrRef.DEFAULT);
      sectPr.getEGHdrFtrReferences().add(headerReference);// add header or
      // footer references
   }



You need to delete the existing header/footer references.

Re: Replacing/copying header/footer

PostPosted: Fri Feb 25, 2011 12:00 am
by kjetilhp
jason wrote:You need to delete the existing header/footer references.


Code: Select all
                List<SectionWrapper> sectionWrappers = source.getDocumentModel().getSections();
                HeaderPart headerPart = null;
                for (SectionWrapper sw : sectionWrappers) {
                    headerPart = sw.getHeaderFooterPolicy().getDefaultHeader();
                    source.getMainDocumentPart().getRelationshipsPart().removeRelationship(headerPart.getPartName());
                    List<CTRel> rel = sw.getSectPr().getEGHdrFtrReferences();
                    for (CTRel ctRel : rel) {
                        if (rel instanceof HeaderReference) {
                            HeaderReference hr = (HeaderReference) ctRel;
                            if (hr.getType().equals(HdrFtrRef.DEFAULT)) {
                                sw.getSectPr().getEGHdrFtrReferences().remove(hr);
                            }
                            log.debug(rel.toString() +" removed!");
                        }
                    }
                }


tried this code for removing the reference, still get the same problem when opening in Word, missing part... also tried adding a new empty header with the same result...

Code: Select all
                headerPart = new HeaderPart();
                headerPart.setPackage(source);
                Hdr ftr = objectFactory.createHdr();
                ftr.getEGBlockLevelElts().add(getP());
                headerPart.setJaxbElement(ftr);
                source.getMainDocumentPart().addTargetPart(headerPart);
                Relationship rel = source.getMainDocumentPart().getRelationshipsPart().getRel(headerPart.getPartName());

                HeaderReference headerReference = objectFactory.createHeaderReference();
                headerReference.setId(rel.getId());
                headerReference.setType(HdrFtrRef.DEFAULT);
                SectPr sectPr = sectionWrappers.get(sectionWrappers.size() - 1).getSectPr();
                sectPr.getEGHdrFtrReferences().add(headerReference);

Re: Replacing/copying header/footer

PostPosted: Fri Feb 25, 2011 10:10 am
by jason
Are you adding your new header / footer reference?

I think you'll need to provide your complete code and sample docx for further advice.

Re: Replacing/copying header/footer

PostPosted: Sat Feb 26, 2011 1:10 am
by kjetilhp
I think so, the last code I provided was directly taken from the CreateHeaderFooter sample..

Code: Select all
sectPr.getEGHdrFtrReferences().add(headerReference);


I'll upload the code & docs shortly...

Re: Replacing/copying header/footer

PostPosted: Fri Mar 04, 2011 9:31 am
by kjetilhp
after some more testing I found that it was just bad coding on my part, sorry about that... so for anybody interested

the line:
Code: Select all
if (rel instanceof HeaderReference) {


shoud of course be:
Code: Select all
if (ctRel instanceof HeaderReference) {

Re: Replacing/copying header/footer

PostPosted: Fri Mar 04, 2011 11:16 pm
by jason
Glad to hear you got it working. Thanks for taking the time to let us know.

Re: Replacing/copying header/footer

PostPosted: Sat Mar 05, 2011 12:06 am
by kjetilhp
Should also be noted that if anyone needs to use the stripParts sample to remove Header/Footer parts this code needs to be added...

Re: Replacing/copying header/footer

PostPosted: Wed Mar 23, 2011 2:27 am
by cristifl
Hello,
I need to write code that does the same thing as discussed here.

After reading this topic, I don't understand what else should be added after copying the parts (the piece of code in the beggining)
and creating header/footer refs to sectPr (like in HeaderFooterCreate sample).

What references should be removed ?
The source file doesn't have any header/footer so I don't think I should delete any reference here.

The errors that I get is at save() method, it throws exception Failed to add parts from relationships .

Thank you,
Cristi

Re: Replacing/copying header/footer

PostPosted: Wed Mar 23, 2011 6:31 am
by cristifl
Sorry, I found out how it should be working.
The Header I copied had a picture, so a reference existed. After copying the part representing the picture it worked.
From what I understood so far, code to delete every Relationship to referenced parts is:
Code: Select all
//remove relations of part p
if (p.getRelationshipsPart()!= null ) {
   RelationshipsPart rp=p.getRelationshipsPart();
   List<Relationship> deletions = new ArrayList<Relationship>();
   for ( Relationship r : rp.getRelationships().getRelationship() )
      deletions.add(r);
   for ( Relationship r : deletions) {
      rp.removeRelationship(r);
}

Re: Replacing/copying header/footer

PostPosted: Wed Aug 07, 2013 7:18 pm
by zg2pro
Hi,

I tried out your code but when erasing the RelationshipsPart I'm actually erasing the whole header, I can't make it work when there are images and I'm always getting a org.apache.poi.openxml4j.exceptions.InvalidFormatException: The part /word/media/image2.png does not have any content type !

I've tried also to override the format instead of erasing the RelationshipsPart like this:
Code: Select all
packageCtm.addOverrideContentType(rp.getSourceURI(), rp.getContentType());

or like that:
Code: Select all
if (rp.getPartName().getURI().toString().contains("word/media/image")){
   rp.setContentType(new  org.docx4j.openpackaging.contenttype.ContentType(org.docx4j.openpackaging.contenttype.ContentTypes.IMAGE_JPEG));
   rp.setRelationshipType(Namespaces.IMAGE);
}

but it don't work either.

How would you copy the part representing the picture instead of deleting it ?

Re: Replacing/copying header/footer

PostPosted: Wed Aug 07, 2013 9:54 pm
by jason
zg2pro wrote: I'm always getting a org.apache.poi.openxml4j.exceptions.InvalidFormatException


org.apache.poi.openxml4j is not part of docx4j, so we can't help with that.

Please feel free to post again:

1. explaining what you are trying to do
2. posting the docx4j code you have written and the docx file you are running it against. You'd better include your import statements :-)
3. what went wrong

Re: Replacing/copying header/footer

PostPosted: Thu Aug 08, 2013 11:02 pm
by zg2pro
Ok, you opened my eyes. I believe it fails because after my document is created by docx4j, it is indexed by a lucene engine and lucene uses openxml4j. Thank you anyway.