Pregunta

Tengo un cuadro de texto enriquecido, que planeo guardar en una base de datos, que se puede volver a cargar en el mismo cuadro de texto enriquecido. Lo tengo funcionando para poder guardar el documento de flujo como DataFormats.XamlPackage, que guarda las imágenes, pero el problema es que el texto no se puede buscar. Con DataFormats.Xaml, tengo el texto, por supuesto, pero no imágenes. El usuario final pegará las imágenes, no las imágenes incluidas con la aplicación.

Intenté usar XamlWriter para obtener el texto en XML, y luego tomar las imágenes del documento por separado e insertarlas como binarias en el XML, pero parece que no puedo encontrar una manera de hacer que las imágenes sean binarias. .

¿Alguien tiene ideas sobre cómo obtener las imágenes en binario, separadas del texto?

¡Gracias de antemano!

GetImageByteArray () es donde está el problema.

Código:

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

Creo que finalmente encontré una solución, que publicaré a continuación. Utiliza BmpBitmapEncoder y BmpBitmapDecoder. Esto me permite obtener binarios de la imagen de mapa de bits, almacenarlos en la base de datos y volver a cargarlos y mostrarlos de nuevo en FlowDocument. Las pruebas iniciales han demostrado ser exitosas. Para fines de prueba, omito el paso de mi base de datos y básicamente duplico la imagen creando un binario, luego tomo el binario y lo convierto en una nueva imagen y lo agrego al FlowDocument. El único problema es que cuando intento tomar el FlowDocument modificado y usar la función XamlWriter.Save, se produce un error en la imagen recién creada con "No se puede serializar un tipo no público 'System.Windows.Media.Imaging.BitmapFrameDecode " ;. Esto requerirá más investigación. Sin embargo, tendré que dejarlo solo por ahora.

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);
        }
¿Fue útil?

Solución

Buenas noticias. Tuve que trabajar en otra cosa por un tiempo, pero esto me permitió volver con un par de ojos nuevos. Rápidamente me di cuenta de que podía combinar lo que sabía que estaba funcionando. Dudo que esta solución gane algún premio, pero funciona. Sé que puedo envolver un FlowDocument como texto usando XamlReader, manteniendo los elementos de la imagen pero perdiendo los datos de la imagen. También sabía que puedo convertir un FlowDocument en binario usando XamlFormat. Así que tuve la idea de tomar el FlowDocument, y usando una función que ya escribí para iterar a través de él para encontrar las imágenes, tomé cada imagen, básicamente la cloné y puse el clon en un nuevo FlowDocument. Tomo ese nuevo FlowDocument que ahora contiene la imagen individual, lo convierto en binario y luego tomo el binario resultante, lo convierto en una cadena base64 y lo pego en la propiedad de etiqueta de la imagen en el FlowDocument original. Esto mantiene los datos de la imagen en el FlowDocument original como texto. De esta manera puedo pasar el FlowDocument con datos de imagen (que llamo SUBString Format) al XamlReader para obtener texto de búsqueda. Cuando sale de la base de datos, saco el FlowDocument del Xaml como de costumbre, pero luego itero a través de cada imagen, extrayendo los datos de la propiedad de la etiqueta usando XamlFormat, y luego creando otra imagen de clon para proporcionar la propiedad Source para mi actual imagen. He proporcionado los pasos para llegar al formato SUBString a continuación.

/// <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());
        }
    }

Otros consejos

Guarde su imagen en un MemoryStream y escriba esa secuencia en su archivo XML.

El flujo de memoria lo convertirá en un Byte [].

Aquí está el código de muestra para mis dos sugerencias que ya he hecho, tendré que investigar el problema de la carga útil si mis ejemplos no funcionan ...

        // 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;
    }
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top