potisanのプログラミングメモ

趣味のプログラマーがプログラミング関係で気になったことや調べたことをいつでも忘れられるようにメモするブログです。はてなブログ無料版なので記事の上の方はたぶん広告です。記事中にも広告挿入されるみたいです。

C# JPEGファイルEXIFデータのWindows用評価とキーワードを取得・設定・削除する

JPEGファイルEXIFデータのWindows用評価とキーワードを取得・設定・削除するサンプルコードです。

#nullable enable

using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Runtime.Serialization;
using System.Text;

const string path = <対象とするJPEGのパス ※EXIF改変注意!>;

using (var stream = new MemoryStream(File.ReadAllBytes(path)))
{
    using var img = Image.FromStream(stream);

    var rating = ExifPropertyTagUtility.GetRating(img);
    var keywords = ExifPropertyTagUtility.GetXPKeywords(img);

    ExifPropertyTagUtility.SetRating(img, 5);
    ExifPropertyTagUtility.SetXPKeywords(img, new[] { "Hello", "EXIF" });

    img.Save(path);
}

Console.ReadKey();

static class ExifPropertyTagUtility
{
    // https://exiv2.org/tags.html
    public enum PropertyTags : int
    {
        Rating = 0x4746,
        XPKeywords = 0x9c9e,
    }

    public enum PropertyTypes : int
    {
        Byte = 1,
        ASCII = 2,
        Short = 3,
        Long = 4,
        Rational = 5,
        SByte = 6,
        Undefined = 7,
        SShort = 8,
        SLONG = 9,
        SRational = 10,
        Float = 11,
        Double = 12,
    }

    public static PropertyItem CreatePropertyItem()
    {
        return (PropertyItem)FormatterServices.GetSafeUninitializedObject(typeof(PropertyItem));
    }

    public static short? GetValueShort(in Image jpeg, PropertyTags tag)
    {
        var item = jpeg.GetPropertyItem((int)tag);
        if (item is null || item.Type != (int)PropertyTypes.Short || item.Len != 2)
            return null;
        return BitConverter.ToInt16(item.Value);
    }

    public static void SetValueShort(in Image jpeg, PropertyTags tag, short value)
    {
        var item = CreatePropertyItem();
        item.Id = (int)tag;
        item.Type = (int)PropertyTypes.Short;
        item.Len = 2;
        item.Value = BitConverter.GetBytes(value);
        jpeg.SetPropertyItem(item);
    }

    public static string? GetValueUni(in Image jpeg, PropertyTags tag)
    {
        var item = jpeg.GetPropertyItem((int)tag);
        if (item is null || item.Type != (int)PropertyTypes.Byte)
            return null;
        return Encoding.Unicode.GetString(item.Value!, 0, item.Len - 2);
    }

    public static void SetValueUni(in Image jpeg, PropertyTags tag, string value)
    {
        var bytes = Encoding.Unicode.GetBytes(value + '\0');
        var item = CreatePropertyItem();
        item.Id = (int)tag;
        item.Type = (int)PropertyTypes.Byte;
        item.Len = bytes.Length;
        item.Value = bytes;
        jpeg.SetPropertyItem(item);
    }

    public static short? GetRating(in Image jpeg)
    {
        return GetValueShort(jpeg, PropertyTags.Rating);
    }

    public static void SetRating(in Image jpeg, short value)
    {
        SetValueShort(jpeg, PropertyTags.Rating, value);
    }

    public static void RemoveRating(in Image jpeg)
    {
        jpeg.RemovePropertyItem((int)PropertyTags.Rating);
    }

    public static string[]? GetXPKeywords(in Image jpeg)
    {
        return GetValueUni(jpeg, PropertyTags.XPKeywords)?.Split(';') ?? null;
    }

    public static void SetXPKeywords(in Image jpeg, string[] value)
    {
        SetValueUni(jpeg, PropertyTags.XPKeywords, String.Join<string>(';', value));
    }

    public static void RemoveXPKeywords(in Image jpeg)
    {
        jpeg.RemovePropertyItem((int)PropertyTags.XPKeywords);
    }
}