C# WPF конвертирует BitmapImage, вставленный в richtextbox, в двоичный файл

StackOverflow https://stackoverflow.com/questions/1820098

Вопрос

У меня есть richtextbox, который я планирую сохранить в базе данных, которую можно будет загрузить обратно в тот же richtextbox.У меня все работает, и я могу сохранить потоковый документ как DataFormats.XamlPackage, который сохраняет изображения, но проблема в том, что текст недоступен для поиска.С DataFormats.Xaml у меня, конечно, есть текст, но нет изображений.Изображения будут вставлены конечным пользователем, а не изображениями, включенными в приложение.

Я попытался использовать XamlWriter, чтобы получить текст в XML, а затем отдельно взять изображения из документа и вставить их в двоичном виде в XML, но я не могу найти способ преобразовать изображения в двоичный формат...

Есть ли у кого-нибудь идеи о том, как преобразовать изображения в двоичный формат отдельно от текста?

Заранее спасибо!

GetImageByteArray() — вот в чем проблема.

Код:

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

ОБНОВЛЯТЬ

Думаю, я наконец нашел решение, о котором я опубликую ниже.Он использует BmpBitmapEncoder и BmpBitmapDecoder.Это позволяет мне получить двоичный файл из растрового изображения, сохранить его в базе данных, загрузить обратно и отобразить обратно в FlowDocument.Первоначальные испытания оказались успешными.В целях тестирования я пропускаю этап работы с базой данных и в основном дублирую изображение, создавая двоичный файл, затем беру двоичный файл, превращаю его в новое изображение и добавляю его в FlowDocument.Единственная проблема заключается в том, что когда я пытаюсь взять измененный FlowDocument и использовать функцию XamlWriter.Save, во вновь созданном изображении возникает ошибка «Невозможно сериализовать закрытый тип System.Windows.Media.Imaging.BitmapFrameDecode».Это потребует дальнейшего расследования.Хотя мне пока придется оставить это в покое.

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);
        }
Это было полезно?

Решение

Хорошие новости.Некоторое время мне пришлось поработать над чем-то другим, но это позволило мне вернуться свежим взглядом.Я быстро понял, что могу просто объединить то, что, как я знал, работает.Я сомневаюсь, что это решение получит какие-либо награды, но оно работает.Я знаю, что могу обернуть FlowDocument как текст с помощью XamlReader, сохранив элементы изображения, но потеряв данные изображения.Я также знал, что могу превратить FlowDocument в двоичный файл с помощью XamlFormat.Итак, у меня возникла идея взять FlowDocument и использовать уже написанную мной функцию для перебора его в поисках изображений. Я беру каждое изображение, по сути клонирую его и помещаю клон в новый FlowDocument.Я беру этот новый FlowDocument, который теперь содержит одно изображение, превращаю его в двоичный файл, а затем беру полученный двоичный файл, превращаю его в строку base64 и вставляю его в свойство tag изображения в исходном FlowDocument.При этом данные изображения в исходном FlowDocument сохраняются в виде текста.Таким образом я могу передать FlowDocument с данными изображения (который я называю форматом SUBString) в XamlReader, чтобы получить текст с возможностью поиска.Когда он выходит из базы данных, я извлекаю FlowDocument из Xaml как обычно, но затем перебираю каждое изображение, извлекая данные из свойства тега с помощью XamlFormat, а затем создаю другое изображение-клон, чтобы предоставить свойство Source для моего фактического изображения. изображение.Ниже я предоставил инструкции по переходу к формату SUBString.

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

Другие советы

Сохраните изображение в MemoryStream и запишите этот поток в свой XML-файл.

Поток памяти преобразует его в Byte[].

Вот пример кода для обоих моих предложений, которые я уже сделал, мне придется изучить проблему с полезной нагрузкой, если мои примеры не работают...

        // 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;
    }
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top