0

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

Community
  • 1
  • 1
user1711233
  • 251
  • 1
  • 8
  • 16
  • Can you write code that simply inserts a test string into a specific element in the XML file? – Kenny Evitt May 15 '14 at 16:43
  • 3
    "all I get is the raw byte string output in the file" - what exactly do you mean by that, and what did you expect to see? It looks to me like you'll get the Base64-encoded version of the binary data - is that not what you want? – Jon Skeet May 15 '14 at 16:47
  • To add to Jon's comment, where are you looking for this data? Is it the file itself or in a program that should translate the bytes into a bitmap on the screen? Also, in your second code block, what is `element`? – bubbinator May 15 '14 at 17:47
  • Sorry if my question was not clear. I have an image (a barcode) stored as a file, with a path + filename saved as a string. It's for a mail merge type solution; each row in the db will generate a unique letter with a unique barcode. I've got the rest of the solution figured out, but I can't get the image to display as an image. Right now, after following the advice from the link I gave, all I get is a jumble of letters which I presume is the "raw" Base 64 string representation of the bytes in the image. I want the actual image to display. @bubbinator, I don't know which element to use. – user1711233 May 16 '14 at 13:40
  • @user1711233 I was wondering the type `element` is. It's used in your provided code. I can't infer what type it is due to `XElement.Add` accepting `object`s... It may not be relevant, but ya' never know. From what it sounds like, you probably should check into where the image gets retrieved (posting that wouldn't hurt) and confirm if the jumble of letters is indeed the barcode's string (try outputting to the debugger for that). – bubbinator May 16 '14 at 14:54
  • @bubbinator the section of code below is another example that I found; its not in my solution. But I don't know how to embed relate the node to an XElement; it won't even allow me to add it to the main document. I'm just showing it because there are two ways here to solve the problem, and either is fine by me. Thanks! – user1711233 May 16 '14 at 17:43
  • After you find your node, you need to convert it back to an image. You can't expect it to automatically happen for you. – Chuck Savage May 16 '14 at 17:53
  • @ChuckSavage don't I need to add the content to the xml node first? I tried creating a new element with the image in, then setting the .InnerXml attribute of my node to the same as the new element, but that gave the same output. And yes, the output in the final xml file matches the Base 64 string. Thanks – user1711233 May 19 '14 at 10:30
  • I don't think we know what you want. The answer below does convert an image to xml and xml back to image. Why don't you put in your post the result you are getting and the result you are expecting. – Chuck Savage May 19 '14 at 15:29
  • That's fair; I'm not sure I asked the right thing. I'm trying to create a mail merge system which can insert a unique barcode into each xml document. So the final result (I'm generating something that opens in Microsoft Word, so it's XML that is understood by Word) needs an image to display when opened. I've tried adding the code below to do the conversion, but the final result is not a valid document. – user1711233 May 19 '14 at 16:54
  • XElement xe = ImageToXElement(picture); XmlElement element = doc.ReadNode(xe.CreateReader()) as XmlElement; XmlNode barcodeNode = doc.DocumentElement.AppendChild(element); n.AppendChild(barcodeNode); <-- picture is of type Image – user1711233 May 19 '14 at 16:54

1 Answers1

1

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.

bubbinator
  • 487
  • 2
  • 7
  • Thanks for helping, I do appreciate it. I just don't understand this at all. What is the purpose of the first line? To turn an xml representation of an image into an actual image? I'm not entirely sure if I have the image correctly represented. An XmlNode doesn't have an attribute called '.Element', so I'm not sure where that code fits or how to add the bitmap image to the xml. I thought the whole process was to convert an image into a representation that can be stored in xml, not to get an actual image? I'm now very confused. – user1711233 May 19 '14 at 08:53
  • You are welcome. Also, good questions. I will address them in an update to this post and explain what is going on as well as what using directives you will need. I will say this, the writer of that blog post did mix up the regular xml library with `LINQ-to-XML` – bubbinator May 19 '14 at 14:02
  • Thanks for this. To finish things off, do you know how I can add the image to the xml file? I've tried a number of techniques without success. When I try to add the XElement to the file, it just shows as – user1711233 May 20 '14 at 08:37
  • @user1711233 I just reread your all comments. Since you are dealing with Word, there is probably a very specific way to place the data within the file and even a certain encoding that must be used. Remember, a string is not just a string! There is [an article](http://www.joelonsoftware.com/articles/Unicode.html) I read the other day all about this. So a few places to start would be to find out if Word can pick out an image that is in as xml file. The next would be to find out the required encoding. Finally, check if there is some command , like a new paragraph, that is needed. – bubbinator May 20 '14 at 22:23
  • Thanks for this. Looks like I need to add the correct node at the correct hierarchical level for the binary data, create a 'drawing' node too, and also declare it as a Relationship. So, 3 things are required. I'll post when I work out a solution. Thanks for the guidance. – user1711233 May 21 '14 at 09:12