예제 #1
0
        private static void PrepareTemplatePart(OpenXmlPart part, FieldTransformIndex xm, TemplateErrorList te)
        {
            XDocument xDoc = part.GetXDocument();

            var xDocRoot = RemoveGoBackBookmarks(xDoc.Root);

            // content controls in cells can surround the W.tc element, so transform so that such content controls are within the cell content
            xDocRoot = (XElement)NormalizeContentControlsInCells(xDocRoot);

            // transform OpenDocx fields into temporary parsed metadata objects (??)
            xDocRoot = (XElement)ParseFields(xDocRoot, xm, te);

            // Repeat, EndRepeat, Conditional, EndConditional are allowed at run level, but only if there is a matching pair
            // if there is only one Repeat, EndRepeat, Conditional, EndConditional, then move to block level.
            // if there is a matching set, then is OK.
            xDocRoot = (XElement)ForceBlockLevelAsAppropriate(xDocRoot, te);

            NormalizeRepeatAndConditional(xDocRoot, te);

            // any EndRepeat, EndConditional that remain are orphans, so replace with an error
            ProcessOrphanEndRepeatEndConditional(xDocRoot, te);

            // add placeholders for list punctuation
            xDocRoot = (XElement)AddListPunctuationPlaceholders(xDocRoot, te);

            // finally, transform the metadata objects BACK into document content, but this time in DocxGen syntax!
            xDocRoot = (XElement)ContentReplacementTransform(xDocRoot, xm, te);

            xDoc.Elements().First().ReplaceWith(xDocRoot);
            part.PutXDocument();
        }
예제 #2
0
        private static object ParseFields(XNode node, FieldTransformIndex xm, TemplateErrorList te)
        {
            XElement element = node as XElement;

            if (element != null)
            {
                if (element.Name == W.sdt)
                {
                    var alias = (string)element.Elements(W.sdtPr).Elements(W.alias).Attributes(W.val).FirstOrDefault();
                    if (string.IsNullOrEmpty(alias))
                    {
                        var tag = (string)element.Elements(W.sdtPr).Elements(W.tag).Attributes(W.val).FirstOrDefault();
                        if (!string.IsNullOrEmpty(tag) && xm.TryGetValue(tag, out var fieldInfo))
                        {
                            XElement xml = new XElement(fieldInfo.fieldType, new XAttribute(OD.Id, tag));
                            xml.Add(element.Elements(W.sdtContent).Elements());
                            return(xml);
                        }
                    }
                }
                return(new XElement(element.Name,
                                    element.Attributes(),
                                    element.Nodes().Select(n => ParseFields(n, xm, te))));
            }
            return(node);
        }
예제 #3
0
        private static TemplateErrorList PrepareTemplate(WordprocessingDocument wordDoc, FieldTransformIndex xm)
        {
            if (RevisionAccepter.HasTrackedRevisions(wordDoc))
            {
                throw new FieldParseException("Invalid template - contains tracked revisions");
            }

            SimplifyTemplateMarkup(wordDoc);

            var te = new TemplateErrorList();

            foreach (var part in wordDoc.ContentParts())
            {
                PrepareTemplatePart(part, xm, te);
            }
            return(te);
        }
예제 #4
0
        static object ContentReplacementTransform(XNode node, FieldTransformIndex xm, TemplateErrorList templateError)
        {
            XElement element = node as XElement;

            if (element != null)
            {
                if (element.Name == OD.Content)
                {
                    var selector = "./" + xm[element.Attribute(OD.Id).Value].atomizedExpr;
                    if (element.Attribute(OD.Punc) == null) // regular content field
                    {
                        selector += "[1]";                  // if xpath query returns multiple elements, just take the first one
                    }
                    else
                    {
                        selector += "1"; // the list selector with a "1" appended to it is where punctuation will be in the XML
                    }

                    var fieldText = "<" + PA.Content + " "
                                    + PA.Select + "=\"" + selector + "\" "
                                    + PA.Optional + "=\"true\"/>";
                    XElement ccc  = null;
                    XElement para = element.Descendants(W.p).FirstOrDefault();
                    XElement run  = element.Descendants(W.r).FirstOrDefault();
                    if (para != null)
                    {
                        XElement pPr = para.Elements(W.pPr).FirstOrDefault();
                        XElement rPr = pPr?.Elements(W.rPr).FirstOrDefault();
                        XElement r   = new XElement(W.r, rPr, new XElement(W.t, fieldText));
                        ccc = PWrap(para.Elements(W.pPr), r);
                    }
                    else
                    {
                        ccc = new XElement(W.r,
                                           run?.Elements(W.rPr).FirstOrDefault(),
                                           new XElement(W.t, fieldText));
                    }
                    return(CCWrap(ccc));
                }
                if (element.Name == OD.List)
                {
                    var listAtom  = xm[element.Attribute(OD.Id).Value].atomizedExpr;
                    var selector  = "./" + listAtom + "[1]/" + listAtom + "0";
                    var startText = "<" + PA.Repeat + " "
                                    + PA.Select + "=\"" + selector + "\" "
                                    + PA.Optional + "=\"true\"/>";
                    var      endText          = "<" + PA.EndRepeat + "/>";
                    XElement para             = element.Descendants(W.p).FirstOrDefault();
                    var      repeatingContent = element
                                                .Elements()
                                                .Select(e => ContentReplacementTransform(e, xm, templateError))
                                                .ToList();
                    XElement startElem = new XElement(W.r, new XElement(W.t, startText));
                    XElement endElem   = new XElement(W.r, new XElement(W.t, endText));
                    if (para != null) // block-level list
                    {
                        // prefix and suffix repeating content with block-level repeat elements/tags
                        repeatingContent.Insert(0, CCWrap(PWrap(startElem)));
                        // repeatingContent here
                        repeatingContent.Add(CCWrap(PWrap(endElem)));
                    }
                    else // run-level
                    {
                        repeatingContent.Insert(0, CCWrap(startElem));
                        // repeatingContent here
                        repeatingContent.Add(CCWrap(endElem));
                    }
                    return(repeatingContent);
                }
                if (element.Name == OD.If || element.Name == OD.ElseIf || element.Name == OD.Else)
                {
                    var      endText    = "<" + PA.EndConditional + "/>";
                    XElement endElem    = new XElement(W.r, new XElement(W.t, endText));
                    bool     blockLevel = element.IsEmpty || (element.Descendants(W.p).FirstOrDefault() != null);
                    if (element.Name == OD.If)
                    {
                        var selector  = xm[element.Attribute(OD.Id).Value].atomizedExpr + "2";
                        var startText = "<" + PA.Conditional + " "
                                        + PA.Select + "=\"" + selector + "[1]\" "
                                        + PA.Match + "=\"true\"/>";
                        var content = element
                                      .Elements()
                                      .Select(e => ContentReplacementTransform(e, xm, templateError))
                                      .ToList();
                        XElement startElem = new XElement(W.r, new XElement(W.t, startText));
                        if (blockLevel)
                        {
                            content.Insert(0, CCWrap(PWrap(startElem)));
                            // content here
                            content.Add(CCWrap(PWrap(endElem)));
                        }
                        else // run-level
                        {
                            content.Insert(0, CCWrap(startElem));
                            // content here
                            content.Add(CCWrap(endElem));
                        }
                        return(content);
                    }
                    if (element.Name == OD.ElseIf)
                    {
                        XElement lookUp = element.Parent;
                        while (lookUp.Name != OD.If && lookUp.Name != OD.ElseIf)
                        {
                            lookUp = lookUp.Parent;
                        }
                        var selector      = xm[lookUp.Attribute(OD.Id).Value].atomizedExpr + "2";
                        var startElseText = "<" + PA.Conditional + " "
                                            + PA.Select + "=\"" + selector + "[1]\" "
                                            + PA.NotMatch + "=\"true\"/>"; // NotMatch instead of Match, represents "Else" branch
                        selector = xm[element.Attribute(OD.Id).Value].atomizedExpr + "2";
                        var nestedIfText = "<" + PA.Conditional + " "
                                           + PA.Select + "=\"" + selector + "[1]\" "
                                           + PA.Match + "=\"true\"/>";
                        var content = element
                                      .Elements()
                                      .Select(e => ContentReplacementTransform(e, xm, templateError))
                                      .ToList();
                        XElement startElseElem = new XElement(W.r, new XElement(W.t, startElseText));
                        XElement nestedIfElem  = new XElement(W.r, new XElement(W.t, nestedIfText));
                        if (blockLevel) // block-level conditional
                        {
                            content.Insert(0, CCWrap(PWrap(endElem)));
                            content.Insert(1, CCWrap(PWrap(startElseElem)));
                            content.Insert(2, CCWrap(PWrap(nestedIfElem)));
                            // content here
                            content.Add(CCWrap(PWrap(endElem)));
                        }
                        else // run-level
                        {
                            content.Insert(0, CCWrap(endElem));
                            content.Insert(1, CCWrap(startElseElem));
                            content.Insert(2, CCWrap(nestedIfElem));
                            // content here
                            content.Add(CCWrap(endElem));
                        }
                        // no "end" tag for the "else" branch, because the end is inserted by the If element after all its contents
                        return(content);
                    }
                    if (element.Name == OD.Else)
                    {
                        XElement lookUp = element.Parent;
                        while (lookUp != null && lookUp.Name != OD.If && lookUp.Name != OD.ElseIf)
                        {
                            lookUp = lookUp.Parent;
                        }
                        // if lookUp == null, Something is wrong -- else not inside an if?
                        if (lookUp != null)
                        {
                            var selector      = xm[lookUp.Attribute(OD.Id).Value].atomizedExpr + "2";
                            var startElseText = "<" + PA.Conditional + " "
                                                + PA.Select + "=\"" + selector + "[1]\" "
                                                + PA.NotMatch + "=\"true\"/>"; // NotMatch instead of Match, represents "Else" branch

                            var content = element
                                          .Elements()
                                          .Select(e => ContentReplacementTransform(e, xm, templateError))
                                          .ToList();
                            XElement startElseElem = new XElement(W.r, new XElement(W.t, startElseText));
                            if (blockLevel) // block-level conditional
                            {
                                content.Insert(0, CCWrap(PWrap(endElem)));
                                content.Insert(1, CCWrap(PWrap(startElseElem)));
                            }
                            else // run-level
                            {
                                content.Insert(0, CCWrap(endElem));
                                content.Insert(1, CCWrap(startElseElem));
                            }
                            // no "end" tag for the "else" branch, because the end is inserted by the If element after all its contents
                            return(content);
                        }
                    }
                }
                return(new XElement(element.Name,
                                    element.Attributes(),
                                    element.Nodes().Select(n => ContentReplacementTransform(n, xm, templateError))));
            }
            return(node);
        }
예제 #5
0
        #pragma warning restore CS1998

        private static CompileResult TransformTemplate(string originalTemplateFile, string preProcessedTemplateFile, FieldTransformIndex xm)
        {
            string newDocxFilename = originalTemplateFile + "gen.docx";

            byte[]            byteArray           = File.ReadAllBytes(preProcessedTemplateFile);
            WmlDocument       transformedTemplate = null;
            TemplateErrorList templateErrors;

            using (MemoryStream memStream = new MemoryStream())
            {
                memStream.Write(byteArray, 0, byteArray.Length);                                      // copy the bytes into an expandable MemoryStream
                using (WordprocessingDocument wordDoc = WordprocessingDocument.Open(memStream, true)) // read & parse that memory stream into an editable OXML document (also in memory)
                {
                    templateErrors = PrepareTemplate(wordDoc, xm);
                }
                transformedTemplate = new WmlDocument(newDocxFilename, memStream.ToArray());
            }
            // delete output file if it already exists (Save() below is supposed to always overwrite, but I just want to be sure)
            if (File.Exists(newDocxFilename))
            {
                File.Delete(newDocxFilename);
            }
            // save the output (even in the case of error, since error messages are in the file)
            transformedTemplate.Save();

            return(new CompileResult(transformedTemplate.FileName, templateErrors.ErrorList.Select(e => e.ToString()).ToArray()));
        }