123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390 |
- using PaintDotNet.SystemLayer;
- using System;
- using System.Collections;
- using System.Collections.Specialized;
- using System.Drawing.Imaging;
- namespace PaintDotNet
- {
- /// <summary>
- /// This class exposes two types of metadata: system, and user.
- /// It is provided mostly for batching operations: loading all the data, modifying the copy,
- /// and then saving back all the data.
- /// </summary>
- public class Metadata
- {
- /// <summary>
- /// This is the name of the section where EXIF tags are stored.
- /// </summary>
- /// <remarks>
- /// All entries in here are expected to be PropertyItem objects which were serialized
- /// using PdnGraphics.SerializePropertyItem. The name of each entry in this section is
- /// irrelevant, as some EXIF tags are allowed to occur more than once. Thus, if you
- /// want to search for EXIF tags of a certain ID you will have to deserialize each
- /// one and compare the Id property.
- /// It is the responsibility of the FileType implementation to load and save these.
- /// </remarks>
- public const string ExifSectionName = "$exif";
- /// <summary>
- /// This is the name of the section where user-defined metadata may go.
- /// </summary>
- public const string UserSectionName = "$user";
- /// <summary>
- /// This is the name of the section where the main document metadata goes that
- /// can be user-provided but is not necessarily user-defined.
- /// </summary>
- public const string MainSectionName = "$main";
- private NameValueCollection userMetaData;
- private const string sectionSeparator = ".";
- private int suppressChangeEvents = 0;
- public event EventHandler Changing;
- protected virtual void OnChanging()
- {
- if (suppressChangeEvents <= 0 && Changing != null)
- {
- Changing(this, EventArgs.Empty);
- }
- }
- public event EventHandler Changed;
- protected virtual void OnChanged()
- {
- if (suppressChangeEvents <= 0 && Changed != null)
- {
- Changed(this, EventArgs.Empty);
- }
- }
- private class ExifInfo
- {
- public string[] names;
- public PropertyItem[] items;
- public ExifInfo(string[] names, PropertyItem[] items)
- {
- this.names = names;
- this.items = items;
- }
- }
- private Hashtable exifIdToExifInfo = new Hashtable(); // maps short -> ExifInfo
- public string[] GetKeys(string section)
- {
- string sectionName = section + sectionSeparator;
- ArrayList keys = new ArrayList();
- foreach (string key in userMetaData.Keys)
- {
- if (key.StartsWith(sectionName))
- {
- keys.Add(key.Substring(sectionName.Length));
- }
- }
- return (string[])keys.ToArray(typeof(string));
- }
- public string[] GetSections()
- {
- Set sections = new Set();
- foreach (string key in userMetaData.Keys)
- {
- int dotIndex = key.IndexOf(sectionSeparator);
- if (dotIndex != -1)
- {
- string sectionName = key.Substring(0, dotIndex);
- if (!sections.Contains(sectionName))
- {
- sections.Add(sectionName);
- }
- }
- }
- return (string[])sections.ToArray(typeof(string));
- }
- /// <summary>
- /// Gets a value from the metadata collection.
- /// </summary>
- /// <param name="section">The logical section to retrieve from.</param>
- /// <param name="name">The name of the value to retrieve.</param>
- /// <returns>A string containing the value, or null if the value wasn't present.</returns>
- public string GetValue(string section, string name)
- {
- return userMetaData.Get(section + sectionSeparator + name);
- }
- public PropertyItem[] GetExifValues(ExifTagID id)
- {
- return GetExifValues((short)id);
- }
- public PropertyItem[] GetExifValues(short id)
- {
- ExifInfo info = (ExifInfo)this.exifIdToExifInfo[id];
- if (info == null)
- {
- return new PropertyItem[0];
- }
- else
- {
- return (PropertyItem[])info.items.Clone();
- }
- }
- public string GetUserValue(string name)
- {
- return GetValue(UserSectionName, name);
- }
- /// <summary>
- /// Removes a value from the metadata collection.
- /// </summary>
- /// <param name="section">The logical section to remove from.</param>
- /// <param name="name">The name of the value to retrieve.</param>
- public void RemoveValue(string section, string name)
- {
- OnChanging();
- userMetaData.Remove(section + sectionSeparator + name);
- OnChanged();
- }
- public void ReplaceExifValues(ExifTagID id, PropertyItem[] items)
- {
- ReplaceExifValues((short)id, items);
- }
- public void ReplaceExifValues(short id, PropertyItem[] items)
- {
- OnChanging();
- ++suppressChangeEvents;
- RemoveExifValues(id);
- AddExifValues(items);
- --suppressChangeEvents;
- OnChanged();
- }
- public void RemoveExifValues(ExifTagID id)
- {
- RemoveExifValues((short)id);
- }
- public void RemoveExifValues(short id)
- {
- object idObj = (object)id;
- ExifInfo info = (ExifInfo)this.exifIdToExifInfo[idObj];
- OnChanging();
- ++suppressChangeEvents;
- if (info != null)
- {
- foreach (string name in info.names)
- {
- RemoveValue(ExifSectionName, name);
- }
- this.exifIdToExifInfo.Remove(idObj);
- }
- --suppressChangeEvents;
- OnChanged();
- }
- public void RemoveUserValue(string name)
- {
- RemoveValue(UserSectionName, name);
- }
- private void SetValueConcrete(string section, string name, string value)
- {
- OnChanging();
- userMetaData.Set(section + sectionSeparator + name, value);
- OnChanged();
- }
- /// <summary>
- /// Sets a value in the metadata collection.
- /// </summary>
- /// <param name="section">The logical section to add or update date in.</param>
- /// <param name="name">The name of the value to set.</param>
- /// <param name="value">The value to set.</param>
- public void SetValue(string section, string name, string value)
- {
- if (section == ExifSectionName)
- {
- throw new ArgumentException("you must use AddExifValues() to add items to the " + ExifSectionName + " section");
- }
- SetValueConcrete(section, name, value);
- }
- public void SetUserValue(string name, string value)
- {
- SetValue(Metadata.UserSectionName, name, value);
- }
- public void AddExifValues(PropertyItem[] items)
- {
- if (items.Length == 0)
- {
- return;
- }
- short id = unchecked((short)items[0].Id);
- for (int i = 1; i < items.Length; ++i)
- {
- if (unchecked((short)items[i].Id) != id)
- {
- throw new ArgumentException("all PropertyItem instances in items must have the same id");
- }
- }
- string[] names = new string[items.Length];
- OnChanging();
- ++suppressChangeEvents;
- for (int i = 0; i < items.Length; ++i)
- {
- names[i] = GetUniqueExifName();
- string blob = PdnGraphics.SerializePropertyItem(items[i]);
- SetValueConcrete(ExifSectionName, names[i], blob);
- }
- object idObj = (object)id; // avoid boxing twice
- ExifInfo info = (ExifInfo)this.exifIdToExifInfo[idObj];
- if (info == null)
- {
- PropertyItem[] newItems = new PropertyItem[items.Length];
- for (int i = 0; i < newItems.Length; ++i)
- {
- newItems[i] = PdnGraphics.ClonePropertyItem(items[i]);
- }
- info = new ExifInfo(names, newItems);
- }
- else
- {
- string[] names2 = new string[info.names.Length + names.Length];
- PropertyItem[] items2 = new PropertyItem[info.items.Length + items.Length];
- info.names.CopyTo(names2, 0);
- names.CopyTo(names2, info.names.Length);
- info.items.CopyTo(items2, 0);
- for (int i = 0; i < items.Length; ++i)
- {
- items2[i + info.items.Length] = PdnGraphics.ClonePropertyItem(items[i]);
- }
- info = new ExifInfo(names2, items2);
- }
- this.exifIdToExifInfo[idObj] = info;
- --suppressChangeEvents;
- OnChanged();
- }
- private int nextUniqueId = 0;
- private string GetUniqueExifName()
- {
- int num = nextUniqueId;
- const string namePrefix = "tag";
- while (true)
- {
- string name = namePrefix + num.ToString();
- if (GetValue(ExifSectionName, name) == null)
- {
- nextUniqueId = num + 1;
- return name;
- }
- else
- {
- ++num;
- }
- }
- }
- public void ReplaceWithDataFrom(Metadata source)
- {
- OnChanging();
- ++suppressChangeEvents;
- if (source != this && source.userMetaData != this.userMetaData)
- {
- Clear();
- foreach (string key in source.userMetaData.Keys)
- {
- string value = source.userMetaData.Get(key);
- this.userMetaData.Set(key, value);
- }
- ReconstructExifInfoCache();
- }
- --suppressChangeEvents;
- OnChanged();
- }
- public void Clear()
- {
- OnChanging();
- ++suppressChangeEvents;
- this.userMetaData.Clear();
- this.exifIdToExifInfo.Clear();
- --suppressChangeEvents;
- OnChanged();
- }
- private void ReconstructExifInfoCache()
- {
- OnChanging();
- ++suppressChangeEvents;
- exifIdToExifInfo.Clear();
- string[] exifKeys = GetKeys(ExifSectionName);
- string[] piBlobs = new string[exifKeys.Length];
- for (int i = 0; i < exifKeys.Length; ++i)
- {
- piBlobs[i] = GetValue(ExifSectionName, exifKeys[i]);
- this.RemoveValue(ExifSectionName, exifKeys[i]);
- }
- foreach (string piBlob in piBlobs)
- {
- PropertyItem pi = PdnGraphics.DeserializePropertyItem(piBlob);
- AddExifValues(new PropertyItem[] { pi });
- }
- --suppressChangeEvents;
- OnChanged();
- }
- internal Metadata(NameValueCollection userMetaData)
- {
- this.userMetaData = userMetaData;
- ReconstructExifInfoCache();
- }
- }
- }
|