Featured Post

SQL Query in SharePoint

The "FullTextSqlQuery" object's constructor requires an object that has context but don't be fooled. This context will no...

Showing posts with label header. Show all posts
Showing posts with label header. Show all posts

Thursday, December 5, 2013

Understanding word headers, footers and section break manipulation

To access a header or footer you can double click at the top or bottom of your word document page.


I'm going to go through some of the areas of the headers/footers. Under the options section there are 2 check boxes "Different First Page" and "Different Odd & Even Pages". So if you check these boxes word will allow the first page to have a different header & footer. The odd and even check box will allow every second page to have a different header/footer. So how does this look in the xml?
In the xml you can see 3 headerReferences and 3 footReferences and they are a child of the "w:sectPr"/section properties node.  A document must contain at least one section property at the bottom of the body and it can contain more depending on whether you add section breaks inside the document. There are 3 types of header/footer references that can be associated with one section property - "even", "default" and "first".

To access the header/footer parts in a document using open xml you can use the following code

byte[] document = File.ReadAllBytes("C:\Demo Doc.docx");
using (MemoryStream memoryStream = new MemoryStream())
{
  memoryStream.Write(document, 0, document.Length);
  using (WordprocessingDocument wordprocessingDocument =     WordprocessingDocument.Open(memoryStream, true))
  {
     foreach (HeaderPart headerPart in wordprocessingDocument.MainDocumentPart.HeaderParts)
     {
         // do something
     }

     foreach (FooterPart footerPart in wordprocessingDocument.MainDocumentPart.FooterParts)
     {
        // do something
     }
  }
}

Another way to have different headers/footers on a page is to insert a section break in your document. To do this go to Page Layout -> Breaks -> Next Page.

Go into the header/footer and then unselect the "Link to Previous" option. Then modify the header/footer.

Something important to note about section breaks if you are planning on modifying them programmatically is the placement of the section break in document.xml. As I mentioned earlier, the document.xml will always have a section break at the end of the body element. The elements that are applicable to this section are all the ones above it - until the previous section break (if there is one). But all the other section breaks refer to the elements below them - until the next section break.

For example. I've created a new word document and inserted 2 section breaks. On each page I have added text to indicate what page number it is. Look at the xml that got generated to understand further what I am trying to explain:

What is also very important to note here is that the last section property exists as a child of body however the other section properties are nested inside a paragraph object (w:p).

Tuesday, January 25, 2011

Moving images in word headers using OpenXML

From what I discovered moving images from one header in a document to another seems to be a huge problem for most people so I decided to try do this myself. I'm not 100% sure what other issues it might cause but I when I run the "Validate" command in the OpenXML Productivity Tools I get no errors and the images I'm moving are displaying correctly so for now I'm giving it the go ahead :)


Background


To move data from one document to another you can use altChunk but this does not copy headers and footers. If this doesn't phase you - you can read more about altChunk on Brian Jones blog: entry: http://blogs.msdn.com/b/brian_jones/archive/2008/12/08/the-easy-way-to-assemble-multiple-word-documents.aspx.


Brian Jones has tons of examples of how to manipulate documents. He also has some nice information about some open source code called "DocumentBuilder" which I stumbled upon but when I run the "Validate" command using the OpenXML Productivity Tools I get an error for each document I tried to merge so I decided this was not a good option for me. If you are interested though check it out here:  http://www.pubsub.com/events/5e5f8d3fd2346a54739b8b6bb0438b82


Another way to move elements from one part of a document to another is to actually use the "FeedData" method and pass in the other element by getting its Stream. This however doesn't work when moving images in headers between documents.

Looking Deeper




Create a blank document and insert any image into it's header. Use the OpenXML Productivity Tool to open the document and expand it's /word/document.xml node. You will see that there are few header xml nodes inside the document xml node. Thats because there are 3 types of headers inside a document or none at all. The three types distuinguish between First, Default and Even.


If you go to expand "w:document", "w:body" you will see "w:sectPr". If you reflect this node you will see the references to your headers. In my document my "Default" header has been referenced using id "rId8". If you reflect your "/word/document.xml node you should see which Header has been assigned "rId8". This helped me to find out which HeaderPart I need to retrieve to find the elements I'm looking for.


Start Coding


A HeaderPart can contain a few things but if I just have a plain header with an image it should contain a "/word/media/image1.png" element as well as a "w:hdr" element. What I did was create a new Header by passing the old headers OuterXML into the constructor and manually copy paste the Images seperately and this worked. Here is some example code:


reportImagePart.FeedData(headerImagePart.GetStream());

private static void ReplaceImages(HeaderPart headerPart, HeaderPart reportHeaderPart) 

{ 
  foreach (IdPartPair partPair in headerPart.Parts)   
  {
    OpenXmlPart openXmlPart = partPair.OpenXmlPart;   
    Type type = openXmlPart.GetType();   
    if (type.Name == "ImagePart")     
    {
      ImagePart headerImagePart = (ImagePart)openXmlPart;     
      string idImage = headerPart.GetIdOfPart(openXmlPart);     
      ImagePart reportImagePart = reportPart.AddNewPart<ImagePart>("image/png", idImage);
      Header headerNew = new Header(headerPart.Header.OuterXml);
    }
  }
}

This could probably be coded better but I'm still in the POC process. If anyone is having any issues with this strategy please let me know!!!