Modify the png metadata, add the value of this file

advertisements

I am trying to create a function that will load a PNG from a URL into memory and then add an iTXt chunk with the keyword "openbadges" and some json data. I have every part working except when I execute

metadata.SetQuery("/iTXt/openbadges", "");

I get an exception:

Value does not fall within the expected range.

Here is the function:

private static byte[] CreateOpenBadge(BadgeAssertionEntity assertion)
{
    using (var image = LoadImage(new Uri(assertion.Badge.ImageUrl)))
    using (var imageStream = new MemoryStream())
    {
        image.Save(imageStream, ImageFormat.Png);

        var pngDecoder = new PngBitmapDecoder(imageStream, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default);

        using (var badgeStream = new MemoryStream())
        {
            var pngEncoder = new PngBitmapEncoder();

            pngEncoder.Frames.Add(BitmapFrame.Create(pngDecoder.Frames[0]));

            var metadata = pngEncoder.Frames[0].Metadata as BitmapMetadata;

            if (metadata == null)
                throw new ApplicationException();

            metadata.SetQuery("/iTXt/openbadges", "");

            pngEncoder.Save(badgeStream);

            return badgeStream.ToArray();
        }
    }
}

Any ideas what I am doing wrong?


The trick is to setquery() "/iTXt/Keyword" and "/iTXt/TextEntry", but you must use a char[] for the value of Keyword.

This is because internally "/iTXt/Keyword" wants a VT_LPSTR as the value type, and only a char[] will correctly convert into that.

Here's a sample that uses WIC to write a checkerboard png, including some iTXt metadata.

using System.IO;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;

namespace PNGEncoder
{
    class Program
    {
        static void Main(string[] args)
        {
            var width = 256;
            var height = 256;

            var pngMetadata = new BitmapMetadata("png");

            pngMetadata.SetQuery("/iTXt/Keyword", "keyword0".ToCharArray());
            pngMetadata.SetQuery("/iTXt/TextEntry", "textentry0");

            pngMetadata.SetQuery("/[1]iTXt/Keyword", "keyword1".ToCharArray());
            pngMetadata.SetQuery("/[1]iTXt/TextEntry", "textentry1");

            var bitmap = new WriteableBitmap(width, height, 96, 96, PixelFormats.Gray8, null);

            var pixels = new byte[width * height];
            for (var y = 0; y < height; y++)
            {
                for (var x = 0; x < width; x++)
                {
                    pixels[y * width + x] = (byte)(255 * (((x >> 4) ^ (y >> 4)) & 1));
                }
            }

            bitmap.WritePixels(new Int32Rect(0, 0, width, height), pixels, width, 0);

            var encoder = new PngBitmapEncoder();
            encoder.Frames.Add(BitmapFrame.Create(bitmap, null, pngMetadata, null));

            using (var stream = File.Create("checkerBoard.png"))
            {
                encoder.Save(stream);
            }
        }
    }
}