Different header on first and subsequent pages

The problem:
after creating a docx using Aspose.Words, I seem unable to set Header reference using standard OpenXml to anything other than default. See code below:

public static void ApplyWordHeader(ref MainDocumentPart mainDocPart)
{
    SectionProperties sectionProperties = mainDocPart.Document.Body.Descendants().FirstOrDefault();
    if (sectionProperties == null)
    {
        sectionProperties = new SectionProperties(new HeaderReference() { Type = HeaderFooterValues.First, Id = "r001" },
        new PageMargin(), new TitlePage());

        mainDocPart.Document.Body.Append(sectionProperties);
    }

    var firstPageHeaderPart = mainDocPart.AddNewPart("r003");
    GeneratePageHeaderPart("First page header").Save(firstPageHeaderPart);
    sectionProperties.InsertAt(new HeaderReference() { Type = HeaderFooterValues.First, Id = "r003" }, 0);

    var evenPageHeaderPart = mainDocPart.AddNewPart("r004");
    GeneratePageHeaderPart("even page header").Save(evenPageHeaderPart);
    sectionProperties.InsertAt(new HeaderReference() { Type = HeaderFooterValues.Even, Id = "r004" }, 1);

    var defaultPageHeaderPart = mainDocPart.AddNewPart("r005");
    GeneratePageHeaderPart("default page header").Save(defaultPageHeaderPart);
    sectionProperties.InsertAt(new HeaderReference() { Type = HeaderFooterValues.Default, Id = "r005" }, 2);
}

private static Header GeneratePageHeaderPart(string HeaderText)
{
    var element = new Header(new DocumentFormat.OpenXml.Wordprocessing.Paragraph(
    new ParagraphProperties(
    new ParagraphStyleId() { Val = "Header" }),
    new DocumentFormat.OpenXml.Wordprocessing.Run(new Text(HeaderText))
    ));

    return element;
}

ONLY the Default HeaderFooterValues is retained. If it is omitted, no header is displayed. Everything else is ignored.

Note that we are not using the Aspose header/footer functionality because the paragraph/run values are actually HTML strings that must first be interpreted (by us) and rendered.

So I’m not clear what is wrong with the above but the code used to work when we used an alternate library (HtmlToOpenXML opensource on codePlex).

regards,

Chris

Hi Chris,

Thanks for your inquiry.

chriscardinal66:
Note that we are not using the Aspose header/footer functionality because the paragraph/run values are actually HTML strings that must first be interpreted (by us) and rendered.

You can use DocumentBuilder.InsertHtml method to insert HTML string into the document.

I suggest you please read following documentaion links for your kind reference.
https://reference.aspose.com/words/net/aspose.words/pagesetup/differentfirstpageheaderfooter/
https://reference.aspose.com/words/net/aspose.words/pagesetup/oddandevenpagesheaderfooter/
https://docs.aspose.com/words/net/working-with-headers-and-footers/

Following code example creates headers and footers different for first, even and odd pages using DocumentBuilder.

DocumentBuilder builder = new DocumentBuilder();
PageSetup ps = builder.PageSetup;
ps.DifferentFirstPageHeaderFooter = true;
ps.OddAndEvenPagesHeaderFooter = true;
builder.MoveToHeaderFooter(HeaderFooterType.HeaderFirst);
builder.Writeln("First page header.");
builder.MoveToHeaderFooter(HeaderFooterType.HeaderEven);
builder.Writeln("Even pages header.");
builder.MoveToHeaderFooter(HeaderFooterType.HeaderPrimary);
builder.Writeln("Odd pages header.");
// Move back to the main story of the first section.
builder.MoveToSection(0);
builder.Writeln("Text page 1.");
builder.InsertBreak(BreakType.PageBreak);
builder.Writeln("Text page 2.");
builder.InsertBreak(BreakType.PageBreak);
builder.Writeln("Text page 3.");
builder.Document.Save(MyDir + "PageSetup.DifferentHeaders Out.doc");

Tahir

Thanks for this but I had looked at this code. The problem is that the strings I am passing into the header (for first and for all other pages) are in html which we must ourselves interpret for font, style etc, translating that into the appropriate paragraph/run/text OpenXML sequences. I can’t therefore just do this since the HeaderPrimary (which I assume maps to default) is actually an HTML string. That’s why I can’t use the builder -> which has no HTML interpretation facility.

builder.MoveToHeaderFooter(HeaderFooterType.HeaderFirst);
builder.Writeln("First page header.");
builder.MoveToHeaderFooter(HeaderFooterType.HeaderEven);
builder.Writeln("Even pages header.");
builder.MoveToHeaderFooter(HeaderFooterType.HeaderPrimary);
builder.Writeln("Odd pages header.");

Tahir,

I’m sorry I didn’t see that one little line about InsertHTML(). I will try this and get back to you.

Hi Chris,

Thanks for your inquiry. Yes, you can use DocumentBuilder.InsertHtml method to insert HTML string into the document. You can use InsertHtml to insert an HTML fragment or whole HTML document.

If you still face problem, please manually create your expected Word document using Microsoft Word and attach it here along with your html for our reference. We will investigate, how you want your final Word output be generated like. We will then provide you more information on this along with code.

Here’s the logical follow-on issue:

My HTML contains custom tags to indicate page and page number variables ex. [[P#]] and [[#P]] respectively. So the HTML header/footer might look like:

This is the document title page [[P#]] of [[#P]]

In my old OpenXML code, in the RUNs, I would replace these tags with the appropriate fieldCodes (e.g. " PAGE * MERGEFORMAT ").

How do can I do this with Aspose? I need in the end to get to something like this:
This is the document title page PAGE of NUMPAGES

where Page and NUMPAGES are interpreted as Fields.

I’m thinking that the following:

DocumentBuilder builder = new DocumentBuilder(doc);
Aspose.Words.PageSetup ps = builder.PageSetup;
builder.MoveToHeaderFooter(HeaderFooterType.FooterPrimary);
string html = html.Replace("[[p#]]", "PAGE").Replace("[[#p]]", "NUMPAGES");
builder.InsertHtml(html);

will not work as the builder doesn’t get a chance to put it in as a Field (aka InsertField).

so…how to do that?

Chris

Hi Chris,

Thanks for your inquiry. You can achieve your requirement by implementing IReplacingCallback interface. Please use the same approach shared at following documentation link to find text and insert page field.
https://docs.aspose.com/words/java/find-and-replace/

Please read the following article about ‘Find and Replace’ and check the following code example for your kind reference.
https://docs.aspose.com/words/java/find-and-replace/

// Open the document.
Document doc = new Document(MyDir + "in.html");
doc.Range.Replace(new Regex(@"\[\[P#\]\]"), new FindTextandInsertField(), false);
doc.UpdateFields();
doc.Save(MyDir + "Out.docx");
public class FindTextandInsertField : IReplacingCallback
{
    ReplaceAction IReplacingCallback.Replacing(ReplacingArgs e)
    {
        // This is a Run node that contains either the beginning or the complete match.
        Node currentNode = e.MatchNode;
        // The first (and may be the only) run can contain text before the match, 
        // in this case it is necessary to split the run.
        if (e.MatchOffset > 0)
            currentNode = SplitRun((Run)currentNode, e.MatchOffset);
        ArrayList runs = new ArrayList();
        // Find all runs that contain parts of the match string.
        int remainingLength = e.Match.Value.Length;
        while (
        (remainingLength > 0) &&
        (currentNode != null) &&
        (currentNode.GetText().Length <= remainingLength))
        {
            runs.Add(currentNode);
            remainingLength = remainingLength - currentNode.GetText().Length;
            // Select the next Run node. 
            // Have to loop because there could be other nodes such as BookmarkStart etc.
            do
            {
                currentNode = currentNode.NextSibling;
            }
            while ((currentNode != null) && (currentNode.NodeType != NodeType.Run));
        }
        // Split the last run that contains the match if there is any text left.
        if ((currentNode != null) && (remainingLength > 0))
        {
            SplitRun((Run)currentNode, remainingLength);
            runs.Add(currentNode);
        }
        // Create Document Buidler aond insert MergeField
        DocumentBuilder builder = new DocumentBuilder(e.MatchNode.Document as Document);
        builder.MoveTo((Run)runs[runs.Count - 1]);
        builder.InsertField("PAGE", "");
        // Remove all run nodes
        foreach (Run run in runs)
            run.Remove();
        return ReplaceAction.Skip;
    }
}
/// 
/// Splits text of the specified run into two runs.
/// Inserts the new run just after the specified run.
/// 
private static Run SplitRun(Run run, int position)
{
    Run afterRun = (Run)run.Clone(true);
    afterRun.Text = run.Text.Substring(position);
    run.Text = run.Text.Substring(0, position);
    run.ParentNode.InsertAfter(afterRun, run);
    return afterRun;
}

Hope this helps you. Please let us know if you have any more queries.

Hi there

Thanks for this. It works fine.
However, we have a new problem.
Using aspose.Words v. 14:

workflow: DOCX with Header and Footer when saved to HTML then resaved to DOCX, the footer loses the PAGE Number value.

Looking at the HTML, the
for the footer is certainly there but the page number is an ordinal (1), not a variable. So it outputs on the roundtrip as a 1.

If the initial document has 2 pages, then both pages in the roundtrip say 1 (instead of 1 then 2).
I have attached the originating document (docx), the output HTML (.htm) and the output DOCX to show the effect.

Also it looks like the Word Header/Footer option: Different First Page
is not being respected.
This means that the resulting roundtrip, the header appears on the first page, even if this is not desired.

Chris

Hi Chris,

Thanks for your inquiry. I would suggest you please first take a look at HtmlSaveOptions.ExportHeadersFootersMode property below:
https://reference.aspose.com/words/net/aspose.words.saving/exportheadersfootersmode/

It is hard to meaningfully output headers and footers to HTML/MHTML because HTML/MHTML is not paginated. When this property is ‘PerSection’, Aspose.Words exports only primary headers and footers at the beginning and the end of each section. When it is ‘FirstSectionHeaderLastSectionFooter’ only first primary header and the last primary footer (including linked to previous) are exported. You can disable export of headers and footers altogether by setting this property to ‘None’.

If we can help you with anything else, please feel free to ask.

Hi Tahir

The issue is not that they don’t appear. It’s that the page numbers are numbers, not variables. So if I put in my word footer the pagenum, on rendering to html then back again to docx, the footer will have a “1” in it for ALL THE PAGES!

Basically, it doesn’t work as a page number var.

As for the primary header/footer, I get it. I’ll need to work around that.

Chris

Hi Chris,

Thanks for your inquiry. Please note that Aspose.Words mimics the same behaviour as MS Word does. If you convert your document to HTML by using MS Word, you will get the same output.

Moreover, upon processing HTML, some features of HTML might be lost. You can find a list of limitations upon HTML exporting/importing here:
https://docs.aspose.com/words/net/load-in-the-html-html-xhtml-mhtml-format/
https://docs.aspose.com/words/net/save-in-html-xhtml-mhtml-formats/

I am afraid there is no way to roundtrip DOCX-HTML-DOCX without any changes. As you know, HTML format is really different and it does not support lot of things supported in Word.

However, I have logged this feature request as WORDSNET-9769 (preserve Page field while Docx-Html-Docx round-trip) in our issue tracking system. Our development team will look into the possibility of implementation of this feature. Once we have any information about this feature, we will update you via this thread.

Please let us know if you have any more queries.

The issues you have found earlier (filed as WORDSNET-9769) have been fixed in this Aspose.Words for .NET 16.10.0 update and this Aspose.Words for Java 16.10.0 update.

This message was posted using Notification2Forum from Downloads module by aspose.notifier.