user1711233
user1711233

Reputation: 251

Insert image into XML document

I'm trying to insert an image for a barcode into an XML document using C#; the method passes a path to an image, which is then inserted (alongside other data) into an XML file.

I've tried following some other people's recommendations (like this one) , but I'm not getting the correct result; all I get is the raw byte string output in the file.

        byte[] bytes = File.ReadAllBytes(barcodePath);
        string barcodeString = Convert.ToBase64String(bytes);

        //find barcode position
        XmlNodeList barcodePossiblesList = doc.SelectNodes("//w:t", namespaceManager);

        foreach(XmlNode n in barcodePossiblesList)
        {
            if(n.InnerText == "Barcode")
            {
                n.InnerText = barcodeString;
            }
        }

I've also tried the following, but I still get the same result:

Bitmap bmp = new Bitmap(imageFileName);
TypeConverter converter = TypeDescriptor.GetConverter(typeof(Bitmap));
XElement img = new XElement("image",
Convert.ToBase64String(
    (byte[])converter.ConvertTo(bmp, typeof(byte[]))));
element.Add(img);

Can anyone help me? Thanks

Upvotes: 0

Views: 4160

Answers (1)

bubbinator
bubbinator

Reputation: 495

I went through all the links in the linked question and found the one that you have been using. What seems to be the issue is that in your program you haven't placed code in there to convert the data in the xml file back into an image, just like Chuck pointed out. The code you provided is most likely saving the data correctly, but if you are using a generic editor (like Notepad or the XML editor built-in to Visual Studio) you will only see the string equivalents to the pixel data of the bitmap. Since you are trying to convert the data back to an image, just use the code provided in the post under the heading Reading the Image from the XML file. Namely,

string val = currentXml.Element("image").Value;
byte[] bytes = Convert.FromBase64String(val);
MemoryStream mem = new MemoryStream(bytes);
Bitmap bmp2 = new Bitmap(mem);

This is a one-liner of the above code. Also taken from the linked site.

Bitmap bmp = new Bitmap(new MemoryStream(Convert.FromBase64String(currentXml.Element("image").Value)));

UPDATE: I tested the code provided in the OP as well as in this post. It does work.

UPDATE 2: In response to your questions, I have decided to expand upon what the blog had posted. I understood it, but I do know LINQ-to-XML well, which eliminated any bumps in the road when the blogger made a terminology error.

To start off, the entire process is to take the pixel data from an image and place it within an xml file. That way the image can be sent with other things. The string that you see when opening the xml file in a plain text editor or xml editor is the image data that has been encoded. You could think of that as the raw data.

In my test case, I created a texture atlas (all frames to an animation placed within a single image file by tiling them in a given order), placed within the xml file some metadata (the number of frames horizontally and vertically). When explaining some of this in the context of experience, the above is for some background.

Something that I should point out now is the using directives used to make this all work:

using System;
using System.ComponentModel;
using System.Drawing;
using System.Xml.Linq;

I will step through the image to xml code. Here is the code modified to work as a static method:

public static XElement ImageToXElement(System.Drawing.Image image)
{
    Bitmap bitmap = new Bitmap(image);
    TypeConverter converter = TypeDescriptor.GetConverter(typeof(Bitmap));
    return new XElement("Image",
        new XAttribute("PixelData",
            Convert.ToBase64String(
                (byte[])converter.ConvertTo(bmp, typeof(byte[]))));
}

The first lines are pretty self explanatory. The last line, the return line all has to do with LINQ-to-XML. It first creates an xml node with an attribute to store the data. I set it up this way in case you wanted to include some other data. I put the animation metadata in there when tweaking my test case. The final portion converts the pixel data from the Image parameter into a string, encoded using base 64 encoding.

Now to convert the Image data from xml to Bitmap format:

public static Bitmap XmlToBitmap(XElement xmlData)
{
    string imageAsBase64String = xmlData.Attribute("PixelData").Value;
    byte[] imageAsBytes = Convert.FromBase64String(val);
    Bitmap result;
    using (MemoryStream imageAsMemoryStream = new MemoryStream(bytes))
    {
        result = new Bitmap(imageAsMemoryStream);
    }

    return result;
}

I hope that this version explains itself better than the copied code I posted originally, if it does, great! Here's a little explanation of what's going on, which will also serve as a crash course in understanding LINQ-to-XML. First off, this uses an XElement not, I repeat NOT an XmlElement. I know the blogger posted that it's an XmlElement, but as you have already found out, there is a substantial difference between the 2. The first line actually has been totally rewritten to utilize the Image to xml code, so that should help a little bit. What it's doing is getting the image data that is stored as a string. If you noticed, I placed it within an Attribute. This is considered by many to be the proper way of doing it, and I agree with them. There are subtle bugs that can be created by placing data outside of an Attribute. In this example, I was able to retrieve my animation metadata from the same XElement. The next line converts the string into a byte array, which as you can imagine, is more like how an actual image is constructed on a computer. The last lines are wrapping the image data, now in a byte array format, in a MemoryStream and creating a new Bitmap instance to be returned.

If you want the code to play with, just grab the code portions written in the update and place them in a public class file.

Upvotes: 1

Related Questions