using System; using System.IO; using System.Reflection; using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Documents; using System.Windows.Markup; using System.Windows.Media; using System.Windows.Resources; using System.Xml; using System35; using JetBrains.Annotations; using JetBrains.Omea.Base; using JetBrains.Omea.OpenAPI; using JetBrains.Util; namespace GUIControls.RichText { /// /// Converts rich content between various formats. /// public static class RichContentConverter { #region Operations /// /// Returns a document that contains a presentation of the exception message. /// Useful for wrapping the presentation methods execution. /// [NotNull] public static FlowDocument DocumentFromException([NotNull] Exception ex) { if(ex == null) throw new ArgumentNullException("ex"); // TODO: monospaced font? return new FlowDocument(new Paragraph(new Run(ex.Message) {Foreground = Brushes.Red})); } /// /// Parses inline XAML and returns a flow document with that content inside. /// “Inline” means inline XAML elements, in the same syntax as inside a . /// Suppose that your content will be wrapped into a or a element and parsed with a XAML reader. /// Basically, this can be a string with formatting elements inside. /// /// Hello, <Bold>World</Bold>! /// XAML string. [NotNull] public static FlowDocument DocumentFromInlineXaml([NotNull] string xaml) { if(xaml == null) throw new ArgumentNullException("xaml"); if(xaml.IsEmpty()) return new FlowDocument(); // Get the XMLNS to use with the fragment /* string xmlns = typeof(Paragraph).Assembly.GetCustomAttributes(typeof(XmlnsDefinitionAttribute), true).Cast().First(attr => attr.ClrNamespace == typeof(Paragraph).Namespace).XmlNamespace; // TODO: use LINQ */ string xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation"; // Wrap the fragment into a top-level element to make some valid XML from it string content = string.Format("{0}", xaml, xmlns); // Parse return new FlowDocument((Paragraph)XamlReader.Load(XmlReader.Create(new StringReader(content)))); } /// /// Creates a flow document that contains the specified unformatted text. /// /// The text content. [NotNull] public static FlowDocument DocumentFromPlainText([NotNull] string text) { if(text == null) throw new ArgumentNullException("text"); if(text.IsEmpty()) return new FlowDocument(); return new FlowDocument(new Paragraph(new Run(text))); // TODO: make paragraphs out of text lines?.. } /// /// Tries to load a flow document from a XAML Flow Document resource. /// /// Either an absolute or a relative URI. Relative URIs will be resolved against the . /// The assembly for handling relative URIs. [NotNull] public static FlowDocument DocumentFromResource([NotNull] string sUri, [NotNull] Assembly assembly) { if(sUri.IsEmpty()) throw new ArgumentNullException("sUri"); if(assembly == null) throw new ArgumentNullException("assembly"); // Adapt the URI to support relative URIs Uri uri = Uri.IsWellFormedUriString(sUri, UriKind.Absolute) ? new Uri(sUri) : Utils.MakeResourceUri(sUri, assembly); // Locate the resource by its URI StreamResourceInfo stream = Application.GetResourceStream(uri); if(stream == null) throw new InvalidOperationException(string.Format("Could not load the document from {0}. The resource could not be found.", sUri.QuoteIfNeeded())); // Try loading the resource (XML and XAML exceptions might be thrown) object resdata = XamlReader.Load(stream.Stream); // Check the type of the loaded data var retval = resdata as FlowDocument; if(retval == null) throw new InvalidOperationException(string.Format("The resource located at {0} is not a flow document.", uri)); return retval; } /// /// Converts the RTF text into a flow document. /// [NotNull] public static FlowDocument DocumentFromRtf([NotNull] string rtf) { if(rtf == null) throw new ArgumentNullException("rtf"); if(rtf.IsEmpty()) return new FlowDocument(); Run run; using(var stream = new MemoryStream()) { // Serialize (with all the encoding-dependent BOMs) var sw = new StreamWriter(stream, Encoding.UTF8); sw.Write(rtf); // Deserialize as XAML run = new Run(); new TextRange(run.ContentStart, run.ContentStart).Load(stream, DataFormats.Rtf); } return new FlowDocument(new Paragraph(run)); } /// /// Invokes to format the document, and returns that document. In case fails with an exception, returns that exception message as a document. /// /// Whether to report the exception as a background Omea exception. /// The function that produces the document. [NotNull] public static FlowDocument DocumentOrException(bool bReportBackgroundException, [NotNull] Func funcGetDocument) { if(funcGetDocument == null) throw new ArgumentNullException("funcGetDocument"); try { return funcGetDocument(); } catch(Exception ex) { if(bReportBackgroundException) Core.ReportBackgroundException(ex); return DocumentFromException(ex); } } #endregion } }