Fastest way to convert RTF to FlowDocument - wpf

What is the fastest way to convert a RTF to FlowDocument? I store RTF as plain string and then reload it back, I am using following method,
FlowDocument document = new FlowDocument();
document.SetValue(FlowDocument.TextAlignmentProperty, TextAlignment.Left);
TextRange content = new TextRange(document.ContentStart, document.ContentEnd);
if (content.CanLoad(DataFormats.Rtf) && string.IsNullOrEmpty(rtf) == false)
{
// If so then load it with RTF
byte[] valueArray = Encoding.ASCII.GetBytes(rtf);
using (MemoryStream stream = new MemoryStream(valueArray))
{
content.Load(stream, DataFormats.Rtf);
}
}
But this method is very slow. I need to load many RTFs (around 1000). What can be the trick to make the process fast? Is there any other way around to load a Flowdocument?

You really need to define what you actually need. TextBlock is not weak at all.
It has things to offer ;).
Colours(Background/Foreground + you can color specific part of TextBlock even)
Alignments(you can align TextBlocks as you want, and perhaps even part of them?! Not sure about the last one.
It has TextDecorations, which means it supports bold/italic/underline/strikethrough etc.
Fonts(yeah it supports custom fonts and whatever font you want)
But fair enough. I think you should store FlowDocument XAML instead of actual RTF. This way there will be no conversion and it should be multiple times faster. (See DataFormats.xaml)

Hello Vibhore the TextBlock element should be used when limited text support is required, Label can be used when minimal text support is required.
The FlowDocument element is a container for re-flowable documents that support rich presentation of content, and therefore, has a greater performance impact than using the TextBlock or Label controls.

Related

WPF DrawingContext DrawGlyphRun blur text

I'm using the DrawingContext DrawGlyphRun(GlyphRun) function to draw text in the Canvas using the solution from https://smellegantcode.wordpress.com/2008/07/03/glyphrun-and-so-forth/.
I'm using this over the FormattedText because it's faster and it's also used for calculating text width.
This works well except for 2 problems:
Text is blurred (see below the image). Text at the top is displayed using GlyphRun. The bottom text is displayed using FormattedText which has better quality.
Cannot display Japanese or Chinese characters.
Problem with characters seems to be that GlyphTypeface.CharacterToGlyphMap cannot find the jp or cn character, so I'm not sure how exactly to deal with these characters.
I just found your question after some research that I made.
GlyphRun created using public constructors creates object with TextFormattingMode = Ideal
All WPF contols for their rendring use methods/constructors that accepts TextFormattingMode as parameter.
You can call GlyphRun.TryCreate() static method via reflection:
internal static GlyphRun TryCreate(
GlyphTypeface glyphTypeface,
int bidiLevel,
bool isSideways,
double renderingEmSize,
IList<ushort> glyphIndices,
Point baselineOrigin,
IList<double> advanceWidths,
IList<Point> glyphOffsets,
IList<char> characters,
string deviceFontName,
IList<ushort> clusterMap,
IList<bool> caretStops,
XmlLanguage language,
TextFormattingMode textLayout
)
but the problem that you need to get advanceWidths with TextFormattingMode = Ideal. For this you need access via reflection to internal methods provided by GlyphTypeface class.
GlyphTypeface.AdvanceWidths property that returns dictionary with these widths internally calls to
internal double GetAdvanceWidth(ushort glyph, TextFormattingMode textFormattingMode, bool isSideways)
when you access dictionary by index with textFormattingMode = TextFormattingMode.Ideal
You can download .Net source code and check it yourself.
As for your second question I think that you use chars instead of unicode code points to get glyph index.

Highlight line(s)/characters in WPF

Scenario
I am currently developing an application. Within this application, I have a TextBox/RichTextBox. I have not decided on the control yet. Within this control, there will be a few paragraphs of text.
Problem
I want to be able to highlight a range of lines, or particular characters within that line, using two given numbers. What would the easiest way to do this be?
Use RichTextBox. You can't use TextBox, that is because TextBox has only one style applied to all text.
Use the TextRange.ApplyPropertyValue method. A TextRange is specified by its starting and ending position, which are two TextPointer.
Something like this
var startingPos = RichTextBox1.ContentStart.GetPositionAtOffset(n1, LogicalDirection.Forward);
var endingPos = startingPos.GetPositionAtOffset(n2 - n1, LogicalDirection.Forward);
var textrange = new TextRange(startingPos, endingPos);
textrange.ApplyPropertyValue(TextElement.BackgroundProperty, Brushes.DarkRed);
You have to carefully calculate the offsets of the starting and ending positions, taking linebreaks into account.

RichTextBox SelectionFont is unexpectedly *not* null

I'd like to change the font size of a chunk of RTF without erasing the bold / italic / underline formatting (an issue similar to the one in this question). The accepted answer is to modify the selection of the text box until the SelectionFont propery is null in order to find runs of consistently formatted text which can be modified individually. Sounds reasonable. However the actual behavior of the RichTextBox control seems to be inconsistent with the documentation.
In the documentation for RichTextBox.SelectionFont MSDN states:
If the current text selection has more than one font specified, this
property is null.
However, this code which uses mixed bold / regular text doesn't behave as you'd expect:
var rtb = new RichTextBox {
Rtf = #"{\rtf1 This is \b bold\b0.}"
};
rtb.SelectAll();
// Now you'd expect rtb.SelectionFont to be null,
// but it actually returns a Font object
Is there any other reliable way of formatting the text so that I can change the font size without clobbering the other formatting. (Manipulating the RTF directly is OK, I'm not absolutely set on using WinForms to achieve this).
I've given up on trying to go through Winforms to fix this. As I'm applying the change to a whole document (rather than just one portion), it turns out that it's not too hard to modify the RTF directly.
In this case I'm interested in the font size, which is represented by the \fs command. So to replace all the 8.5pt text with 10pt text, you can replace \fs17 with \fs20. (Yes, RTF font sizes come in units of half a point, apparently).
This seems to work well enough, although it does feel like one of those "let's mangle our HTML using regular expressions" type solutions, so I'm not convinced that it's very robust.
Take a look at this:
Changing font for richtextbox without losing formatting
I think it's the same issue. LarsTech's solution is working perfectly for me.

Formatting specifc lines of text in WPF RichTextBox

In a WPF .NET 4.0 RichTextBox with the following text:
Apple
Cheese
Orange
Pear
Chicken
How would I programmatically with C#, (not with XAML markup), bold all lines that start with the character "C"?
More generally, how do you get a reference to a given line of text from a RichTextBox and then apply some formatting to it?
Well that was trickier than I expected but I think the code below does it:
foreach (var paragraph in richTextBox1.Document.Blocks)
{
var text = new TextRange(paragraph.ContentStart,
paragraph.ContentEnd).Text;
paragraph.FontWeight = text.StartsWith("C") ?
FontWeights.Bold : FontWeights.Normal;
}
Basically, the RichTextBox holds its content in a FlowDocument (accessed through the Document property), which in turn has a collection of Blocks containing each Paragraph. Actually, each item in the Blocks collection can be anything derived from the abstract class Block...but I'm assuming if you only ever add simple text to your RichTextBox then they'll always just be Paragraphs. See here for a better explanation!
The trickiest part is that to get the text out of the paragraph you need to use the TextRange class...but the good news is that, once we have the text, the Paragraph has simple properties on it for setting the font weight, etc!

Are there any FlowDocument diff viewers for WPF?

We have 2 flowdocuments that we'd like to compare similar to when using a diff viewer (winmerge, beyond compare, etc). Has anybody done this or know how to get the text out of a flowdocument to do a compare?
Here's a way to save it as raw xaml (text file) from the code-behind file, assuming that the flowdocument (not viewer) itself is named "myFlowDoc", if only the viewer is named, use the property .Document of the viewer to get it. And a stream to a stream myStream (FileStream, MemoryStream, etc doesn't matter).
// Create a TextRange around the entire document.
TextRange documentTextRange = new TextRange(myFlowDoc.ContentStart, myFlowDoc.ContentEnd);
// Save it. Note that it will not respect current stream position;
// it'll assume that it gets the entire stream.
documentTextRange.Save(myStream, DataFormats.Xaml);
I just forged together a basic WPF diff viewer. Shouldn't be too hard to adapt it to write a side by side Flowdocument diff view.
Find more information here: http://www.eqqon.com/index.php/GitSharp#GitSharp.Demo
-- henon

Resources