DocumentBuilder inserting header/footer?

Home Forums Open-Xml-PowerTools DocumentBuilder inserting header/footer?

This topic contains 5 replies, has 2 voices, and was last updated by  thmorrison 7 years, 7 months ago.

Viewing 6 posts - 1 through 6 (of 6 total)
  • Author
    Posts
  • #3732

    thmorrison
    Participant

    I use an XML file to represent a set of documents to be assembled into a single document. I am using the regex capability to do field substitutions on the template documents (with fields cleverly named FIELDnnnn), the data for which are contained in another document. (This all to explain the outer loops.)

    Using a rather simple template document that has no header or footer, I get a result document that has both. The header and footer appear to be generated by DocumentBuilder, but I am at a loss to understand why. The original document does not have header or footer information in the document.xml tree; the result document does, along with footer.xml, footer2.xml, footer3.xml, header.xml, header2.xml, and header3.xml.

    I have debugged the code up to the sources.Add, and the WmlDocument being added does not contain any footer information in the <w:body> element.

    Any help would be appreciated. Here is the code:

            List<Source> sources = new List<Source>();
    
            var myFiles = XDocument.Load(valuesDoc.FullName).Descendants("file");
    
            int i = 0;
    
            /* Boolean isFirst = true; */
            foreach (XElement fileEntry in myFiles)
            {
                i++;
                /*
                if (! isFirst)
                {
                    sources.Add(new Source(pageBreakDoc, 0, 1, false)); //add empty page break paragraph
                }
                isFirst = false;
                */
                string templateFile = fileEntry.Element("template").Value;
                string fieldsFile = fileEntry.Element("data").Value;
    
                var myFields = XDocument.Load(fieldsFile).Descendants("fe");
    
                byte[] byteArray = File.ReadAllBytes(templateFile);
                
                using (MemoryStream msModify = new MemoryStream())
                {
                    msModify.Write(byteArray, 0, (int)byteArray.Length);
    
                    // File.Copy(sourceDoc.FullName, newDoc.FullName,true);
                    // using (WordprocessingDocument wDoc = WordprocessingDocument.Open(newDoc.FullName, true))
    
                    using (WordprocessingDocument wDoc = WordprocessingDocument.Open(msModify, true))
                    {
                        int count;
                        var xDoc = wDoc.MainDocumentPart.GetXDocument();
                        Regex regex;
                        IEnumerable<XElement> content;
    
                        foreach (XElement substitution in myFields)
                        {
                            string sFldNum = substitution.Element("fn").Value;
                            string sFldVal1 = substitution.Element("fv").Value;
                            string matchString1 = "FIELD" + sFldNum;
                            if (sFldVal1.Length > 0)
                            {
                                content = xDoc.Descendants(W.p);
                                regex = new Regex(matchString1);
                                count = OpenXmlRegex.Replace(content, regex, sFldVal1, null, true);
                                /* Console.WriteLine("{1} {2} Count: {0}", count, matchString1, sFldVal1); */
                            }
                        }
    
                        content = xDoc.Descendants(W.p);
                        string matchString = "FIELD\\d\\d\\d\\d";
                        string sFldVal = "";
                        regex = new Regex(matchString);
                        count = OpenXmlRegex.Replace(content, regex, sFldVal, null, true);
                        /* Console.WriteLine("{1} {2} Count: {0}", count, matchString, sFldVal); */
                        wDoc.MainDocumentPart.PutXDocument();
                    }
                    File.WriteAllBytes(newDoc.FullName + i.ToString(), msModify.ToArray()); /* debug purpose */
                    sources.Add(new Source(new WmlDocument("dummy", msModify.ToArray()), true));
                }
            }
            DocumentBuilder.BuildDocument(sources, newDoc.FullName);
    
    • This topic was modified 7 years, 8 months ago by  thmorrison. Reason: Remove extraneous comments from code
    #3748

    thmorrison
    Participant

    After reading the update made this weekend in thread Header/Footer with Different First Page set I inserted as zero height header and footer into the template document, and that got carried through to the output document.

    The question remains, however: Why are these (for lack of a better term) ‘default’ header and footer being inserted?

    #3766

    Eric White
    Keymaster

    Hi,

    This is a very good question.

    There was an interesting issue a couple of years ago that had to do with headers being inherited from previous sections with fairly random behavior, so at that time I enhanced DocumentBuilder to be deterministic and precise about generation of headers/footers, and IIRC, there was an issue where a section has no headers/footers, perhaps I generate blank headers / footers for the section, because if they are not there, then they can inherit *wrong* headers/footers from previous sections.

    But frankly I can’t recall the details, and without reading the code don’t fully know why you are seeing what you are seeing.

    There is a lot of code in DocumentBuilder to deal with headers/footers, and I am not surprised to hear of an issue.

    One think to keep in mind – after generating the document, it is a super-simple operation to remove the headers/footers as necessary. However, changing them or adding them is a different story – can be a bit complicated. The entire semantics are documented in the standard, but it is not simple.

    Cheers, Eric

    #3813

    thmorrison
    Participant

    after generating the document, it is a super-simple operation to remove the headers/footers as necessary

    Unfortunately, my result document can be a mixture of different template documents. In this mode, the business rule triggers the need for several documents that are most conveniently delivered in a single DOCX. In this mode, it would be difficult to determine which headers/footers to remove.

    I am taking the approach of fixing up the input template documents (over which I have no control), and supplying an almost zero length header and footer for input documents that do not contain header/footer information. I use another document (which is under my control) to house the defaults that I copy to the input document when needed. Almost have this working – but took a vacation, so need to get back up to speed.

    #3824

    thmorrison
    Participant

    I finally got an acceptable result, though there are still a bunch of headers and footers being generated in the DocumentBuild step. As I wrote above, I have a document that houses my default header/footer stuff. I copy from that document into the input document before adding the input document to the list for DocumentBuilder. Key to getting this to work correctly seems to be setting the Header[Footer]Reference type attribute to default. Note I also set the margins to 1. The example below is a modified example that probably looks very familiar.

    I think I have this where I need it now. Thanks for the answer…

       public static void AddHeaderFromTo(WordprocessingDocument wdDocSource, WordprocessingDocument wdDoc)
        {
            // Replace header in target document with header of source document.
            MainDocumentPart mainPart = wdDoc.MainDocumentPart;
    
            // Delete the existing header part.
            mainPart.DeleteParts(mainPart.HeaderParts);
    
            // Create a new header part.
            HeaderPart headerPart = mainPart.AddNewPart<HeaderPart>();
    
            // Get Id of the headerPart.
            string rId = mainPart.GetIdOfPart(headerPart);
    
            // Feed target headerPart with source headerPart.
            HeaderPart firstHeader = wdDocSource.MainDocumentPart.HeaderParts.FirstOrDefault();
    
            if (firstHeader != null)
            {
                headerPart.FeedData(firstHeader.GetStream());
            }
    
            // Get SectionProperties and Replace HeaderReference with new Id.
            IEnumerable<SectionProperties> sectPrs = mainPart.Document.Body.Elements<SectionProperties>();
            foreach (var sectPr in sectPrs)
            {
                // Delete existing references to headers.
                sectPr.RemoveAllChildren<HeaderReference>();
    
                // Create the new header reference node.
                sectPr.PrependChild<HeaderReference>(new HeaderReference() { Id = rId, Type = HeaderFooterValues.Default });
                PageMargin sectMargin = sectPr.Descendants<PageMargin>().FirstOrDefault();
                if (sectMargin != null)
                {
                    sectMargin.Header = 1U;
                }
            }
        }
    
    #3837

    thmorrison
    Participant

    Minor correction.

    Change IEnumerable<SectionProperties> sectPrs = mainPart.Document.Body.Elements<SectionProperties>();
    to
    IEnumerable<SectionProperties> sectPrs = mainPart.Document.Body.Descendants<SectionProperties>();

    Previous code was missing some of the sectPr and for those DocumentBuilder was creating the default one-inch margin empty header/footer.

    • This reply was modified 7 years, 7 months ago by  thmorrison.
    • This reply was modified 7 years, 7 months ago by  thmorrison.
Viewing 6 posts - 1 through 6 (of 6 total)

You must be logged in to reply to this topic.