/// <summary> /// Visit all text (non-formatting) characters in the rtf string /// </summary> /// <param name="rtf">The raw rtf string to walk.</param> /// <param name="visitor">The <see cref="VisitCharacter"/> callback delegate</param> public static void VisitTextCharacters(string rtf, VisitCharacter visitor) { int characterIndex = -1; int rtfLength = rtf.Length; int currentIndex = 1; // Skip the leading open { char currentCharacter; while (currentIndex < rtfLength) { switch (currentCharacter = rtf[currentIndex]) { case '\\': // Start of a formatting block ++currentIndex; switch (currentCharacter = rtf[currentIndex]) { case '\\': case '}': case '{': if (!visitor(currentCharacter, ++characterIndex, currentIndex)) { return; } ++currentIndex; break; default: currentIndex = SkipFormattingBlock(rtf, currentIndex); break; } break; case '{': // Start of a header block currentIndex = SkipHeaderBlock(rtf, currentIndex); break; case '}': // Should only get this unescaped as the last character, we skipped the opening { return; default: if (!visitor(currentCharacter, ++characterIndex, currentIndex)) { return; } ++currentIndex; break; } } }