Pergunta

Eu tenho um richtextbox, que planeja salvar em um banco de dados, que pode ser carregado de volta para o mesmo richtextbox. Eu tenho que trabalhar para que eu possa salvar o FlowDocument como DataFormats.XamlPackage, que salva as imagens, mas a questão é que o texto não é pesquisável. Com DataFormats.Xaml, eu tenho o texto é claro, mas sem imagens. As imagens serão coladas em pelo usuário final, e não imagens incluídas com o aplicativo.

Eu tentei usar XamlWriter para obter o texto em XML, e em seguida, pegue as imagens do documento separadamente e inseri-los como binário para o XML, mas eu não consigo encontrar uma maneira de obter as imagens para binário .. .

Alguém tem ideias sobre como obter as imagens em binário, separar do texto?

Agradecemos antecipadamente!

GetImageByteArray () é onde o problema está.

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;
}

Atualizar

Eu acho que pode ter finalmente encontrado uma solução, que vou postar abaixo. Ele usa BmpBitmapEncoder e BmpBitmapDecoder. Isso me permite obter binário a partir da imagem bitmap, armazená-lo no banco de dados, e carregá-lo de volta e exibi-lo de volta para o FlowDocument. Os testes iniciais provaram ser um sucesso. Para fins de teste eu estou ignorando o meu passo de banco de dados e, basicamente duplicar a imagem através da criação de binário, em seguida, tendo o binário e transformá-lo em uma nova imagem e adicioná-lo ao FlowDocument. O único problema é que quando eu tentar tirar o FlowDocument modificado e usar a função XamlWriter.Save, ele erros na imagem recém-criada com "Não é possível serializar um tipo não-público 'System.Windows.Media.Imaging.BitmapFrameDecode". Isso vai levar algum investigação mais aprofundada. Eu vou ter que deixá-lo sozinho por agora embora.

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

Solução

Boa notícia. Eu tive que trabalhar em outra coisa por um tempo, mas isso me permitiu voltar com um novo par de olhos. Eu rapidamente percebi que eu poderia apenas combinar o que eu sabia que estava trabalhando. Eu duvido que esta solução vai ganhar nenhum prêmio, mas funciona. Eu sei que eu posso embrulhar um FlowDocument como texto usando o XamlReader, mantendo os elementos de imagem, mas a perda de dados de imagem. Eu também sabia que eu possa virar um FlowDocument em binário usando XamlFormat. Então eu tive a idéia de levar o FlowDocument, e usando uma função que eu já escrevi para percorrer para encontrar as imagens, eu tomo cada imagem, basicamente clone-lo e colocar o clone em um novo FlowDocument. Eu tomo essa nova FlowDocument que agora contém a imagem única, transformá-lo em binário, e em seguida, tomar o binário resultante, transformá-lo em seqüência de base64 e colá-la na propriedade tag de imagem no FlowDocument originais. Isso mantém os dados de imagem no FlowDocument original como texto. Dessa forma eu posso passar o FlowDocument com dados de imagem (que eu chamo SubString Format) para o XamlReader para obter texto pesquisável. Quando ele sai do banco de dados, eu puxo o FlowDocument fora do Xaml como normal, mas depois iterate através de cada imagem, extrair os dados da propriedade tag usando XamlFormat, e em seguida, criar uma outra imagem clone para fornecer a propriedade Source para o meu real imagem. Eu tenho desde os passos para chegar a substring formato abaixo.

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

Outras dicas

Salvar sua imagem para um MemoryStream e escrever esse fluxo para o arquivo XML.

O fluxo de memória irá convertê-lo em um byte [].

Aqui está o código de exemplo para ambas as minhas sugestões que eu já feitas, mal tem que olhar para a questão da carga útil se os meus exemplos não funcionam ...

        // 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 em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top