Insert image into XML document - c#

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

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.

Related

Convert iTextSharp.text.pdf.BarcodeQRCode to System.Drawing.Image

Looking for a way to convert iTextSharp.text.pdf.BarcodeQRCode to System.Drawing.Image
This is what I have so far...
public System.Drawing.Image GetQRCode(string content)
{
iTextSharp.text.pdf.BarcodeQRCode qrcode = new iTextSharp.text.pdf.BarcodeQRCode(content, 115, 115, null);
iTextSharp.text.Image img = qrcode.GetImage();
MemoryStream ms = new MemoryStream(img.OriginalData);
return System.Drawing.Image.FromStream(ms);
}
In line 3 above using img.OriginalData returns null
Using img.RawData on line 3 instead thows invalid parameter error on line 4.
I've googled some of the code samples on how to perform the thing you want and your code (the "OriginalData" approach) is basicaly the same: https://csharp.hotexamples.com/examples/iTextSharp.text.pdf/BarcodeQRCode/-/php-barcodeqrcode-class-examples.html .
However, I don't see how it could work. From my investigations of BarcodeQRCode#getImage it seems that OriginalData is not set while processing such a barcode, so it will always be null.
More than that, the code you mention belongs to iText 5, which is end of life and no longer maintained (with an exception of considerable security fixes), so it's recommended to update to iText 7.
As for iText 7, I do see how to achieve the same in Java, since barcode classes do have a createAwtImage method there. .NET, on the other hand, lacks such a functionality, so I'd day that one unfortunately couldn't do it in .NET.
There are some good reasons for that. iText's Images (and a barcode could be easily converted to an iText's Image object as shown here: https://kb.itextpdf.com/home/it7kb/faq/how-to-generate-2d-barcode-as-vector-image) represent a PDF's XObject. In PDF syntax, an image file (jpg, png, etc.) is an XObject with the raw image data stored inside. However, an XObject can also contain PDF syntaxt content (it is not just used for image files). So to render such a content one needs to process the data from PDF syntax to image syntax, which is not that easy. There are some means in Java's awt to do so, that's why it's implemented in Java. As for .NET, since there is no out-of-the-box means to convert PDF images to System.Drawing.Image, it was decided not to implement it.
To conclude, there is another iText product, pdfRender, which allows one to convert PDF files (and you could create a page just for a barcode) to images. Perhaps you might want to play with it: https://itextpdf.com/en/products/itext-7/convert-pdf-to-image-pdfrender

Output of System.Drawing.Image Save is not the same as what fed into Image.FromStream

I am trying to validate an image submitted to backend in Base64 string format by parsing it into an Image object, extracting it from the same Image object and finally comparing input byte array and output byte array assuming these two should be the same or there was something wrong in the input image. Here is the code:
private void UpdatePhoto(string photoBase64)
{
var imageDataInBytes = Convert.FromBase64String(photoBase64);
ValidateImageContent(imageDataInBytes);
}
private void ValidateImageContent(byte[] imageDataInBytes)
{
using (var inputMem = new MemoryStream(imageDataInBytes))
{
var img = Image.FromStream(inputMem, false, true);
using (MemoryStream outputMemStream = new MemoryStream())
{
img.Save(outputMemStream, img.RawFormat);
var outputSerialized = outputMemStream.ToArray();
if (!outputSerialized.SequenceEqual(imageDataInBytes))
throw new Exception("Invalid image. Identified extra data in the input. Please upload another photo.");
}
}
}
and it fails on an image that I know is a valid one.
Is my assumption wrong that output of Image.Save must be the same as what Image.FromStream is fed with? Is there a way to correct this logic to achieve this way of validation correctly?
If you compare the original image with the created image, you will notice a few differences in the metadata: For my sample image, I could observe that some metadata was stripped (XMP data was completely removed). In addition, while the EXIF data was preserved, the endianness it is written in was reversed from little endian to big endian. This alone explains why the data won’t match.
In my example, the actual image data was identical but you won’t be able to tell easily from just looking at the bytes.
If you wanted to produce a result identical to the source, you would have to produce the metadata in the exact same way as the source did. You won’t be able to do so without actually looking closely at the metadata of the original photo though. .NET’s Image simply isn’t able to pertain all the metadata that a file could contain. And even if you were able to extract all the metadata and store it in the right format again, there are lots of fine nuances between metadata serializers that make it very difficult to produce the exact same result.
So if you wanted to compare the images, you should probably strip the metadata and just compare the image data. But then, when you think about how you save the image (Raw), then you will just get the exact same blob of data again, so I wouldn’t expect differences there.

JpegBitmapDecoder decode jpeg failed?

I'm using the following code to Decode Jpge image
UnmanagedMemoryStream^ mStream = gcnew UnmanagedMemoryStream(_jpegDataBuff, _buffLength);
JpegBitmapDecoder^ decoder = gcnew JpegBitmapDecoder(mStream, BitmapCreateOptions::PreservePixelFormat, BitmapCacheOption::OnLoad);
BitmapSource^ bitmapSource = decoder->Frames[0];
I use the bitmapSource to display on control of WPF, but sometimes the the result as below
I have searched very much on internet, but I did not solve it yet!
Someone can help me?
Many Thanks,
T&T
From the looks of it I would say that either JPEG source is incomplete or invalid. The only exceptions JpegBitmapDecoder throws are ArgumentNullException (stream is null) and FileFormatException. The file format is most probably detected by analyzing file header. The JIF/JFIF file structure holds image description information in a relatively small header followed by a "raw" data.
I guess that decoder is built resilient to ignore invalid (rendered last before gray) and/or missing (rendered gray) blocks. I think the only way to detect is to either analyze final image (maybe checking that last 8x8px block doesn't have same color?) or source (maybe counting data blocks?).

How do I store arbitrary binary data in a binary serialized class?

Using C#/.NET for my application, I've got a series of classes in my main data model that represent "binary" (as opposed to text) content. I've got a inheritance setup like this:
Basically, the abstract class BinaryContent contains a MemoryStream that stores arbitrary binary data. That data is read from a file on disk. Each type of binary data I plan to store will be a derived type, such as ImageContent and FontContent. Those derived types will interpret the binary data in BinaryContent.Content. ImageContent for example, would create a BitmapImage (stored in an ImageSource) from the MemoryStream. FontContent would of course create a font from the BinaryContent.Content. I chose this way of doing things because I wanted to be able to basically store a copy of the content file (ie an image) and not have to rely on the file being in a particular location on disk time after time.
I'm also storing instances of these classes in a "project file" using binary serialization. I've done this to basically "package" everything together. I'm having trouble when I attempt to deserialize the MemoryStream, it seems. The problem happens when I create the image from the MemoryStream. When the following method runs after deserialization, a FileFormatexception occurs.
private void RefreshImageFromContent()
{
BitmapImage image = null;
if (Content != null &&
Content.Length != 0L)
{
image = new BitmapImage();
image.BeginInit();
image.StreamSource = Content;
image.EndInit(); //throws FileFormatException
}
Image = image;
}
The FileFormatException message is:
"The image cannot be decoded. The image header might be corrupted."
Inner Exception: "Exception from HRESULT: 0x88982F61"
My best guess right now is that something is happening to corrupt the data in BinaryContent.Content when during serialization or deserialization.
This leads me to ask 2 questions.
Does anyone have any suggestions to fix this problem?
Does anyone have other suggested ways to store arbitrary binary data that is going to be (de)serialized?
Please feel free to ask for clarification on anything about my question.
Thanks.
What is the content.Position at image.StreamSource = Content;?
It is likely that the position of the stream is not set to the start (or the correct position in thw stream).

Change Image Encoding Dynamically?

I want to know whether it is possible to programmatically change an image encoding on the fly without saving the image to a file?
Use Case: When the user copies a binary image from a source, would it be possible to change the image encoding from binary to base64?
You could change the encoding of an image without saving it to a file, but not without saving it to a variable in your code. The Clipboard class basically just has some Get and Set methods. The only way to change what's in the clipboard is to call one of the Get methods into a local variable, change whatever it is you just got, and then call one of the Set methods, passing in your changed object. This results in a changed clipboard object, but not without the intermediate step of "saving" it to a variable.
Clipboard does not expose any methods for directly manipulating the memory of the object in the clipboard. Even if such a method were exposed, changing the encoding of an image from binary to Base64 involves fundamentally changing all of the memory, so there wouldn't be much value to it.
Update: here's a method that will take an image from the clipboard, convert it to a base64 string, and put it back into the clipboard:
if (Clipboard.ContainsImage())
{
using (MemoryStream memory = new MemoryStream())
{
using (Image img = Clipboard.GetImage())
{
img.Save(memory, img.RawFormat);
}
string base64 = Convert.ToBase64String(memory.ToArray());
Clipboard.SetText(base64);
}
}
And you'll need these two using statements:
using System.IO;
using System.Windows.Forms;
It's untested (because it's past my bedtime), but it should work. It does involve the use of local variables, but this is unavoidable (as well as normal).
Using the new ClipBoard class in WPF
The below example reads a stream from a file but you could use any stream
var image = new BitmapImage();
image.BeginInit();
image.StreamSource = File.Open("image.png", FileMode.Open);
image.EndInit();
System.Windows.Clipboard.SetImage(image);
http://msdn.microsoft.com/en-us/library/system.windows.clipboard.setimage.aspx
I think that you might be asking how to intercept the copy operation and replace the logical contents of the clipboard with new contents, all from some background app, rather than how to replace the memory bytes allocated to the original copy operation.
If that's the intention, you should look for Win32 API calls to hook into the clipboard so that the background app can process the data copied before it is available for paste.
This article might get you going:
http://www.radsoftware.com.au/articles/clipboardmonitor.aspx

Categories