【问题标题】:Edit png metadata, add itxt value编辑 png 元数据,添加 itxt 值
【发布时间】:2017-07-08 14:22:31
【问题描述】:

我正在尝试创建一个函数,它将从 URL 加载 PNG 到内存中,然后添加一个带有关键字“openbadges”和一些 json 数据的 iTXt 块。除了执行时,我的每个部分都在工作

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

我得到一个例外:

Value does not fall within the expected range.

函数如下:

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

任何想法我做错了什么?

【问题讨论】:

  • 如果您使用的是 WPF,您可以使用BitmapMetadata.SetQuery() 函数来执行此操作。如果它的 WinForms,您应该能够引用 PresentationCore 和 WindowsBase 并仍然使用它。如果它的 .NET
  • 我在 .net 4.5 中使用 WindowsBase 和 PresentationCore。

标签: c# png


【解决方案1】:

诀窍是 setquery() "/iTXt/Keyword" 和 "/iTXt/TextEntry",但你必须使用 char[] 为关键字的值。

这是因为“/iTXt/Keyword”内部需要一个VT_LPSTR作为值类型,只有char[]才能正确转换成那个。

这是一个使用 WIC 编写棋盘 png 的示例,包括一些 iTXt 元数据。

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

【讨论】:

    【解决方案2】:

    我放弃了使用 PngBitmapEncoder。相反,我将直接修改 png 字节。我附上了我为此目的制作的课程,以防其他人发现它有用。

    本课程深受 AShelly 在 Using Chunks in a PNG 的回复的启发

    我还使用http://damieng.com/blog/2006/08/08/calculating_crc32_in_c_and_net 作为 crc 哈希。

    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using System.Net;
    using System.Text;
    
    using Badger.Libraries.Hashing;
    
    namespace Badger.Libraries.Images
    {
        public class Png
        {
            private readonly byte[] _header;
            private readonly IList<Chunk> _chunks;
    
            public Png(Uri imageUri)
            {
                _header = new byte[8];
                _chunks = new List<Chunk>();
    
                var webResponse = WebRequest.Create(imageUri).GetResponse();
    
                using (var webResponseStream = webResponse.GetResponseStream())
                using (var memoryStream = new MemoryStream())
                {
                    if (webResponseStream == null)
                        throw new ArgumentException("invalid uri");
    
                    webResponseStream.CopyTo(memoryStream);
    
                    memoryStream.Seek(0, SeekOrigin.Begin);
    
                    memoryStream.Read(_header, 0, _header.Length);
    
                    while (memoryStream.Position < memoryStream.Length)
                        _chunks.Add(ChunkFromStream(memoryStream));
    
                    memoryStream.Close();
                }
            }
    
            public void AddInternationalText(string keyword, string text)
            {
                // 1-79     (keyword)
                // 1        (null character)
                // 1        (compression flag)
                // 1        (compression method)
                // 0+       (language)
                // 1        (null character)
                // 0+       (translated keyword)
                // 1        (null character)
                // 0+       (text)
    
                var typeBytes = Encoding.UTF8.GetBytes("iTXt");
                var keywordBytes = Encoding.UTF8.GetBytes(keyword);
                var textBytes = Encoding.UTF8.GetBytes(text);
                var nullByte = BitConverter.GetBytes('\0')[0];
                var zeroByte = BitConverter.GetBytes(0)[0];
    
                var data = new List<byte>();
    
                data.AddRange(keywordBytes);
                data.Add(nullByte);
                data.Add(zeroByte);
                data.Add(zeroByte);
                data.Add(nullByte);
                data.Add(nullByte);
                data.AddRange(textBytes);
    
                var chunk = new Chunk(typeBytes, data.ToArray());
    
                _chunks.Insert(1, chunk);
            }
    
            public byte[] ToBytes()
            {
                using (var stream = new MemoryStream())
                {
                    stream.Write(_header, 0, _header.Length);
    
                    foreach (var chunk in _chunks)
                        chunk.WriteToStream(stream);
    
                    var bytes = stream.ToArray();
    
                    stream.Close();
    
                    return bytes;
                }
            }
    
            private static Chunk ChunkFromStream(Stream stream)
            {
                var length = ReadBytes(stream, 4);
                var type = ReadBytes(stream, 4);
                var data = ReadBytes(stream, Convert.ToInt32(BitConverter.ToUInt32(length.Reverse().ToArray(), 0)));
    
                stream.Seek(4, SeekOrigin.Current);
    
                return new Chunk(type, data);
            }
    
            private static byte[] ReadBytes(Stream stream, int n)
            {
                var buffer = new byte[n];
                stream.Read(buffer, 0, n);
                return buffer;
            }
    
            private static void WriteBytes(Stream stream, byte[] bytes)
            {
                stream.Write(bytes, 0, bytes.Length);
            }
    
            private class Chunk
            {
                public Chunk(byte[] type, byte[] data)
                {
                    _type = type;
                    _data = data;
                }
    
                public void WriteToStream(Stream stream)
                {
                    WriteBytes(stream, BitConverter.GetBytes(Convert.ToUInt32(_data.Length)).Reverse().ToArray());
                    WriteBytes(stream, _type);
                    WriteBytes(stream, _data);
                    WriteBytes(stream, CalculateCrc(_type, _data));
                }
    
                private static byte[] CalculateCrc(IEnumerable<byte> type, IEnumerable<byte> data)
                {
                    var bytes = new List<byte>();
    
                    bytes.AddRange(type);
                    bytes.AddRange(data);
    
                    var hasher = new Crc32();
    
                    using (var stream = new MemoryStream(bytes.ToArray()))
                        return hasher.ComputeHash(stream);
                }
    
                private readonly byte[] _type;
                private readonly byte[] _data;
            }
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-03-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-01-31
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多