C # WPF converts BitmapImage inserted in richtextbox to binary

I have a richtextbox, which I plan to save in a database, which can be loaded back into the same richtextbox. This works for me, so I can save the flowdocument as DataFormats.XamlPackage, which saves images, but the problem is that the text is not searchable. With DataFormats.Xaml, I have text, of course, but no images. Images will be inserted by the end user, not images included in the application.

I tried using XamlWriter to get the text in XML and then capture the images from the document separately and paste them as binary in XML, but I can not find a way to get the images in binary format ..

Does anyone have any ideas on how to get images to binary files separately from text?

Thanks in advance!

GetImageByteArray () is the problem.

the 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 that maybe I found a solution that I will post below. It uses BmpBitmapEncoder and BmpBitmapDecoder. This allows me to retrieve the binary from the bitmap, save it to the database and load it back and display it back in the FlowDocument. Initial tests were successful. For testing purposes, I will go around my database step and basically duplicate the image by creating a binary, then taking the binary and turning it into a new image and adding it to the FlowDocument. The only problem is that when I try to use the modified FlowDocument and use the XamlWriter.Save function, these are errors for the newly created image with "Cannot serialize the non-public type" System.Windows.Media.Imaging.BitmapFrameDecode. This will require further study. for the moment I will have to leave him alone.

 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); } 
+6
c # serialization image wpf richtextbox
source share
3 answers

Good news. I had to work a bit on something else, but it allowed me to return with fresh pairs of eyes. I quickly realized that I could just combine what I knew was working. I doubt that this solution will win any awards, but it works. I know that I can wrap FlowDocument as text using XamlReader, retaining image elements but losing image data. I also knew that I could turn a FlowDocument into a binary using XamlFormat. So I had an idea to take a FlowDocument and, using the function that I already wrote to iterate through it to find the images, I take each image, basically clone it and put it in a new FlowDocument. I take this new FlowDocument, which now contains one image, turns it into binary, and then takes the resulting binary, turns it into a base64 string and inserts it into the image tag property in the original FlowDocument. This saves the image data in the original FlowDocument as text. That way, I can pass a FlowDocument with image data (which I call SUBString Format) to XamlReader to get searchable text. When it exits the database, I wrap the FlowDocument from Xaml as usual, but then repeat 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 given the steps to go to the 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()); } } 
+2
source share

Save the image to a MemoryStream and write this stream to your XML file.

The memory stream converts it to byte [].

0
source share

Here is a sample code for both of my suggestions that I have already made, you need to study the problem with the payload if my examples do not work ...

  // get raw bytes from BitmapImage using BaseUri and SourceUri private byte[] GetImageByteArray(BitmapImage bi) { byte[] result = new byte[0]; string strImagePath = Path.Combine(Path.GetDirectoryName(bi.BaseUri.OriginalString), bi.UriSource.OriginalString); byte[] fileBuffer; using (FileStream fileStream = new FileStream(strImagePath, FileMode.Open)) { fileBuffer = new byte[fileStream.Length]; fileStream.Write(fileBuffer, 0, (int)fileStream.Length); } using (MemoryStream ms = new MemoryStream(fileBuffer)) { XamlWriter.Save(bi, ms); //result = new byte[ms.Length]; result = ms.ToArray(); } return result; } // get raw bytes from BitmapImage using BitmapImage.CopyPixels private byte[] GetImageByteArray(BitmapSource bi) { int rawStride = (bi.PixelWidth * bi.Format.BitsPerPixel + 7) / 8; byte[] result = new byte[rawStride * bi.PixelHeight]; bi.CopyPixels(result, rawStride, 0); return result; } private BitmapSource GetImageFromByteArray(byte[] pixelInfo, int height, int width) { PixelFormat pf = PixelFormats.Bgr32; int stride = (width * pf.BitsPerPixel + 7) / 8; BitmapSource image = BitmapSource.Create(width, height, 96, 96, pf, null, pixelInfo, stride); return image; } 
0
source share

All Articles