Чтение и запись метаданных (Exif) под WPF

Exif – один из форматов данных, в котором содержится дополнительная информация о изображении (JPEG, TIFF). Это может быть краткая информация о названии и авторе, или о том когда был сделан снимок. Это может быть также подробная информация о параметрах устройства, при помощи которого изображение было получено.

С чтением и записью Exif-а под WPF оказалось не совсем все очевидно. Вся проблема в многообразии форматов, а также в попытке микрософта все унифицировать и предложить свой формат.

Получить основную информацию достаточно легко:

Stream imageStreamSource = new FileStream(fn, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite);
BitmapDecoder decoder = BitmapDecoder.Create(imageStreamSource, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default);
InPlaceBitmapMetadataWriter pngInplace = decoder.Frames[0].CreateInPlaceBitmapMetadataWriter();

if (pngInplace != null)
{
if (pngInplace.CameraModel != null)
  tbExifInfo.Text = pngInplace.CameraModel.ToString();
else
  tbExifInfo.Text = "не указана";
}
else
  tbExifInfo.Text = "не возможно получить метаданные";

Вот с записью Exif возникают некоторые проблемы. В документации слабо освещен тот момент, что нужно обязательно указать отступ – место, которое выделяется для хранения данных:

metadata = new BitmapMetadata("jpg");

uint paddingAmount = 2048;
metadata.SetQuery("/app1/ifd/PaddingSchema:Padding", paddingAmount);
metadata.SetQuery("/app1/ifd/exif/PaddingSchema:Padding", paddingAmount);
metadata.SetQuery("/xmp/PaddingSchema:Padding", paddingAmount);

metadata.Title = "The title";

JpegBitmapEncoder output = new JpegBitmapEncoder();
output.Frames.Add(BitmapFrame.Create(MainImage.Source as BitmapSource, null, metadata, null);

using (Stream outputFile = File.Open(fn, FileMode.Create, FileAccess.ReadWrite))
{
output.Save(outputFile);
}

Самый тонкий момент, это когда надо изменить что-то в уже существующей Metadata. Необходимо клонировать метаданные, так как по умолчанию они заморожены. Необходимо обновить Padding. Также необходимо держать поток файла, из которого берутся данные, открытым до конца записи!

BitmapCreateOptions createOptions = BitmapCreateOptions.PreservePixelFormat | BitmapCreateOptions.IgnoreColorProfile;
Stream oldStream = new System.IO.FileStream(MetaInputFileName, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite);
BitmapDecoder oldDecoder = BitmapDecoder.Create(oldStream, createOptions, BitmapCacheOption.None);

uint paddingAmount = 2048;
BitmapMetadata metadata = oldDecoder.Frames[0].Metadata.Clone() as BitmapMetadata;
metadata.SetQuery("/app1/ifd/PaddingSchema:Padding", paddingAmount);
metadata.SetQuery("/app1/ifd/exif/PaddingSchema:Padding", paddingAmount);
metadata.SetQuery("/xmp/PaddingSchema:Padding", paddingAmount);

metadata.CameraModel = "UberModel";

JpegBitmapEncoder output = new JpegBitmapEncoder();
output.Frames.Add(BitmapFrame.Create(MainImage.Source as BitmapSource, null, metadata, null);

using (Stream outputFile = File.Open(newfn, FileMode.Create, FileAccess.ReadWrite))
{
output.Save(outputFile);
}

oldStream.Close();

Похоже метаданные привязаны к формату файла. Если метаданные из Jpeg положить в Tiff, то они не будут видны если открыть свойства картинки в Explorer-е.