And to finalize, here is the Rtf helper: using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Documents; using CatenaLogic.Windows.Controls; namespace CatenaLogic.Windows.Documents { /// <summary> /// Class defining all the Rtf constants and methods. /// </summary> public static class Rtf { #region Constants /// <summary> /// Rtf marker for font style. /// </summary> public const string RtfFontStyleMarker = "{//fonttbl"; /// <summary> /// Rtf marker for font size. /// </summary> public const string RtfFontSizeMarker = "//fs"; private const string CarriageReturn = "/r"; private const string LineFeed = "/n"; private const string CarriageReturnLineFeed = "/r/n"; private const string CustomCarriageReturn = "[cr]"; private const string CustomLineFeed = "[lf]"; private const string CustomCarriageReturnLineFeed = "[crlf]"; #endregion #region Methods /// <summary> /// Converts plain text to Rtf with the newline bug fix. /// </summary> /// <param name="text">Text to convert.</param> /// <returns>Rtf version of the text.</returns> public static string ConvertPlainTextToRtf(this string text) { // Declare constants const string RtfCarriageReturn = "//line "; // Replace all known feeds to a custom constant text = text.Replace(CarriageReturnLineFeed, CustomCarriageReturnLineFeed).Replace(CarriageReturn, CustomCarriageReturn).Replace(LineFeed, CustomLineFeed); // Now convert all working feeds back text = text.Replace(CustomCarriageReturnLineFeed, CarriageReturnLineFeed).Replace(CustomLineFeed, CarriageReturnLineFeed); // Invoke from main application dispatcher Application.Current.Dispatcher.Invoke((Action)(delegate() { // Convert using a control RichTextBox rtfControl = CreateRichTextBoxWithText(text); // Read the text again (now it should be rtf) text = rtfControl.GetText(); }), null); // Now replace all known bugs text = text.Replace(CustomCarriageReturn, RtfCarriageReturn); #region Remove all formatting options // Font styles while (text.Contains(RtfFontStyleMarker)) { // Get index of font definition int fontStart = text.IndexOf(RtfFontStyleMarker); int fontEnd = fontStart + 1; int openings = 1; while (openings > 0) { // Check current character if (text[fontEnd] == '}') { // Decrease openings openings--; } else if (text[fontEnd] == '{') { // Increase openings openings++; } // Increase current character fontEnd++; } // Remove marker text = text.Remove(fontStart, fontEnd - fontStart - 1); } // Font size while (text.Contains(RtfFontSizeMarker)) { // Get index of font definition int fontStart = text.IndexOf(RtfFontSizeMarker); int fontEnd = fontStart + 1; int openings = 1; while (openings > 0) { // Check current character if ((text[fontEnd] == '//') || (text[fontEnd] == ' ')) { // Decrease openings openings--; } // Increase current character fontEnd++; } // Remove marker text = text.Remove(fontStart, fontEnd - fontStart - 1); } #endregion // Return result return text; } /// <summary> /// Converts Rtf to plain text. /// </summary> /// <param name="text">Text to convert.</param> /// <returns>Plain text version of the text.</returns> public static string ConvertRtfToPlainText(this string text) { // Invoke from main application dispatcher Application.Current.Dispatcher.Invoke((Action)(delegate() { // Fix the newline bug by replacing /par by /par/par and /line by /par text = text.Replace("//par", "//par//par").Replace("//line", "//par").Replace("//par//pard", "//pard"); // Convert using a control RichTextBox rtfControl = CreateRichTextBoxWithText(text); // Get the text range TextRange textRange = new TextRange(rtfControl.Document.ContentStart, rtfControl.Document.ContentEnd); // Get the plain text from the text range text = textRange.Text; }), null); // Remove last linefeeds while (text.EndsWith(Environment.NewLine)) { text = text.Substring(0, text.Length - Environment.NewLine.Length); } // Replace all known feeds to a custom constant text = text.Replace(CarriageReturnLineFeed + CarriageReturnLineFeed, CustomCarriageReturnLineFeed).Replace(CarriageReturnLineFeed, CustomCarriageReturn).Replace(LineFeed, CustomLineFeed); // Now convert all working feeds back text = text.Replace(CustomCarriageReturnLineFeed, CarriageReturnLineFeed).Replace(CustomCarriageReturn, CarriageReturn).Replace(CustomLineFeed, CarriageReturnLineFeed); // Return text return text; } #endregion #region Private helper methods /// <summary> /// Creates a <see cref="RichTextBox"/> that contains the specific text. /// </summary> /// <param name="text">Text to put into the Rtf control.</param> /// <returns><see cref="RichTextBox"/> control with the text.</returns> private static RichTextBox CreateRichTextBoxWithText(string text) { // Create control RichTextBox rtfControl = new RichTextBox(); rtfControl.Width = 300; rtfControl.Height = 300; // Get text range and load the text into it TextRange textRange = new TextRange(rtfControl.Document.ContentStart, rtfControl.Document.ContentEnd); textRange.SetText(text); // Clear margins of paragraphs textRange.ClearTextMargins(); // Return control return rtfControl; } #endregion } }