private static object MergeAdjacentRunsTransform(XNode node) { if (node is XElement element) { return(element.Name == W.p ? WordprocessingMLUtil.CoalesceAdjacentRunsWithIdenticalFormatting(element) : new XElement(element.Name, element.Attributes(), element.Nodes().Select(MergeAdjacentRunsTransform))); } return(node); }
private static object MergeAdjacentRunsTransform(XNode node) { var element = node as XElement; if (element == null) { return(node); } if (element.Name == W.p) { return(WordprocessingMLUtil.CoalesceAdjacentRunsWithIdenticalFormatting(element)); } return(new XElement(element.Name, element.Attributes(), element.Nodes().Select(n => MergeAdjacentRunsTransform(n)))); }
private static object WmlSearchAndReplaceTransform(XNode node, Regex regex, string replacement, Func <XElement, Match, bool> callback, bool trackRevisions, string revisionTrackingAuthor, ReplaceInternalInfo replInfo, bool coalesceContent) { XElement element = node as XElement; if (element != null) { if (element.Name == W.p) { var paragraph = element; string contents = element .DescendantsTrimmed(W.txbxContent) .Where(d => d.Name == W.t) .Select(t => (string)t) .StringConcatenate(); if (regex.IsMatch(contents)) { contents = element .DescendantsTrimmed(W.txbxContent) .Where(d => d.Name == W.t) .Select(t => (string)t) .StringConcatenate(); if (regex.IsMatch(contents)) { XElement paragraphWithSplitRuns = new XElement(W.p, paragraph.Attributes(), paragraph.Nodes().Select(n => WmlSearchAndReplaceTransform(n, regex, replacement, callback, trackRevisions, revisionTrackingAuthor, replInfo, coalesceContent))); var runsTrimmed = paragraphWithSplitRuns .DescendantsTrimmed(W.txbxContent) .Where(d => d.Name == W.r) .ToList(); var charsAndRuns = runsTrimmed .Select(r => { if (r.Element(W.t) != null) { return new { Ch = r.Element(W.t).Value, r, } } ; else { return new { Ch = "\x01", r, } }; }) .ToList(); var content = charsAndRuns.Select(t => t.Ch).StringConcatenate(); var alignedRuns = charsAndRuns.Select(t => t.r).ToArray(); var matchCollection = regex.Matches(content); replInfo.Count += matchCollection.Count; if (replacement == null) { if (callback != null) { foreach (var match in matchCollection.Cast <Match>()) { callback(paragraph, match); } } } else { foreach (var match in matchCollection.Cast <Match>()) { if (match.Length == 0) { continue; } if (callback == null || callback(paragraph, match)) { var runCollection = alignedRuns .Skip(match.Index) .Take(match.Length) .ToList(); // uses the Skip / Take special semantics of array to implement efficient finding of sub array var firstRun = runCollection.First(); var firstRunProperties = firstRun.Elements(W.rPr).FirstOrDefault(); // save away first run properties if (trackRevisions) { if (replacement != null && replacement != "") { var newIns = new XElement(W.ins, new XAttribute(W.author, revisionTrackingAuthor), new XAttribute(W.date, DateTime.Now.ToString("s") + "Z"), new XElement(W.r, firstRunProperties, new XElement(W.t, replacement))); if (firstRun.Parent.Name == W.ins) { firstRun.Parent.AddBeforeSelf(newIns); } else { firstRun.AddBeforeSelf(newIns); } } foreach (var run in runCollection) { var isInIns = run.Parent.Name == W.ins; if (isInIns) { var parentIns = run.Parent; if ((string)parentIns.Attributes(W.author).FirstOrDefault() == revisionTrackingAuthor) { var parentInsSiblings = parentIns .Parent .Elements() .Where(c => c != parentIns) .ToList(); parentIns.Parent.ReplaceNodes(parentInsSiblings); } else { var parentInsSiblings = parentIns .Parent .Elements() .Select(c => { if (c == parentIns) { var newIns = new XElement(W.ins, parentIns.Attributes(), new XElement(W.del, new XAttribute(W.author, revisionTrackingAuthor), new XAttribute(W.date, DateTime.Now.ToString("s") + "Z"), parentIns.Elements().Select(r => TransformToDelText(r)))); return(newIns); } else { return(c); } }) .ToList(); parentIns.Parent.ReplaceNodes(parentInsSiblings); } } else { var delRun = new XElement(W.del, new XAttribute(W.author, revisionTrackingAuthor), new XAttribute(W.date, DateTime.Now.ToString("s") + "Z"), TransformToDelText(run)); run.ReplaceWith(delRun); } } } else // not tracked revisions { foreach (var runToDelete in runCollection.Skip(1).ToList()) { if (runToDelete.Parent.Name == W.ins) { runToDelete.Parent.Remove(); } else { runToDelete.Remove(); } } XAttribute xs = null; if (replacement.Length > 0 && (replacement[0] == ' ' || replacement[replacement.Length - 1] == ' ')) { xs = new XAttribute(XNamespace.Xml + "space", "preserve"); } var newFirstRun = new XElement(W.r, firstRun.Element(W.rPr), new XElement(W.t, xs, replacement)); // creates a new run with proper run properties if (firstRun.Parent.Name == W.ins) { firstRun.Parent.ReplaceWith(newFirstRun); } else { firstRun.ReplaceWith(newFirstRun); // finds firstRun in its parent's list of children, unparents firstRun, } // sets newFirstRun's parent to firstRuns old parent, and inserts in the list // of children at the right place. } } } if (coalesceContent) { paragraph = WordprocessingMLUtil.CoalesceAdjacentRunsWithIdenticalFormatting(paragraphWithSplitRuns); } else { paragraph = paragraphWithSplitRuns; } } } return(paragraph); } var newEle = new XElement(element.Name, element.Attributes(), element.Nodes().Select(n => { var e = n as XElement; if (e != null) { if (e.Name == W.pPr || e.Name == W.rPr) { return(e); } if (e.Name == W.r && (e.Element(W.t) != null) || e.Element(W.tab) != null) { return(e); } if (e.Name == W.ins && e.Element(W.r) != null && e.Element(W.r).Element(W.t) != null) { return(e); } var newContent = WmlSearchAndReplaceTransform(e, regex, replacement, callback, trackRevisions, revisionTrackingAuthor, replInfo, coalesceContent); return(newContent); } return(n); })); if (newEle.Name == W.p) { //if (newEle.Descendants(W.ins).Any()) // Console.WriteLine(); if (coalesceContent) { var newPara = CoalesceContent(newEle); return(newPara); } else { return(newEle); } } else { return(newEle); } } if (element.Name == W.ins && element.Elements(W.r).Any()) { var collectionOfCollections = element .Elements() .Select(n => WmlSearchAndReplaceTransform(n, regex, replacement, callback, trackRevisions, revisionTrackingAuthor, replInfo, coalesceContent)) .ToList(); var collectionOfIns = collectionOfCollections .Select(c => { if (c is IEnumerable <XElement> ) { var ix = (IEnumerable <XElement>)c; var collectionOfNewIns = ix .Select(ixc => { var newIns = new XElement(W.ins, element.Attributes(), ixc); return(newIns); }); return(collectionOfNewIns); } return(c); }) .ToList(); return(collectionOfIns); } if (element.Name == W.r && element.Elements(W.t).Any()) { var collectionOfCollections = element.Elements() .Where(e => e.Name != W.rPr) .Select(e => { if (e.Name == W.t) { string s = (string)e; IEnumerable <XElement> collectionOfSubRuns = s.Select(c => { XElement newRun = new XElement(W.r, element.Elements(W.rPr), new XElement(W.t, c == ' ' ? new XAttribute(XNamespace.Xml + "space", "preserve") : null, c)); return(newRun); }); return(collectionOfSubRuns); } else { XElement newRun = new XElement(W.r, element.Elements(W.rPr), e); return(new [] { newRun }); } }) .ToList(); var collectionOfRuns = collectionOfCollections.SelectMany(t => t); return(collectionOfRuns); } return(new XElement(element.Name, element.Attributes(), element.Nodes().Select(n => WmlSearchAndReplaceTransform(n, regex, replacement, callback, trackRevisions, revisionTrackingAuthor, replInfo, coalesceContent)))); } return(node); }
private static object TransformToMetadata(XNode node, XElement data, TemplateError 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 (alias == null || alias == "" || s_AliasList.Contains(alias)) { var ccContents = element .DescendantsTrimmed(W.txbxContent) .Where(e => e.Name == W.t) .Select(t => (string)t) .StringConcatenate() .Trim() .Replace('“', '"') .Replace('”', '"'); if (ccContents.StartsWith("<")) { XElement xml = TransformXmlTextToMetadata(te, ccContents); if (xml.Name == W.p || xml.Name == W.r) // this means there was an error processing the XML. { if (element.Parent.Name == W.p) { return(xml.Elements(W.r)); } return(xml); } if (alias != null && xml.Name.LocalName != alias) { if (element.Parent.Name == W.p) { return(CreateRunErrorMessage("Error: Content control alias does not match metadata element name", te)); } else { return(CreateParaErrorMessage("Error: Content control alias does not match metadata element name", te)); } } xml.Add(element.Elements(W.sdtContent).Elements()); return(xml); } return(new XElement(element.Name, element.Attributes(), element.Nodes().Select(n => TransformToMetadata(n, data, te)))); } return(new XElement(element.Name, element.Attributes(), element.Nodes().Select(n => TransformToMetadata(n, data, te)))); } if (element.Name == W.p) { var paraContents = element .DescendantsTrimmed(W.txbxContent) .Where(e => e.Name == W.t) .Select(t => (string)t) .StringConcatenate() .Trim(); int occurances = paraContents.Select((c, i) => paraContents.Substring(i)).Count(sub => sub.StartsWith("<#")); if (paraContents.StartsWith("<#") && paraContents.EndsWith("#>") && occurances == 1) { var xmlText = paraContents.Substring(2, paraContents.Length - 4).Trim(); XElement xml = TransformXmlTextToMetadata(te, xmlText); if (xml.Name == W.p || xml.Name == W.r) { return(xml); } xml.Add(element); return(xml); } if (paraContents.Contains("<#")) { List <RunReplacementInfo> runReplacementInfo = new List <RunReplacementInfo>(); var thisGuid = Guid.NewGuid().ToString(); var r = new Regex("<#.*?#>"); XElement xml = null; OpenXmlRegex.Replace(new[] { element }, r, thisGuid, (para, match) => { var matchString = match.Value.Trim(); var xmlText = matchString.Substring(2, matchString.Length - 4).Trim().Replace('“', '"').Replace('”', '"'); try { xml = XElement.Parse(xmlText); } catch (XmlException e) { RunReplacementInfo rri = new RunReplacementInfo() { Xml = null, XmlExceptionMessage = "XmlException: " + e.Message, SchemaValidationMessage = null, }; runReplacementInfo.Add(rri); return(true); } string schemaError = ValidatePerSchema(xml); if (schemaError != null) { RunReplacementInfo rri = new RunReplacementInfo() { Xml = null, XmlExceptionMessage = null, SchemaValidationMessage = "Schema Validation Error: " + schemaError, }; runReplacementInfo.Add(rri); return(true); } RunReplacementInfo rri2 = new RunReplacementInfo() { Xml = xml, XmlExceptionMessage = null, SchemaValidationMessage = null, }; runReplacementInfo.Add(rri2); return(true); }, false); var newPara = new XElement(element); foreach (var rri in runReplacementInfo) { var runToReplace = newPara.Descendants(W.r).FirstOrDefault(rn => rn.Value == thisGuid && rn.Parent.Name != PA.Content); if (runToReplace == null) { throw new OpenXmlPowerToolsException("Internal error"); } if (rri.XmlExceptionMessage != null) { runToReplace.ReplaceWith(CreateRunErrorMessage(rri.XmlExceptionMessage, te)); } else if (rri.SchemaValidationMessage != null) { runToReplace.ReplaceWith(CreateRunErrorMessage(rri.SchemaValidationMessage, te)); } else { var newXml = new XElement(rri.Xml); newXml.Add(runToReplace); runToReplace.ReplaceWith(newXml); } } var coalescedParagraph = WordprocessingMLUtil.CoalesceAdjacentRunsWithIdenticalFormatting(newPara); return(coalescedParagraph); } } return(new XElement(element.Name, element.Attributes(), element.Nodes().Select(n => TransformToMetadata(n, data, te)))); } return(node); }
private static object WmlSearchAndReplaceTransform(XNode node, Regex regex, string replacement, Func <XElement, Match, bool> callback, bool trackRevisions, string revisionTrackingAuthor, ReplaceInternalInfo replInfo, bool coalesceContent) { var element = node as XElement; if (element == null) { return(node); } if (element.Name == W.p) { XElement paragraph = element; string preliminaryContent = paragraph .DescendantsTrimmed(W.txbxContent) .Where(d => d.Name == W.r && (d.Parent == null || d.Parent.Name != W.del)) .Select(UnicodeMapper.RunToString) .StringConcatenate(); if (regex.IsMatch(preliminaryContent)) { var paragraphWithSplitRuns = new XElement(W.p, paragraph.Attributes(), paragraph.Nodes().Select(n => WmlSearchAndReplaceTransform(n, regex, replacement, callback, trackRevisions, revisionTrackingAuthor, replInfo, coalesceContent))); IEnumerable <XElement> runsTrimmed = paragraphWithSplitRuns .DescendantsTrimmed(W.txbxContent) .Where(d => d.Name == W.r && (d.Parent == null || d.Parent.Name != W.del)); var charsAndRuns = runsTrimmed .Select(r => new { Ch = UnicodeMapper.RunToString(r), r }) .ToList(); string content = charsAndRuns.Select(t => t.Ch).StringConcatenate(); XElement[] alignedRuns = charsAndRuns.Select(t => t.r).ToArray(); MatchCollection matchCollection = regex.Matches(content); replInfo.Count += matchCollection.Count; // Process Match if (replacement == null) { if (callback == null) { return(paragraph); } foreach (Match match in matchCollection.Cast <Match>()) { callback(paragraph, match); } return(paragraph); } // Process Replace foreach (Match match in matchCollection.Cast <Match>()) { if (match.Length == 0) { continue; } if ((callback != null) && !callback(paragraph, match)) { continue; } List <XElement> runCollection = alignedRuns .Skip(match.Index) .Take(match.Length) .ToList(); // uses the Skip / Take special semantics of array to implement efficient finding of sub array XElement firstRun = runCollection.First(); XElement firstRunProperties = firstRun.Elements(W.rPr).FirstOrDefault(); // save away first run properties if (trackRevisions) { if (replacement != "") { // We coalesce runs as some methods, e.g., in DocumentAssembler, // will try to find the replacement string even though they // set coalesceContent to false. string newTextValue = match.Result(replacement); List <XElement> newRuns = UnicodeMapper.StringToCoalescedRunList(newTextValue, firstRunProperties); var newIns = new XElement(W.ins, new XAttribute(W.author, revisionTrackingAuthor), new XAttribute(W.date, DateTime.UtcNow.ToString("s") + "Z"), newRuns); if (firstRun.Parent != null && firstRun.Parent.Name == W.ins) { firstRun.Parent.AddBeforeSelf(newIns); } else { firstRun.AddBeforeSelf(newIns); } } foreach (XElement run in runCollection) { bool isInIns = run.Parent != null && run.Parent.Name == W.ins; if (isInIns) { XElement parentIns = run.Parent; XElement grandParentParagraph = parentIns.Parent; if (grandParentParagraph != null) { if ((string)parentIns.Attributes(W.author).FirstOrDefault() == revisionTrackingAuthor) { List <XElement> parentInsSiblings = grandParentParagraph .Elements() .Where(c => c != parentIns) .ToList(); grandParentParagraph.ReplaceNodes(parentInsSiblings); } else { List <XElement> parentInsSiblings = grandParentParagraph .Elements() .Select(c => c == parentIns ? new XElement(W.ins, parentIns.Attributes(), new XElement(W.del, new XAttribute(W.author, revisionTrackingAuthor), new XAttribute(W.date, DateTime.UtcNow.ToString("s") + "Z"), parentIns.Elements().Select(TransformToDelText))) : c) .ToList(); grandParentParagraph.ReplaceNodes(parentInsSiblings); } } } else { var delRun = new XElement(W.del, new XAttribute(W.author, revisionTrackingAuthor), new XAttribute(W.date, DateTime.UtcNow.ToString("s") + "Z"), TransformToDelText(run)); run.ReplaceWith(delRun); } } } else // not tracked revisions { foreach (XElement runToDelete in runCollection.Skip(1).ToList()) { if (runToDelete.Parent != null && runToDelete.Parent.Name == W.ins) { runToDelete.Parent.Remove(); } else { runToDelete.Remove(); } } // We coalesce runs as some methods, e.g., in DocumentAssembler, // will try to find the replacement string even though they // set coalesceContent to false. string newTextValue = match.Result(replacement); List <XElement> newRuns = UnicodeMapper.StringToCoalescedRunList(newTextValue, firstRunProperties); if (firstRun.Parent != null && firstRun.Parent.Name == W.ins) { firstRun.Parent.ReplaceWith(newRuns); } else { firstRun.ReplaceWith(newRuns); } } } return(coalesceContent ? WordprocessingMLUtil.CoalesceAdjacentRunsWithIdenticalFormatting(paragraphWithSplitRuns) : paragraphWithSplitRuns); } var newParagraph = new XElement(W.p, paragraph.Attributes(), paragraph.Nodes().Select(n => { var e = n as XElement; if (e == null) { return(n); } if (e.Name == W.pPr) { return(e); } if (((e.Name == W.r) && e.Elements(W.t).Any()) || e.Elements(W.tab).Any()) { return(e); } if ((e.Name == W.ins) && e.Elements(W.r).Elements(W.t).Any()) { return(e); } return(WmlSearchAndReplaceTransform(e, regex, replacement, callback, trackRevisions, revisionTrackingAuthor, replInfo, coalesceContent)); })); return(coalesceContent ? WordprocessingMLUtil.CoalesceAdjacentRunsWithIdenticalFormatting(newParagraph) // CoalesceContent(newParagraph) : newParagraph); } if (element.Name == W.ins && element.Elements(W.r).Any()) { List <object> collectionOfCollections = element .Elements() .Select(n => WmlSearchAndReplaceTransform(n, regex, replacement, callback, trackRevisions, revisionTrackingAuthor, replInfo, coalesceContent)) .ToList(); List <object> collectionOfIns = collectionOfCollections .Select(c => { var elements = c as IEnumerable <XElement>; return(elements != null ? elements.Select(ixc => new XElement(W.ins, element.Attributes(), ixc)) : c); }) .ToList(); return(collectionOfIns); } if (element.Name == W.r) { return(element.Elements() .Where(e => e.Name != W.rPr) .Select(e => e.Name == W.t ? ((string)e).Select(c => new XElement(W.r, element.Elements(W.rPr), new XElement(W.t, XmlUtil.GetXmlSpaceAttribute(c), c))) : new[] { new XElement(W.r, element.Elements(W.rPr), e) }) .SelectMany(t => t)); } return(new XElement(element.Name, element.Attributes(), element.Nodes() .Select(n => WmlSearchAndReplaceTransform(n, regex, replacement, callback, trackRevisions, revisionTrackingAuthor, replInfo, coalesceContent)))); }