/// <summary> /// Gets a list of all marked ranges in the specified range. The current selection is updated to the end of the range to scan. /// </summary> /// <param name="rangeToScan">The Range to scan.</param> /// <param name="ShadingColor">The color for redaction marks in the document.</param> /// <param name="mergeAdjacent">True to merge adjacent ranges with identical formatting, False otherwise.</param> /// <returns>A List of RangeDataEx objects containing each marked subrange.</returns> internal static List <RangeDataEx> GetAllMarkedRanges(Word.Range rangeToScan, Word.WdColor ShadingColor, bool mergeAdjacent) { object Missing = Type.Missing; object CollapseStart = Word.WdCollapseDirection.wdCollapseStart; object CharacterFormatting = Word.WdUnits.wdCharacterFormatting; int LastPosition; int OriginalPosition; List <RangeDataEx> Ranges = new List <RangeDataEx>(); List <RangeDataEx> RangesToDelete = new List <RangeDataEx>(); List <RangeDataEx> ConcatenatedRanges = new List <RangeDataEx>(); //we don't redact comments - if that's where we are, return if (rangeToScan.StoryType == Word.WdStoryType.wdCommentsStory) { return(ConcatenatedRanges); } //move the selection to the beginning of the requested range rangeToScan.Select(); Word.Selection CurrentSelection = rangeToScan.Application.Selection; CurrentSelection.Collapse(ref CollapseStart); OriginalPosition = CurrentSelection.Start; //scan for distinct ranges of formatting do { //update LastPosition LastPosition = CurrentSelection.Start; //move to the next position CurrentSelection.Move(ref CharacterFormatting, ref Missing); //BUG 3913: if we detect that .Move has moved us out of the scan range (which appears to happen because of a Word bug) // break out and don't save the current range if (CurrentSelection.Start < OriginalPosition) { CurrentSelection.End = rangeToScan.End; break; } //store that range if (CurrentSelection.Start != LastPosition && rangeToScan.End != LastPosition) { if (mergeAdjacent) { Ranges.Add(new RangeDataEx(LastPosition, CurrentSelection.Start < rangeToScan.End ? CurrentSelection.Start : rangeToScan.End, new RangeDataEx())); //since we're going to merge, don't fetch the extra properties } else { Word.Font CurrentFont = CurrentSelection.Font; Ranges.Add(new RangeDataEx(LastPosition, CurrentSelection.Start < rangeToScan.End ? CurrentSelection.Start : rangeToScan.End, CurrentFont.Name, CurrentFont.Size, CurrentFont.Bold, CurrentFont.Italic, CurrentSelection.OMaths.Count > 0)); } } }while (CurrentSelection.End <= rangeToScan.End && CurrentSelection.End > LastPosition); if (CurrentSelection.End != rangeToScan.End) { if (mergeAdjacent) { Ranges.Add(new RangeDataEx(CurrentSelection.End, rangeToScan.End, new RangeDataEx())); //since we're going to merge, don't fetch the extra properties } else { Word.Font CurrentFont = CurrentSelection.Font; Ranges.Add(new RangeDataEx(CurrentSelection.End, rangeToScan.End, CurrentFont.Name, CurrentFont.Size, CurrentFont.Bold, CurrentFont.Italic, CurrentSelection.OMaths.Count > 0)); } } //go through those ranges and check if they are marked foreach (RangeDataEx UniqueRange in Ranges) { rangeToScan.Start = UniqueRange.Start; rangeToScan.End = UniqueRange.End; //remove the range from the list if (!IsMarkedRange(rangeToScan, ShadingColor)) { RangesToDelete.Add(UniqueRange); } } //clean out the list foreach (RangeDataEx RangeToDelete in RangesToDelete) { Ranges.Remove(RangeToDelete); } RangesToDelete.Clear(); //concatenate ranges that are next to each other int?Start = null; int?End = null; for (int i = 0; i < Ranges.Count; i++) { //set start and end points if (Start == null) { Start = Ranges[i].Start; } if (End == null) { End = Ranges[i].End; } if ((i + 1) < Ranges.Count && (mergeAdjacent || (Ranges[i].InMath && Ranges[i + 1].InMath) || Ranges[i].IdenticalTo(Ranges[i + 1])) && End == Ranges[i + 1].Start) { End = null; } else { ConcatenatedRanges.Add(new RangeDataEx((int)Start, (int)End, Ranges[i])); Start = End = null; } } //return the marked ranges return(ConcatenatedRanges); }