I've got a richtextbox, that I plan on saving to a database, which can be loaded back into the same richtextbox. I've got it working so that I can save the flowdocument as DataFormats.XamlPackage, which saves the images, but the issue is that the text isn't searchable. With DataFormats.Xaml, I've got the text of course, but no images. The images will be pasted in by the end user, not images included with the application.
I tried using XamlWriter to get the text into XML, and then grab the images from the document separately and insert them as binary into the XML, but I can't seem to find a way to get the images to binary...
Does anyone have ideas on how to get the images into binary, separate from the text?
Thanks in advance!
GetImageByteArray() is where the issue is.
Code:
private void SaveXML()
{
TextRange documentTextRange = new TextRange(richTextBox.Document.ContentStart, richTextBox.Document.ContentEnd);
FlowDocument flowDocument = richTextBox.Document;
using (StringWriter stringwriter = new StringWriter())
{
using (System.Xml.XmlWriter writer = System.Xml.XmlWriter.Create(stringwriter))
{
XamlWriter.Save(flowDocument, writer );
}
testRTF t = new testRTF();
t.RtfText = new byte[0];
t.RtfXML = GetImagesXML(flowDocument);
t.RtfFullText = stringwriter.ToString();
//save t to database
}
richTextBox.Document.Blocks.Clear();
}
private string GetImagesXML(FlowDocument flowDocument)
{
using (StringWriter stringwriter = new StringWriter())
{
using (System.Xml.XmlWriter writer = System.Xml.XmlWriter.Create(stringwriter))
{
Type inlineType;
InlineUIContainer uic;
System.Windows.Controls.Image replacementImage;
byte[] bytes;
System.Text.ASCIIEncoding enc;
//loop through replacing images in the flowdoc with the byte versions
foreach (Block b in flowDocument.Blocks)
{
foreach (Inline i in ((Paragraph)b).Inlines)
{
inlineType = i.GetType();
if (inlineType == typeof(Run))
{
//The inline is TEXT!!!
}
else if (inlineType == typeof(InlineUIContainer))
{
//The inline has an object, likely an IMAGE!!!
uic = ((InlineUIContainer)i);
//if it is an image
if (uic.Child.GetType() == typeof(System.Windows.Controls.Image))
{
//grab the image
replacementImage = (System.Windows.Controls.Image)uic.Child;
//get its byte array
bytes = GetImageByteArray((BitmapImage)replacementImage.Source);
//write the element
writer.WriteStartElement("Image");
//put the bytes into the tag
enc = new System.Text.ASCIIEncoding();
writer.WriteString(enc.GetString(bytes));
//close the element
writer.WriteEndElement();
}
}
}
}
}
return stringwriter.ToString();
}
}
//This function is where the problem is, i need a way to get the byte array
private byte[] GetImageByteArray(BitmapImage bi)
{
byte[] result = new byte[0];
using (MemoryStream ms = new MemoryStream())
{
XamlWriter.Save(bi, ms);
//result = new byte[ms.Length];
result = ms.ToArray();
}
return result;
}
UPDATE
I think I may have finally found a solution, which I will post below. It uses BmpBitmapEncoder and BmpBitmapDecoder. This allows me to get binary from the bitmap image, store it to the database, and load it back up and display it right back into the FlowDocument. Initial tests have proven successful. For testing purposes I'm bypassing my database step and basically duplicating the image by creating binary, then taking the binary and turning it into a new image and adding it to the FlowDocument. The only issue is that when I try and take the modified FlowDocument and use the XamlWriter.Save function, it errors on the newly created Image with "Cannot serialize a non-public type 'System.Windows.Media.Imaging.BitmapFrameDecode". This will take some further investigation. I'll have to leave it alone for now though.
private void SaveXML()
{
TextRange documentTextRange = new TextRange(richTextBox.Document.ContentStart, richTextBox.Document.ContentEnd);
FlowDocument flowDocument = richTextBox.Document;
string s = GetImagesXML(flowDocument);//temp
LoadImagesIntoXML(s);
using (StringWriter stringwriter = new StringWriter())
{
using (System.Xml.XmlWriter writer = System.Xml.XmlWriter.Create(stringwriter))
{
XamlWriter.Save(flowDocument, writer );//Throws error here
}
}
}
private string GetImagesXML(FlowDocument flowDocument)
{
string s= "";
using (StringWriter stringwriter = new StringWriter())
{
Type inlineType;
InlineUIContainer uic;
System.Windows.Controls.Image replacementImage;
byte[] bytes;
BitmapImage bi;
//loop through replacing images in the flowdoc with the byte versions
foreach (Block b in flowDocument.Blocks)
{
foreach (Inline i in ((Paragraph)b).Inlines)
{
inlineType = i.GetType();
if (inlineType == typeof(Run))
{
//The inline is TEXT!!!
}
else if (inlineType == typeof(InlineUIContainer))
{
//The inline has an object, likely an IMAGE!!!
uic = ((InlineUIContainer)i);
//if it is an image
if (uic.Child.GetType() == typeof(System.Windows.Controls.Image))
{
//grab the image
replacementImage = (System.Windows.Controls.Image)uic.Child;
bi = (BitmapImage)replacementImage.Source;
//get its byte array
bytes = GetImageByteArray(bi);
s = Convert.ToBase64String(bytes);//temp
}
}
}
}
return s;
}
}
private byte[] GetImageByteArray(BitmapImage src)
{
MemoryStream stream = new MemoryStream();
BmpBitmapEncoder encoder = new BmpBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create((BitmapSource)src));
encoder.Save(stream);
stream.Flush();
return stream.ToArray();
}
private void LoadImagesIntoXML(string xml)
{
byte[] imageArr = Convert.FromBase64String(xml);
System.Windows.Controls.Image img = new System.Windows.Controls.Image()
MemoryStream stream = new MemoryStream(imageArr);
BmpBitmapDecoder decoder = new BmpBitmapDecoder(stream, BitmapCreateOptions.None, BitmapCacheOption.Default);
img.Source = decoder.Frames[0];
img.Stretch = Stretch.None;
Paragraph p = new Paragraph();
p.Inlines.Add(img);
richTextBox.Document.Blocks.Add(p);
}
C programming language is a machine-independent programming language that is mainly used to create many types of applications and operating systems such as Windows, and other complicated programs such as the Oracle database, Git, Python interpreter, and games and is considered a programming foundation in the process of ...
C is a general-purpose language that most programmers learn before moving on to more complex languages. From Unix and Windows to Tic Tac Toe and Photoshop, several of the most commonly used applications today have been built on C. It is easy to learn because: A simple syntax with only 32 keywords.
In the real sense it has no meaning or full form. It was developed by Dennis Ritchie and Ken Thompson at AT&T bell Lab. First, they used to call it as B language then later they made some improvement into it and renamed it as C and its superscript as C++ which was invented by Dr. Stroustroupe.
C is more difficult to learn than JavaScript, but it's a valuable skill to have because most programming languages are actually implemented in C. This is because C is a “machine-level” language. So learning it will teach you how a computer works and will actually make learning new languages in the future easier.
Good news. I had to work on something else for a while, but this allowed me to come back with a fresh pair of eyes. I quickly realized that I could just combine what I knew was working. I doubt this solution will win any awards, but it works. I know that I can wrap a FlowDocument up as text using the XamlReader, keeping the image elements but losing image data. I also knew that I can turn a FlowDocument into binary using XamlFormat. So I had the idea of taking the FlowDocument, and using a function I already wrote to iterate through it to find the images, I take each image, basically clone it and put the clone into a new FlowDocument. I take that new FlowDocument that now contains the single image, turn it into binary, and then take the resulting binary, turn it into base64 string and stick it into the tag property of the image in the original FlowDocument. This keeps image data in the original FlowDocument as text. This way I can pass the FlowDocument with image data (which I call SUBString Format) into the XamlReader to get searchable text. When it comes out of the database, I pull the FlowDocument out of the Xaml as normal, but then iterate through each image, extracting the data from the tag property using XamlFormat, and then creating another clone image to provide the Source property for my actual image. I have provided the steps to get to SUBString format below.
/// <summary>
/// Returns a FlowDocument in SearchableText UI Binary (SUB)String format.
/// </summary>
/// <param name="flowDocument">The FlowDocument containing images/UI formats to be converted</param>
/// <returns>Returns a string representation of the FlowDocument with images in base64 string in image tag property</returns>
private string ConvertFlowDocumentToSUBStringFormat(FlowDocument flowDocument)
{
//take the flow document and change all of its images into a base64 string
FlowDocument fd = TransformImagesTo64(flowDocument);
//apply the XamlWriter to the newly transformed flowdocument
using (StringWriter stringwriter = new StringWriter())
{
using (System.Xml.XmlWriter writer = System.Xml.XmlWriter.Create(stringwriter))
{
XamlWriter.Save(flowDocument, writer);
}
return stringwriter.ToString();
}
}
/// <summary>
/// Returns a FlowDocument with images in base64 stored in their own tag property
/// </summary>
/// <param name="flowDocument">The FlowDocument containing images/UI formats to be converted</param>
/// <returns>Returns a FlowDocument with images in base 64 string in image tag property</returns>
private FlowDocument TransformImagesTo64(FlowDocument flowDocument)
{
FlowDocument img_flowDocument;
Paragraph img_paragraph;
InlineUIContainer img_inline;
System.Windows.Controls.Image newImage;
Type inlineType;
InlineUIContainer uic;
System.Windows.Controls.Image replacementImage;
//loop through replacing images in the flowdoc with the base64 versions
foreach (Block b in flowDocument.Blocks)
{
//loop through inlines looking for images
foreach (Inline i in ((Paragraph)b).Inlines)
{
inlineType = i.GetType();
/*if (inlineType == typeof(Run))
{
//The inline is TEXT!!! $$$$$ Kept in case needed $$$$$
}
else */if (inlineType == typeof(InlineUIContainer))
{
//The inline has an object, likely an IMAGE!!!
uic = ((InlineUIContainer)i);
//if it is an image
if (uic.Child.GetType() == typeof(System.Windows.Controls.Image))
{
//grab the image
replacementImage = (System.Windows.Controls.Image)uic.Child;
//create a new image to be used to get base64
newImage = new System.Windows.Controls.Image();
//clone the image from the image in the flowdocument
newImage.Source = replacementImage.Source;
//create necessary objects to obtain a flowdocument in XamlFormat to get base 64 from
img_inline = new InlineUIContainer(newImage);
img_paragraph = new Paragraph(img_inline);
img_flowDocument = new FlowDocument(img_paragraph);
//Get the base 64 version of the XamlFormat binary
replacementImage.Tag = TransformImageTo64String(img_flowDocument);
}
}
}
}
return flowDocument;
}
/// <summary>
/// Takes a FlowDocument containing a SINGLE Image, and converts to base 64 using XamlFormat
/// </summary>
/// <param name="flowDocument">The FlowDocument containing a SINGLE Image</param>
/// <returns>Returns base 64 representation of image</returns>
private string TransformImageTo64String(FlowDocument flowDocument)
{
TextRange documentTextRange = new TextRange(flowDocument.ContentStart, flowDocument.ContentEnd);
using (MemoryStream ms = new MemoryStream())
{
documentTextRange.Save(ms, DataFormats.XamlPackage);
ms.Position = 0;
return Convert.ToBase64String(ms.ToArray());
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With