Revert "Temp8 tinymce"
This commit is contained in:
@@ -1,88 +1,88 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
using System.Xml;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Web;
|
||||
using Newtonsoft.Json;
|
||||
using Umbraco.Core.Media;
|
||||
|
||||
namespace Umbraco.Web.Media.EmbedProviders
|
||||
{
|
||||
//TODO: Make all Http calls async
|
||||
|
||||
public abstract class AbstractOEmbedProvider : IEmbedProvider
|
||||
{
|
||||
private static HttpClient _httpClient;
|
||||
|
||||
public virtual bool SupportsDimensions
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
[ProviderSetting]
|
||||
public string APIEndpoint { get; set; }
|
||||
|
||||
[ProviderSetting]
|
||||
public Dictionary<string, string> RequestParams { get; set; }
|
||||
|
||||
public abstract string GetMarkup(string url, int maxWidth, int maxHeight);
|
||||
|
||||
public virtual string BuildFullUrl(string url, int maxWidth, int maxHeight)
|
||||
{
|
||||
if (Uri.IsWellFormedUriString(url, UriKind.RelativeOrAbsolute) == false)
|
||||
throw new ArgumentException("Not a valid Url");
|
||||
|
||||
var fullUrl = new StringBuilder();
|
||||
|
||||
fullUrl.Append(APIEndpoint);
|
||||
fullUrl.Append("?url=" + HttpUtility.UrlEncode(url));
|
||||
|
||||
foreach (var p in RequestParams)
|
||||
fullUrl.Append(string.Format("&{0}={1}", p.Key, p.Value));
|
||||
|
||||
if (maxWidth > 0)
|
||||
fullUrl.Append("&maxwidth=" + maxWidth);
|
||||
|
||||
if (maxHeight > 0)
|
||||
fullUrl.Append("&maxheight=" + maxHeight);
|
||||
|
||||
return fullUrl.ToString();
|
||||
}
|
||||
|
||||
public virtual string DownloadResponse(string url)
|
||||
{
|
||||
if (_httpClient == null)
|
||||
_httpClient = new HttpClient();
|
||||
|
||||
using (var request = new HttpRequestMessage(HttpMethod.Get, url))
|
||||
{
|
||||
var response = _httpClient.SendAsync(request).Result;
|
||||
return response.Content.ReadAsStringAsync().Result;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual T GetJsonResponse<T>(string url) where T : class
|
||||
{
|
||||
var response = DownloadResponse(url);
|
||||
return JsonConvert.DeserializeObject<T>(response);
|
||||
}
|
||||
|
||||
public virtual XmlDocument GetXmlResponse(string url)
|
||||
{
|
||||
var response = DownloadResponse(url);
|
||||
var doc = new XmlDocument();
|
||||
doc.LoadXml(response);
|
||||
|
||||
return doc;
|
||||
}
|
||||
|
||||
public virtual string GetXmlProperty(XmlDocument doc, string property)
|
||||
{
|
||||
var selectSingleNode = doc.SelectSingleNode(property);
|
||||
return selectSingleNode != null ? selectSingleNode.InnerText : string.Empty;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.Text;
|
||||
using System.Xml;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Web;
|
||||
using Newtonsoft.Json;
|
||||
using Umbraco.Core.Media;
|
||||
|
||||
namespace Umbraco.Web.Media.EmbedProviders
|
||||
{
|
||||
//TODO: Make all Http calls async
|
||||
|
||||
public abstract class AbstractOEmbedProvider : IEmbedProvider
|
||||
{
|
||||
private static HttpClient _httpClient;
|
||||
|
||||
public virtual bool SupportsDimensions
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
[ProviderSetting]
|
||||
public string APIEndpoint { get; set; }
|
||||
|
||||
[ProviderSetting]
|
||||
public Dictionary<string, string> RequestParams { get; set; }
|
||||
|
||||
public abstract string GetMarkup(string url, int maxWidth, int maxHeight);
|
||||
|
||||
public virtual string BuildFullUrl(string url, int maxWidth, int maxHeight)
|
||||
{
|
||||
if (Uri.IsWellFormedUriString(url, UriKind.RelativeOrAbsolute) == false)
|
||||
throw new ArgumentException("Not a valid Url");
|
||||
|
||||
var fullUrl = new StringBuilder();
|
||||
|
||||
fullUrl.Append(APIEndpoint);
|
||||
fullUrl.Append("?url=" + HttpUtility.UrlEncode(url));
|
||||
|
||||
foreach (var p in RequestParams)
|
||||
fullUrl.Append(string.Format("&{0}={1}", p.Key, p.Value));
|
||||
|
||||
if (maxWidth > 0)
|
||||
fullUrl.Append("&maxwidth=" + maxWidth);
|
||||
|
||||
if (maxHeight > 0)
|
||||
fullUrl.Append("&maxheight=" + maxHeight);
|
||||
|
||||
return fullUrl.ToString();
|
||||
}
|
||||
|
||||
public virtual string DownloadResponse(string url)
|
||||
{
|
||||
if (_httpClient == null)
|
||||
_httpClient = new HttpClient();
|
||||
|
||||
using (var request = new HttpRequestMessage(HttpMethod.Get, url))
|
||||
{
|
||||
var response = _httpClient.SendAsync(request).Result;
|
||||
return response.Content.ReadAsStringAsync().Result;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual T GetJsonResponse<T>(string url) where T : class
|
||||
{
|
||||
var response = DownloadResponse(url);
|
||||
return JsonConvert.DeserializeObject<T>(response);
|
||||
}
|
||||
|
||||
public virtual XmlDocument GetXmlResponse(string url)
|
||||
{
|
||||
var response = DownloadResponse(url);
|
||||
var doc = new XmlDocument();
|
||||
doc.LoadXml(response);
|
||||
|
||||
return doc;
|
||||
}
|
||||
|
||||
public virtual string GetXmlProperty(XmlDocument doc, string property)
|
||||
{
|
||||
var selectSingleNode = doc.SelectSingleNode(property);
|
||||
return selectSingleNode != null ? selectSingleNode.InnerText : string.Empty;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
using Umbraco.Core.Media;
|
||||
|
||||
namespace Umbraco.Web.Media.EmbedProviders
|
||||
{
|
||||
public abstract class AbstractProvider : IEmbedProvider
|
||||
{
|
||||
public virtual bool SupportsDimensions
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
public abstract string GetMarkup(string url, int maxWidth, int maxHeight);
|
||||
}
|
||||
}
|
||||
using Umbraco.Core.Media;
|
||||
|
||||
namespace Umbraco.Web.Media.EmbedProviders
|
||||
{
|
||||
public abstract class AbstractProvider : IEmbedProvider
|
||||
{
|
||||
public virtual bool SupportsDimensions
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
public abstract string GetMarkup(string url, int maxWidth, int maxHeight);
|
||||
}
|
||||
}
|
||||
25
src/Umbraco.Web/Media/EmbedProviders/Flickr.cs
Normal file
25
src/Umbraco.Web/Media/EmbedProviders/Flickr.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Web;
|
||||
|
||||
namespace Umbraco.Web.Media.EmbedProviders
|
||||
{
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
[Obsolete("This is no longer used and will be removed from the codebase in the future, for Flickr, use the Umbraco.Web.Media.EmbedProviders.OEmbedPhoto provider")]
|
||||
public class Flickr : AbstractOEmbedProvider
|
||||
{
|
||||
public override string GetMarkup(string url, int maxWidth, int maxHeight)
|
||||
{
|
||||
var flickrUrl = BuildFullUrl(url, maxWidth, maxHeight);
|
||||
var doc = GetXmlResponse(flickrUrl);
|
||||
|
||||
string imageUrl = doc.SelectSingleNode("/oembed/url").InnerText;
|
||||
string imageWidth = doc.SelectSingleNode("/oembed/width").InnerText;
|
||||
string imageHeight = doc.SelectSingleNode("/oembed/height").InnerText;
|
||||
string imageTitle = doc.SelectSingleNode("/oembed/title").InnerText;
|
||||
|
||||
return string.Format("<img src=\"{0}\" width\"{1}\" height=\"{2}\" alt=\"{3}\" />",
|
||||
imageUrl, imageWidth, imageHeight, HttpUtility.HtmlEncode(imageTitle));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,4 +19,4 @@ namespace Umbraco.Web.Media.EmbedProviders
|
||||
imageUrl, imageWidth, imageHeight, HttpUtility.HtmlEncode(imageTitle));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
namespace Umbraco.Web.Media.EmbedProviders
|
||||
{
|
||||
public class OEmbedRich : OEmbedVideo
|
||||
{
|
||||
}
|
||||
}
|
||||
namespace Umbraco.Web.Media.EmbedProviders
|
||||
{
|
||||
public class OEmbedRich : OEmbedVideo
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,15 @@
|
||||
using System.Xml;
|
||||
|
||||
namespace Umbraco.Web.Media.EmbedProviders
|
||||
{
|
||||
public class OEmbedVideo : AbstractOEmbedProvider
|
||||
{
|
||||
public override string GetMarkup(string url, int maxWidth, int maxHeight)
|
||||
{
|
||||
string requestUrl = BuildFullUrl(url, maxWidth, maxHeight);
|
||||
|
||||
XmlDocument doc = GetXmlResponse(requestUrl);
|
||||
return GetXmlProperty(doc, "/oembed/html");
|
||||
}
|
||||
}
|
||||
}
|
||||
using System.Xml;
|
||||
|
||||
namespace Umbraco.Web.Media.EmbedProviders
|
||||
{
|
||||
public class OEmbedVideo : AbstractOEmbedProvider
|
||||
{
|
||||
public override string GetMarkup(string url, int maxWidth, int maxHeight)
|
||||
{
|
||||
string requestUrl = BuildFullUrl(url, maxWidth, maxHeight);
|
||||
|
||||
XmlDocument doc = GetXmlResponse(requestUrl);
|
||||
return GetXmlProperty(doc, "/oembed/html");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,14 @@
|
||||
using System.Linq;
|
||||
using System.Xml;
|
||||
using Umbraco.Core.Media;
|
||||
|
||||
namespace Umbraco.Web.Media.EmbedProviders.Settings
|
||||
{
|
||||
public class Dictionary : IEmbedSettingProvider
|
||||
{
|
||||
public object GetSetting(XmlNode settingNode)
|
||||
{
|
||||
return settingNode.ChildNodes.Cast<XmlNode>().ToDictionary(item => item.Attributes != null ? item.Attributes["name"].Value : null, item => item.InnerText);
|
||||
}
|
||||
}
|
||||
}
|
||||
using System.Linq;
|
||||
using System.Xml;
|
||||
using Umbraco.Core.Media;
|
||||
|
||||
namespace Umbraco.Web.Media.EmbedProviders.Settings
|
||||
{
|
||||
public class Dictionary : IEmbedSettingProvider
|
||||
{
|
||||
public object GetSetting(XmlNode settingNode)
|
||||
{
|
||||
return settingNode.ChildNodes.Cast<XmlNode>().ToDictionary(item => item.Attributes != null ? item.Attributes["name"].Value : null, item => item.InnerText);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,13 @@
|
||||
using System.Xml;
|
||||
using Umbraco.Core.Media;
|
||||
|
||||
namespace Umbraco.Web.Media.EmbedProviders.Settings
|
||||
{
|
||||
public class String : IEmbedSettingProvider
|
||||
{
|
||||
public object GetSetting(XmlNode settingNode)
|
||||
{
|
||||
return settingNode.InnerText;
|
||||
}
|
||||
}
|
||||
}
|
||||
using System.Xml;
|
||||
using Umbraco.Core.Media;
|
||||
|
||||
namespace Umbraco.Web.Media.EmbedProviders.Settings
|
||||
{
|
||||
public class String : IEmbedSettingProvider
|
||||
{
|
||||
public object GetSetting(XmlNode settingNode)
|
||||
{
|
||||
return settingNode.InnerText;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,23 +1,23 @@
|
||||
using HtmlAgilityPack;
|
||||
|
||||
namespace Umbraco.Web.Media.EmbedProviders
|
||||
{
|
||||
public class Twitgoo : AbstractProvider
|
||||
{
|
||||
public override bool SupportsDimensions
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
public override string GetMarkup(string url, int maxWidth, int maxHeight)
|
||||
{
|
||||
var web = new HtmlWeb();
|
||||
var doc = web.Load(url);
|
||||
|
||||
var img = doc.DocumentNode.SelectSingleNode("//img [@id = 'fullsize']").Attributes["src"];
|
||||
|
||||
return string.Format("<img src=\"{0}\"/>",
|
||||
img.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
using HtmlAgilityPack;
|
||||
|
||||
namespace Umbraco.Web.Media.EmbedProviders
|
||||
{
|
||||
public class Twitgoo : AbstractProvider
|
||||
{
|
||||
public override bool SupportsDimensions
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
public override string GetMarkup(string url, int maxWidth, int maxHeight)
|
||||
{
|
||||
var web = new HtmlWeb();
|
||||
var doc = web.Load(url);
|
||||
|
||||
var img = doc.DocumentNode.SelectSingleNode("//img [@id = 'fullsize']").Attributes["src"];
|
||||
|
||||
return string.Format("<img src=\"{0}\"/>",
|
||||
img.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,405 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace Umbraco.Web.Media.Exif
|
||||
{
|
||||
/// <summary>
|
||||
/// An endian-aware converter for converting between base data types
|
||||
/// and an array of bytes.
|
||||
/// </summary>
|
||||
internal class BitConverterEx
|
||||
{
|
||||
#region Public Enums
|
||||
/// <summary>
|
||||
/// Represents the byte order.
|
||||
/// </summary>
|
||||
public enum ByteOrder
|
||||
{
|
||||
LittleEndian = 1,
|
||||
BigEndian = 2,
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Member Variables
|
||||
private ByteOrder mFrom, mTo;
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
public BitConverterEx(ByteOrder from, ByteOrder to)
|
||||
{
|
||||
mFrom = from;
|
||||
mTo = to;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
/// <summary>
|
||||
/// Indicates the byte order in which data is stored in this platform.
|
||||
/// </summary>
|
||||
public static ByteOrder SystemByteOrder
|
||||
{
|
||||
get
|
||||
{
|
||||
return (BitConverter.IsLittleEndian ? ByteOrder.LittleEndian : ByteOrder.BigEndian);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Predefined Values
|
||||
/// <summary>
|
||||
/// Returns a bit converter that converts between little-endian and system byte-order.
|
||||
/// </summary>
|
||||
public static BitConverterEx LittleEndian
|
||||
{
|
||||
get
|
||||
{
|
||||
return new BitConverterEx(ByteOrder.LittleEndian, BitConverterEx.SystemByteOrder);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a bit converter that converts between big-endian and system byte-order.
|
||||
/// </summary>
|
||||
public static BitConverterEx BigEndian
|
||||
{
|
||||
get
|
||||
{
|
||||
return new BitConverterEx(ByteOrder.BigEndian, BitConverterEx.SystemByteOrder);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a bit converter that does not do any byte-order conversion.
|
||||
/// </summary>
|
||||
public static BitConverterEx SystemEndian
|
||||
{
|
||||
get
|
||||
{
|
||||
return new BitConverterEx(BitConverterEx.SystemByteOrder, BitConverterEx.SystemByteOrder);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Static Methods
|
||||
/// <summary>
|
||||
/// Converts the given array of bytes to a Unicode character.
|
||||
/// </summary>
|
||||
public static char ToChar(byte[] value, long startIndex, ByteOrder from, ByteOrder to)
|
||||
{
|
||||
byte[] data = CheckData(value, startIndex, 2, from, to);
|
||||
return BitConverter.ToChar(data, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given array of bytes to a 16-bit unsigned integer.
|
||||
/// </summary>
|
||||
public static ushort ToUInt16(byte[] value, long startIndex, ByteOrder from, ByteOrder to)
|
||||
{
|
||||
byte[] data = CheckData(value, startIndex, 2, from, to);
|
||||
return BitConverter.ToUInt16(data, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given array of bytes to a 32-bit unsigned integer.
|
||||
/// </summary>
|
||||
public static uint ToUInt32(byte[] value, long startIndex, ByteOrder from, ByteOrder to)
|
||||
{
|
||||
byte[] data = CheckData(value, startIndex, 4, from, to);
|
||||
return BitConverter.ToUInt32(data, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given array of bytes to a 64-bit unsigned integer.
|
||||
/// </summary>
|
||||
public static ulong ToUInt64(byte[] value, long startIndex, ByteOrder from, ByteOrder to)
|
||||
{
|
||||
byte[] data = CheckData(value, startIndex, 8, from, to);
|
||||
return BitConverter.ToUInt64(data, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given array of bytes to a 16-bit signed integer.
|
||||
/// </summary>
|
||||
public static short ToInt16(byte[] value, long startIndex, ByteOrder from, ByteOrder to)
|
||||
{
|
||||
byte[] data = CheckData(value, startIndex, 2, from, to);
|
||||
return BitConverter.ToInt16(data, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given array of bytes to a 32-bit signed integer.
|
||||
/// </summary>
|
||||
public static int ToInt32(byte[] value, long startIndex, ByteOrder from, ByteOrder to)
|
||||
{
|
||||
byte[] data = CheckData(value, startIndex, 4, from, to);
|
||||
return BitConverter.ToInt32(data, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given array of bytes to a 64-bit signed integer.
|
||||
/// </summary>
|
||||
public static long ToInt64(byte[] value, long startIndex, ByteOrder from, ByteOrder to)
|
||||
{
|
||||
byte[] data = CheckData(value, startIndex, 8, from, to);
|
||||
return BitConverter.ToInt64(data, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given array of bytes to a single precision floating number.
|
||||
/// </summary>
|
||||
public static float ToSingle(byte[] value, long startIndex, ByteOrder from, ByteOrder to)
|
||||
{
|
||||
byte[] data = CheckData(value, startIndex, 4, from, to);
|
||||
return BitConverter.ToSingle(data, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given array of bytes to a double precision floating number.
|
||||
/// </summary>
|
||||
public static double ToDouble(byte[] value, long startIndex, ByteOrder from, ByteOrder to)
|
||||
{
|
||||
byte[] data = CheckData(value, startIndex, 8, from, to);
|
||||
return BitConverter.ToDouble(data, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given 16-bit unsigned integer to an array of bytes.
|
||||
/// </summary>
|
||||
public static byte[] GetBytes(ushort value, ByteOrder from, ByteOrder to)
|
||||
{
|
||||
byte[] data = BitConverter.GetBytes(value);
|
||||
data = CheckData(data, from, to);
|
||||
return data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given 32-bit unsigned integer to an array of bytes.
|
||||
/// </summary>
|
||||
public static byte[] GetBytes(uint value, ByteOrder from, ByteOrder to)
|
||||
{
|
||||
byte[] data = BitConverter.GetBytes(value);
|
||||
data = CheckData(data, from, to);
|
||||
return data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given 64-bit unsigned integer to an array of bytes.
|
||||
/// </summary>
|
||||
public static byte[] GetBytes(ulong value, ByteOrder from, ByteOrder to)
|
||||
{
|
||||
byte[] data = BitConverter.GetBytes(value);
|
||||
data = CheckData(data, from, to);
|
||||
return data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given 16-bit signed integer to an array of bytes.
|
||||
/// </summary>
|
||||
public static byte[] GetBytes(short value, ByteOrder from, ByteOrder to)
|
||||
{
|
||||
byte[] data = BitConverter.GetBytes(value);
|
||||
data = CheckData(data, from, to);
|
||||
return data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given 32-bit signed integer to an array of bytes.
|
||||
/// </summary>
|
||||
public static byte[] GetBytes(int value, ByteOrder from, ByteOrder to)
|
||||
{
|
||||
byte[] data = BitConverter.GetBytes(value);
|
||||
data = CheckData(data, from, to);
|
||||
return data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given 64-bit signed integer to an array of bytes.
|
||||
/// </summary>
|
||||
public static byte[] GetBytes(long value, ByteOrder from, ByteOrder to)
|
||||
{
|
||||
byte[] data = BitConverter.GetBytes(value);
|
||||
data = CheckData(data, from, to);
|
||||
return data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given single precision floating-point number to an array of bytes.
|
||||
/// </summary>
|
||||
public static byte[] GetBytes(float value, ByteOrder from, ByteOrder to)
|
||||
{
|
||||
byte[] data = BitConverter.GetBytes(value);
|
||||
data = CheckData(data, from, to);
|
||||
return data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given double precision floating-point number to an array of bytes.
|
||||
/// </summary>
|
||||
public static byte[] GetBytes(double value, ByteOrder from, ByteOrder to)
|
||||
{
|
||||
byte[] data = BitConverter.GetBytes(value);
|
||||
data = CheckData(data, from, to);
|
||||
return data;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Instance Methods
|
||||
/// <summary>
|
||||
/// Converts the given array of bytes to a 16-bit unsigned integer.
|
||||
/// </summary>
|
||||
public char ToChar(byte[] value, long startIndex)
|
||||
{
|
||||
return BitConverterEx.ToChar(value, startIndex, mFrom, mTo);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given array of bytes to a 16-bit unsigned integer.
|
||||
/// </summary>
|
||||
public ushort ToUInt16(byte[] value, long startIndex)
|
||||
{
|
||||
return BitConverterEx.ToUInt16(value, startIndex, mFrom, mTo);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given array of bytes to a 32-bit unsigned integer.
|
||||
/// </summary>
|
||||
public uint ToUInt32(byte[] value, long startIndex)
|
||||
{
|
||||
return BitConverterEx.ToUInt32(value, startIndex, mFrom, mTo);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given array of bytes to a 64-bit unsigned integer.
|
||||
/// </summary>
|
||||
public ulong ToUInt64(byte[] value, long startIndex)
|
||||
{
|
||||
return BitConverterEx.ToUInt64(value, startIndex, mFrom, mTo);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given array of bytes to a 16-bit signed integer.
|
||||
/// </summary>
|
||||
public short ToInt16(byte[] value, long startIndex)
|
||||
{
|
||||
return BitConverterEx.ToInt16(value, startIndex, mFrom, mTo);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given array of bytes to a 32-bit signed integer.
|
||||
/// </summary>
|
||||
public int ToInt32(byte[] value, long startIndex)
|
||||
{
|
||||
return BitConverterEx.ToInt32(value, startIndex, mFrom, mTo);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given array of bytes to a 64-bit signed integer.
|
||||
/// </summary>
|
||||
public long ToInt64(byte[] value, long startIndex)
|
||||
{
|
||||
return BitConverterEx.ToInt64(value, startIndex, mFrom, mTo);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given array of bytes to a single precision floating number.
|
||||
/// </summary>
|
||||
public float ToSingle(byte[] value, long startIndex)
|
||||
{
|
||||
return BitConverterEx.ToSingle(value, startIndex, mFrom, mTo);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given array of bytes to a double precision floating number.
|
||||
/// </summary>
|
||||
public double ToDouble(byte[] value, long startIndex)
|
||||
{
|
||||
return BitConverterEx.ToDouble(value, startIndex, mFrom, mTo);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given 16-bit unsigned integer to an array of bytes.
|
||||
/// </summary>
|
||||
public byte[] GetBytes(ushort value)
|
||||
{
|
||||
return BitConverterEx.GetBytes(value, mFrom, mTo);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given 32-bit unsigned integer to an array of bytes.
|
||||
/// </summary>
|
||||
public byte[] GetBytes(uint value)
|
||||
{
|
||||
return BitConverterEx.GetBytes(value, mFrom, mTo);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given 64-bit unsigned integer to an array of bytes.
|
||||
/// </summary>
|
||||
public byte[] GetBytes(ulong value)
|
||||
{
|
||||
return BitConverterEx.GetBytes(value, mFrom, mTo);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given 16-bit signed integer to an array of bytes.
|
||||
/// </summary>
|
||||
public byte[] GetBytes(short value)
|
||||
{
|
||||
return BitConverterEx.GetBytes(value, mFrom, mTo);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given 32-bit signed integer to an array of bytes.
|
||||
/// </summary>
|
||||
public byte[] GetBytes(int value)
|
||||
{
|
||||
return BitConverterEx.GetBytes(value, mFrom, mTo);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given 64-bit signed integer to an array of bytes.
|
||||
/// </summary>
|
||||
public byte[] GetBytes(long value)
|
||||
{
|
||||
return BitConverterEx.GetBytes(value, mFrom, mTo);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given single precision floating-point number to an array of bytes.
|
||||
/// </summary>
|
||||
public byte[] GetBytes(float value)
|
||||
{
|
||||
return BitConverterEx.GetBytes(value, mFrom, mTo);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given double precision floating-point number to an array of bytes.
|
||||
/// </summary>
|
||||
public byte[] GetBytes(double value)
|
||||
{
|
||||
return BitConverterEx.GetBytes(value, mFrom, mTo);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Private Helpers
|
||||
/// <summary>
|
||||
/// Reverse the array of bytes as needed.
|
||||
/// </summary>
|
||||
private static byte[] CheckData(byte[] value, long startIndex, long length, ByteOrder from, ByteOrder to)
|
||||
{
|
||||
byte[] data = new byte[length];
|
||||
Array.Copy(value, startIndex, data, 0, length);
|
||||
if (from != to)
|
||||
Array.Reverse(data);
|
||||
return data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reverse the array of bytes as needed.
|
||||
/// </summary>
|
||||
private static byte[] CheckData(byte[] value, ByteOrder from, ByteOrder to)
|
||||
{
|
||||
return CheckData(value, 0, value.Length, from, to);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,357 +0,0 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
|
||||
namespace Umbraco.Web.Media.Exif
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts between exif data types and array of bytes.
|
||||
/// </summary>
|
||||
internal class ExifBitConverter : BitConverterEx
|
||||
{
|
||||
#region Constructors
|
||||
public ExifBitConverter(ByteOrder from, ByteOrder to)
|
||||
: base(from, to)
|
||||
{
|
||||
;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Static Methods
|
||||
/// <summary>
|
||||
/// Returns an ASCII string converted from the given byte array.
|
||||
/// </summary>
|
||||
public static string ToAscii(byte[] data, bool endatfirstnull, Encoding encoding)
|
||||
{
|
||||
int len = data.Length;
|
||||
if (endatfirstnull)
|
||||
{
|
||||
len = Array.IndexOf(data, (byte)0);
|
||||
if (len == -1) len = data.Length;
|
||||
}
|
||||
return encoding.GetString(data, 0, len);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns an ASCII string converted from the given byte array.
|
||||
/// </summary>
|
||||
public static string ToAscii(byte[] data, Encoding encoding)
|
||||
{
|
||||
return ToAscii(data, true, encoding);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a string converted from the given byte array.
|
||||
/// from the numeric value of each byte.
|
||||
/// </summary>
|
||||
public static string ToString(byte[] data)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
foreach (byte b in data)
|
||||
sb.Append(b);
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a DateTime object converted from the given byte array.
|
||||
/// </summary>
|
||||
public static DateTime ToDateTime(byte[] data, bool hastime)
|
||||
{
|
||||
string str = ToAscii(data, Encoding.ASCII);
|
||||
string[] parts = str.Split(new char[] { ':', ' ' });
|
||||
try
|
||||
{
|
||||
if (hastime && parts.Length == 6)
|
||||
{
|
||||
// yyyy:MM:dd HH:mm:ss
|
||||
// This is the expected format though some cameras
|
||||
// can use single digits. See Issue 21.
|
||||
return new DateTime(int.Parse(parts[0]), int.Parse(parts[1]), int.Parse(parts[2]), int.Parse(parts[3]), int.Parse(parts[4]), int.Parse(parts[5]));
|
||||
}
|
||||
else if (!hastime && parts.Length == 3)
|
||||
{
|
||||
// yyyy:MM:dd
|
||||
return new DateTime(int.Parse(parts[0]), int.Parse(parts[1]), int.Parse(parts[2]));
|
||||
}
|
||||
else
|
||||
{
|
||||
return DateTime.MinValue;
|
||||
}
|
||||
}
|
||||
catch (ArgumentOutOfRangeException)
|
||||
{
|
||||
return DateTime.MinValue;
|
||||
}
|
||||
catch (ArgumentException)
|
||||
{
|
||||
return DateTime.MinValue;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a DateTime object converted from the given byte array.
|
||||
/// </summary>
|
||||
public static DateTime ToDateTime(byte[] data)
|
||||
{
|
||||
return ToDateTime(data, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns an unsigned rational number converted from the first
|
||||
/// eight bytes of the given byte array. The first four bytes are
|
||||
/// assumed to be the numerator and the next four bytes are the
|
||||
/// denumerator.
|
||||
/// Numbers are converted from the given byte-order to platform byte-order.
|
||||
/// </summary>
|
||||
public static MathEx.UFraction32 ToURational(byte[] data, ByteOrder frombyteorder)
|
||||
{
|
||||
byte[] num = new byte[4];
|
||||
byte[] den = new byte[4];
|
||||
Array.Copy(data, 0, num, 0, 4);
|
||||
Array.Copy(data, 4, den, 0, 4);
|
||||
return new MathEx.UFraction32(ToUInt32(num, 0, frombyteorder, BitConverterEx.SystemByteOrder), ToUInt32(den, 0, frombyteorder, BitConverterEx.SystemByteOrder));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a signed rational number converted from the first
|
||||
/// eight bytes of the given byte array. The first four bytes are
|
||||
/// assumed to be the numerator and the next four bytes are the
|
||||
/// denumerator.
|
||||
/// Numbers are converted from the given byte-order to platform byte-order.
|
||||
/// </summary>
|
||||
public static MathEx.Fraction32 ToSRational(byte[] data, ByteOrder frombyteorder)
|
||||
{
|
||||
byte[] num = new byte[4];
|
||||
byte[] den = new byte[4];
|
||||
Array.Copy(data, 0, num, 0, 4);
|
||||
Array.Copy(data, 4, den, 0, 4);
|
||||
return new MathEx.Fraction32(ToInt32(num, 0, frombyteorder, BitConverterEx.SystemByteOrder), ToInt32(den, 0, frombyteorder, BitConverterEx.SystemByteOrder));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns an array of 16-bit unsigned integers converted from
|
||||
/// the given byte array.
|
||||
/// Numbers are converted from the given byte-order to platform byte-order.
|
||||
/// </summary>
|
||||
public static ushort[] ToUShortArray(byte[] data, int count, ByteOrder frombyteorder)
|
||||
{
|
||||
ushort[] numbers = new ushort[count];
|
||||
for (uint i = 0; i < count; i++)
|
||||
{
|
||||
byte[] num = new byte[2];
|
||||
Array.Copy(data, i * 2, num, 0, 2);
|
||||
numbers[i] = ToUInt16(num, 0, frombyteorder, BitConverterEx.SystemByteOrder);
|
||||
}
|
||||
return numbers;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns an array of 32-bit unsigned integers converted from
|
||||
/// the given byte array.
|
||||
/// Numbers are converted from the given byte-order to platform byte-order.
|
||||
/// </summary>
|
||||
public static uint[] ToUIntArray(byte[] data, int count, ByteOrder frombyteorder)
|
||||
{
|
||||
uint[] numbers = new uint[count];
|
||||
for (uint i = 0; i < count; i++)
|
||||
{
|
||||
byte[] num = new byte[4];
|
||||
Array.Copy(data, i * 4, num, 0, 4);
|
||||
numbers[i] = ToUInt32(num, 0, frombyteorder, BitConverterEx.SystemByteOrder);
|
||||
}
|
||||
return numbers;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns an array of 32-bit signed integers converted from
|
||||
/// the given byte array.
|
||||
/// Numbers are converted from the given byte-order to platform byte-order.
|
||||
/// </summary>
|
||||
public static int[] ToSIntArray(byte[] data, int count, ByteOrder byteorder)
|
||||
{
|
||||
int[] numbers = new int[count];
|
||||
for (uint i = 0; i < count; i++)
|
||||
{
|
||||
byte[] num = new byte[4];
|
||||
Array.Copy(data, i * 4, num, 0, 4);
|
||||
numbers[i] = ToInt32(num, 0, byteorder, BitConverterEx.SystemByteOrder);
|
||||
}
|
||||
return numbers;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns an array of unsigned rational numbers converted from
|
||||
/// the given byte array.
|
||||
/// Numbers are converted from the given byte-order to platform byte-order.
|
||||
/// </summary>
|
||||
public static MathEx.UFraction32[] ToURationalArray(byte[] data, int count, ByteOrder frombyteorder)
|
||||
{
|
||||
MathEx.UFraction32[] numbers = new MathEx.UFraction32[count];
|
||||
for (uint i = 0; i < count; i++)
|
||||
{
|
||||
byte[] num = new byte[4];
|
||||
byte[] den = new byte[4];
|
||||
Array.Copy(data, i * 8, num, 0, 4);
|
||||
Array.Copy(data, i * 8 + 4, den, 0, 4);
|
||||
numbers[i].Set(ToUInt32(num, 0, frombyteorder, BitConverterEx.SystemByteOrder), ToUInt32(den, 0, frombyteorder, BitConverterEx.SystemByteOrder));
|
||||
}
|
||||
return numbers;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns an array of signed rational numbers converted from
|
||||
/// the given byte array.
|
||||
/// Numbers are converted from the given byte-order to platform byte-order.
|
||||
/// </summary>
|
||||
public static MathEx.Fraction32[] ToSRationalArray(byte[] data, int count, ByteOrder frombyteorder)
|
||||
{
|
||||
MathEx.Fraction32[] numbers = new MathEx.Fraction32[count];
|
||||
for (uint i = 0; i < count; i++)
|
||||
{
|
||||
byte[] num = new byte[4];
|
||||
byte[] den = new byte[4];
|
||||
Array.Copy(data, i * 8, num, 0, 4);
|
||||
Array.Copy(data, i * 8 + 4, den, 0, 4);
|
||||
numbers[i].Set(ToInt32(num, 0, frombyteorder, BitConverterEx.SystemByteOrder), ToInt32(den, 0, frombyteorder, BitConverterEx.SystemByteOrder));
|
||||
}
|
||||
return numbers;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given ascii string to an array of bytes optionally adding a null terminator.
|
||||
/// </summary>
|
||||
public static byte[] GetBytes(string value, bool addnull, Encoding encoding)
|
||||
{
|
||||
if (addnull) value += '\0';
|
||||
return encoding.GetBytes(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given ascii string to an array of bytes without adding a null terminator.
|
||||
/// </summary>
|
||||
public static byte[] GetBytes(string value, Encoding encoding)
|
||||
{
|
||||
return GetBytes(value, false, encoding);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given datetime to an array of bytes with a null terminator.
|
||||
/// </summary>
|
||||
public static byte[] GetBytes(DateTime value, bool hastime)
|
||||
{
|
||||
string str = "";
|
||||
if (hastime)
|
||||
str = value.ToString("yyyy:MM:dd HH:mm:ss", System.Globalization.CultureInfo.InvariantCulture);
|
||||
else
|
||||
str = value.ToString("yyyy:MM:dd", System.Globalization.CultureInfo.InvariantCulture);
|
||||
return GetBytes(str, true, Encoding.ASCII);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given unsigned rational number to an array of bytes.
|
||||
/// Numbers are converted from the platform byte-order to the given byte-order.
|
||||
/// </summary>
|
||||
public static byte[] GetBytes(MathEx.UFraction32 value, ByteOrder tobyteorder)
|
||||
{
|
||||
byte[] num = GetBytes(value.Numerator, BitConverterEx.SystemByteOrder, tobyteorder);
|
||||
byte[] den = GetBytes(value.Denominator, BitConverterEx.SystemByteOrder, tobyteorder);
|
||||
byte[] data = new byte[8];
|
||||
Array.Copy(num, 0, data, 0, 4);
|
||||
Array.Copy(den, 0, data, 4, 4);
|
||||
return data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given signed rational number to an array of bytes.
|
||||
/// Numbers are converted from the platform byte-order to the given byte-order.
|
||||
/// </summary>
|
||||
public static byte[] GetBytes(MathEx.Fraction32 value, ByteOrder tobyteorder)
|
||||
{
|
||||
byte[] num = GetBytes(value.Numerator, BitConverterEx.SystemByteOrder, tobyteorder);
|
||||
byte[] den = GetBytes(value.Denominator, BitConverterEx.SystemByteOrder, tobyteorder);
|
||||
byte[] data = new byte[8];
|
||||
Array.Copy(num, 0, data, 0, 4);
|
||||
Array.Copy(den, 0, data, 4, 4);
|
||||
return data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given array of 16-bit unsigned integers to an array of bytes.
|
||||
/// Numbers are converted from the platform byte-order to the given byte-order.
|
||||
/// </summary>
|
||||
public static byte[] GetBytes(ushort[] value, ByteOrder tobyteorder)
|
||||
{
|
||||
byte[] data = new byte[2 * value.Length];
|
||||
for (int i = 0; i < value.Length; i++)
|
||||
{
|
||||
byte[] num = GetBytes(value[i], BitConverterEx.SystemByteOrder, tobyteorder);
|
||||
Array.Copy(num, 0, data, i * 2, 2);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given array of 32-bit unsigned integers to an array of bytes.
|
||||
/// Numbers are converted from the platform byte-order to the given byte-order.
|
||||
/// </summary>
|
||||
public static byte[] GetBytes(uint[] value, ByteOrder tobyteorder)
|
||||
{
|
||||
byte[] data = new byte[4 * value.Length];
|
||||
for (int i = 0; i < value.Length; i++)
|
||||
{
|
||||
byte[] num = GetBytes(value[i], BitConverterEx.SystemByteOrder, tobyteorder);
|
||||
Array.Copy(num, 0, data, i * 4, 4);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given array of 32-bit signed integers to an array of bytes.
|
||||
/// Numbers are converted from the platform byte-order to the given byte-order.
|
||||
/// </summary>
|
||||
public static byte[] GetBytes(int[] value, ByteOrder tobyteorder)
|
||||
{
|
||||
byte[] data = new byte[4 * value.Length];
|
||||
for (int i = 0; i < value.Length; i++)
|
||||
{
|
||||
byte[] num = GetBytes(value[i], BitConverterEx.SystemByteOrder, tobyteorder);
|
||||
Array.Copy(num, 0, data, i * 4, 4);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given array of unsigned rationals to an array of bytes.
|
||||
/// Numbers are converted from the platform byte-order to the given byte-order.
|
||||
/// </summary>
|
||||
public static byte[] GetBytes(MathEx.UFraction32[] value, ByteOrder tobyteorder)
|
||||
{
|
||||
byte[] data = new byte[8 * value.Length];
|
||||
for (int i = 0; i < value.Length; i++)
|
||||
{
|
||||
byte[] num = GetBytes(value[i].Numerator, BitConverterEx.SystemByteOrder, tobyteorder);
|
||||
byte[] den = GetBytes(value[i].Denominator, BitConverterEx.SystemByteOrder, tobyteorder);
|
||||
Array.Copy(num, 0, data, i * 8, 4);
|
||||
Array.Copy(den, 0, data, i * 8 + 4, 4);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given array of signed rationals to an array of bytes.
|
||||
/// Numbers are converted from the platform byte-order to the given byte-order.
|
||||
/// </summary>
|
||||
public static byte[] GetBytes(MathEx.Fraction32[] value, ByteOrder tobyteorder)
|
||||
{
|
||||
byte[] data = new byte[8 * value.Length];
|
||||
for (int i = 0; i < value.Length; i++)
|
||||
{
|
||||
byte[] num = GetBytes(value[i].Numerator, BitConverterEx.SystemByteOrder, tobyteorder);
|
||||
byte[] den = GetBytes(value[i].Denominator, BitConverterEx.SystemByteOrder, tobyteorder);
|
||||
Array.Copy(num, 0, data, i * 8, 4);
|
||||
Array.Copy(den, 0, data, i * 8 + 4, 4);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,292 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace Umbraco.Web.Media.Exif
|
||||
{
|
||||
internal enum Compression : ushort
|
||||
{
|
||||
Uncompressed = 1,
|
||||
CCITT1D = 2,
|
||||
Group3Fax = 3,
|
||||
Group4Fax = 4,
|
||||
LZW = 5,
|
||||
JPEG = 6,
|
||||
PackBits = 32773,
|
||||
}
|
||||
|
||||
internal enum PhotometricInterpretation : ushort
|
||||
{
|
||||
WhiteIsZero = 0,
|
||||
BlackIsZero = 1,
|
||||
RGB = 2,
|
||||
RGBPalette = 3,
|
||||
TransparencyMask = 4,
|
||||
CMYK = 5,
|
||||
YCbCr = 6,
|
||||
CIELab = 8,
|
||||
}
|
||||
|
||||
internal enum Orientation : ushort
|
||||
{
|
||||
Normal = 1,
|
||||
MirroredVertically = 2,
|
||||
Rotated180 = 3,
|
||||
MirroredHorizontally = 4,
|
||||
RotatedLeftAndMirroredVertically = 5,
|
||||
RotatedRight = 6,
|
||||
RotatedLeft = 7,
|
||||
RotatedRightAndMirroredVertically = 8,
|
||||
}
|
||||
|
||||
internal enum PlanarConfiguration : ushort
|
||||
{
|
||||
ChunkyFormat = 1,
|
||||
PlanarFormat = 2,
|
||||
}
|
||||
|
||||
internal enum YCbCrPositioning : ushort
|
||||
{
|
||||
Centered = 1,
|
||||
CoSited = 2,
|
||||
}
|
||||
|
||||
internal enum ResolutionUnit : ushort
|
||||
{
|
||||
Inches = 2,
|
||||
Centimeters = 3,
|
||||
}
|
||||
|
||||
internal enum ColorSpace : ushort
|
||||
{
|
||||
sRGB = 1,
|
||||
Uncalibrated = 0xfff,
|
||||
}
|
||||
|
||||
internal enum ExposureProgram : ushort
|
||||
{
|
||||
NotDefined = 0,
|
||||
Manual = 1,
|
||||
Normal = 2,
|
||||
AperturePriority = 3,
|
||||
ShutterPriority = 4,
|
||||
/// <summary>
|
||||
/// Biased toward depth of field.
|
||||
/// </summary>
|
||||
Creative = 5,
|
||||
/// <summary>
|
||||
/// Biased toward fast shutter speed.
|
||||
/// </summary>
|
||||
Action = 6,
|
||||
/// <summary>
|
||||
/// For closeup photos with the background out of focus.
|
||||
/// </summary>
|
||||
Portrait = 7,
|
||||
/// <summary>
|
||||
/// For landscape photos with the background in focus.
|
||||
/// </summary>
|
||||
Landscape = 8,
|
||||
}
|
||||
|
||||
internal enum MeteringMode : ushort
|
||||
{
|
||||
Unknown = 0,
|
||||
Average = 1,
|
||||
CenterWeightedAverage = 2,
|
||||
Spot = 3,
|
||||
MultiSpot = 4,
|
||||
Pattern = 5,
|
||||
Partial = 6,
|
||||
Other = 255,
|
||||
}
|
||||
|
||||
internal enum LightSource : ushort
|
||||
{
|
||||
Unknown = 0,
|
||||
Daylight = 1,
|
||||
Fluorescent = 2,
|
||||
Tungsten = 3,
|
||||
Flash = 4,
|
||||
FineWeather = 9,
|
||||
CloudyWeather = 10,
|
||||
Shade = 11,
|
||||
/// <summary>
|
||||
/// D 5700 – 7100K
|
||||
/// </summary>
|
||||
DaylightFluorescent = 12,
|
||||
/// <summary>
|
||||
/// N 4600 – 5400K
|
||||
/// </summary>
|
||||
DayWhiteFluorescent = 13,
|
||||
/// <summary>
|
||||
/// W 3900 – 4500K
|
||||
/// </summary>
|
||||
CoolWhiteFluorescent = 14,
|
||||
/// <summary>
|
||||
/// WW 3200 – 3700K
|
||||
/// </summary>
|
||||
WhiteFluorescent = 15,
|
||||
StandardLightA = 17,
|
||||
StandardLightB = 18,
|
||||
StandardLightC = 19,
|
||||
D55 = 20,
|
||||
D65 = 21,
|
||||
D75 = 22,
|
||||
D50 = 23,
|
||||
ISOStudioTungsten = 24,
|
||||
OtherLightSource = 255,
|
||||
}
|
||||
|
||||
[Flags]
|
||||
internal enum Flash : ushort
|
||||
{
|
||||
FlashDidNotFire = 0,
|
||||
StrobeReturnLightNotDetected = 4,
|
||||
StrobeReturnLightDetected = 2,
|
||||
FlashFired = 1,
|
||||
CompulsoryFlashMode = 8,
|
||||
AutoMode = 16,
|
||||
NoFlashFunction = 32,
|
||||
RedEyeReductionMode = 64,
|
||||
}
|
||||
|
||||
internal enum SensingMethod : ushort
|
||||
{
|
||||
NotDefined = 1,
|
||||
OneChipColorAreaSensor = 2,
|
||||
TwoChipColorAreaSensor = 3,
|
||||
ThreeChipColorAreaSensor = 4,
|
||||
ColorSequentialAreaSensor = 5,
|
||||
TriLinearSensor = 7,
|
||||
ColorSequentialLinearSensor = 8,
|
||||
}
|
||||
|
||||
internal enum FileSource : byte // UNDEFINED
|
||||
{
|
||||
DSC = 3,
|
||||
}
|
||||
|
||||
internal enum SceneType : byte // UNDEFINED
|
||||
{
|
||||
DirectlyPhotographedImage = 1,
|
||||
}
|
||||
|
||||
internal enum CustomRendered : ushort
|
||||
{
|
||||
NormalProcess = 0,
|
||||
CustomProcess = 1,
|
||||
}
|
||||
|
||||
internal enum ExposureMode : ushort
|
||||
{
|
||||
Auto = 0,
|
||||
Manual = 1,
|
||||
AutoBracket = 2,
|
||||
}
|
||||
|
||||
internal enum WhiteBalance : ushort
|
||||
{
|
||||
Auto = 0,
|
||||
Manual = 1,
|
||||
}
|
||||
|
||||
internal enum SceneCaptureType : ushort
|
||||
{
|
||||
Standard = 0,
|
||||
Landscape = 1,
|
||||
Portrait = 2,
|
||||
NightScene = 3,
|
||||
}
|
||||
|
||||
internal enum GainControl : ushort
|
||||
{
|
||||
None = 0,
|
||||
LowGainUp = 1,
|
||||
HighGainUp = 2,
|
||||
LowGainDown = 3,
|
||||
HighGainDown = 4,
|
||||
}
|
||||
|
||||
internal enum Contrast : ushort
|
||||
{
|
||||
Normal = 0,
|
||||
Soft = 1,
|
||||
Hard = 2,
|
||||
}
|
||||
|
||||
internal enum Saturation : ushort
|
||||
{
|
||||
Normal = 0,
|
||||
Low = 1,
|
||||
High = 2,
|
||||
}
|
||||
|
||||
internal enum Sharpness : ushort
|
||||
{
|
||||
Normal = 0,
|
||||
Soft = 1,
|
||||
Hard = 2,
|
||||
}
|
||||
|
||||
internal enum SubjectDistanceRange : ushort
|
||||
{
|
||||
Unknown = 0,
|
||||
Macro = 1,
|
||||
CloseView = 2,
|
||||
DistantView = 3,
|
||||
}
|
||||
|
||||
internal enum GPSLatitudeRef : byte // ASCII
|
||||
{
|
||||
North = 78, // 'N'
|
||||
South = 83, // 'S'
|
||||
}
|
||||
|
||||
internal enum GPSLongitudeRef : byte // ASCII
|
||||
{
|
||||
West = 87, // 'W'
|
||||
East = 69, // 'E'
|
||||
}
|
||||
|
||||
internal enum GPSAltitudeRef : byte
|
||||
{
|
||||
AboveSeaLevel = 0,
|
||||
BelowSeaLevel = 1,
|
||||
}
|
||||
|
||||
internal enum GPSStatus : byte // ASCII
|
||||
{
|
||||
MeasurementInProgress = 65, // 'A'
|
||||
MeasurementInteroperability = 86, // 'V'
|
||||
}
|
||||
|
||||
internal enum GPSMeasureMode : byte // ASCII
|
||||
{
|
||||
TwoDimensional = 50, // '2'
|
||||
ThreeDimensional = 51, // '3'
|
||||
}
|
||||
|
||||
internal enum GPSSpeedRef : byte // ASCII
|
||||
{
|
||||
KilometersPerHour = 75, // 'K'
|
||||
MilesPerHour = 77, // 'M'
|
||||
Knots = 78, // 'N'
|
||||
}
|
||||
|
||||
internal enum GPSDirectionRef : byte // ASCII
|
||||
{
|
||||
TrueDirection = 84, // 'T'
|
||||
MagneticDirection = 77, // 'M'
|
||||
}
|
||||
|
||||
internal enum GPSDistanceRef : byte // ASCII
|
||||
{
|
||||
Kilometers = 75, // 'K'
|
||||
Miles = 77, // 'M'
|
||||
Knots = 78, // 'N'
|
||||
}
|
||||
|
||||
internal enum GPSDifferential : ushort
|
||||
{
|
||||
MeasurementWithoutDifferentialCorrection = 0,
|
||||
DifferentialCorrectionApplied = 1,
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace Umbraco.Web.Media.Exif
|
||||
{
|
||||
/// <summary>
|
||||
/// The exception that is thrown when the format of the JPEG/Exif file
|
||||
/// could not be understood.
|
||||
/// </summary>
|
||||
internal class NotValidExifFileException : Exception
|
||||
{
|
||||
public NotValidExifFileException()
|
||||
: base("Not a valid JPEG/Exif file.")
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
public NotValidExifFileException(string message)
|
||||
: base(message)
|
||||
{
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The exception that is thrown when an invalid enum type is given to an
|
||||
/// ExifEnumProperty.
|
||||
/// </summary>
|
||||
internal class UnknownEnumTypeException : Exception
|
||||
{
|
||||
public UnknownEnumTypeException()
|
||||
: base("Unknown enum type.")
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
public UnknownEnumTypeException(string message)
|
||||
: base(message)
|
||||
{
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,373 +0,0 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
|
||||
namespace Umbraco.Web.Media.Exif
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents an enumerated value.
|
||||
/// </summary>
|
||||
internal class ExifEnumProperty<T> : ExifProperty
|
||||
{
|
||||
protected T mValue;
|
||||
protected bool mIsBitField;
|
||||
protected override object _Value { get { return Value; } set { Value = (T)value; } }
|
||||
public new T Value { get { return mValue; } set { mValue = value; } }
|
||||
public bool IsBitField { get { return mIsBitField; } }
|
||||
|
||||
static public implicit operator T(ExifEnumProperty<T> obj) { return (T)obj.mValue; }
|
||||
|
||||
public override string ToString() { return mValue.ToString(); }
|
||||
|
||||
public ExifEnumProperty(ExifTag tag, T value, bool isbitfield)
|
||||
: base(tag)
|
||||
{
|
||||
mValue = value;
|
||||
mIsBitField = isbitfield;
|
||||
}
|
||||
|
||||
public ExifEnumProperty(ExifTag tag, T value)
|
||||
: this(tag, value, false)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
public override ExifInterOperability Interoperability
|
||||
{
|
||||
get
|
||||
{
|
||||
ushort tagid = ExifTagFactory.GetTagID(mTag);
|
||||
|
||||
Type type = typeof(T);
|
||||
Type basetype = Enum.GetUnderlyingType(type);
|
||||
|
||||
if (type == typeof(FileSource) || type == typeof(SceneType))
|
||||
{
|
||||
// UNDEFINED
|
||||
return new ExifInterOperability(tagid, 7, 1, new byte[] { (byte)((object)mValue) });
|
||||
}
|
||||
else if (type == typeof(GPSLatitudeRef) || type == typeof(GPSLongitudeRef) ||
|
||||
type == typeof(GPSStatus) || type == typeof(GPSMeasureMode) ||
|
||||
type == typeof(GPSSpeedRef) || type == typeof(GPSDirectionRef) ||
|
||||
type == typeof(GPSDistanceRef))
|
||||
{
|
||||
// ASCII
|
||||
return new ExifInterOperability(tagid, 2, 2, new byte[] { (byte)((object)mValue), 0 });
|
||||
}
|
||||
else if (basetype == typeof(byte))
|
||||
{
|
||||
// BYTE
|
||||
return new ExifInterOperability(tagid, 1, 1, new byte[] { (byte)((object)mValue) });
|
||||
}
|
||||
else if (basetype == typeof(ushort))
|
||||
{
|
||||
// SHORT
|
||||
return new ExifInterOperability(tagid, 3, 1, ExifBitConverter.GetBytes((ushort)((object)mValue), BitConverterEx.SystemByteOrder, BitConverterEx.SystemByteOrder));
|
||||
}
|
||||
else
|
||||
throw new UnknownEnumTypeException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents an ASCII string. (EXIF Specification: UNDEFINED) Used for the UserComment field.
|
||||
/// </summary>
|
||||
internal class ExifEncodedString : ExifProperty
|
||||
{
|
||||
protected string mValue;
|
||||
private Encoding mEncoding;
|
||||
protected override object _Value { get { return Value; } set { Value = (string)value; } }
|
||||
public new string Value { get { return mValue; } set { mValue = value; } }
|
||||
public Encoding Encoding { get { return mEncoding; } set { mEncoding = value; } }
|
||||
|
||||
static public implicit operator string(ExifEncodedString obj) { return obj.mValue; }
|
||||
|
||||
public override string ToString() { return mValue; }
|
||||
|
||||
public ExifEncodedString(ExifTag tag, string value, Encoding encoding)
|
||||
: base(tag)
|
||||
{
|
||||
mValue = value;
|
||||
mEncoding = encoding;
|
||||
}
|
||||
|
||||
public override ExifInterOperability Interoperability
|
||||
{
|
||||
get
|
||||
{
|
||||
string enc = "";
|
||||
if (mEncoding == null)
|
||||
enc = "\0\0\0\0\0\0\0\0";
|
||||
else if (mEncoding.EncodingName == "US-ASCII")
|
||||
enc = "ASCII\0\0\0";
|
||||
else if (mEncoding.EncodingName == "Japanese (JIS 0208-1990 and 0212-1990)")
|
||||
enc = "JIS\0\0\0\0\0";
|
||||
else if (mEncoding.EncodingName == "Unicode")
|
||||
enc = "Unicode\0";
|
||||
else
|
||||
enc = "\0\0\0\0\0\0\0\0";
|
||||
|
||||
byte[] benc = Encoding.ASCII.GetBytes(enc);
|
||||
byte[] bstr = (mEncoding == null ? Encoding.ASCII.GetBytes(mValue) : mEncoding.GetBytes(mValue));
|
||||
byte[] data = new byte[benc.Length + bstr.Length];
|
||||
Array.Copy(benc, 0, data, 0, benc.Length);
|
||||
Array.Copy(bstr, 0, data, benc.Length, bstr.Length);
|
||||
|
||||
return new ExifInterOperability(ExifTagFactory.GetTagID(mTag), 7, (uint)data.Length, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents an ASCII string formatted as DateTime. (EXIF Specification: ASCII) Used for the date time fields.
|
||||
/// </summary>
|
||||
internal class ExifDateTime : ExifProperty
|
||||
{
|
||||
protected DateTime mValue;
|
||||
protected override object _Value { get { return Value; } set { Value = (DateTime)value; } }
|
||||
public new DateTime Value { get { return mValue; } set { mValue = value; } }
|
||||
|
||||
static public implicit operator DateTime(ExifDateTime obj) { return obj.mValue; }
|
||||
|
||||
public override string ToString() { return mValue.ToString("yyyy.MM.dd HH:mm:ss"); }
|
||||
|
||||
public ExifDateTime(ExifTag tag, DateTime value)
|
||||
: base(tag)
|
||||
{
|
||||
mValue = value;
|
||||
}
|
||||
|
||||
public override ExifInterOperability Interoperability
|
||||
{
|
||||
get
|
||||
{
|
||||
return new ExifInterOperability(ExifTagFactory.GetTagID(mTag), 2, (uint)20, ExifBitConverter.GetBytes(mValue, true));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents the exif version as a 4 byte ASCII string. (EXIF Specification: UNDEFINED)
|
||||
/// Used for the ExifVersion, FlashpixVersion, InteroperabilityVersion and GPSVersionID fields.
|
||||
/// </summary>
|
||||
internal class ExifVersion : ExifProperty
|
||||
{
|
||||
protected string mValue;
|
||||
protected override object _Value { get { return Value; } set { Value = (string)value; } }
|
||||
public new string Value { get { return mValue; } set { mValue = value.Substring(0, 4); } }
|
||||
|
||||
public ExifVersion(ExifTag tag, string value)
|
||||
: base(tag)
|
||||
{
|
||||
if (value.Length > 4)
|
||||
mValue = value.Substring(0, 4);
|
||||
else if (value.Length < 4)
|
||||
mValue = value + new string(' ', 4 - value.Length);
|
||||
else
|
||||
mValue = value;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return mValue;
|
||||
}
|
||||
|
||||
public override ExifInterOperability Interoperability
|
||||
{
|
||||
get
|
||||
{
|
||||
if (mTag == ExifTag.ExifVersion || mTag == ExifTag.FlashpixVersion || mTag == ExifTag.InteroperabilityVersion)
|
||||
return new ExifInterOperability(ExifTagFactory.GetTagID(mTag), 7, 4, Encoding.ASCII.GetBytes(mValue));
|
||||
else
|
||||
{
|
||||
byte[] data = new byte[4];
|
||||
for (int i = 0; i < 4; i++)
|
||||
data[i] = byte.Parse(mValue[0].ToString());
|
||||
return new ExifInterOperability(ExifTagFactory.GetTagID(mTag), 7, 4, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents the location and area of the subject (EXIF Specification: 2xSHORT)
|
||||
/// The coordinate values, width, and height are expressed in relation to the
|
||||
/// upper left as origin, prior to rotation processing as per the Rotation tag.
|
||||
/// </summary>
|
||||
internal class ExifPointSubjectArea : ExifUShortArray
|
||||
{
|
||||
protected new ushort[] Value { get { return mValue; } set { mValue = value; } }
|
||||
public ushort X { get { return mValue[0]; } set { mValue[0] = value; } }
|
||||
public ushort Y { get { return mValue[1]; } set { mValue[1] = value; } }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.AppendFormat("({0:d}, {1:d})", mValue[0], mValue[1]);
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public ExifPointSubjectArea(ExifTag tag, ushort[] value)
|
||||
: base(tag, value)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
public ExifPointSubjectArea(ExifTag tag, ushort x, ushort y)
|
||||
: base(tag, new ushort[] { x, y })
|
||||
{
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents the location and area of the subject (EXIF Specification: 3xSHORT)
|
||||
/// The coordinate values, width, and height are expressed in relation to the
|
||||
/// upper left as origin, prior to rotation processing as per the Rotation tag.
|
||||
/// </summary>
|
||||
internal class ExifCircularSubjectArea : ExifPointSubjectArea
|
||||
{
|
||||
public ushort Diamater { get { return mValue[2]; } set { mValue[2] = value; } }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.AppendFormat("({0:d}, {1:d}) {2:d}", mValue[0], mValue[1], mValue[2]);
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public ExifCircularSubjectArea(ExifTag tag, ushort[] value)
|
||||
: base(tag, value)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
public ExifCircularSubjectArea(ExifTag tag, ushort x, ushort y, ushort d)
|
||||
: base(tag, new ushort[] { x, y, d })
|
||||
{
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents the location and area of the subject (EXIF Specification: 4xSHORT)
|
||||
/// The coordinate values, width, and height are expressed in relation to the
|
||||
/// upper left as origin, prior to rotation processing as per the Rotation tag.
|
||||
/// </summary>
|
||||
internal class ExifRectangularSubjectArea : ExifPointSubjectArea
|
||||
{
|
||||
public ushort Width { get { return mValue[2]; } set { mValue[2] = value; } }
|
||||
public ushort Height { get { return mValue[3]; } set { mValue[3] = value; } }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.AppendFormat("({0:d}, {1:d}) ({2:d} x {3:d})", mValue[0], mValue[1], mValue[2], mValue[3]);
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public ExifRectangularSubjectArea(ExifTag tag, ushort[] value)
|
||||
: base(tag, value)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
public ExifRectangularSubjectArea(ExifTag tag, ushort x, ushort y, ushort w, ushort h)
|
||||
: base(tag, new ushort[] { x, y, w, h })
|
||||
{
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents GPS latitudes and longitudes (EXIF Specification: 3xRATIONAL)
|
||||
/// </summary>
|
||||
internal class GPSLatitudeLongitude : ExifURationalArray
|
||||
{
|
||||
protected new MathEx.UFraction32[] Value { get { return mValue; } set { mValue = value; } }
|
||||
public MathEx.UFraction32 Degrees { get { return mValue[0]; } set { mValue[0] = value; } }
|
||||
public MathEx.UFraction32 Minutes { get { return mValue[1]; } set { mValue[1] = value; } }
|
||||
public MathEx.UFraction32 Seconds { get { return mValue[2]; } set { mValue[2] = value; } }
|
||||
|
||||
public static explicit operator float(GPSLatitudeLongitude obj) { return obj.ToFloat(); }
|
||||
public float ToFloat()
|
||||
{
|
||||
return (float)Degrees + ((float)Minutes) / 60.0f + ((float)Seconds) / 3600.0f;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("{0:F2}°{1:F2}'{2:F2}\"", (float)Degrees, (float)Minutes, (float)Seconds);
|
||||
}
|
||||
|
||||
public GPSLatitudeLongitude(ExifTag tag, MathEx.UFraction32[] value)
|
||||
: base(tag, value)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
public GPSLatitudeLongitude(ExifTag tag, float d, float m, float s)
|
||||
: base(tag, new MathEx.UFraction32[] { new MathEx.UFraction32(d), new MathEx.UFraction32(m), new MathEx.UFraction32(s) })
|
||||
{
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a GPS time stamp as UTC (EXIF Specification: 3xRATIONAL)
|
||||
/// </summary>
|
||||
internal class GPSTimeStamp : ExifURationalArray
|
||||
{
|
||||
protected new MathEx.UFraction32[] Value { get { return mValue; } set { mValue = value; } }
|
||||
public MathEx.UFraction32 Hour { get { return mValue[0]; } set { mValue[0] = value; } }
|
||||
public MathEx.UFraction32 Minute { get { return mValue[1]; } set { mValue[1] = value; } }
|
||||
public MathEx.UFraction32 Second { get { return mValue[2]; } set { mValue[2] = value; } }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("{0:F2}:{1:F2}:{2:F2}\"", (float)Hour, (float)Minute, (float)Second);
|
||||
}
|
||||
|
||||
public GPSTimeStamp(ExifTag tag, MathEx.UFraction32[] value)
|
||||
: base(tag, value)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
public GPSTimeStamp(ExifTag tag, float h, float m, float s)
|
||||
: base(tag, new MathEx.UFraction32[] { new MathEx.UFraction32(h), new MathEx.UFraction32(m), new MathEx.UFraction32(s) })
|
||||
{
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents an ASCII string. (EXIF Specification: BYTE)
|
||||
/// Used by Windows XP.
|
||||
/// </summary>
|
||||
internal class WindowsByteString : ExifProperty
|
||||
{
|
||||
protected string mValue;
|
||||
protected override object _Value { get { return Value; } set { Value = (string)value; } }
|
||||
public new string Value { get { return mValue; } set { mValue = value; } }
|
||||
|
||||
static public implicit operator string(WindowsByteString obj) { return obj.mValue; }
|
||||
|
||||
public override string ToString() { return mValue; }
|
||||
|
||||
public WindowsByteString(ExifTag tag, string value)
|
||||
: base(tag)
|
||||
{
|
||||
mValue = value;
|
||||
}
|
||||
|
||||
public override ExifInterOperability Interoperability
|
||||
{
|
||||
get
|
||||
{
|
||||
byte[] data = Encoding.Unicode.GetBytes(mValue);
|
||||
return new ExifInterOperability(ExifTagFactory.GetTagID(mTag), 1, (uint)data.Length, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,125 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace Umbraco.Web.Media.Exif
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides a custom type descriptor for an ExifFile instance.
|
||||
/// </summary>
|
||||
internal sealed class ExifFileTypeDescriptionProvider : TypeDescriptionProvider
|
||||
{
|
||||
public ExifFileTypeDescriptionProvider()
|
||||
: this(TypeDescriptor.GetProvider(typeof(ImageFile)))
|
||||
{
|
||||
}
|
||||
|
||||
public ExifFileTypeDescriptionProvider(TypeDescriptionProvider parent)
|
||||
: base(parent)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a custom type descriptor for the given type and object.
|
||||
/// </summary>
|
||||
/// <param name="objectType">The type of object for which to retrieve the type descriptor.</param>
|
||||
/// <param name="instance">An instance of the type. Can be null if no instance was passed to the <see cref="T:System.ComponentModel.TypeDescriptor"/>.</param>
|
||||
/// <returns>
|
||||
/// An <see cref="T:System.ComponentModel.ICustomTypeDescriptor"/> that can provide metadata for the type.
|
||||
/// </returns>
|
||||
public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType, object instance)
|
||||
{
|
||||
return new ExifFileTypeDescriptor(base.GetTypeDescriptor(objectType, instance), instance);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Expands ExifProperty objects contained in an ExifFile as separate properties.
|
||||
/// </summary>
|
||||
internal sealed class ExifFileTypeDescriptor : CustomTypeDescriptor
|
||||
{
|
||||
ImageFile owner;
|
||||
|
||||
public ExifFileTypeDescriptor(ICustomTypeDescriptor parent, object instance)
|
||||
: base(parent)
|
||||
{
|
||||
owner = (ImageFile)instance;
|
||||
}
|
||||
public override PropertyDescriptorCollection GetProperties(Attribute[] attributes)
|
||||
{
|
||||
return GetProperties();
|
||||
}
|
||||
/// <summary>
|
||||
/// Returns a collection of property descriptors for the object represented by this type descriptor.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A <see cref="T:System.ComponentModel.PropertyDescriptorCollection"/> containing the property descriptions for the object represented by this type descriptor. The default is <see cref="F:System.ComponentModel.PropertyDescriptorCollection.Empty"/>.
|
||||
/// </returns>
|
||||
public override PropertyDescriptorCollection GetProperties()
|
||||
{
|
||||
// Enumerate the original set of properties and create our new set with it
|
||||
List<PropertyDescriptor> properties = new List<PropertyDescriptor>();
|
||||
|
||||
foreach (ExifProperty prop in owner.Properties)
|
||||
{
|
||||
ExifPropertyDescriptor pd = new ExifPropertyDescriptor(prop);
|
||||
properties.Add(pd);
|
||||
}
|
||||
|
||||
// Finally return the list
|
||||
return new PropertyDescriptorCollection(properties.ToArray(), true);
|
||||
}
|
||||
}
|
||||
internal sealed class ExifPropertyDescriptor : PropertyDescriptor
|
||||
{
|
||||
object originalValue;
|
||||
ExifProperty linkedProperty;
|
||||
|
||||
public ExifPropertyDescriptor(ExifProperty property)
|
||||
: base(property.Name, new Attribute[] { new BrowsableAttribute(true) })
|
||||
{
|
||||
linkedProperty = property;
|
||||
originalValue = property.Value;
|
||||
}
|
||||
|
||||
public override bool CanResetValue(object component)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public override Type ComponentType
|
||||
{
|
||||
get { return typeof(JPEGFile); }
|
||||
}
|
||||
|
||||
public override object GetValue(object component)
|
||||
{
|
||||
return linkedProperty.Value;
|
||||
}
|
||||
|
||||
public override bool IsReadOnly
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
public override Type PropertyType
|
||||
{
|
||||
get { return linkedProperty.Value.GetType(); }
|
||||
}
|
||||
|
||||
public override void ResetValue(object component)
|
||||
{
|
||||
linkedProperty.Value = originalValue;
|
||||
}
|
||||
|
||||
public override void SetValue(object component, object value)
|
||||
{
|
||||
linkedProperty.Value = value;
|
||||
}
|
||||
|
||||
public override bool ShouldSerializeValue(object component)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
namespace Umbraco.Web.Media.Exif
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents interoperability data for an exif tag in the platform byte order.
|
||||
/// </summary>
|
||||
internal struct ExifInterOperability
|
||||
{
|
||||
private ushort mTagID;
|
||||
private ushort mTypeID;
|
||||
private uint mCount;
|
||||
private byte[] mData;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the tag ID defined in the Exif standard.
|
||||
/// </summary>
|
||||
public ushort TagID { get { return mTagID; } }
|
||||
/// <summary>
|
||||
/// Gets the type code defined in the Exif standard.
|
||||
/// <list type="bullet">
|
||||
/// <item>1 = BYTE (byte)</item>
|
||||
/// <item>2 = ASCII (byte array)</item>
|
||||
/// <item>3 = SHORT (ushort)</item>
|
||||
/// <item>4 = LONG (uint)</item>
|
||||
/// <item>5 = RATIONAL (2 x uint: numerator, denominator)</item>
|
||||
/// <item>6 = BYTE (sbyte)</item>
|
||||
/// <item>7 = UNDEFINED (byte array)</item>
|
||||
/// <item>8 = SSHORT (short)</item>
|
||||
/// <item>9 = SLONG (int)</item>
|
||||
/// <item>10 = SRATIONAL (2 x int: numerator, denominator)</item>
|
||||
/// <item>11 = FLOAT (float)</item>
|
||||
/// <item>12 = DOUBLE (double)</item>
|
||||
/// </list>
|
||||
/// </summary>
|
||||
public ushort TypeID { get { return mTypeID; } }
|
||||
/// <summary>
|
||||
/// Gets the byte count or number of components.
|
||||
/// </summary>
|
||||
public uint Count { get { return mCount; } }
|
||||
/// <summary>
|
||||
/// Gets the field value as an array of bytes.
|
||||
/// </summary>
|
||||
public byte[] Data { get { return mData; } }
|
||||
/// <summary>
|
||||
/// Returns the string representation of this instance.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("Tag: {0}, Type: {1}, Count: {2}, Data Length: {3}", mTagID, mTypeID, mCount, mData.Length);
|
||||
}
|
||||
|
||||
public ExifInterOperability(ushort tagid, ushort typeid, uint count, byte[] data)
|
||||
{
|
||||
mTagID = tagid;
|
||||
mTypeID = typeid;
|
||||
mCount = count;
|
||||
mData = data;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,578 +0,0 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
|
||||
namespace Umbraco.Web.Media.Exif
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the abstract base class for an Exif property.
|
||||
/// </summary>
|
||||
internal abstract class ExifProperty
|
||||
{
|
||||
protected ExifTag mTag;
|
||||
protected IFD mIFD;
|
||||
protected string mName;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Exif tag associated with this property.
|
||||
/// </summary>
|
||||
public ExifTag Tag { get { return mTag; } }
|
||||
/// <summary>
|
||||
/// Gets the IFD section contaning this property.
|
||||
/// </summary>
|
||||
public IFD IFD { get { return mIFD; } }
|
||||
/// <summary>
|
||||
/// Gets or sets the name of this property.
|
||||
/// </summary>
|
||||
public string Name
|
||||
{
|
||||
get
|
||||
{
|
||||
if (string.IsNullOrEmpty(mName))
|
||||
return ExifTagFactory.GetTagName(mTag);
|
||||
else
|
||||
return mName;
|
||||
}
|
||||
set
|
||||
{
|
||||
mName = value;
|
||||
}
|
||||
}
|
||||
protected abstract object _Value { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the value of this property.
|
||||
/// </summary>
|
||||
public object Value { get { return _Value; } set { _Value = value; } }
|
||||
/// <summary>
|
||||
/// Gets interoperability data for this property.
|
||||
/// </summary>
|
||||
public abstract ExifInterOperability Interoperability { get; }
|
||||
|
||||
public ExifProperty(ExifTag tag)
|
||||
{
|
||||
mTag = tag;
|
||||
mIFD = ExifTagFactory.GetTagIFD(tag);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents an 8-bit unsigned integer. (EXIF Specification: BYTE)
|
||||
/// </summary>
|
||||
internal class ExifByte : ExifProperty
|
||||
{
|
||||
protected byte mValue;
|
||||
protected override object _Value { get { return Value; } set { Value = Convert.ToByte(value); } }
|
||||
public new byte Value { get { return mValue; } set { mValue = value; } }
|
||||
|
||||
static public implicit operator byte(ExifByte obj) { return obj.mValue; }
|
||||
|
||||
public override string ToString() { return mValue.ToString(); }
|
||||
|
||||
public ExifByte(ExifTag tag, byte value)
|
||||
: base(tag)
|
||||
{
|
||||
mValue = value;
|
||||
}
|
||||
|
||||
public override ExifInterOperability Interoperability
|
||||
{
|
||||
get
|
||||
{
|
||||
return new ExifInterOperability(ExifTagFactory.GetTagID(mTag), 1, 1, new byte[] { mValue });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents an array of 8-bit unsigned integers. (EXIF Specification: BYTE with count > 1)
|
||||
/// </summary>
|
||||
internal class ExifByteArray : ExifProperty
|
||||
{
|
||||
protected byte[] mValue;
|
||||
protected override object _Value { get { return Value; } set { Value = (byte[])value; } }
|
||||
public new byte[] Value { get { return mValue; } set { mValue = value; } }
|
||||
|
||||
static public implicit operator byte[](ExifByteArray obj) { return obj.mValue; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.Append('[');
|
||||
foreach (byte b in mValue)
|
||||
{
|
||||
sb.Append(b);
|
||||
sb.Append(' ');
|
||||
}
|
||||
sb.Remove(sb.Length - 1, 1);
|
||||
sb.Append(']');
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public ExifByteArray(ExifTag tag, byte[] value)
|
||||
: base(tag)
|
||||
{
|
||||
mValue = value;
|
||||
}
|
||||
|
||||
public override ExifInterOperability Interoperability
|
||||
{
|
||||
get
|
||||
{
|
||||
return new ExifInterOperability(ExifTagFactory.GetTagID(mTag), 1, (uint)mValue.Length, mValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents an ASCII string. (EXIF Specification: ASCII)
|
||||
/// </summary>
|
||||
internal class ExifAscii : ExifProperty
|
||||
{
|
||||
protected string mValue;
|
||||
protected override object _Value { get { return Value; } set { Value = (string)value; } }
|
||||
public new string Value { get { return mValue; } set { mValue = value; } }
|
||||
|
||||
public Encoding Encoding { get; private set; }
|
||||
|
||||
static public implicit operator string(ExifAscii obj) { return obj.mValue; }
|
||||
|
||||
public override string ToString() { return mValue; }
|
||||
|
||||
public ExifAscii(ExifTag tag, string value, Encoding encoding)
|
||||
: base(tag)
|
||||
{
|
||||
mValue = value;
|
||||
Encoding = encoding;
|
||||
}
|
||||
|
||||
public override ExifInterOperability Interoperability
|
||||
{
|
||||
get
|
||||
{
|
||||
return new ExifInterOperability(ExifTagFactory.GetTagID(mTag), 2, (uint)mValue.Length + 1, ExifBitConverter.GetBytes(mValue, true, Encoding));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a 16-bit unsigned integer. (EXIF Specification: SHORT)
|
||||
/// </summary>
|
||||
internal class ExifUShort : ExifProperty
|
||||
{
|
||||
protected ushort mValue;
|
||||
protected override object _Value { get { return Value; } set { Value = Convert.ToUInt16(value); } }
|
||||
public new ushort Value { get { return mValue; } set { mValue = value; } }
|
||||
|
||||
static public implicit operator ushort(ExifUShort obj) { return obj.mValue; }
|
||||
|
||||
public override string ToString() { return mValue.ToString(); }
|
||||
|
||||
public ExifUShort(ExifTag tag, ushort value)
|
||||
: base(tag)
|
||||
{
|
||||
mValue = value;
|
||||
}
|
||||
|
||||
public override ExifInterOperability Interoperability
|
||||
{
|
||||
get
|
||||
{
|
||||
return new ExifInterOperability(ExifTagFactory.GetTagID(mTag), 3, 1, ExifBitConverter.GetBytes(mValue, BitConverterEx.SystemByteOrder, BitConverterEx.SystemByteOrder));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents an array of 16-bit unsigned integers.
|
||||
/// (EXIF Specification: SHORT with count > 1)
|
||||
/// </summary>
|
||||
internal class ExifUShortArray : ExifProperty
|
||||
{
|
||||
protected ushort[] mValue;
|
||||
protected override object _Value { get { return Value; } set { Value = (ushort[])value; } }
|
||||
public new ushort[] Value { get { return mValue; } set { mValue = value; } }
|
||||
|
||||
static public implicit operator ushort[](ExifUShortArray obj) { return obj.mValue; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.Append('[');
|
||||
foreach (ushort b in mValue)
|
||||
{
|
||||
sb.Append(b);
|
||||
sb.Append(' ');
|
||||
}
|
||||
sb.Remove(sb.Length - 1, 1);
|
||||
sb.Append(']');
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public ExifUShortArray(ExifTag tag, ushort[] value)
|
||||
: base(tag)
|
||||
{
|
||||
mValue = value;
|
||||
}
|
||||
|
||||
public override ExifInterOperability Interoperability
|
||||
{
|
||||
get
|
||||
{
|
||||
return new ExifInterOperability(ExifTagFactory.GetTagID(mTag), 3, (uint)mValue.Length, ExifBitConverter.GetBytes(mValue, BitConverterEx.SystemByteOrder));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a 32-bit unsigned integer. (EXIF Specification: LONG)
|
||||
/// </summary>
|
||||
internal class ExifUInt : ExifProperty
|
||||
{
|
||||
protected uint mValue;
|
||||
protected override object _Value { get { return Value; } set { Value = Convert.ToUInt32(value); } }
|
||||
public new uint Value { get { return mValue; } set { mValue = value; } }
|
||||
|
||||
static public implicit operator uint(ExifUInt obj) { return obj.mValue; }
|
||||
|
||||
public override string ToString() { return mValue.ToString(); }
|
||||
|
||||
public ExifUInt(ExifTag tag, uint value)
|
||||
: base(tag)
|
||||
{
|
||||
mValue = value;
|
||||
}
|
||||
|
||||
public override ExifInterOperability Interoperability
|
||||
{
|
||||
get
|
||||
{
|
||||
return new ExifInterOperability(ExifTagFactory.GetTagID(mTag), 4, 1, ExifBitConverter.GetBytes(mValue, BitConverterEx.SystemByteOrder, BitConverterEx.SystemByteOrder));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents an array of 16-bit unsigned integers.
|
||||
/// (EXIF Specification: LONG with count > 1)
|
||||
/// </summary>
|
||||
internal class ExifUIntArray : ExifProperty
|
||||
{
|
||||
protected uint[] mValue;
|
||||
protected override object _Value { get { return Value; } set { Value = (uint[])value; } }
|
||||
public new uint[] Value { get { return mValue; } set { mValue = value; } }
|
||||
|
||||
static public implicit operator uint[](ExifUIntArray obj) { return obj.mValue; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.Append('[');
|
||||
foreach (uint b in mValue)
|
||||
{
|
||||
sb.Append(b);
|
||||
sb.Append(' ');
|
||||
}
|
||||
sb.Remove(sb.Length - 1, 1);
|
||||
sb.Append(']');
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public ExifUIntArray(ExifTag tag, uint[] value)
|
||||
: base(tag)
|
||||
{
|
||||
mValue = value;
|
||||
}
|
||||
|
||||
public override ExifInterOperability Interoperability
|
||||
{
|
||||
get
|
||||
{
|
||||
return new ExifInterOperability(ExifTagFactory.GetTagID(mTag), 3, (uint)mValue.Length, ExifBitConverter.GetBytes(mValue, BitConverterEx.SystemByteOrder));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a rational number defined with a 32-bit unsigned numerator
|
||||
/// and denominator. (EXIF Specification: RATIONAL)
|
||||
/// </summary>
|
||||
internal class ExifURational : ExifProperty
|
||||
{
|
||||
protected MathEx.UFraction32 mValue;
|
||||
protected override object _Value { get { return Value; } set { Value = (MathEx.UFraction32)value; } }
|
||||
public new MathEx.UFraction32 Value { get { return mValue; } set { mValue = value; } }
|
||||
|
||||
public override string ToString() { return mValue.ToString(); }
|
||||
public float ToFloat() { return (float)mValue; }
|
||||
|
||||
static public explicit operator float(ExifURational obj) { return (float)obj.mValue; }
|
||||
|
||||
public uint[] ToArray()
|
||||
{
|
||||
return new uint[] { mValue.Numerator, mValue.Denominator };
|
||||
}
|
||||
|
||||
public ExifURational(ExifTag tag, uint numerator, uint denominator)
|
||||
: base(tag)
|
||||
{
|
||||
mValue = new MathEx.UFraction32(numerator, denominator);
|
||||
}
|
||||
|
||||
public ExifURational(ExifTag tag, MathEx.UFraction32 value)
|
||||
: base(tag)
|
||||
{
|
||||
mValue = value;
|
||||
}
|
||||
|
||||
public override ExifInterOperability Interoperability
|
||||
{
|
||||
get
|
||||
{
|
||||
return new ExifInterOperability(ExifTagFactory.GetTagID(mTag), 5, 1, ExifBitConverter.GetBytes(mValue, BitConverterEx.SystemByteOrder));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents an array of unsigned rational numbers.
|
||||
/// (EXIF Specification: RATIONAL with count > 1)
|
||||
/// </summary>
|
||||
internal class ExifURationalArray : ExifProperty
|
||||
{
|
||||
protected MathEx.UFraction32[] mValue;
|
||||
protected override object _Value { get { return Value; } set { Value = (MathEx.UFraction32[])value; } }
|
||||
public new MathEx.UFraction32[] Value { get { return mValue; } set { mValue = value; } }
|
||||
|
||||
static public explicit operator float[](ExifURationalArray obj)
|
||||
{
|
||||
float[] result = new float[obj.mValue.Length];
|
||||
for (int i = 0; i < obj.mValue.Length; i++)
|
||||
result[i] = (float)obj.mValue[i];
|
||||
return result;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.Append('[');
|
||||
foreach (MathEx.UFraction32 b in mValue)
|
||||
{
|
||||
sb.Append(b.ToString());
|
||||
sb.Append(' ');
|
||||
}
|
||||
sb.Remove(sb.Length - 1, 1);
|
||||
sb.Append(']');
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public ExifURationalArray(ExifTag tag, MathEx.UFraction32[] value)
|
||||
: base(tag)
|
||||
{
|
||||
mValue = value;
|
||||
}
|
||||
|
||||
public override ExifInterOperability Interoperability
|
||||
{
|
||||
get
|
||||
{
|
||||
return new ExifInterOperability(ExifTagFactory.GetTagID(mTag), 5, (uint)mValue.Length, ExifBitConverter.GetBytes(mValue, BitConverterEx.SystemByteOrder));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a byte array that can take any value. (EXIF Specification: UNDEFINED)
|
||||
/// </summary>
|
||||
internal class ExifUndefined : ExifProperty
|
||||
{
|
||||
protected byte[] mValue;
|
||||
protected override object _Value { get { return Value; } set { Value = (byte[])value; } }
|
||||
public new byte[] Value { get { return mValue; } set { mValue = value; } }
|
||||
|
||||
static public implicit operator byte[](ExifUndefined obj) { return obj.mValue; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.Append('[');
|
||||
foreach (byte b in mValue)
|
||||
{
|
||||
sb.Append(b);
|
||||
sb.Append(' ');
|
||||
}
|
||||
sb.Remove(sb.Length - 1, 1);
|
||||
sb.Append(']');
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public ExifUndefined(ExifTag tag, byte[] value)
|
||||
: base(tag)
|
||||
{
|
||||
mValue = value;
|
||||
}
|
||||
|
||||
public override ExifInterOperability Interoperability
|
||||
{
|
||||
get
|
||||
{
|
||||
return new ExifInterOperability(ExifTagFactory.GetTagID(mTag), 7, (uint)mValue.Length, mValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a 32-bit signed integer. (EXIF Specification: SLONG)
|
||||
/// </summary>
|
||||
internal class ExifSInt : ExifProperty
|
||||
{
|
||||
protected int mValue;
|
||||
protected override object _Value { get { return Value; } set { Value = Convert.ToInt32(value); } }
|
||||
public new int Value { get { return mValue; } set { mValue = value; } }
|
||||
|
||||
public override string ToString() { return mValue.ToString(); }
|
||||
|
||||
static public implicit operator int(ExifSInt obj) { return obj.mValue; }
|
||||
|
||||
public ExifSInt(ExifTag tag, int value)
|
||||
: base(tag)
|
||||
{
|
||||
mValue = value;
|
||||
}
|
||||
|
||||
public override ExifInterOperability Interoperability
|
||||
{
|
||||
get
|
||||
{
|
||||
return new ExifInterOperability(ExifTagFactory.GetTagID(mTag), 9, 1, ExifBitConverter.GetBytes(mValue, BitConverterEx.SystemByteOrder, BitConverterEx.SystemByteOrder));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents an array of 32-bit signed integers.
|
||||
/// (EXIF Specification: SLONG with count > 1)
|
||||
/// </summary>
|
||||
internal class ExifSIntArray : ExifProperty
|
||||
{
|
||||
protected int[] mValue;
|
||||
protected override object _Value { get { return Value; } set { Value = (int[])value; } }
|
||||
public new int[] Value { get { return mValue; } set { mValue = value; } }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.Append('[');
|
||||
foreach (int b in mValue)
|
||||
{
|
||||
sb.Append(b);
|
||||
sb.Append(' ');
|
||||
}
|
||||
sb.Remove(sb.Length - 1, 1);
|
||||
sb.Append(']');
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
static public implicit operator int[](ExifSIntArray obj) { return obj.mValue; }
|
||||
|
||||
public ExifSIntArray(ExifTag tag, int[] value)
|
||||
: base(tag)
|
||||
{
|
||||
mValue = value;
|
||||
}
|
||||
|
||||
public override ExifInterOperability Interoperability
|
||||
{
|
||||
get
|
||||
{
|
||||
return new ExifInterOperability(ExifTagFactory.GetTagID(mTag), 9, (uint)mValue.Length, ExifBitConverter.GetBytes(mValue, BitConverterEx.SystemByteOrder));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a rational number defined with a 32-bit signed numerator
|
||||
/// and denominator. (EXIF Specification: SRATIONAL)
|
||||
/// </summary>
|
||||
internal class ExifSRational : ExifProperty
|
||||
{
|
||||
protected MathEx.Fraction32 mValue;
|
||||
protected override object _Value { get { return Value; } set { Value = (MathEx.Fraction32)value; } }
|
||||
public new MathEx.Fraction32 Value { get { return mValue; } set { mValue = value; } }
|
||||
|
||||
public override string ToString() { return mValue.ToString(); }
|
||||
public float ToFloat() { return (float)mValue; }
|
||||
|
||||
static public explicit operator float(ExifSRational obj) { return (float)obj.mValue; }
|
||||
|
||||
public int[] ToArray()
|
||||
{
|
||||
return new int[] { mValue.Numerator, mValue.Denominator };
|
||||
}
|
||||
|
||||
public ExifSRational(ExifTag tag, int numerator, int denominator)
|
||||
: base(tag)
|
||||
{
|
||||
mValue = new MathEx.Fraction32(numerator, denominator);
|
||||
}
|
||||
|
||||
public ExifSRational(ExifTag tag, MathEx.Fraction32 value)
|
||||
: base(tag)
|
||||
{
|
||||
mValue = value;
|
||||
}
|
||||
|
||||
public override ExifInterOperability Interoperability
|
||||
{
|
||||
get
|
||||
{
|
||||
return new ExifInterOperability(ExifTagFactory.GetTagID(mTag), 10, 1, ExifBitConverter.GetBytes(mValue, BitConverterEx.SystemByteOrder));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents an array of signed rational numbers.
|
||||
/// (EXIF Specification: SRATIONAL with count > 1)
|
||||
/// </summary>
|
||||
internal class ExifSRationalArray : ExifProperty
|
||||
{
|
||||
protected MathEx.Fraction32[] mValue;
|
||||
protected override object _Value { get { return Value; } set { Value = (MathEx.Fraction32[])value; } }
|
||||
public new MathEx.Fraction32[] Value { get { return mValue; } set { mValue = value; } }
|
||||
|
||||
static public explicit operator float[](ExifSRationalArray obj)
|
||||
{
|
||||
float[] result = new float[obj.mValue.Length];
|
||||
for (int i = 0; i < obj.mValue.Length; i++)
|
||||
result[i] = (float)obj.mValue[i];
|
||||
return result;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.Append('[');
|
||||
foreach (MathEx.Fraction32 b in mValue)
|
||||
{
|
||||
sb.Append(b.ToString());
|
||||
sb.Append(' ');
|
||||
}
|
||||
sb.Remove(sb.Length - 1, 1);
|
||||
sb.Append(']');
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public ExifSRationalArray(ExifTag tag, MathEx.Fraction32[] value)
|
||||
: base(tag)
|
||||
{
|
||||
mValue = value;
|
||||
}
|
||||
|
||||
public override ExifInterOperability Interoperability
|
||||
{
|
||||
get
|
||||
{
|
||||
return new ExifInterOperability(ExifTagFactory.GetTagID(mTag), 10, (uint)mValue.Length, ExifBitConverter.GetBytes(mValue, BitConverterEx.SystemByteOrder));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,380 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Umbraco.Web.Media.Exif
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a collection of <see cref="ExifProperty"/> objects.
|
||||
/// </summary>
|
||||
internal class ExifPropertyCollection : IDictionary<ExifTag, ExifProperty>
|
||||
{
|
||||
#region Member Variables
|
||||
private ImageFile parent;
|
||||
private Dictionary<ExifTag, ExifProperty> items;
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
internal ExifPropertyCollection (ImageFile parentFile)
|
||||
{
|
||||
parent = parentFile;
|
||||
items = new Dictionary<ExifTag, ExifProperty> ();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
/// <summary>
|
||||
/// Gets the number of elements contained in the collection.
|
||||
/// </summary>
|
||||
public int Count {
|
||||
get { return items.Count; }
|
||||
}
|
||||
/// <summary>
|
||||
/// Gets a collection containing the keys in this collection.
|
||||
/// </summary>
|
||||
public ICollection<ExifTag> Keys {
|
||||
get { return items.Keys; }
|
||||
}
|
||||
/// <summary>
|
||||
/// Gets a collection containing the values in this collection.
|
||||
/// </summary>
|
||||
public ICollection<ExifProperty> Values {
|
||||
get { return items.Values; }
|
||||
}
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="ExifProperty"/> with the specified key.
|
||||
/// </summary>
|
||||
public ExifProperty this[ExifTag key] {
|
||||
get { return items[key]; }
|
||||
set {
|
||||
if (items.ContainsKey (key))
|
||||
items.Remove (key);
|
||||
items.Add (key, value);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region ExifProperty Collection Setters
|
||||
/// <summary>
|
||||
/// Sets the <see cref="ExifProperty"/> with the specified key.
|
||||
/// </summary>
|
||||
/// <param name="key">The tag to set.</param>
|
||||
/// <param name="value">The value of tag.</param>
|
||||
public void Set (ExifTag key, byte value)
|
||||
{
|
||||
if (items.ContainsKey (key))
|
||||
items.Remove (key);
|
||||
items.Add (key, new ExifByte (key, value));
|
||||
}
|
||||
/// <summary>
|
||||
/// Sets the <see cref="ExifProperty"/> with the specified key.
|
||||
/// </summary>
|
||||
/// <param name="key">The tag to set.</param>
|
||||
/// <param name="value">The value of tag.</param>
|
||||
public void Set (ExifTag key, string value)
|
||||
{
|
||||
if (items.ContainsKey (key))
|
||||
items.Remove (key);
|
||||
if (key == ExifTag.WindowsTitle || key == ExifTag.WindowsComment || key == ExifTag.WindowsAuthor || key == ExifTag.WindowsKeywords || key == ExifTag.WindowsSubject) {
|
||||
items.Add (key, new WindowsByteString (key, value));
|
||||
} else {
|
||||
items.Add (key, new ExifAscii (key, value, parent.Encoding));
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Sets the <see cref="ExifProperty"/> with the specified key.
|
||||
/// </summary>
|
||||
/// <param name="key">The tag to set.</param>
|
||||
/// <param name="value">The value of tag.</param>
|
||||
public void Set (ExifTag key, ushort value)
|
||||
{
|
||||
if (items.ContainsKey (key))
|
||||
items.Remove (key);
|
||||
items.Add (key, new ExifUShort (key, value));
|
||||
}
|
||||
/// <summary>
|
||||
/// Sets the <see cref="ExifProperty"/> with the specified key.
|
||||
/// </summary>
|
||||
/// <param name="key">The tag to set.</param>
|
||||
/// <param name="value">The value of tag.</param>
|
||||
public void Set (ExifTag key, int value)
|
||||
{
|
||||
if (items.ContainsKey (key))
|
||||
items.Remove (key);
|
||||
items.Add (key, new ExifSInt (key, value));
|
||||
}
|
||||
/// <summary>
|
||||
/// Sets the <see cref="ExifProperty"/> with the specified key.
|
||||
/// </summary>
|
||||
/// <param name="key">The tag to set.</param>
|
||||
/// <param name="value">The value of tag.</param>
|
||||
public void Set (ExifTag key, uint value)
|
||||
{
|
||||
if (items.ContainsKey (key))
|
||||
items.Remove (key);
|
||||
items.Add (key, new ExifUInt (key, value));
|
||||
}
|
||||
/// <summary>
|
||||
/// Sets the <see cref="ExifProperty"/> with the specified key.
|
||||
/// </summary>
|
||||
/// <param name="key">The tag to set.</param>
|
||||
/// <param name="value">The value of tag.</param>
|
||||
public void Set (ExifTag key, float value)
|
||||
{
|
||||
if (items.ContainsKey (key))
|
||||
items.Remove (key);
|
||||
items.Add (key, new ExifURational (key, new MathEx.UFraction32 (value)));
|
||||
}
|
||||
/// <summary>
|
||||
/// Sets the <see cref="ExifProperty"/> with the specified key.
|
||||
/// </summary>
|
||||
/// <param name="key">The tag to set.</param>
|
||||
/// <param name="value">The value of tag.</param>
|
||||
public void Set (ExifTag key, double value)
|
||||
{
|
||||
if (items.ContainsKey (key))
|
||||
items.Remove (key);
|
||||
items.Add (key, new ExifURational (key, new MathEx.UFraction32 (value)));
|
||||
}
|
||||
/// <summary>
|
||||
/// Sets the <see cref="ExifProperty"/> with the specified key.
|
||||
/// </summary>
|
||||
/// <param name="key">The tag to set.</param>
|
||||
/// <param name="value">The value of tag.</param>
|
||||
public void Set (ExifTag key, object value)
|
||||
{
|
||||
Type type = value.GetType ();
|
||||
if (type.IsEnum) {
|
||||
Type etype = typeof(ExifEnumProperty<>).MakeGenericType (new Type[] { type });
|
||||
object prop = Activator.CreateInstance (etype, new object[] { key, value });
|
||||
if (items.ContainsKey (key))
|
||||
items.Remove (key);
|
||||
items.Add (key, (ExifProperty)prop);
|
||||
} else
|
||||
throw new ArgumentException ("No exif property exists for this tag.", "value");
|
||||
}
|
||||
/// <summary>
|
||||
/// Sets the <see cref="ExifProperty"/> with the specified key.
|
||||
/// </summary>
|
||||
/// <param name="key">The tag to set.</param>
|
||||
/// <param name="value">The value of tag.</param>
|
||||
/// <param name="encoding">String encoding.</param>
|
||||
public void Set (ExifTag key, string value, Encoding encoding)
|
||||
{
|
||||
if (items.ContainsKey (key))
|
||||
items.Remove (key);
|
||||
items.Add (key, new ExifEncodedString (key, value, encoding));
|
||||
}
|
||||
/// <summary>
|
||||
/// Sets the <see cref="ExifProperty"/> with the specified key.
|
||||
/// </summary>
|
||||
/// <param name="key">The tag to set.</param>
|
||||
/// <param name="value">The value of tag.</param>
|
||||
public void Set (ExifTag key, DateTime value)
|
||||
{
|
||||
if (items.ContainsKey (key))
|
||||
items.Remove (key);
|
||||
items.Add (key, new ExifDateTime (key, value));
|
||||
}
|
||||
/// <summary>
|
||||
/// Sets the <see cref="ExifProperty"/> with the specified key.
|
||||
/// </summary>
|
||||
/// <param name="key">The tag to set.</param>
|
||||
/// <param name="d">Angular degrees (or clock hours for a timestamp).</param>
|
||||
/// <param name="m">Angular minutes (or clock minutes for a timestamp).</param>
|
||||
/// <param name="s">Angular seconds (or clock seconds for a timestamp).</param>
|
||||
public void Set (ExifTag key, float d, float m, float s)
|
||||
{
|
||||
if (items.ContainsKey (key))
|
||||
items.Remove (key);
|
||||
items.Add (key, new ExifURationalArray (key, new MathEx.UFraction32[] { new MathEx.UFraction32 (d), new MathEx.UFraction32 (m), new MathEx.UFraction32 (s) }));
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Instance Methods
|
||||
/// <summary>
|
||||
/// Adds the specified item to the collection.
|
||||
/// </summary>
|
||||
/// <param name="item">The <see cref="ExifProperty"/> to add to the collection.</param>
|
||||
public void Add (ExifProperty item)
|
||||
{
|
||||
ExifProperty oldItem = null;
|
||||
if (items.TryGetValue (item.Tag, out oldItem))
|
||||
items[item.Tag] = item;
|
||||
else
|
||||
items.Add (item.Tag, item);
|
||||
}
|
||||
/// <summary>
|
||||
/// Removes all items from the collection.
|
||||
/// </summary>
|
||||
public void Clear ()
|
||||
{
|
||||
items.Clear ();
|
||||
}
|
||||
/// <summary>
|
||||
/// Determines whether the collection contains an element with the specified key.
|
||||
/// </summary>
|
||||
/// <param name="key">The key to locate in the collection.</param>
|
||||
/// <returns>
|
||||
/// true if the collection contains an element with the key; otherwise, false.
|
||||
/// </returns>
|
||||
/// <exception cref="T:System.ArgumentNullException">
|
||||
/// <paramref name="key"/> is null.</exception>
|
||||
public bool ContainsKey (ExifTag key)
|
||||
{
|
||||
return items.ContainsKey (key);
|
||||
}
|
||||
/// <summary>
|
||||
/// Removes the element with the specified key from the collection.
|
||||
/// </summary>
|
||||
/// <param name="key">The key of the element to remove.</param>
|
||||
/// <returns>
|
||||
/// true if the element is successfully removed; otherwise, false. This method also returns false if <paramref name="key"/> was not found in the original collection.
|
||||
/// </returns>
|
||||
/// <exception cref="T:System.ArgumentNullException">
|
||||
/// <paramref name="key"/> is null.</exception>
|
||||
public bool Remove (ExifTag key)
|
||||
{
|
||||
return items.Remove (key);
|
||||
}
|
||||
/// <summary>
|
||||
/// Removes all items with the given IFD from the collection.
|
||||
/// </summary>
|
||||
/// <param name="ifd">The IFD section to remove.</param>
|
||||
public void Remove (IFD ifd)
|
||||
{
|
||||
List<ExifTag> toRemove = new List<ExifTag> ();
|
||||
foreach (KeyValuePair<ExifTag, ExifProperty> item in items) {
|
||||
if (item.Value.IFD == ifd)
|
||||
toRemove.Add (item.Key);
|
||||
}
|
||||
foreach (ExifTag tag in toRemove)
|
||||
items.Remove (tag);
|
||||
}
|
||||
/// <summary>
|
||||
/// Gets the value associated with the specified key.
|
||||
/// </summary>
|
||||
/// <param name="key">The key whose value to get.</param>
|
||||
/// <param name="value">When this method returns, the value associated with the specified key, if the key is found; otherwise, the default value for the type of the <paramref name="value"/> parameter. This parameter is passed uninitialized.</param>
|
||||
/// <returns>
|
||||
/// true if the collection contains an element with the specified key; otherwise, false.
|
||||
/// </returns>
|
||||
/// <exception cref="T:System.ArgumentNullException">
|
||||
/// <paramref name="key"/> is null.</exception>
|
||||
public bool TryGetValue (ExifTag key, out ExifProperty value)
|
||||
{
|
||||
return items.TryGetValue (key, out value);
|
||||
}
|
||||
/// <summary>
|
||||
/// Returns an enumerator that iterates through a collection.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// An <see cref="T:System.Collections.IEnumerator"/> object that can be used to iterate through the collection.
|
||||
/// </returns>
|
||||
public IEnumerator<ExifProperty> GetEnumerator ()
|
||||
{
|
||||
return Values.GetEnumerator ();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Hidden Interface
|
||||
/// <summary>
|
||||
/// Adds an element with the provided key and value to the <see cref="T:System.Collections.Generic.IDictionary`2"/>.
|
||||
/// </summary>
|
||||
/// <param name="key">The object to use as the key of the element to add.</param>
|
||||
/// <param name="value">The object to use as the value of the element to add.</param>
|
||||
/// <exception cref="T:System.ArgumentNullException">
|
||||
/// <paramref name="key"/> is null.</exception>
|
||||
/// <exception cref="T:System.ArgumentException">An element with the same key already exists in the <see cref="T:System.Collections.Generic.IDictionary`2"/>.</exception>
|
||||
/// <exception cref="T:System.NotSupportedException">The <see cref="T:System.Collections.Generic.IDictionary`2"/> is read-only.</exception>
|
||||
void IDictionary<ExifTag, ExifProperty>.Add (ExifTag key, ExifProperty value)
|
||||
{
|
||||
Add (value);
|
||||
}
|
||||
/// <summary>
|
||||
/// Adds an item to the <see cref="T:System.Collections.Generic.ICollection`1"/>.
|
||||
/// </summary>
|
||||
/// <param name="item">The object to add to the <see cref="T:System.Collections.Generic.ICollection`1"/>.</param>
|
||||
/// <exception cref="T:System.NotSupportedException">The <see cref="T:System.Collections.Generic.ICollection`1"/> is read-only.</exception>
|
||||
void ICollection<KeyValuePair<ExifTag, ExifProperty>>.Add (KeyValuePair<ExifTag, ExifProperty> item)
|
||||
{
|
||||
Add (item.Value);
|
||||
}
|
||||
bool ICollection<KeyValuePair<ExifTag, ExifProperty>>.Contains (KeyValuePair<ExifTag, ExifProperty> item)
|
||||
{
|
||||
throw new NotSupportedException ();
|
||||
}
|
||||
/// <summary>
|
||||
/// Copies the elements of the <see cref="T:System.Collections.Generic.ICollection`1"/> to an <see cref="T:System.Array"/>, starting at a particular <see cref="T:System.Array"/> index.
|
||||
/// </summary>
|
||||
/// <param name="array">The one-dimensional <see cref="T:System.Array"/> that is the destination of the elements copied from <see cref="T:System.Collections.Generic.ICollection`1"/>. The <see cref="T:System.Array"/> must have zero-based indexing.</param>
|
||||
/// <param name="arrayIndex">The zero-based index in <paramref name="array"/> at which copying begins.</param>
|
||||
/// <exception cref="T:System.ArgumentNullException">
|
||||
/// <paramref name="array"/> is null.</exception>
|
||||
/// <exception cref="T:System.ArgumentOutOfRangeException">
|
||||
/// <paramref name="arrayIndex"/> is less than 0.</exception>
|
||||
/// <exception cref="T:System.ArgumentException">
|
||||
/// <paramref name="array"/> is multidimensional.-or-<paramref name="arrayIndex"/> is equal to or greater than the length of <paramref name="array"/>.-or-The number of elements in the source <see cref="T:System.Collections.Generic.ICollection`1"/> is greater than the available space from <paramref name="arrayIndex"/> to the end of the destination <paramref name="array"/>.-or-Type <paramref name="T"/> cannot be cast automatically to the type of the destination <paramref name="array"/>.</exception>
|
||||
void ICollection<KeyValuePair<ExifTag, ExifProperty>>.CopyTo (KeyValuePair<ExifTag, ExifProperty>[] array, int arrayIndex)
|
||||
{
|
||||
if (array == null)
|
||||
throw new ArgumentNullException ("array");
|
||||
if (arrayIndex < 0)
|
||||
throw new ArgumentOutOfRangeException ("arrayIndex");
|
||||
if (array.Rank > 1)
|
||||
throw new ArgumentException ("Destination array is multidimensional.", "array");
|
||||
if (arrayIndex >= array.Length)
|
||||
throw new ArgumentException ("arrayIndex is equal to or greater than the length of destination array", "array");
|
||||
if (arrayIndex + items.Count > array.Length)
|
||||
throw new ArgumentException ("There is not enough space in destination array.", "array");
|
||||
|
||||
int i = 0;
|
||||
foreach (KeyValuePair<ExifTag, ExifProperty> item in items) {
|
||||
if (i >= arrayIndex) {
|
||||
array[i] = item;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the <see cref="T:System.Collections.Generic.ICollection`1"/> is read-only.
|
||||
/// </summary>
|
||||
/// <returns>true if the <see cref="T:System.Collections.Generic.ICollection`1"/> is read-only; otherwise, false.</returns>
|
||||
bool ICollection<KeyValuePair<ExifTag, ExifProperty>>.IsReadOnly {
|
||||
get { return false; }
|
||||
}
|
||||
/// <summary>
|
||||
/// Removes the first occurrence of a specific object from the <see cref="T:System.Collections.Generic.ICollection`1"/>.
|
||||
/// </summary>
|
||||
/// <param name="item">The object to remove from the <see cref="T:System.Collections.Generic.ICollection`1"/>.</param>
|
||||
/// <returns>
|
||||
/// true if <paramref name="item"/> was successfully removed from the <see cref="T:System.Collections.Generic.ICollection`1"/>; otherwise, false. This method also returns false if <paramref name="item"/> is not found in the original <see cref="T:System.Collections.Generic.ICollection`1"/>.
|
||||
/// </returns>
|
||||
/// <exception cref="T:System.NotSupportedException">The <see cref="T:System.Collections.Generic.ICollection`1"/> is read-only.</exception>
|
||||
bool ICollection<KeyValuePair<ExifTag, ExifProperty>>.Remove (KeyValuePair<ExifTag, ExifProperty> item)
|
||||
{
|
||||
throw new NotSupportedException ();
|
||||
}
|
||||
/// <summary>
|
||||
/// Returns an enumerator that iterates through a collection.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// An <see cref="T:System.Collections.IEnumerator"/> object that can be used to iterate through the collection.
|
||||
/// </returns>
|
||||
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator ()
|
||||
{
|
||||
return GetEnumerator ();
|
||||
}
|
||||
/// <summary>
|
||||
/// Returns an enumerator that iterates through the collection.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A <see cref="T:System.Collections.Generic.IEnumerator`1"/> that can be used to iterate through the collection.
|
||||
/// </returns>
|
||||
IEnumerator<KeyValuePair<ExifTag, ExifProperty>> IEnumerable<KeyValuePair<ExifTag, ExifProperty>>.GetEnumerator ()
|
||||
{
|
||||
return items.GetEnumerator ();
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,253 +0,0 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
|
||||
namespace Umbraco.Web.Media.Exif
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates exif properties from interoperability parameters.
|
||||
/// </summary>
|
||||
internal static class ExifPropertyFactory
|
||||
{
|
||||
#region Static Methods
|
||||
/// <summary>
|
||||
/// Creates an ExifProperty from the given interoperability parameters.
|
||||
/// </summary>
|
||||
/// <param name="tag">The tag id of the exif property.</param>
|
||||
/// <param name="type">The type id of the exif property.</param>
|
||||
/// <param name="count">Byte or component count.</param>
|
||||
/// <param name="value">Field data as an array of bytes.</param>
|
||||
/// <param name="byteOrder">Byte order of value.</param>
|
||||
/// <param name="ifd">IFD section containing this propery.</param>
|
||||
/// <param name="encoding">The encoding to be used for text metadata when the source encoding is unknown.</param>
|
||||
/// <returns>an ExifProperty initialized from the interoperability parameters.</returns>
|
||||
public static ExifProperty Get(ushort tag, ushort type, uint count, byte[] value, BitConverterEx.ByteOrder byteOrder, IFD ifd, Encoding encoding)
|
||||
{
|
||||
BitConverterEx conv = new BitConverterEx(byteOrder, BitConverterEx.SystemByteOrder);
|
||||
// Find the exif tag corresponding to given tag id
|
||||
ExifTag etag = ExifTagFactory.GetExifTag(ifd, tag);
|
||||
|
||||
if (ifd == IFD.Zeroth)
|
||||
{
|
||||
if (tag == 0x103) // Compression
|
||||
return new ExifEnumProperty<Compression>(ExifTag.Compression, (Compression)conv.ToUInt16(value, 0));
|
||||
else if (tag == 0x106) // PhotometricInterpretation
|
||||
return new ExifEnumProperty<PhotometricInterpretation>(ExifTag.PhotometricInterpretation, (PhotometricInterpretation)conv.ToUInt16(value, 0));
|
||||
else if (tag == 0x112) // Orientation
|
||||
return new ExifEnumProperty<Orientation>(ExifTag.Orientation, (Orientation)conv.ToUInt16(value, 0));
|
||||
else if (tag == 0x11c) // PlanarConfiguration
|
||||
return new ExifEnumProperty<PlanarConfiguration>(ExifTag.PlanarConfiguration, (PlanarConfiguration)conv.ToUInt16(value, 0));
|
||||
else if (tag == 0x213) // YCbCrPositioning
|
||||
return new ExifEnumProperty<YCbCrPositioning>(ExifTag.YCbCrPositioning, (YCbCrPositioning)conv.ToUInt16(value, 0));
|
||||
else if (tag == 0x128) // ResolutionUnit
|
||||
return new ExifEnumProperty<ResolutionUnit>(ExifTag.ResolutionUnit, (ResolutionUnit)conv.ToUInt16(value, 0));
|
||||
else if (tag == 0x132) // DateTime
|
||||
return new ExifDateTime(ExifTag.DateTime, ExifBitConverter.ToDateTime(value));
|
||||
else if (tag == 0x9c9b || tag == 0x9c9c || // Windows tags
|
||||
tag == 0x9c9d || tag == 0x9c9e || tag == 0x9c9f)
|
||||
return new WindowsByteString(etag, Encoding.Unicode.GetString(value).TrimEnd('\0'));
|
||||
}
|
||||
else if (ifd == IFD.EXIF)
|
||||
{
|
||||
if (tag == 0x9000) // ExifVersion
|
||||
return new ExifVersion(ExifTag.ExifVersion, ExifBitConverter.ToAscii(value, Encoding.ASCII));
|
||||
else if (tag == 0xa000) // FlashpixVersion
|
||||
return new ExifVersion(ExifTag.FlashpixVersion, ExifBitConverter.ToAscii(value, Encoding.ASCII));
|
||||
else if (tag == 0xa001) // ColorSpace
|
||||
return new ExifEnumProperty<ColorSpace>(ExifTag.ColorSpace, (ColorSpace)conv.ToUInt16(value, 0));
|
||||
else if (tag == 0x9286) // UserComment
|
||||
{
|
||||
// Default to ASCII
|
||||
Encoding enc = Encoding.ASCII;
|
||||
bool hasenc;
|
||||
if (value.Length < 8)
|
||||
hasenc = false;
|
||||
else
|
||||
{
|
||||
hasenc = true;
|
||||
string encstr = enc.GetString(value, 0, 8);
|
||||
if (string.Compare(encstr, "ASCII\0\0\0", StringComparison.OrdinalIgnoreCase) == 0)
|
||||
enc = Encoding.ASCII;
|
||||
else if (string.Compare(encstr, "JIS\0\0\0\0\0", StringComparison.OrdinalIgnoreCase) == 0)
|
||||
enc = Encoding.GetEncoding("Japanese (JIS 0208-1990 and 0212-1990)");
|
||||
else if (string.Compare(encstr, "Unicode\0", StringComparison.OrdinalIgnoreCase) == 0)
|
||||
enc = Encoding.Unicode;
|
||||
else
|
||||
hasenc = false;
|
||||
}
|
||||
|
||||
string val = (hasenc ? enc.GetString(value, 8, value.Length - 8) : enc.GetString(value)).Trim('\0');
|
||||
|
||||
return new ExifEncodedString(ExifTag.UserComment, val, enc);
|
||||
}
|
||||
else if (tag == 0x9003) // DateTimeOriginal
|
||||
return new ExifDateTime(ExifTag.DateTimeOriginal, ExifBitConverter.ToDateTime(value));
|
||||
else if (tag == 0x9004) // DateTimeDigitized
|
||||
return new ExifDateTime(ExifTag.DateTimeDigitized, ExifBitConverter.ToDateTime(value));
|
||||
else if (tag == 0x8822) // ExposureProgram
|
||||
return new ExifEnumProperty<ExposureProgram>(ExifTag.ExposureProgram, (ExposureProgram)conv.ToUInt16(value, 0));
|
||||
else if (tag == 0x9207) // MeteringMode
|
||||
return new ExifEnumProperty<MeteringMode>(ExifTag.MeteringMode, (MeteringMode)conv.ToUInt16(value, 0));
|
||||
else if (tag == 0x9208) // LightSource
|
||||
return new ExifEnumProperty<LightSource>(ExifTag.LightSource, (LightSource)conv.ToUInt16(value, 0));
|
||||
else if (tag == 0x9209) // Flash
|
||||
return new ExifEnumProperty<Flash>(ExifTag.Flash, (Flash)conv.ToUInt16(value, 0), true);
|
||||
else if (tag == 0x9214) // SubjectArea
|
||||
{
|
||||
if (count == 3)
|
||||
return new ExifCircularSubjectArea(ExifTag.SubjectArea, ExifBitConverter.ToUShortArray(value, (int)count, byteOrder));
|
||||
else if (count == 4)
|
||||
return new ExifRectangularSubjectArea(ExifTag.SubjectArea, ExifBitConverter.ToUShortArray(value, (int)count, byteOrder));
|
||||
else // count == 2
|
||||
return new ExifPointSubjectArea(ExifTag.SubjectArea, ExifBitConverter.ToUShortArray(value, (int)count, byteOrder));
|
||||
}
|
||||
else if (tag == 0xa210) // FocalPlaneResolutionUnit
|
||||
return new ExifEnumProperty<ResolutionUnit>(ExifTag.FocalPlaneResolutionUnit, (ResolutionUnit)conv.ToUInt16(value, 0), true);
|
||||
else if (tag == 0xa214) // SubjectLocation
|
||||
return new ExifPointSubjectArea(ExifTag.SubjectLocation, ExifBitConverter.ToUShortArray(value, (int)count, byteOrder));
|
||||
else if (tag == 0xa217) // SensingMethod
|
||||
return new ExifEnumProperty<SensingMethod>(ExifTag.SensingMethod, (SensingMethod)conv.ToUInt16(value, 0), true);
|
||||
else if (tag == 0xa300) // FileSource
|
||||
return new ExifEnumProperty<FileSource>(ExifTag.FileSource, (FileSource)conv.ToUInt16(value, 0), true);
|
||||
else if (tag == 0xa301) // SceneType
|
||||
return new ExifEnumProperty<SceneType>(ExifTag.SceneType, (SceneType)conv.ToUInt16(value, 0), true);
|
||||
else if (tag == 0xa401) // CustomRendered
|
||||
return new ExifEnumProperty<CustomRendered>(ExifTag.CustomRendered, (CustomRendered)conv.ToUInt16(value, 0), true);
|
||||
else if (tag == 0xa402) // ExposureMode
|
||||
return new ExifEnumProperty<ExposureMode>(ExifTag.ExposureMode, (ExposureMode)conv.ToUInt16(value, 0), true);
|
||||
else if (tag == 0xa403) // WhiteBalance
|
||||
return new ExifEnumProperty<WhiteBalance>(ExifTag.WhiteBalance, (WhiteBalance)conv.ToUInt16(value, 0), true);
|
||||
else if (tag == 0xa406) // SceneCaptureType
|
||||
return new ExifEnumProperty<SceneCaptureType>(ExifTag.SceneCaptureType, (SceneCaptureType)conv.ToUInt16(value, 0), true);
|
||||
else if (tag == 0xa407) // GainControl
|
||||
return new ExifEnumProperty<GainControl>(ExifTag.GainControl, (GainControl)conv.ToUInt16(value, 0), true);
|
||||
else if (tag == 0xa408) // Contrast
|
||||
return new ExifEnumProperty<Contrast>(ExifTag.Contrast, (Contrast)conv.ToUInt16(value, 0), true);
|
||||
else if (tag == 0xa409) // Saturation
|
||||
return new ExifEnumProperty<Saturation>(ExifTag.Saturation, (Saturation)conv.ToUInt16(value, 0), true);
|
||||
else if (tag == 0xa40a) // Sharpness
|
||||
return new ExifEnumProperty<Sharpness>(ExifTag.Sharpness, (Sharpness)conv.ToUInt16(value, 0), true);
|
||||
else if (tag == 0xa40c) // SubjectDistanceRange
|
||||
return new ExifEnumProperty<SubjectDistanceRange>(ExifTag.SubjectDistanceRange, (SubjectDistanceRange)conv.ToUInt16(value, 0), true);
|
||||
}
|
||||
else if (ifd == IFD.GPS)
|
||||
{
|
||||
if (tag == 0) // GPSVersionID
|
||||
return new ExifVersion(ExifTag.GPSVersionID, ExifBitConverter.ToString(value));
|
||||
else if (tag == 1) // GPSLatitudeRef
|
||||
return new ExifEnumProperty<GPSLatitudeRef>(ExifTag.GPSLatitudeRef, (GPSLatitudeRef)value[0]);
|
||||
else if (tag == 2) // GPSLatitude
|
||||
return new GPSLatitudeLongitude(ExifTag.GPSLatitude, ExifBitConverter.ToURationalArray(value, (int)count, byteOrder));
|
||||
else if (tag == 3) // GPSLongitudeRef
|
||||
return new ExifEnumProperty<GPSLongitudeRef>(ExifTag.GPSLongitudeRef, (GPSLongitudeRef)value[0]);
|
||||
else if (tag == 4) // GPSLongitude
|
||||
return new GPSLatitudeLongitude(ExifTag.GPSLongitude, ExifBitConverter.ToURationalArray(value, (int)count, byteOrder));
|
||||
else if (tag == 5) // GPSAltitudeRef
|
||||
return new ExifEnumProperty<GPSAltitudeRef>(ExifTag.GPSAltitudeRef, (GPSAltitudeRef)value[0]);
|
||||
else if (tag == 7) // GPSTimeStamp
|
||||
return new GPSTimeStamp(ExifTag.GPSTimeStamp, ExifBitConverter.ToURationalArray(value, (int)count, byteOrder));
|
||||
else if (tag == 9) // GPSStatus
|
||||
return new ExifEnumProperty<GPSStatus>(ExifTag.GPSStatus, (GPSStatus)value[0]);
|
||||
else if (tag == 10) // GPSMeasureMode
|
||||
return new ExifEnumProperty<GPSMeasureMode>(ExifTag.GPSMeasureMode, (GPSMeasureMode)value[0]);
|
||||
else if (tag == 12) // GPSSpeedRef
|
||||
return new ExifEnumProperty<GPSSpeedRef>(ExifTag.GPSSpeedRef, (GPSSpeedRef)value[0]);
|
||||
else if (tag == 14) // GPSTrackRef
|
||||
return new ExifEnumProperty<GPSDirectionRef>(ExifTag.GPSTrackRef, (GPSDirectionRef)value[0]);
|
||||
else if (tag == 16) // GPSImgDirectionRef
|
||||
return new ExifEnumProperty<GPSDirectionRef>(ExifTag.GPSImgDirectionRef, (GPSDirectionRef)value[0]);
|
||||
else if (tag == 19) // GPSDestLatitudeRef
|
||||
return new ExifEnumProperty<GPSLatitudeRef>(ExifTag.GPSDestLatitudeRef, (GPSLatitudeRef)value[0]);
|
||||
else if (tag == 20) // GPSDestLatitude
|
||||
return new GPSLatitudeLongitude(ExifTag.GPSDestLatitude, ExifBitConverter.ToURationalArray(value, (int)count, byteOrder));
|
||||
else if (tag == 21) // GPSDestLongitudeRef
|
||||
return new ExifEnumProperty<GPSLongitudeRef>(ExifTag.GPSDestLongitudeRef, (GPSLongitudeRef)value[0]);
|
||||
else if (tag == 22) // GPSDestLongitude
|
||||
return new GPSLatitudeLongitude(ExifTag.GPSDestLongitude, ExifBitConverter.ToURationalArray(value, (int)count, byteOrder));
|
||||
else if (tag == 23) // GPSDestBearingRef
|
||||
return new ExifEnumProperty<GPSDirectionRef>(ExifTag.GPSDestBearingRef, (GPSDirectionRef)value[0]);
|
||||
else if (tag == 25) // GPSDestDistanceRef
|
||||
return new ExifEnumProperty<GPSDistanceRef>(ExifTag.GPSDestDistanceRef, (GPSDistanceRef)value[0]);
|
||||
else if (tag == 29) // GPSDate
|
||||
return new ExifDateTime(ExifTag.GPSDateStamp, ExifBitConverter.ToDateTime(value, false));
|
||||
else if (tag == 30) // GPSDifferential
|
||||
return new ExifEnumProperty<GPSDifferential>(ExifTag.GPSDifferential, (GPSDifferential)conv.ToUInt16(value, 0));
|
||||
}
|
||||
else if (ifd == IFD.Interop)
|
||||
{
|
||||
if (tag == 1) // InteroperabilityIndex
|
||||
return new ExifAscii(ExifTag.InteroperabilityIndex, ExifBitConverter.ToAscii(value, Encoding.ASCII), Encoding.ASCII);
|
||||
else if (tag == 2) // InteroperabilityVersion
|
||||
return new ExifVersion(ExifTag.InteroperabilityVersion, ExifBitConverter.ToAscii(value, Encoding.ASCII));
|
||||
}
|
||||
else if (ifd == IFD.First)
|
||||
{
|
||||
if (tag == 0x103) // Compression
|
||||
return new ExifEnumProperty<Compression>(ExifTag.ThumbnailCompression, (Compression)conv.ToUInt16(value, 0));
|
||||
else if (tag == 0x106) // PhotometricInterpretation
|
||||
return new ExifEnumProperty<PhotometricInterpretation>(ExifTag.ThumbnailPhotometricInterpretation, (PhotometricInterpretation)conv.ToUInt16(value, 0));
|
||||
else if (tag == 0x112) // Orientation
|
||||
return new ExifEnumProperty<Orientation>(ExifTag.ThumbnailOrientation, (Orientation)conv.ToUInt16(value, 0));
|
||||
else if (tag == 0x11c) // PlanarConfiguration
|
||||
return new ExifEnumProperty<PlanarConfiguration>(ExifTag.ThumbnailPlanarConfiguration, (PlanarConfiguration)conv.ToUInt16(value, 0));
|
||||
else if (tag == 0x213) // YCbCrPositioning
|
||||
return new ExifEnumProperty<YCbCrPositioning>(ExifTag.ThumbnailYCbCrPositioning, (YCbCrPositioning)conv.ToUInt16(value, 0));
|
||||
else if (tag == 0x128) // ResolutionUnit
|
||||
return new ExifEnumProperty<ResolutionUnit>(ExifTag.ThumbnailResolutionUnit, (ResolutionUnit)conv.ToUInt16(value, 0));
|
||||
else if (tag == 0x132) // DateTime
|
||||
return new ExifDateTime(ExifTag.ThumbnailDateTime, ExifBitConverter.ToDateTime(value));
|
||||
}
|
||||
|
||||
if (type == 1) // 1 = BYTE An 8-bit unsigned integer.
|
||||
{
|
||||
if (count == 1)
|
||||
return new ExifByte(etag, value[0]);
|
||||
else
|
||||
return new ExifByteArray(etag, value);
|
||||
}
|
||||
else if (type == 2) // 2 = ASCII An 8-bit byte containing one 7-bit ASCII code.
|
||||
{
|
||||
return new ExifAscii(etag, ExifBitConverter.ToAscii(value, encoding), encoding);
|
||||
}
|
||||
else if (type == 3) // 3 = SHORT A 16-bit (2-byte) unsigned integer.
|
||||
{
|
||||
if (count == 1)
|
||||
return new ExifUShort(etag, conv.ToUInt16(value, 0));
|
||||
else
|
||||
return new ExifUShortArray(etag, ExifBitConverter.ToUShortArray(value, (int)count, byteOrder));
|
||||
}
|
||||
else if (type == 4) // 4 = LONG A 32-bit (4-byte) unsigned integer.
|
||||
{
|
||||
if (count == 1)
|
||||
return new ExifUInt(etag, conv.ToUInt32(value, 0));
|
||||
else
|
||||
return new ExifUIntArray(etag, ExifBitConverter.ToUIntArray(value, (int)count, byteOrder));
|
||||
}
|
||||
else if (type == 5) // 5 = RATIONAL Two LONGs. The first LONG is the numerator and the second LONG expresses the denominator.
|
||||
{
|
||||
if (count == 1)
|
||||
return new ExifURational(etag, ExifBitConverter.ToURational(value, byteOrder));
|
||||
else
|
||||
return new ExifURationalArray(etag, ExifBitConverter.ToURationalArray(value, (int)count, byteOrder));
|
||||
}
|
||||
else if (type == 7) // 7 = UNDEFINED An 8-bit byte that can take any value depending on the field definition.
|
||||
return new ExifUndefined(etag, value);
|
||||
else if (type == 9) // 9 = SLONG A 32-bit (4-byte) signed integer (2's complement notation).
|
||||
{
|
||||
if (count == 1)
|
||||
return new ExifSInt(etag, conv.ToInt32(value, 0));
|
||||
else
|
||||
return new ExifSIntArray(etag, ExifBitConverter.ToSIntArray(value, (int)count, byteOrder));
|
||||
}
|
||||
else if (type == 10) // 10 = SRATIONAL Two SLONGs. The first SLONG is the numerator and the second SLONG is the denominator.
|
||||
{
|
||||
if (count == 1)
|
||||
return new ExifSRational(etag, ExifBitConverter.ToSRational(value, byteOrder));
|
||||
else
|
||||
return new ExifSRationalArray(etag, ExifBitConverter.ToSRationalArray(value, (int)count, byteOrder));
|
||||
}
|
||||
else
|
||||
throw new ArgumentException("Unknown property type.");
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,290 +0,0 @@
|
||||
|
||||
namespace Umbraco.Web.Media.Exif
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the tags associated with exif fields.
|
||||
/// </summary>
|
||||
internal enum ExifTag : int
|
||||
{
|
||||
// ****************************
|
||||
// Zeroth IFD
|
||||
// ****************************
|
||||
NewSubfileType = IFD.Zeroth + 254,
|
||||
SubfileType = IFD.Zeroth + 255,
|
||||
ImageWidth = IFD.Zeroth + 256,
|
||||
ImageLength = IFD.Zeroth + 257,
|
||||
BitsPerSample = IFD.Zeroth + 258,
|
||||
Compression = IFD.Zeroth + 259,
|
||||
PhotometricInterpretation = IFD.Zeroth + 262,
|
||||
Threshholding = IFD.Zeroth + 263,
|
||||
CellWidth = IFD.Zeroth + 264,
|
||||
CellLength = IFD.Zeroth + 265,
|
||||
FillOrder = IFD.Zeroth + 266,
|
||||
DocumentName = IFD.Zeroth + 269,
|
||||
ImageDescription = IFD.Zeroth + 270,
|
||||
Make = IFD.Zeroth + 271,
|
||||
Model = IFD.Zeroth + 272,
|
||||
StripOffsets = IFD.Zeroth + 273,
|
||||
Orientation = IFD.Zeroth + 274,
|
||||
SamplesPerPixel = IFD.Zeroth + 277,
|
||||
RowsPerStrip = IFD.Zeroth + 278,
|
||||
StripByteCounts = IFD.Zeroth + 279,
|
||||
MinSampleValue = IFD.Zeroth + 280,
|
||||
MaxSampleValue = IFD.Zeroth + 281,
|
||||
XResolution = IFD.Zeroth + 282,
|
||||
YResolution = IFD.Zeroth + 283,
|
||||
PlanarConfiguration = IFD.Zeroth + 284,
|
||||
PageName = IFD.Zeroth + 285,
|
||||
XPosition = IFD.Zeroth + 286,
|
||||
YPosition = IFD.Zeroth + 287,
|
||||
FreeOffsets = IFD.Zeroth + 288,
|
||||
FreeByteCounts = IFD.Zeroth + 289,
|
||||
GrayResponseUnit = IFD.Zeroth + 290,
|
||||
GrayResponseCurve = IFD.Zeroth + 291,
|
||||
T4Options = IFD.Zeroth + 292,
|
||||
T6Options = IFD.Zeroth + 293,
|
||||
ResolutionUnit = IFD.Zeroth + 296,
|
||||
PageNumber = IFD.Zeroth + 297,
|
||||
TransferFunction = IFD.Zeroth + 301,
|
||||
Software = IFD.Zeroth + 305,
|
||||
DateTime = IFD.Zeroth + 306,
|
||||
Artist = IFD.Zeroth + 315,
|
||||
HostComputer = IFD.Zeroth + 316,
|
||||
Predictor = IFD.Zeroth + 317,
|
||||
WhitePoint = IFD.Zeroth + 318,
|
||||
PrimaryChromaticities = IFD.Zeroth + 319,
|
||||
ColorMap = IFD.Zeroth + 320,
|
||||
HalftoneHints = IFD.Zeroth + 321,
|
||||
TileWidth = IFD.Zeroth + 322,
|
||||
TileLength = IFD.Zeroth + 323,
|
||||
TileOffsets = IFD.Zeroth + 324,
|
||||
TileByteCounts = IFD.Zeroth + 325,
|
||||
InkSet = IFD.Zeroth + 332,
|
||||
InkNames = IFD.Zeroth + 333,
|
||||
NumberOfInks = IFD.Zeroth + 334,
|
||||
DotRange = IFD.Zeroth + 336,
|
||||
TargetPrinter = IFD.Zeroth + 337,
|
||||
ExtraSamples = IFD.Zeroth + 338,
|
||||
SampleFormat = IFD.Zeroth + 339,
|
||||
SMinSampleValue = IFD.Zeroth + 340,
|
||||
SMaxSampleValue = IFD.Zeroth + 341,
|
||||
TransferRange = IFD.Zeroth + 342,
|
||||
JPEGProc = IFD.Zeroth + 512,
|
||||
JPEGInterchangeFormat = IFD.Zeroth + 513,
|
||||
JPEGInterchangeFormatLength = IFD.Zeroth + 514,
|
||||
JPEGRestartInterval = IFD.Zeroth + 515,
|
||||
JPEGLosslessPredictors = IFD.Zeroth + 517,
|
||||
JPEGPointTransforms = IFD.Zeroth + 518,
|
||||
JPEGQTables = IFD.Zeroth + 519,
|
||||
JPEGDCTables = IFD.Zeroth + 520,
|
||||
JPEGACTables = IFD.Zeroth + 521,
|
||||
YCbCrCoefficients = IFD.Zeroth + 529,
|
||||
YCbCrSubSampling = IFD.Zeroth + 530,
|
||||
YCbCrPositioning = IFD.Zeroth + 531,
|
||||
ReferenceBlackWhite = IFD.Zeroth + 532,
|
||||
Copyright = IFD.Zeroth + 33432,
|
||||
// Pointers to other IFDs
|
||||
EXIFIFDPointer = IFD.Zeroth + 34665,
|
||||
GPSIFDPointer = IFD.Zeroth + 34853,
|
||||
// Windows Tags
|
||||
WindowsTitle = IFD.Zeroth + 0x9c9b,
|
||||
WindowsComment = IFD.Zeroth + 0x9c9c,
|
||||
WindowsAuthor = IFD.Zeroth + 0x9c9d,
|
||||
WindowsKeywords = IFD.Zeroth + 0x9c9e,
|
||||
WindowsSubject = IFD.Zeroth + 0x9c9f,
|
||||
// Rating
|
||||
Rating = IFD.Zeroth + 0x4746,
|
||||
RatingPercent = IFD.Zeroth + 0x4749,
|
||||
// Microsoft specifing padding and offset tags
|
||||
ZerothIFDPadding = IFD.Zeroth + 0xea1c,
|
||||
// ****************************
|
||||
// EXIF Tags
|
||||
// ****************************
|
||||
ExifVersion = IFD.EXIF + 36864,
|
||||
FlashpixVersion = IFD.EXIF + 40960,
|
||||
ColorSpace = IFD.EXIF + 40961,
|
||||
ComponentsConfiguration = IFD.EXIF + 37121,
|
||||
CompressedBitsPerPixel = IFD.EXIF + 37122,
|
||||
PixelXDimension = IFD.EXIF + 40962,
|
||||
PixelYDimension = IFD.EXIF + 40963,
|
||||
MakerNote = IFD.EXIF + 37500,
|
||||
UserComment = IFD.EXIF + 37510,
|
||||
RelatedSoundFile = IFD.EXIF + 40964,
|
||||
DateTimeOriginal = IFD.EXIF + 36867,
|
||||
DateTimeDigitized = IFD.EXIF + 36868,
|
||||
SubSecTime = IFD.EXIF + 37520,
|
||||
SubSecTimeOriginal = IFD.EXIF + 37521,
|
||||
SubSecTimeDigitized = IFD.EXIF + 37522,
|
||||
ExposureTime = IFD.EXIF + 33434,
|
||||
FNumber = IFD.EXIF + 33437,
|
||||
ExposureProgram = IFD.EXIF + 34850,
|
||||
SpectralSensitivity = IFD.EXIF + 34852,
|
||||
ISOSpeedRatings = IFD.EXIF + 34855,
|
||||
OECF = IFD.EXIF + 34856,
|
||||
ShutterSpeedValue = IFD.EXIF + 37377,
|
||||
ApertureValue = IFD.EXIF + 37378,
|
||||
BrightnessValue = IFD.EXIF + 37379,
|
||||
ExposureBiasValue = IFD.EXIF + 37380,
|
||||
MaxApertureValue = IFD.EXIF + 37381,
|
||||
SubjectDistance = IFD.EXIF + 37382,
|
||||
MeteringMode = IFD.EXIF + 37383,
|
||||
LightSource = IFD.EXIF + 37384,
|
||||
Flash = IFD.EXIF + 37385,
|
||||
FocalLength = IFD.EXIF + 37386,
|
||||
SubjectArea = IFD.EXIF + 37396,
|
||||
FlashEnergy = IFD.EXIF + 41483,
|
||||
SpatialFrequencyResponse = IFD.EXIF + 41484,
|
||||
FocalPlaneXResolution = IFD.EXIF + 41486,
|
||||
FocalPlaneYResolution = IFD.EXIF + 41487,
|
||||
FocalPlaneResolutionUnit = IFD.EXIF + 41488,
|
||||
SubjectLocation = IFD.EXIF + 41492,
|
||||
ExposureIndex = IFD.EXIF + 41493,
|
||||
SensingMethod = IFD.EXIF + 41495,
|
||||
FileSource = IFD.EXIF + 41728,
|
||||
SceneType = IFD.EXIF + 41729,
|
||||
CFAPattern = IFD.EXIF + 41730,
|
||||
CustomRendered = IFD.EXIF + 41985,
|
||||
ExposureMode = IFD.EXIF + 41986,
|
||||
WhiteBalance = IFD.EXIF + 41987,
|
||||
DigitalZoomRatio = IFD.EXIF + 41988,
|
||||
FocalLengthIn35mmFilm = IFD.EXIF + 41989,
|
||||
SceneCaptureType = IFD.EXIF + 41990,
|
||||
GainControl = IFD.EXIF + 41991,
|
||||
Contrast = IFD.EXIF + 41992,
|
||||
Saturation = IFD.EXIF + 41993,
|
||||
Sharpness = IFD.EXIF + 41994,
|
||||
DeviceSettingDescription = IFD.EXIF + 41995,
|
||||
SubjectDistanceRange = IFD.EXIF + 41996,
|
||||
ImageUniqueID = IFD.EXIF + 42016,
|
||||
InteroperabilityIFDPointer = IFD.EXIF + 40965,
|
||||
// Microsoft specifing padding and offset tags
|
||||
ExifIFDPadding = IFD.EXIF + 0xea1c,
|
||||
OffsetSchema = IFD.EXIF + 0xea1d,
|
||||
// ****************************
|
||||
// GPS Tags
|
||||
// ****************************
|
||||
GPSVersionID = IFD.GPS + 0,
|
||||
GPSLatitudeRef = IFD.GPS + 1,
|
||||
GPSLatitude = IFD.GPS + 2,
|
||||
GPSLongitudeRef = IFD.GPS + 3,
|
||||
GPSLongitude = IFD.GPS + 4,
|
||||
GPSAltitudeRef = IFD.GPS + 5,
|
||||
GPSAltitude = IFD.GPS + 6,
|
||||
GPSTimeStamp = IFD.GPS + 7,
|
||||
GPSSatellites = IFD.GPS + 8,
|
||||
GPSStatus = IFD.GPS + 9,
|
||||
GPSMeasureMode = IFD.GPS + 10,
|
||||
GPSDOP = IFD.GPS + 11,
|
||||
GPSSpeedRef = IFD.GPS + 12,
|
||||
GPSSpeed = IFD.GPS + 13,
|
||||
GPSTrackRef = IFD.GPS + 14,
|
||||
GPSTrack = IFD.GPS + 15,
|
||||
GPSImgDirectionRef = IFD.GPS + 16,
|
||||
GPSImgDirection = IFD.GPS + 17,
|
||||
GPSMapDatum = IFD.GPS + 18,
|
||||
GPSDestLatitudeRef = IFD.GPS + 19,
|
||||
GPSDestLatitude = IFD.GPS + 20,
|
||||
GPSDestLongitudeRef = IFD.GPS + 21,
|
||||
GPSDestLongitude = IFD.GPS + 22,
|
||||
GPSDestBearingRef = IFD.GPS + 23,
|
||||
GPSDestBearing = IFD.GPS + 24,
|
||||
GPSDestDistanceRef = IFD.GPS + 25,
|
||||
GPSDestDistance = IFD.GPS + 26,
|
||||
GPSProcessingMethod = IFD.GPS + 27,
|
||||
GPSAreaInformation = IFD.GPS + 28,
|
||||
GPSDateStamp = IFD.GPS + 29,
|
||||
GPSDifferential = IFD.GPS + 30,
|
||||
// ****************************
|
||||
// InterOp Tags
|
||||
// ****************************
|
||||
InteroperabilityIndex = IFD.Interop + 1,
|
||||
InteroperabilityVersion = IFD.Interop + 2,
|
||||
RelatedImageWidth = IFD.Interop + 0x1001,
|
||||
RelatedImageHeight = IFD.Interop + 0x1002,
|
||||
// ****************************
|
||||
// First IFD TIFF Tags
|
||||
// ****************************
|
||||
ThumbnailImageWidth = IFD.First + 256,
|
||||
ThumbnailImageLength = IFD.First + 257,
|
||||
ThumbnailBitsPerSample = IFD.First + 258,
|
||||
ThumbnailCompression = IFD.First + 259,
|
||||
ThumbnailPhotometricInterpretation = IFD.First + 262,
|
||||
ThumbnailOrientation = IFD.First + 274,
|
||||
ThumbnailSamplesPerPixel = IFD.First + 277,
|
||||
ThumbnailPlanarConfiguration = IFD.First + 284,
|
||||
ThumbnailYCbCrSubSampling = IFD.First + 530,
|
||||
ThumbnailYCbCrPositioning = IFD.First + 531,
|
||||
ThumbnailXResolution = IFD.First + 282,
|
||||
ThumbnailYResolution = IFD.First + 283,
|
||||
ThumbnailResolutionUnit = IFD.First + 296,
|
||||
ThumbnailStripOffsets = IFD.First + 273,
|
||||
ThumbnailRowsPerStrip = IFD.First + 278,
|
||||
ThumbnailStripByteCounts = IFD.First + 279,
|
||||
ThumbnailJPEGInterchangeFormat = IFD.First + 513,
|
||||
ThumbnailJPEGInterchangeFormatLength = IFD.First + 514,
|
||||
ThumbnailTransferFunction = IFD.First + 301,
|
||||
ThumbnailWhitePoint = IFD.First + 318,
|
||||
ThumbnailPrimaryChromaticities = IFD.First + 319,
|
||||
ThumbnailYCbCrCoefficients = IFD.First + 529,
|
||||
ThumbnailReferenceBlackWhite = IFD.First + 532,
|
||||
ThumbnailDateTime = IFD.First + 306,
|
||||
ThumbnailImageDescription = IFD.First + 270,
|
||||
ThumbnailMake = IFD.First + 271,
|
||||
ThumbnailModel = IFD.First + 272,
|
||||
ThumbnailSoftware = IFD.First + 305,
|
||||
ThumbnailArtist = IFD.First + 315,
|
||||
ThumbnailCopyright = IFD.First + 33432,
|
||||
// ****************************
|
||||
// JFIF Tags
|
||||
// ****************************
|
||||
/// <summary>
|
||||
/// Represents the JFIF version.
|
||||
/// </summary>
|
||||
JFIFVersion = IFD.JFIF + 1,
|
||||
/// <summary>
|
||||
/// Represents units for X and Y densities.
|
||||
/// </summary>
|
||||
JFIFUnits = IFD.JFIF + 101,
|
||||
/// <summary>
|
||||
/// Horizontal pixel density.
|
||||
/// </summary>
|
||||
XDensity = IFD.JFIF + 102,
|
||||
/// <summary>
|
||||
/// Vertical pixel density
|
||||
/// </summary>
|
||||
YDensity = IFD.JFIF + 103,
|
||||
/// <summary>
|
||||
/// Thumbnail horizontal pixel count.
|
||||
/// </summary>
|
||||
JFIFXThumbnail = IFD.JFIF + 201,
|
||||
/// <summary>
|
||||
/// Thumbnail vertical pixel count.
|
||||
/// </summary>
|
||||
JFIFYThumbnail = IFD.JFIF + 202,
|
||||
/// <summary>
|
||||
/// JFIF JPEG thumbnail.
|
||||
/// </summary>
|
||||
JFIFThumbnail = IFD.JFIF + 203,
|
||||
/// <summary>
|
||||
/// Code which identifies the JFIF extension.
|
||||
/// </summary>
|
||||
JFXXExtensionCode = IFD.JFXX + 1,
|
||||
/// <summary>
|
||||
/// Thumbnail horizontal pixel count.
|
||||
/// </summary>
|
||||
JFXXXThumbnail = IFD.JFXX + 101,
|
||||
/// <summary>
|
||||
/// Thumbnail vertical pixel count.
|
||||
/// </summary>
|
||||
JFXXYThumbnail = IFD.JFXX + 102,
|
||||
/// <summary>
|
||||
/// The 256-Color RGB palette.
|
||||
/// </summary>
|
||||
JFXXPalette = IFD.JFXX + 201,
|
||||
/// <summary>
|
||||
/// JFIF thumbnail. The thumbnail will be either a JPEG,
|
||||
/// a 256 color palette bitmap, or a 24-bit RGB bitmap.
|
||||
/// </summary>
|
||||
JFXXThumbnail = IFD.JFXX + 202,
|
||||
}
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace Umbraco.Web.Media.Exif
|
||||
{
|
||||
internal static class ExifTagFactory
|
||||
{
|
||||
#region Static Methods
|
||||
/// <summary>
|
||||
/// Returns the ExifTag corresponding to the given tag id.
|
||||
/// </summary>
|
||||
public static ExifTag GetExifTag(IFD ifd, ushort tagid)
|
||||
{
|
||||
return (ExifTag)(ifd + tagid);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the tag id corresponding to the given ExifTag.
|
||||
/// </summary>
|
||||
public static ushort GetTagID(ExifTag exiftag)
|
||||
{
|
||||
IFD ifd = GetTagIFD(exiftag);
|
||||
return (ushort)((int)exiftag - (int)ifd);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the IFD section containing the given tag.
|
||||
/// </summary>
|
||||
public static IFD GetTagIFD(ExifTag tag)
|
||||
{
|
||||
return (IFD)(((int)tag / 100000) * 100000);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the string representation for the given exif tag.
|
||||
/// </summary>
|
||||
public static string GetTagName(ExifTag tag)
|
||||
{
|
||||
string name = Enum.GetName(typeof(ExifTag), tag);
|
||||
if (name == null)
|
||||
return "Unknown";
|
||||
else
|
||||
return name;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the string representation for the given tag id.
|
||||
/// </summary>
|
||||
public static string GetTagName(IFD ifd, ushort tagid)
|
||||
{
|
||||
return GetTagName(GetExifTag(ifd, tagid));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the string representation for the given exif tag including
|
||||
/// IFD section and tag id.
|
||||
/// </summary>
|
||||
public static string GetTagLongName(ExifTag tag)
|
||||
{
|
||||
string ifdname = Enum.GetName(typeof(IFD), GetTagIFD(tag));
|
||||
string name = Enum.GetName(typeof(ExifTag), tag);
|
||||
if (name == null)
|
||||
name = "Unknown";
|
||||
string tagidname = GetTagID(tag).ToString();
|
||||
return ifdname + ": " + name + " (" + tagidname + ")";
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
namespace Umbraco.Web.Media.Exif
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the IFD section containing tags.
|
||||
/// </summary>
|
||||
internal enum IFD : int
|
||||
{
|
||||
Unknown = 0,
|
||||
Zeroth = 100000,
|
||||
EXIF = 200000,
|
||||
GPS = 300000,
|
||||
Interop = 400000,
|
||||
First = 500000,
|
||||
MakerNote = 600000,
|
||||
JFIF = 700000,
|
||||
JFXX = 800000,
|
||||
}
|
||||
}
|
||||
@@ -1,144 +0,0 @@
|
||||
using System.ComponentModel;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using Umbraco.Web.Media.TypeDetector;
|
||||
|
||||
namespace Umbraco.Web.Media.Exif
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the base class for image files.
|
||||
/// </summary>
|
||||
[TypeDescriptionProvider(typeof(ExifFileTypeDescriptionProvider))]
|
||||
internal abstract class ImageFile
|
||||
{
|
||||
#region Constructor
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ImageFile"/> class.
|
||||
/// </summary>
|
||||
protected ImageFile ()
|
||||
{
|
||||
Format = ImageFileFormat.Unknown;
|
||||
Properties = new ExifPropertyCollection (this);
|
||||
Encoding = Encoding.Default;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
/// <summary>
|
||||
/// Returns the format of the <see cref="ImageFile"/>.
|
||||
/// </summary>
|
||||
public ImageFileFormat Format { get; protected set; }
|
||||
/// <summary>
|
||||
/// Gets the collection of Exif properties contained in the <see cref="ImageFile"/>.
|
||||
/// </summary>
|
||||
public ExifPropertyCollection Properties { get; private set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the embedded thumbnail image.
|
||||
/// </summary>
|
||||
public ImageFile Thumbnail { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the Exif property with the given key.
|
||||
/// </summary>
|
||||
/// <param name="key">The Exif tag associated with the Exif property.</param>
|
||||
public ExifProperty this[ExifTag key] {
|
||||
get { return Properties[key]; }
|
||||
set { Properties[key] = value; }
|
||||
}
|
||||
/// <summary>
|
||||
/// Gets the encoding used for text metadata when the source encoding is unknown.
|
||||
/// </summary>
|
||||
public Encoding Encoding { get; protected set; }
|
||||
#endregion
|
||||
|
||||
#region Instance Methods
|
||||
/// <summary>
|
||||
/// Converts the <see cref="ImageFile"/> to a <see cref="System.Drawing.Image"/>.
|
||||
/// </summary>
|
||||
/// <returns>Returns a <see cref="System.Drawing.Image"/> containing image data.</returns>
|
||||
public abstract Image ToImage ();
|
||||
|
||||
/// <summary>
|
||||
/// Saves the <see cref="ImageFile"/> to the specified file.
|
||||
/// </summary>
|
||||
/// <param name="filename">A string that contains the name of the file.</param>
|
||||
public virtual void Save (string filename)
|
||||
{
|
||||
using (FileStream stream = new FileStream (filename, FileMode.Create, FileAccess.Write, FileShare.None)) {
|
||||
Save (stream);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves the <see cref="ImageFile"/> to the specified stream.
|
||||
/// </summary>
|
||||
/// <param name="stream">A <see cref="Sytem.IO.Stream"/> to save image data to.</param>
|
||||
public abstract void Save (Stream stream);
|
||||
#endregion
|
||||
|
||||
#region Static Methods
|
||||
/// <summary>
|
||||
/// Creates an <see cref="ImageFile"/> from the specified file.
|
||||
/// </summary>
|
||||
/// <param name="filename">A string that contains the name of the file.</param>
|
||||
/// <returns>The <see cref="ImageFile"/> created from the file.</returns>
|
||||
public static ImageFile FromFile (string filename)
|
||||
{
|
||||
return FromFile(filename, Encoding.Default);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an <see cref="ImageFile"/> from the specified file.
|
||||
/// </summary>
|
||||
/// <param name="filename">A string that contains the name of the file.</param>
|
||||
/// <param name="encoding">The encoding to be used for text metadata when the source encoding is unknown.</param>
|
||||
/// <returns>The <see cref="ImageFile"/> created from the file.</returns>
|
||||
public static ImageFile FromFile(string filename, Encoding encoding)
|
||||
{
|
||||
using (FileStream stream = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
return FromStream(stream, encoding);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an <see cref="ImageFile"/> from the specified data stream.
|
||||
/// </summary>
|
||||
/// <param name="stream">A <see cref="Sytem.IO.Stream"/> that contains image data.</param>
|
||||
/// <returns>The <see cref="ImageFile"/> created from the file.</returns>
|
||||
public static ImageFile FromStream(Stream stream)
|
||||
{
|
||||
return FromStream(stream, Encoding.Default);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an <see cref="ImageFile"/> from the specified data stream.
|
||||
/// </summary>
|
||||
/// <param name="stream">A <see cref="Sytem.IO.Stream"/> that contains image data.</param>
|
||||
/// <param name="encoding">The encoding to be used for text metadata when the source encoding is unknown.</param>
|
||||
/// <returns>The <see cref="ImageFile"/> created from the file.</returns>
|
||||
public static ImageFile FromStream(Stream stream, Encoding encoding)
|
||||
{
|
||||
// JPEG
|
||||
if (JpegDetector.IsOfType(stream))
|
||||
{
|
||||
return new JPEGFile(stream, encoding);
|
||||
}
|
||||
|
||||
// TIFF
|
||||
if (TIFFDetector.IsOfType(stream))
|
||||
{
|
||||
return new TIFFFile(stream, encoding);
|
||||
}
|
||||
|
||||
// SVG
|
||||
if (SvgDetector.IsOfType(stream))
|
||||
{
|
||||
return new SvgFile(stream);
|
||||
}
|
||||
|
||||
throw new NotValidImageFileException();
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Umbraco.Web.Media.Exif
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents an image file directory.
|
||||
/// </summary>
|
||||
internal class ImageFileDirectory
|
||||
{
|
||||
/// <summary>
|
||||
/// The fields contained in this IFD.
|
||||
/// </summary>
|
||||
public List<ImageFileDirectoryEntry> Fields { get; private set; }
|
||||
/// <summary>
|
||||
/// Offset to the next IFD.
|
||||
/// </summary>
|
||||
public uint NextIFDOffset { get; private set; }
|
||||
/// <summary>
|
||||
/// Compressed image data.
|
||||
/// </summary>
|
||||
public List<TIFFStrip> Strips { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ImageFileDirectory"/> class.
|
||||
/// </summary>
|
||||
public ImageFileDirectory()
|
||||
{
|
||||
Fields = new List<ImageFileDirectoryEntry>();
|
||||
Strips = new List<TIFFStrip>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a <see cref="ImageFileDirectory"/> initialized from the given byte data.
|
||||
/// </summary>
|
||||
/// <param name="data">The data.</param>
|
||||
/// <param name="offset">The offset into <paramref name="data"/>.</param>
|
||||
/// <param name="byteOrder">The byte order of <paramref name="data"/>.</param>
|
||||
/// <returns>A <see cref="ImageFileDirectory"/> initialized from the given byte data.</returns>
|
||||
public static ImageFileDirectory FromBytes(byte[] data, uint offset, BitConverterEx.ByteOrder byteOrder)
|
||||
{
|
||||
ImageFileDirectory ifd = new ImageFileDirectory();
|
||||
BitConverterEx conv = new BitConverterEx(byteOrder, BitConverterEx.SystemByteOrder);
|
||||
|
||||
List<uint> stripOffsets = new List<uint>();
|
||||
List<uint> stripLengths = new List<uint>();
|
||||
|
||||
// Count
|
||||
ushort fieldcount = conv.ToUInt16(data, offset);
|
||||
|
||||
// Read fields
|
||||
for (uint i = 0; i < fieldcount; i++)
|
||||
{
|
||||
uint fieldoffset = offset + 2 + 12 * i;
|
||||
ImageFileDirectoryEntry field = ImageFileDirectoryEntry.FromBytes(data, fieldoffset, byteOrder);
|
||||
ifd.Fields.Add(field);
|
||||
|
||||
// Read strip offsets
|
||||
if (field.Tag == 273)
|
||||
{
|
||||
int baselen = field.Data.Length / (int)field.Count;
|
||||
for (uint j = 0; j < field.Count; j++)
|
||||
{
|
||||
byte[] val = new byte[baselen];
|
||||
Array.Copy(field.Data, j * baselen, val, 0, baselen);
|
||||
uint stripOffset = (field.Type == 3 ? (uint)BitConverter.ToUInt16(val, 0) : BitConverter.ToUInt32(val, 0));
|
||||
stripOffsets.Add(stripOffset);
|
||||
}
|
||||
}
|
||||
|
||||
// Read strip lengths
|
||||
if (field.Tag == 279)
|
||||
{
|
||||
int baselen = field.Data.Length / (int)field.Count;
|
||||
for (uint j = 0; j < field.Count; j++)
|
||||
{
|
||||
byte[] val = new byte[baselen];
|
||||
Array.Copy(field.Data, j * baselen, val, 0, baselen);
|
||||
uint stripLength = (field.Type == 3 ? (uint)BitConverter.ToUInt16(val, 0) : BitConverter.ToUInt32(val, 0));
|
||||
stripLengths.Add(stripLength);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Save strips
|
||||
if (stripOffsets.Count != stripLengths.Count)
|
||||
throw new NotValidTIFFileException();
|
||||
for (int i = 0; i < stripOffsets.Count; i++)
|
||||
ifd.Strips.Add(new TIFFStrip(data, stripOffsets[i], stripLengths[i]));
|
||||
|
||||
// Offset to next ifd
|
||||
ifd.NextIFDOffset = conv.ToUInt32(data, offset + 2 + 12 * fieldcount);
|
||||
|
||||
return ifd;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,117 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace Umbraco.Web.Media.Exif
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents an entry in the image file directory.
|
||||
/// </summary>
|
||||
internal struct ImageFileDirectoryEntry
|
||||
{
|
||||
/// <summary>
|
||||
/// The tag that identifies the field.
|
||||
/// </summary>
|
||||
public ushort Tag;
|
||||
/// <summary>
|
||||
/// Field type identifier.
|
||||
/// </summary>
|
||||
public ushort Type;
|
||||
/// <summary>
|
||||
/// Count of Type.
|
||||
/// </summary>
|
||||
public uint Count;
|
||||
/// <summary>
|
||||
/// Field data.
|
||||
/// </summary>
|
||||
public byte[] Data;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ImageFileDirectoryEntry"/> struct.
|
||||
/// </summary>
|
||||
/// <param name="tag">The tag that identifies the field.</param>
|
||||
/// <param name="type">Field type identifier.</param>
|
||||
/// <param name="count">Count of Type.</param>
|
||||
/// <param name="data">Field data.</param>
|
||||
public ImageFileDirectoryEntry(ushort tag, ushort type, uint count, byte[] data)
|
||||
{
|
||||
Tag = tag;
|
||||
Type = type;
|
||||
Count = count;
|
||||
Data = data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a <see cref="ImageFileDirectoryEntry"/> initialized from the given byte data.
|
||||
/// </summary>
|
||||
/// <param name="data">The data.</param>
|
||||
/// <param name="offset">The offset into <paramref name="data"/>.</param>
|
||||
/// <param name="byteOrder">The byte order of <paramref name="data"/>.</param>
|
||||
/// <returns>A <see cref="ImageFileDirectoryEntry"/> initialized from the given byte data.</returns>
|
||||
public static ImageFileDirectoryEntry FromBytes(byte[] data, uint offset, BitConverterEx.ByteOrder byteOrder)
|
||||
{
|
||||
// Tag ID
|
||||
ushort tag = BitConverterEx.ToUInt16(data, offset, byteOrder, BitConverterEx.SystemByteOrder);
|
||||
|
||||
// Tag Type
|
||||
ushort type = BitConverterEx.ToUInt16(data, offset + 2, byteOrder, BitConverterEx.SystemByteOrder);
|
||||
|
||||
// Count of Type
|
||||
uint count = BitConverterEx.ToUInt32(data, offset + 4, byteOrder, BitConverterEx.SystemByteOrder);
|
||||
|
||||
// Field value or offset to field data
|
||||
byte[] value = new byte[4];
|
||||
Array.Copy(data, offset + 8, value, 0, 4);
|
||||
|
||||
// Calculate the bytes we need to read
|
||||
uint baselength = GetBaseLength(type);
|
||||
uint totallength = count * baselength;
|
||||
|
||||
// If field value does not fit in 4 bytes
|
||||
// the value field is an offset to the actual
|
||||
// field value
|
||||
if (totallength > 4)
|
||||
{
|
||||
uint dataoffset = BitConverterEx.ToUInt32(value, 0, byteOrder, BitConverterEx.SystemByteOrder);
|
||||
value = new byte[totallength];
|
||||
Array.Copy(data, dataoffset, value, 0, totallength);
|
||||
}
|
||||
|
||||
// Reverse array order if byte orders are different
|
||||
if (byteOrder != BitConverterEx.SystemByteOrder)
|
||||
{
|
||||
for (uint i = 0; i < count; i++)
|
||||
{
|
||||
byte[] val = new byte[baselength];
|
||||
Array.Copy(value, i * baselength, val, 0, baselength);
|
||||
Array.Reverse(val);
|
||||
Array.Copy(val, 0, value, i * baselength, baselength);
|
||||
}
|
||||
}
|
||||
|
||||
return new ImageFileDirectoryEntry(tag, type, count, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the base byte length for the given type.
|
||||
/// </summary>
|
||||
/// <param name="type">Type identifier.</param>
|
||||
private static uint GetBaseLength(ushort type)
|
||||
{
|
||||
if (type == 1 || type == 6) // BYTE and SBYTE
|
||||
return 1;
|
||||
else if (type == 2 || type == 7) // ASCII and UNDEFINED
|
||||
return 1;
|
||||
else if (type == 3 || type == 8) // SHORT and SSHORT
|
||||
return 2;
|
||||
else if (type == 4 || type == 9) // LONG and SLONG
|
||||
return 4;
|
||||
else if (type == 5 || type == 10) // RATIONAL (2xLONG) and SRATIONAL (2xSLONG)
|
||||
return 8;
|
||||
else if (type == 11) // FLOAT
|
||||
return 4;
|
||||
else if (type == 12) // DOUBLE
|
||||
return 8;
|
||||
|
||||
throw new ArgumentException("Unknown type identifier.", "type");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
namespace Umbraco.Web.Media.Exif
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the format of the <see cref="ImageFile"/>.
|
||||
/// </summary>
|
||||
internal enum ImageFileFormat
|
||||
{
|
||||
/// <summary>
|
||||
/// The file is not recognized.
|
||||
/// </summary>
|
||||
Unknown,
|
||||
/// <summary>
|
||||
/// The file is a JPEG/Exif or JPEG/JFIF file.
|
||||
/// </summary>
|
||||
JPEG,
|
||||
/// <summary>
|
||||
/// The file is a TIFF File.
|
||||
/// </summary>
|
||||
TIFF,
|
||||
/// <summary>
|
||||
/// The file is a SVG File.
|
||||
/// </summary>
|
||||
SVG,
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
namespace Umbraco.Web.Media.Exif
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the units for the X and Y densities
|
||||
/// for a JFIF file.
|
||||
/// </summary>
|
||||
internal enum JFIFDensityUnit : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// No units, XDensity and YDensity specify the pixel aspect ratio.
|
||||
/// </summary>
|
||||
None = 0,
|
||||
/// <summary>
|
||||
/// XDensity and YDensity are dots per inch.
|
||||
/// </summary>
|
||||
DotsPerInch = 1,
|
||||
/// <summary>
|
||||
/// XDensity and YDensity are dots per cm.
|
||||
/// </summary>
|
||||
DotsPerCm = 2,
|
||||
}
|
||||
/// <summary>
|
||||
/// Represents the JFIF extension.
|
||||
/// </summary>
|
||||
internal enum JFIFExtension : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// Thumbnail coded using JPEG.
|
||||
/// </summary>
|
||||
ThumbnailJPEG = 0x10,
|
||||
/// <summary>
|
||||
/// Thumbnail stored using a 256-Color RGB palette.
|
||||
/// </summary>
|
||||
ThumbnailPaletteRGB = 0x11,
|
||||
/// <summary>
|
||||
/// Thumbnail stored using 3 bytes/pixel (24-bit) RGB values.
|
||||
/// </summary>
|
||||
Thumbnail24BitRGB = 0x13,
|
||||
}
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace Umbraco.Web.Media.Exif
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the JFIF version as a 16 bit unsigned integer. (EXIF Specification: SHORT)
|
||||
/// </summary>
|
||||
internal class JFIFVersion : ExifUShort
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the major version.
|
||||
/// </summary>
|
||||
public byte Major { get { return (byte)(mValue >> 8); } }
|
||||
/// <summary>
|
||||
/// Gets the minor version.
|
||||
/// </summary>
|
||||
public byte Minor { get { return (byte)(mValue - (mValue >> 8) * 256); } }
|
||||
|
||||
public JFIFVersion(ExifTag tag, ushort value)
|
||||
: base(tag, value)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("{0}.{1:00}", Major, Minor);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Represents a JFIF thumbnail. (EXIF Specification: BYTE)
|
||||
/// </summary>
|
||||
internal class JFIFThumbnailProperty : ExifProperty
|
||||
{
|
||||
protected JFIFThumbnail mValue;
|
||||
protected override object _Value { get { return Value; } set { Value = (JFIFThumbnail)value; } }
|
||||
public new JFIFThumbnail Value { get { return mValue; } set { mValue = value; } }
|
||||
|
||||
public override string ToString() { return mValue.Format.ToString(); }
|
||||
|
||||
public JFIFThumbnailProperty(ExifTag tag, JFIFThumbnail value)
|
||||
: base(tag)
|
||||
{
|
||||
mValue = value;
|
||||
}
|
||||
|
||||
public override ExifInterOperability Interoperability
|
||||
{
|
||||
get
|
||||
{
|
||||
if (mValue.Format == JFIFThumbnail.ImageFormat.BMP24Bit)
|
||||
return new ExifInterOperability(ExifTagFactory.GetTagID(mTag), 1, (uint)mValue.PixelData.Length, mValue.PixelData);
|
||||
else if (mValue.Format == JFIFThumbnail.ImageFormat.BMPPalette)
|
||||
{
|
||||
byte[] data = new byte[mValue.Palette.Length + mValue.PixelData.Length];
|
||||
Array.Copy(mValue.Palette, data, mValue.Palette.Length);
|
||||
Array.Copy(mValue.PixelData, 0, data, mValue.Palette.Length, mValue.PixelData.Length);
|
||||
return new ExifInterOperability(ExifTagFactory.GetTagID(mTag), 1, (uint)data.Length, data);
|
||||
}
|
||||
else if (mValue.Format == JFIFThumbnail.ImageFormat.JPEG)
|
||||
return new ExifInterOperability(ExifTagFactory.GetTagID(mTag), 1, (uint)mValue.PixelData.Length, mValue.PixelData);
|
||||
else
|
||||
throw new InvalidOperationException("Unknown thumbnail type.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
namespace Umbraco.Web.Media.Exif
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a JFIF thumbnail.
|
||||
/// </summary>
|
||||
internal class JFIFThumbnail
|
||||
{
|
||||
#region Properties
|
||||
/// <summary>
|
||||
/// Gets the 256 color RGB palette.
|
||||
/// </summary>
|
||||
public byte[] Palette { get; private set; }
|
||||
/// <summary>
|
||||
/// Gets raw image data.
|
||||
/// </summary>
|
||||
public byte[] PixelData { get; private set; }
|
||||
/// <summary>
|
||||
/// Gets the image format.
|
||||
/// </summary>
|
||||
public ImageFormat Format { get; private set; }
|
||||
#endregion
|
||||
|
||||
#region Public Enums
|
||||
public enum ImageFormat
|
||||
{
|
||||
JPEG,
|
||||
BMPPalette,
|
||||
BMP24Bit,
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
protected JFIFThumbnail()
|
||||
{
|
||||
Palette = new byte[0];
|
||||
PixelData = new byte[0];
|
||||
}
|
||||
|
||||
public JFIFThumbnail(ImageFormat format, byte[] data)
|
||||
: this()
|
||||
{
|
||||
Format = format;
|
||||
PixelData = data;
|
||||
}
|
||||
|
||||
public JFIFThumbnail(byte[] palette, byte[] data)
|
||||
: this()
|
||||
{
|
||||
Format = ImageFormat.BMPPalette;
|
||||
Palette = palette;
|
||||
PixelData = data;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,99 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace Umbraco.Web.Media.Exif
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// The exception that is thrown when the format of the image file
|
||||
/// could not be understood.
|
||||
/// </summary>
|
||||
internal class NotValidImageFileException : Exception
|
||||
{
|
||||
public NotValidImageFileException()
|
||||
: base("Not a valid image file.")
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
public NotValidImageFileException(string message)
|
||||
: base(message)
|
||||
{
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The exception that is thrown when the format of the JPEG file
|
||||
/// could not be understood.
|
||||
/// </summary>
|
||||
internal class NotValidJPEGFileException : Exception
|
||||
{
|
||||
public NotValidJPEGFileException()
|
||||
: base("Not a valid JPEG file.")
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
public NotValidJPEGFileException(string message)
|
||||
: base(message)
|
||||
{
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The exception that is thrown when the format of the TIFF file
|
||||
/// could not be understood.
|
||||
/// </summary>
|
||||
internal class NotValidTIFFileException : Exception
|
||||
{
|
||||
public NotValidTIFFileException()
|
||||
: base("Not a valid TIFF file.")
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
public NotValidTIFFileException(string message)
|
||||
: base(message)
|
||||
{
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The exception that is thrown when the format of the TIFF header
|
||||
/// could not be understood.
|
||||
/// </summary>
|
||||
internal class NotValidTIFFHeader : Exception
|
||||
{
|
||||
public NotValidTIFFHeader()
|
||||
: base("Not a valid TIFF header.")
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
public NotValidTIFFHeader(string message)
|
||||
: base(message)
|
||||
{
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The exception that is thrown when the length of a section exceeds 64 kB.
|
||||
/// </summary>
|
||||
internal class SectionExceeds64KBException : Exception
|
||||
{
|
||||
public SectionExceeds64KBException()
|
||||
: base("Section length exceeds 64 kB.")
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
public SectionExceeds64KBException(string message)
|
||||
: base(message)
|
||||
{
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,918 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace Umbraco.Web.Media.Exif
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the binary view of a JPEG compressed file.
|
||||
/// </summary>
|
||||
internal class JPEGFile : ImageFile
|
||||
{
|
||||
#region Member Variables
|
||||
private JPEGSection jfifApp0;
|
||||
private JPEGSection jfxxApp0;
|
||||
private JPEGSection exifApp1;
|
||||
private uint makerNoteOffset;
|
||||
private long exifIFDFieldOffset, gpsIFDFieldOffset, interopIFDFieldOffset, firstIFDFieldOffset;
|
||||
private long thumbOffsetLocation, thumbSizeLocation;
|
||||
private uint thumbOffsetValue, thumbSizeValue;
|
||||
private bool makerNoteProcessed;
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
/// <summary>
|
||||
/// Gets or sets the byte-order of the Exif properties.
|
||||
/// </summary>
|
||||
public BitConverterEx.ByteOrder ByteOrder { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the sections contained in the <see cref="ImageFile"/>.
|
||||
/// </summary>
|
||||
public List<JPEGSection> Sections { get; private set; }
|
||||
/// <summary>
|
||||
/// Gets or sets non-standard trailing data following the End of Image (EOI) marker.
|
||||
/// </summary>
|
||||
public byte[] TrailingData { get; private set; }
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ExifFile"/> class.
|
||||
/// </summary>
|
||||
/// <param name="stream">A <see cref="Sytem.IO.Stream"/> that contains image data.</param>
|
||||
/// <param name="encoding">The encoding to be used for text metadata when the source encoding is unknown.</param>
|
||||
protected internal JPEGFile(Stream stream, Encoding encoding)
|
||||
{
|
||||
Format = ImageFileFormat.JPEG;
|
||||
Sections = new List<JPEGSection>();
|
||||
TrailingData = new byte[0];
|
||||
Encoding = encoding;
|
||||
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
// Read the Start of Image (SOI) marker. SOI marker is represented
|
||||
// with two bytes: 0xFF, 0xD8.
|
||||
byte[] markerbytes = new byte[2];
|
||||
if (stream.Read(markerbytes, 0, 2) != 2 || markerbytes[0] != 0xFF || markerbytes[1] != 0xD8)
|
||||
throw new NotValidJPEGFileException();
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
// Search and read sections until we reach the end of file.
|
||||
while (stream.Position != stream.Length)
|
||||
{
|
||||
// Read the next section marker. Section markers are two bytes
|
||||
// with values 0xFF, 0x?? where ?? must not be 0x00 or 0xFF.
|
||||
if (stream.Read(markerbytes, 0, 2) != 2 || markerbytes[0] != 0xFF || markerbytes[1] == 0x00 || markerbytes[1] == 0xFF)
|
||||
throw new NotValidJPEGFileException();
|
||||
|
||||
JPEGMarker marker = (JPEGMarker)markerbytes[1];
|
||||
|
||||
byte[] header = new byte[0];
|
||||
// SOI, EOI and RST markers do not contain any header
|
||||
if (marker != JPEGMarker.SOI && marker != JPEGMarker.EOI && !(marker >= JPEGMarker.RST0 && marker <= JPEGMarker.RST7))
|
||||
{
|
||||
// Length of the header including the length bytes.
|
||||
// This value is a 16-bit unsigned integer
|
||||
// in big endian byte-order.
|
||||
byte[] lengthbytes = new byte[2];
|
||||
if (stream.Read(lengthbytes, 0, 2) != 2)
|
||||
throw new NotValidJPEGFileException();
|
||||
long length = (long)BitConverterEx.BigEndian.ToUInt16(lengthbytes, 0);
|
||||
|
||||
// Read section header.
|
||||
header = new byte[length - 2];
|
||||
int bytestoread = header.Length;
|
||||
while (bytestoread > 0)
|
||||
{
|
||||
int count = Math.Min(bytestoread, 4 * 1024);
|
||||
int bytesread = stream.Read(header, header.Length - bytestoread, count);
|
||||
if (bytesread == 0)
|
||||
throw new NotValidJPEGFileException();
|
||||
bytestoread -= bytesread;
|
||||
}
|
||||
}
|
||||
|
||||
// Start of Scan (SOS) sections and RST sections are immediately
|
||||
// followed by entropy coded data. For that, we need to read until
|
||||
// the next section marker once we reach a SOS or RST.
|
||||
byte[] entropydata = new byte[0];
|
||||
if (marker == JPEGMarker.SOS || (marker >= JPEGMarker.RST0 && marker <= JPEGMarker.RST7))
|
||||
{
|
||||
long position = stream.Position;
|
||||
|
||||
// Search for the next section marker
|
||||
while (true)
|
||||
{
|
||||
// Search for an 0xFF indicating start of a marker
|
||||
int nextbyte = 0;
|
||||
do
|
||||
{
|
||||
nextbyte = stream.ReadByte();
|
||||
if (nextbyte == -1)
|
||||
throw new NotValidJPEGFileException();
|
||||
} while ((byte)nextbyte != 0xFF);
|
||||
|
||||
// Skip filler bytes (0xFF)
|
||||
do
|
||||
{
|
||||
nextbyte = stream.ReadByte();
|
||||
if (nextbyte == -1)
|
||||
throw new NotValidJPEGFileException();
|
||||
} while ((byte)nextbyte == 0xFF);
|
||||
|
||||
// Looks like a section marker. The next byte must not be 0x00.
|
||||
if ((byte)nextbyte != 0x00)
|
||||
{
|
||||
// We reached a section marker. Calculate the
|
||||
// length of the entropy coded data.
|
||||
stream.Seek(-2, SeekOrigin.Current);
|
||||
long edlength = stream.Position - position;
|
||||
stream.Seek(-edlength, SeekOrigin.Current);
|
||||
|
||||
// Read entropy coded data
|
||||
entropydata = new byte[edlength];
|
||||
int bytestoread = entropydata.Length;
|
||||
while (bytestoread > 0)
|
||||
{
|
||||
int count = Math.Min(bytestoread, 4 * 1024);
|
||||
int bytesread = stream.Read(entropydata, entropydata.Length - bytestoread, count);
|
||||
if (bytesread == 0)
|
||||
throw new NotValidJPEGFileException();
|
||||
bytestoread -= bytesread;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Store section.
|
||||
JPEGSection section = new JPEGSection(marker, header, entropydata);
|
||||
Sections.Add(section);
|
||||
|
||||
// Some propriety formats store data past the EOI marker
|
||||
if (marker == JPEGMarker.EOI)
|
||||
{
|
||||
int bytestoread = (int)(stream.Length - stream.Position);
|
||||
TrailingData = new byte[bytestoread];
|
||||
while (bytestoread > 0)
|
||||
{
|
||||
int count = (int)Math.Min(bytestoread, 4 * 1024);
|
||||
int bytesread = stream.Read(TrailingData, TrailingData.Length - bytestoread, count);
|
||||
if (bytesread == 0)
|
||||
throw new NotValidJPEGFileException();
|
||||
bytestoread -= bytesread;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Read metadata sections
|
||||
ReadJFIFAPP0();
|
||||
ReadJFXXAPP0();
|
||||
ReadExifAPP1();
|
||||
|
||||
// Process the maker note
|
||||
makerNoteProcessed = false;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Instance Methods
|
||||
/// <summary>
|
||||
/// Saves the JPEG/Exif image to the given stream.
|
||||
/// </summary>
|
||||
/// <param name="filename">The path to the JPEG/Exif file.</param>
|
||||
/// <param name="preserveMakerNote">Determines whether the maker note offset of
|
||||
/// the original file will be preserved.</param>
|
||||
public void Save(Stream stream, bool preserveMakerNote)
|
||||
{
|
||||
WriteJFIFApp0();
|
||||
WriteJFXXApp0();
|
||||
WriteExifApp1(preserveMakerNote);
|
||||
|
||||
// Write sections
|
||||
foreach (JPEGSection section in Sections)
|
||||
{
|
||||
// Section header (including length bytes and section marker)
|
||||
// must not exceed 64 kB.
|
||||
if (section.Header.Length + 2 + 2 > 64 * 1024)
|
||||
throw new SectionExceeds64KBException();
|
||||
|
||||
// APP sections must have a header.
|
||||
// Otherwise skip the entire section.
|
||||
if (section.Marker >= JPEGMarker.APP0 && section.Marker <= JPEGMarker.APP15 && section.Header.Length == 0)
|
||||
continue;
|
||||
|
||||
// Write section marker
|
||||
stream.Write(new byte[] { 0xFF, (byte)section.Marker }, 0, 2);
|
||||
|
||||
// SOI, EOI and RST markers do not contain any header
|
||||
if (section.Marker != JPEGMarker.SOI && section.Marker != JPEGMarker.EOI && !(section.Marker >= JPEGMarker.RST0 && section.Marker <= JPEGMarker.RST7))
|
||||
{
|
||||
// Header length including the length field itself
|
||||
stream.Write(BitConverterEx.BigEndian.GetBytes((ushort)(section.Header.Length + 2)), 0, 2);
|
||||
|
||||
// Write section header
|
||||
if (section.Header.Length != 0)
|
||||
stream.Write(section.Header, 0, section.Header.Length);
|
||||
}
|
||||
|
||||
// Write entropy coded data
|
||||
if (section.EntropyData.Length != 0)
|
||||
stream.Write(section.EntropyData, 0, section.EntropyData.Length);
|
||||
}
|
||||
|
||||
// Write trailing data, if any
|
||||
if (TrailingData.Length != 0)
|
||||
stream.Write(TrailingData, 0, TrailingData.Length);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves the JPEG/Exif image with the given filename.
|
||||
/// </summary>
|
||||
/// <param name="filename">The path to the JPEG/Exif file.</param>
|
||||
/// <param name="preserveMakerNote">Determines whether the maker note offset of
|
||||
/// the original file will be preserved.</param>
|
||||
public void Save(string filename, bool preserveMakerNote)
|
||||
{
|
||||
using (FileStream stream = new FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.None))
|
||||
{
|
||||
Save(stream, preserveMakerNote);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves the JPEG/Exif image with the given filename.
|
||||
/// </summary>
|
||||
/// <param name="filename">The path to the JPEG/Exif file.</param>
|
||||
public override void Save(string filename)
|
||||
{
|
||||
Save(filename, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves the JPEG/Exif image to the given stream.
|
||||
/// </summary>
|
||||
/// <param name="filename">The path to the JPEG/Exif file.</param>
|
||||
public override void Save(Stream stream)
|
||||
{
|
||||
Save(stream, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a System.Drawing.Image created with image data.
|
||||
/// </summary>
|
||||
public override Image ToImage()
|
||||
{
|
||||
MemoryStream stream = new MemoryStream();
|
||||
Save(stream);
|
||||
return Image.FromStream(stream);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Private Helper Methods
|
||||
/// <summary>
|
||||
/// Reads the APP0 section containing JFIF metadata.
|
||||
/// </summary>
|
||||
private void ReadJFIFAPP0()
|
||||
{
|
||||
// Find the APP0 section containing JFIF metadata
|
||||
jfifApp0 = Sections.Find(a => (a.Marker == JPEGMarker.APP0) &&
|
||||
a.Header.Length >= 5 &&
|
||||
(Encoding.ASCII.GetString(a.Header, 0, 5) == "JFIF\0"));
|
||||
|
||||
// If there is no APP0 section, return.
|
||||
if (jfifApp0 == null)
|
||||
return;
|
||||
|
||||
byte[] header = jfifApp0.Header;
|
||||
BitConverterEx jfifConv = BitConverterEx.BigEndian;
|
||||
|
||||
// Version
|
||||
ushort version = jfifConv.ToUInt16(header, 5);
|
||||
Properties.Add(new JFIFVersion(ExifTag.JFIFVersion, version));
|
||||
|
||||
// Units
|
||||
byte unit = header[7];
|
||||
Properties.Add(new ExifEnumProperty<JFIFDensityUnit>(ExifTag.JFIFUnits, (JFIFDensityUnit)unit));
|
||||
|
||||
// X and Y densities
|
||||
ushort xdensity = jfifConv.ToUInt16(header, 8);
|
||||
Properties.Add(new ExifUShort(ExifTag.XDensity, xdensity));
|
||||
ushort ydensity = jfifConv.ToUInt16(header, 10);
|
||||
Properties.Add(new ExifUShort(ExifTag.YDensity, ydensity));
|
||||
|
||||
// Thumbnails pixel count
|
||||
byte xthumbnail = header[12];
|
||||
Properties.Add(new ExifByte(ExifTag.JFIFXThumbnail, xthumbnail));
|
||||
byte ythumbnail = header[13];
|
||||
Properties.Add(new ExifByte(ExifTag.JFIFYThumbnail, ythumbnail));
|
||||
|
||||
// Read JFIF thumbnail
|
||||
int n = xthumbnail * ythumbnail;
|
||||
byte[] jfifThumbnail = new byte[n];
|
||||
Array.Copy(header, 14, jfifThumbnail, 0, n);
|
||||
Properties.Add(new JFIFThumbnailProperty(ExifTag.JFIFThumbnail, new JFIFThumbnail(JFIFThumbnail.ImageFormat.JPEG, jfifThumbnail)));
|
||||
}
|
||||
/// <summary>
|
||||
/// Replaces the contents of the APP0 section with the JFIF properties.
|
||||
/// </summary>
|
||||
private bool WriteJFIFApp0()
|
||||
{
|
||||
// Which IFD sections do we have?
|
||||
List<ExifProperty> ifdjfef = new List<ExifProperty>();
|
||||
foreach (ExifProperty prop in Properties)
|
||||
{
|
||||
if (prop.IFD == IFD.JFIF)
|
||||
ifdjfef.Add(prop);
|
||||
}
|
||||
|
||||
if (ifdjfef.Count == 0)
|
||||
{
|
||||
// Nothing to write
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Create a memory stream to write the APP0 section to
|
||||
MemoryStream ms = new MemoryStream();
|
||||
|
||||
// JFIF identifer
|
||||
ms.Write(Encoding.ASCII.GetBytes("JFIF\0"), 0, 5);
|
||||
|
||||
// Write tags
|
||||
foreach (ExifProperty prop in ifdjfef)
|
||||
{
|
||||
ExifInterOperability interop = prop.Interoperability;
|
||||
byte[] data = interop.Data;
|
||||
if (BitConverterEx.SystemByteOrder != BitConverterEx.ByteOrder.BigEndian && interop.TypeID == 3)
|
||||
Array.Reverse(data);
|
||||
ms.Write(data, 0, data.Length);
|
||||
}
|
||||
|
||||
ms.Close();
|
||||
|
||||
// Return APP0 header
|
||||
jfifApp0.Header = ms.ToArray();
|
||||
return true;
|
||||
}
|
||||
/// <summary>
|
||||
/// Reads the APP0 section containing JFIF extension metadata.
|
||||
/// </summary>
|
||||
private void ReadJFXXAPP0()
|
||||
{
|
||||
// Find the APP0 section containing JFIF metadata
|
||||
jfxxApp0 = Sections.Find(a => (a.Marker == JPEGMarker.APP0) &&
|
||||
a.Header.Length >= 5 &&
|
||||
(Encoding.ASCII.GetString(a.Header, 0, 5) == "JFXX\0"));
|
||||
|
||||
// If there is no APP0 section, return.
|
||||
if (jfxxApp0 == null)
|
||||
return;
|
||||
|
||||
byte[] header = jfxxApp0.Header;
|
||||
|
||||
// Version
|
||||
JFIFExtension version = (JFIFExtension)header[5];
|
||||
Properties.Add(new ExifEnumProperty<JFIFExtension>(ExifTag.JFXXExtensionCode, version));
|
||||
|
||||
// Read thumbnail
|
||||
if (version == JFIFExtension.ThumbnailJPEG)
|
||||
{
|
||||
byte[] data = new byte[header.Length - 6];
|
||||
Array.Copy(header, 6, data, 0, data.Length);
|
||||
Properties.Add(new JFIFThumbnailProperty(ExifTag.JFXXThumbnail, new JFIFThumbnail(JFIFThumbnail.ImageFormat.JPEG, data)));
|
||||
}
|
||||
else if (version == JFIFExtension.Thumbnail24BitRGB)
|
||||
{
|
||||
// Thumbnails pixel count
|
||||
byte xthumbnail = header[6];
|
||||
Properties.Add(new ExifByte(ExifTag.JFXXXThumbnail, xthumbnail));
|
||||
byte ythumbnail = header[7];
|
||||
Properties.Add(new ExifByte(ExifTag.JFXXYThumbnail, ythumbnail));
|
||||
byte[] data = new byte[3 * xthumbnail * ythumbnail];
|
||||
Array.Copy(header, 8, data, 0, data.Length);
|
||||
Properties.Add(new JFIFThumbnailProperty(ExifTag.JFXXThumbnail, new JFIFThumbnail(JFIFThumbnail.ImageFormat.BMP24Bit, data)));
|
||||
}
|
||||
else if (version == JFIFExtension.ThumbnailPaletteRGB)
|
||||
{
|
||||
// Thumbnails pixel count
|
||||
byte xthumbnail = header[6];
|
||||
Properties.Add(new ExifByte(ExifTag.JFXXXThumbnail, xthumbnail));
|
||||
byte ythumbnail = header[7];
|
||||
Properties.Add(new ExifByte(ExifTag.JFXXYThumbnail, ythumbnail));
|
||||
byte[] palette = new byte[768];
|
||||
Array.Copy(header, 8, palette, 0, palette.Length);
|
||||
byte[] data = new byte[xthumbnail * ythumbnail];
|
||||
Array.Copy(header, 8 + 768, data, 0, data.Length);
|
||||
Properties.Add(new JFIFThumbnailProperty(ExifTag.JFXXThumbnail, new JFIFThumbnail(palette, data)));
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Replaces the contents of the APP0 section with the JFIF extension properties.
|
||||
/// </summary>
|
||||
private bool WriteJFXXApp0()
|
||||
{
|
||||
// Which IFD sections do we have?
|
||||
List<ExifProperty> ifdjfef = new List<ExifProperty>();
|
||||
foreach (ExifProperty prop in Properties)
|
||||
{
|
||||
if (prop.IFD == IFD.JFXX)
|
||||
ifdjfef.Add(prop);
|
||||
}
|
||||
|
||||
if (ifdjfef.Count == 0)
|
||||
{
|
||||
// Nothing to write
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create a memory stream to write the APP0 section to
|
||||
MemoryStream ms = new MemoryStream();
|
||||
|
||||
// JFIF identifer
|
||||
ms.Write(Encoding.ASCII.GetBytes("JFXX\0"), 0, 5);
|
||||
|
||||
// Write tags
|
||||
foreach (ExifProperty prop in ifdjfef)
|
||||
{
|
||||
ExifInterOperability interop = prop.Interoperability;
|
||||
byte[] data = interop.Data;
|
||||
if (BitConverterEx.SystemByteOrder != BitConverterEx.ByteOrder.BigEndian && interop.TypeID == 3)
|
||||
Array.Reverse(data);
|
||||
ms.Write(data, 0, data.Length);
|
||||
}
|
||||
|
||||
ms.Close();
|
||||
|
||||
// Return APP0 header
|
||||
jfxxApp0.Header = ms.ToArray();
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the APP1 section containing Exif metadata.
|
||||
/// </summary>
|
||||
private void ReadExifAPP1()
|
||||
{
|
||||
// Find the APP1 section containing Exif metadata
|
||||
exifApp1 = Sections.Find(a => (a.Marker == JPEGMarker.APP1) &&
|
||||
a.Header.Length >= 6 &&
|
||||
(Encoding.ASCII.GetString(a.Header, 0, 6) == "Exif\0\0"));
|
||||
|
||||
// If there is no APP1 section, add a new one after the last APP0 section (if any).
|
||||
if (exifApp1 == null)
|
||||
{
|
||||
int insertionIndex = Sections.FindLastIndex(a => a.Marker == JPEGMarker.APP0);
|
||||
if (insertionIndex == -1) insertionIndex = 0;
|
||||
insertionIndex++;
|
||||
exifApp1 = new JPEGSection(JPEGMarker.APP1);
|
||||
Sections.Insert(insertionIndex, exifApp1);
|
||||
if (BitConverterEx.SystemByteOrder == BitConverterEx.ByteOrder.LittleEndian)
|
||||
ByteOrder = BitConverterEx.ByteOrder.LittleEndian;
|
||||
else
|
||||
ByteOrder = BitConverterEx.ByteOrder.BigEndian;
|
||||
return;
|
||||
}
|
||||
|
||||
byte[] header = exifApp1.Header;
|
||||
SortedList<int, IFD> ifdqueue = new SortedList<int, IFD>();
|
||||
makerNoteOffset = 0;
|
||||
|
||||
// TIFF header
|
||||
int tiffoffset = 6;
|
||||
if (header[tiffoffset] == 0x49 && header[tiffoffset + 1] == 0x49)
|
||||
ByteOrder = BitConverterEx.ByteOrder.LittleEndian;
|
||||
else if (header[tiffoffset] == 0x4D && header[tiffoffset + 1] == 0x4D)
|
||||
ByteOrder = BitConverterEx.ByteOrder.BigEndian;
|
||||
else
|
||||
throw new NotValidExifFileException();
|
||||
|
||||
// TIFF header may have a different byte order
|
||||
BitConverterEx.ByteOrder tiffByteOrder = ByteOrder;
|
||||
if (BitConverterEx.LittleEndian.ToUInt16(header, tiffoffset + 2) == 42)
|
||||
tiffByteOrder = BitConverterEx.ByteOrder.LittleEndian;
|
||||
else if (BitConverterEx.BigEndian.ToUInt16(header, tiffoffset + 2) == 42)
|
||||
tiffByteOrder = BitConverterEx.ByteOrder.BigEndian;
|
||||
else
|
||||
throw new NotValidExifFileException();
|
||||
|
||||
// Offset to 0th IFD
|
||||
int ifd0offset = (int)BitConverterEx.ToUInt32(header, tiffoffset + 4, tiffByteOrder, BitConverterEx.SystemByteOrder);
|
||||
ifdqueue.Add(ifd0offset, IFD.Zeroth);
|
||||
|
||||
BitConverterEx conv = new BitConverterEx(ByteOrder, BitConverterEx.SystemByteOrder);
|
||||
int thumboffset = -1;
|
||||
int thumblength = 0;
|
||||
int thumbtype = -1;
|
||||
// Read IFDs
|
||||
while (ifdqueue.Count != 0)
|
||||
{
|
||||
int ifdoffset = tiffoffset + ifdqueue.Keys[0];
|
||||
IFD currentifd = ifdqueue.Values[0];
|
||||
ifdqueue.RemoveAt(0);
|
||||
|
||||
// Field count
|
||||
ushort fieldcount = conv.ToUInt16(header, ifdoffset);
|
||||
for (short i = 0; i < fieldcount; i++)
|
||||
{
|
||||
// Read field info
|
||||
int fieldoffset = ifdoffset + 2 + 12 * i;
|
||||
ushort tag = conv.ToUInt16(header, fieldoffset);
|
||||
ushort type = conv.ToUInt16(header, fieldoffset + 2);
|
||||
uint count = conv.ToUInt32(header, fieldoffset + 4);
|
||||
byte[] value = new byte[4];
|
||||
Array.Copy(header, fieldoffset + 8, value, 0, 4);
|
||||
|
||||
// Fields containing offsets to other IFDs
|
||||
if (currentifd == IFD.Zeroth && tag == 0x8769)
|
||||
{
|
||||
int exififdpointer = (int)conv.ToUInt32(value, 0);
|
||||
ifdqueue.Add(exififdpointer, IFD.EXIF);
|
||||
}
|
||||
else if (currentifd == IFD.Zeroth && tag == 0x8825)
|
||||
{
|
||||
int gpsifdpointer = (int)conv.ToUInt32(value, 0);
|
||||
ifdqueue.Add(gpsifdpointer, IFD.GPS);
|
||||
}
|
||||
else if (currentifd == IFD.EXIF && tag == 0xa005)
|
||||
{
|
||||
int interopifdpointer = (int)conv.ToUInt32(value, 0);
|
||||
ifdqueue.Add(interopifdpointer, IFD.Interop);
|
||||
}
|
||||
|
||||
// Save the offset to maker note data
|
||||
if (currentifd == IFD.EXIF && tag == 37500)
|
||||
makerNoteOffset = conv.ToUInt32(value, 0);
|
||||
|
||||
// Calculate the bytes we need to read
|
||||
uint baselength = 0;
|
||||
if (type == 1 || type == 2 || type == 7)
|
||||
baselength = 1;
|
||||
else if (type == 3)
|
||||
baselength = 2;
|
||||
else if (type == 4 || type == 9)
|
||||
baselength = 4;
|
||||
else if (type == 5 || type == 10)
|
||||
baselength = 8;
|
||||
uint totallength = count * baselength;
|
||||
|
||||
// If field value does not fit in 4 bytes
|
||||
// the value field is an offset to the actual
|
||||
// field value
|
||||
int fieldposition = 0;
|
||||
if (totallength > 4)
|
||||
{
|
||||
fieldposition = tiffoffset + (int)conv.ToUInt32(value, 0);
|
||||
value = new byte[totallength];
|
||||
Array.Copy(header, fieldposition, value, 0, totallength);
|
||||
}
|
||||
|
||||
// Compressed thumbnail data
|
||||
if (currentifd == IFD.First && tag == 0x201)
|
||||
{
|
||||
thumbtype = 0;
|
||||
thumboffset = (int)conv.ToUInt32(value, 0);
|
||||
}
|
||||
else if (currentifd == IFD.First && tag == 0x202)
|
||||
thumblength = (int)conv.ToUInt32(value, 0);
|
||||
|
||||
// Uncompressed thumbnail data
|
||||
if (currentifd == IFD.First && tag == 0x111)
|
||||
{
|
||||
thumbtype = 1;
|
||||
// Offset to first strip
|
||||
if (type == 3)
|
||||
thumboffset = (int)conv.ToUInt16(value, 0);
|
||||
else
|
||||
thumboffset = (int)conv.ToUInt32(value, 0);
|
||||
}
|
||||
else if (currentifd == IFD.First && tag == 0x117)
|
||||
{
|
||||
thumblength = 0;
|
||||
for (int j = 0; j < count; j++)
|
||||
{
|
||||
if (type == 3)
|
||||
thumblength += (int)conv.ToUInt16(value, 0);
|
||||
else
|
||||
thumblength += (int)conv.ToUInt32(value, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// Create the exif property from the interop data
|
||||
ExifProperty prop = ExifPropertyFactory.Get(tag, type, count, value, ByteOrder, currentifd, Encoding);
|
||||
Properties.Add(prop);
|
||||
}
|
||||
|
||||
// 1st IFD pointer
|
||||
int firstifdpointer = (int)conv.ToUInt32(header, ifdoffset + 2 + 12 * fieldcount);
|
||||
if (firstifdpointer != 0)
|
||||
ifdqueue.Add(firstifdpointer, IFD.First);
|
||||
// Read thumbnail
|
||||
if (thumboffset != -1 && thumblength != 0 && Thumbnail == null)
|
||||
{
|
||||
if (thumbtype == 0)
|
||||
{
|
||||
using (MemoryStream ts = new MemoryStream(header, tiffoffset + thumboffset, thumblength))
|
||||
{
|
||||
Thumbnail = ImageFile.FromStream(ts);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Replaces the contents of the APP1 section with the Exif properties.
|
||||
/// </summary>
|
||||
private bool WriteExifApp1(bool preserveMakerNote)
|
||||
{
|
||||
// Zero out IFD field offsets. We will fill those as we write the IFD sections
|
||||
exifIFDFieldOffset = 0;
|
||||
gpsIFDFieldOffset = 0;
|
||||
interopIFDFieldOffset = 0;
|
||||
firstIFDFieldOffset = 0;
|
||||
// We also do not know the location of the embedded thumbnail yet
|
||||
thumbOffsetLocation = 0;
|
||||
thumbOffsetValue = 0;
|
||||
thumbSizeLocation = 0;
|
||||
thumbSizeValue = 0;
|
||||
// Write thumbnail tags if they are missing, remove otherwise
|
||||
if (Thumbnail == null)
|
||||
{
|
||||
Properties.Remove(ExifTag.ThumbnailJPEGInterchangeFormat);
|
||||
Properties.Remove(ExifTag.ThumbnailJPEGInterchangeFormatLength);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!Properties.ContainsKey(ExifTag.ThumbnailJPEGInterchangeFormat))
|
||||
Properties.Add(new ExifUInt(ExifTag.ThumbnailJPEGInterchangeFormat, 0));
|
||||
if (!Properties.ContainsKey(ExifTag.ThumbnailJPEGInterchangeFormatLength))
|
||||
Properties.Add(new ExifUInt(ExifTag.ThumbnailJPEGInterchangeFormatLength, 0));
|
||||
}
|
||||
|
||||
// Which IFD sections do we have?
|
||||
Dictionary<ExifTag, ExifProperty> ifdzeroth = new Dictionary<ExifTag, ExifProperty>();
|
||||
Dictionary<ExifTag, ExifProperty> ifdexif = new Dictionary<ExifTag, ExifProperty>();
|
||||
Dictionary<ExifTag, ExifProperty> ifdgps = new Dictionary<ExifTag, ExifProperty>();
|
||||
Dictionary<ExifTag, ExifProperty> ifdinterop = new Dictionary<ExifTag, ExifProperty>();
|
||||
Dictionary<ExifTag, ExifProperty> ifdfirst = new Dictionary<ExifTag, ExifProperty>();
|
||||
|
||||
foreach (ExifProperty prop in Properties)
|
||||
{
|
||||
switch (prop.IFD)
|
||||
{
|
||||
case IFD.Zeroth:
|
||||
ifdzeroth.Add(prop.Tag, prop);
|
||||
break;
|
||||
case IFD.EXIF:
|
||||
ifdexif.Add(prop.Tag, prop);
|
||||
break;
|
||||
case IFD.GPS:
|
||||
ifdgps.Add(prop.Tag, prop);
|
||||
break;
|
||||
case IFD.Interop:
|
||||
ifdinterop.Add(prop.Tag, prop);
|
||||
break;
|
||||
case IFD.First:
|
||||
ifdfirst.Add(prop.Tag, prop);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Add IFD pointers if they are missing
|
||||
// We will write the pointer values later on
|
||||
if (ifdexif.Count != 0 && !ifdzeroth.ContainsKey(ExifTag.EXIFIFDPointer))
|
||||
ifdzeroth.Add(ExifTag.EXIFIFDPointer, new ExifUInt(ExifTag.EXIFIFDPointer, 0));
|
||||
if (ifdgps.Count != 0 && !ifdzeroth.ContainsKey(ExifTag.GPSIFDPointer))
|
||||
ifdzeroth.Add(ExifTag.GPSIFDPointer, new ExifUInt(ExifTag.GPSIFDPointer, 0));
|
||||
if (ifdinterop.Count != 0 && !ifdexif.ContainsKey(ExifTag.InteroperabilityIFDPointer))
|
||||
ifdexif.Add(ExifTag.InteroperabilityIFDPointer, new ExifUInt(ExifTag.InteroperabilityIFDPointer, 0));
|
||||
|
||||
// Remove IFD pointers if IFD sections are missing
|
||||
if (ifdexif.Count == 0 && ifdzeroth.ContainsKey(ExifTag.EXIFIFDPointer))
|
||||
ifdzeroth.Remove(ExifTag.EXIFIFDPointer);
|
||||
if (ifdgps.Count == 0 && ifdzeroth.ContainsKey(ExifTag.GPSIFDPointer))
|
||||
ifdzeroth.Remove(ExifTag.GPSIFDPointer);
|
||||
if (ifdinterop.Count == 0 && ifdexif.ContainsKey(ExifTag.InteroperabilityIFDPointer))
|
||||
ifdexif.Remove(ExifTag.InteroperabilityIFDPointer);
|
||||
|
||||
if (ifdzeroth.Count == 0 && ifdgps.Count == 0 && ifdinterop.Count == 0 && ifdfirst.Count == 0 && Thumbnail == null)
|
||||
{
|
||||
// Nothing to write
|
||||
return false;
|
||||
}
|
||||
|
||||
// We will need these bitconverter to write byte-ordered data
|
||||
BitConverterEx bceExif = new BitConverterEx(BitConverterEx.SystemByteOrder, ByteOrder);
|
||||
|
||||
// Create a memory stream to write the APP1 section to
|
||||
MemoryStream ms = new MemoryStream();
|
||||
|
||||
// Exif identifer
|
||||
ms.Write(Encoding.ASCII.GetBytes("Exif\0\0"), 0, 6);
|
||||
|
||||
// TIFF header
|
||||
// Byte order
|
||||
long tiffoffset = ms.Position;
|
||||
ms.Write((ByteOrder == BitConverterEx.ByteOrder.LittleEndian ? new byte[] { 0x49, 0x49 } : new byte[] { 0x4D, 0x4D }), 0, 2);
|
||||
// TIFF ID
|
||||
ms.Write(bceExif.GetBytes((ushort)42), 0, 2);
|
||||
// Offset to 0th IFD
|
||||
ms.Write(bceExif.GetBytes((uint)8), 0, 4);
|
||||
|
||||
// Write IFDs
|
||||
WriteIFD(ms, ifdzeroth, IFD.Zeroth, tiffoffset, preserveMakerNote);
|
||||
uint exififdrelativeoffset = (uint)(ms.Position - tiffoffset);
|
||||
WriteIFD(ms, ifdexif, IFD.EXIF, tiffoffset, preserveMakerNote);
|
||||
uint gpsifdrelativeoffset = (uint)(ms.Position - tiffoffset);
|
||||
WriteIFD(ms, ifdgps, IFD.GPS, tiffoffset, preserveMakerNote);
|
||||
uint interopifdrelativeoffset = (uint)(ms.Position - tiffoffset);
|
||||
WriteIFD(ms, ifdinterop, IFD.Interop, tiffoffset, preserveMakerNote);
|
||||
uint firstifdrelativeoffset = (uint)(ms.Position - tiffoffset);
|
||||
WriteIFD(ms, ifdfirst, IFD.First, tiffoffset, preserveMakerNote);
|
||||
|
||||
// Now that we now the location of IFDs we can go back and write IFD offsets
|
||||
if (exifIFDFieldOffset != 0)
|
||||
{
|
||||
ms.Seek(exifIFDFieldOffset, SeekOrigin.Begin);
|
||||
ms.Write(bceExif.GetBytes(exififdrelativeoffset), 0, 4);
|
||||
}
|
||||
if (gpsIFDFieldOffset != 0)
|
||||
{
|
||||
ms.Seek(gpsIFDFieldOffset, SeekOrigin.Begin);
|
||||
ms.Write(bceExif.GetBytes(gpsifdrelativeoffset), 0, 4);
|
||||
}
|
||||
if (interopIFDFieldOffset != 0)
|
||||
{
|
||||
ms.Seek(interopIFDFieldOffset, SeekOrigin.Begin);
|
||||
ms.Write(bceExif.GetBytes(interopifdrelativeoffset), 0, 4);
|
||||
}
|
||||
if (firstIFDFieldOffset != 0)
|
||||
{
|
||||
ms.Seek(firstIFDFieldOffset, SeekOrigin.Begin);
|
||||
ms.Write(bceExif.GetBytes(firstifdrelativeoffset), 0, 4);
|
||||
}
|
||||
// We can write thumbnail location now
|
||||
if (thumbOffsetLocation != 0)
|
||||
{
|
||||
ms.Seek(thumbOffsetLocation, SeekOrigin.Begin);
|
||||
ms.Write(bceExif.GetBytes(thumbOffsetValue), 0, 4);
|
||||
}
|
||||
if (thumbSizeLocation != 0)
|
||||
{
|
||||
ms.Seek(thumbSizeLocation, SeekOrigin.Begin);
|
||||
ms.Write(bceExif.GetBytes(thumbSizeValue), 0, 4);
|
||||
}
|
||||
|
||||
ms.Close();
|
||||
|
||||
// Return APP1 header
|
||||
exifApp1.Header = ms.ToArray();
|
||||
return true;
|
||||
}
|
||||
|
||||
private void WriteIFD(MemoryStream stream, Dictionary<ExifTag, ExifProperty> ifd, IFD ifdtype, long tiffoffset, bool preserveMakerNote)
|
||||
{
|
||||
BitConverterEx conv = new BitConverterEx(BitConverterEx.SystemByteOrder, ByteOrder);
|
||||
|
||||
// Create a queue of fields to write
|
||||
Queue<ExifProperty> fieldqueue = new Queue<ExifProperty>();
|
||||
foreach (ExifProperty prop in ifd.Values)
|
||||
if (prop.Tag != ExifTag.MakerNote)
|
||||
fieldqueue.Enqueue(prop);
|
||||
// Push the maker note data to the end
|
||||
if (ifd.ContainsKey(ExifTag.MakerNote))
|
||||
fieldqueue.Enqueue(ifd[ExifTag.MakerNote]);
|
||||
|
||||
// Offset to start of field data from start of TIFF header
|
||||
uint dataoffset = (uint)(2 + ifd.Count * 12 + 4 + stream.Position - tiffoffset);
|
||||
uint currentdataoffset = dataoffset;
|
||||
long absolutedataoffset = stream.Position + (2 + ifd.Count * 12 + 4);
|
||||
|
||||
bool makernotewritten = false;
|
||||
// Field count
|
||||
stream.Write(conv.GetBytes((ushort)ifd.Count), 0, 2);
|
||||
// Fields
|
||||
while (fieldqueue.Count != 0)
|
||||
{
|
||||
ExifProperty field = fieldqueue.Dequeue();
|
||||
ExifInterOperability interop = field.Interoperability;
|
||||
|
||||
uint fillerbytecount = 0;
|
||||
|
||||
// Try to preserve the makernote data offset
|
||||
if (!makernotewritten &&
|
||||
!makerNoteProcessed &&
|
||||
makerNoteOffset != 0 &&
|
||||
ifdtype == IFD.EXIF &&
|
||||
field.Tag != ExifTag.MakerNote &&
|
||||
interop.Data.Length > 4 &&
|
||||
currentdataoffset + interop.Data.Length > makerNoteOffset &&
|
||||
ifd.ContainsKey(ExifTag.MakerNote))
|
||||
{
|
||||
// Delay writing this field until we write makernote data
|
||||
fieldqueue.Enqueue(field);
|
||||
continue;
|
||||
}
|
||||
else if (field.Tag == ExifTag.MakerNote)
|
||||
{
|
||||
makernotewritten = true;
|
||||
// We may need to write filler bytes to preserve maker note offset
|
||||
if (preserveMakerNote && !makerNoteProcessed && (makerNoteOffset > currentdataoffset))
|
||||
fillerbytecount = makerNoteOffset - currentdataoffset;
|
||||
else
|
||||
fillerbytecount = 0;
|
||||
}
|
||||
|
||||
// Tag
|
||||
stream.Write(conv.GetBytes(interop.TagID), 0, 2);
|
||||
// Type
|
||||
stream.Write(conv.GetBytes(interop.TypeID), 0, 2);
|
||||
// Count
|
||||
stream.Write(conv.GetBytes(interop.Count), 0, 4);
|
||||
// Field data
|
||||
byte[] data = interop.Data;
|
||||
if (ByteOrder != BitConverterEx.SystemByteOrder &&
|
||||
(interop.TypeID == 3 || interop.TypeID == 4 || interop.TypeID == 9 ||
|
||||
interop.TypeID == 5 || interop.TypeID == 10))
|
||||
{
|
||||
int vlen = 4;
|
||||
if (interop.TypeID == 3) vlen = 2;
|
||||
int n = data.Length / vlen;
|
||||
|
||||
for (int i = 0; i < n; i++)
|
||||
Array.Reverse(data, i * vlen, vlen);
|
||||
}
|
||||
|
||||
// Fields containing offsets to other IFDs
|
||||
// Just store their offets, we will write the values later on when we know the lengths of IFDs
|
||||
if (ifdtype == IFD.Zeroth && interop.TagID == 0x8769)
|
||||
exifIFDFieldOffset = stream.Position;
|
||||
else if (ifdtype == IFD.Zeroth && interop.TagID == 0x8825)
|
||||
gpsIFDFieldOffset = stream.Position;
|
||||
else if (ifdtype == IFD.EXIF && interop.TagID == 0xa005)
|
||||
interopIFDFieldOffset = stream.Position;
|
||||
else if (ifdtype == IFD.First && interop.TagID == 0x201)
|
||||
thumbOffsetLocation = stream.Position;
|
||||
else if (ifdtype == IFD.First && interop.TagID == 0x202)
|
||||
thumbSizeLocation = stream.Position;
|
||||
|
||||
// Write 4 byte field value or field data
|
||||
if (data.Length <= 4)
|
||||
{
|
||||
stream.Write(data, 0, data.Length);
|
||||
for (int i = data.Length; i < 4; i++)
|
||||
stream.WriteByte(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Pointer to data area relative to TIFF header
|
||||
stream.Write(conv.GetBytes(currentdataoffset + fillerbytecount), 0, 4);
|
||||
// Actual data
|
||||
long currentoffset = stream.Position;
|
||||
stream.Seek(absolutedataoffset, SeekOrigin.Begin);
|
||||
// Write filler bytes
|
||||
for (int i = 0; i < fillerbytecount; i++)
|
||||
stream.WriteByte(0xFF);
|
||||
stream.Write(data, 0, data.Length);
|
||||
stream.Seek(currentoffset, SeekOrigin.Begin);
|
||||
// Increment pointers
|
||||
currentdataoffset += fillerbytecount + (uint)data.Length;
|
||||
absolutedataoffset += fillerbytecount + data.Length;
|
||||
}
|
||||
}
|
||||
// Offset to 1st IFD
|
||||
// We will write zeros for now. This will be filled after we write all IFDs
|
||||
if (ifdtype == IFD.Zeroth)
|
||||
firstIFDFieldOffset = stream.Position;
|
||||
stream.Write(new byte[] { 0, 0, 0, 0 }, 0, 4);
|
||||
|
||||
// Seek to end of IFD
|
||||
stream.Seek(absolutedataoffset, SeekOrigin.Begin);
|
||||
|
||||
// Write thumbnail data
|
||||
if (ifdtype == IFD.First)
|
||||
{
|
||||
if (Thumbnail != null)
|
||||
{
|
||||
MemoryStream ts = new MemoryStream();
|
||||
Thumbnail.Save(ts);
|
||||
ts.Close();
|
||||
byte[] thumb = ts.ToArray();
|
||||
thumbOffsetValue = (uint)(stream.Position - tiffoffset);
|
||||
thumbSizeValue = (uint)thumb.Length;
|
||||
stream.Write(thumb, 0, thumb.Length);
|
||||
ts.Dispose();
|
||||
}
|
||||
else
|
||||
{
|
||||
thumbOffsetValue = 0;
|
||||
thumbSizeValue = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
namespace Umbraco.Web.Media.Exif
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a JPEG marker byte.
|
||||
/// </summary>
|
||||
internal enum JPEGMarker : byte
|
||||
{
|
||||
// Start Of Frame markers, non-differential, Huffman coding
|
||||
SOF0 = 0xc0,
|
||||
SOF1 = 0xc1,
|
||||
SOF2 = 0xc2,
|
||||
SOF3 = 0xc3,
|
||||
// Start Of Frame markers, differential, Huffman coding
|
||||
SOF5 = 0xc5,
|
||||
SOF6 = 0xc6,
|
||||
SOF7 = 0xc7,
|
||||
// Start Of Frame markers, non-differential, arithmetic coding
|
||||
JPG = 0xc8,
|
||||
SOF9 = 0xc9,
|
||||
SOF10 = 0xca,
|
||||
SOF11 = 0xcb,
|
||||
// Start Of Frame markers, differential, arithmetic coding
|
||||
SOF13 = 0xcd,
|
||||
SOF14 = 0xce,
|
||||
SOF15 = 0xcf,
|
||||
// Huffman table specification
|
||||
DHT = 0xc4,
|
||||
// Arithmetic coding conditioning specification
|
||||
DAC = 0xcc,
|
||||
// Restart interval termination
|
||||
RST0 = 0xd0,
|
||||
RST1 = 0xd1,
|
||||
RST2 = 0xd2,
|
||||
RST3 = 0xd3,
|
||||
RST4 = 0xd4,
|
||||
RST5 = 0xd5,
|
||||
RST6 = 0xd6,
|
||||
RST7 = 0xd7,
|
||||
// Other markers
|
||||
SOI = 0xd8,
|
||||
EOI = 0xd9,
|
||||
SOS = 0xda,
|
||||
DQT = 0xdb,
|
||||
DNL = 0xdc,
|
||||
DRI = 0xdd,
|
||||
DHP = 0xde,
|
||||
EXP = 0xdf,
|
||||
// application segments
|
||||
APP0 = 0xe0,
|
||||
APP1 = 0xe1,
|
||||
APP2 = 0xe2,
|
||||
APP3 = 0xe3,
|
||||
APP4 = 0xe4,
|
||||
APP5 = 0xe5,
|
||||
APP6 = 0xe6,
|
||||
APP7 = 0xe7,
|
||||
APP8 = 0xe8,
|
||||
APP9 = 0xe9,
|
||||
APP10 = 0xea,
|
||||
APP11 = 0xeb,
|
||||
APP12 = 0xec,
|
||||
APP13 = 0xed,
|
||||
APP14 = 0xee,
|
||||
APP15 = 0xef,
|
||||
// JPEG extensions
|
||||
JPG0 = 0xf0,
|
||||
JPG1 = 0xf1,
|
||||
JPG2 = 0xf2,
|
||||
JPG3 = 0xf3,
|
||||
JPG4 = 0xf4,
|
||||
JPG5 = 0xf5,
|
||||
JPG6 = 0xf6,
|
||||
JPG7 = 0xf7,
|
||||
JPG8 = 0xf8,
|
||||
JPG9 = 0xf9,
|
||||
JPG10 = 0xfa,
|
||||
JPG11 = 0xfb,
|
||||
JP1G2 = 0xfc,
|
||||
JPG13 = 0xfd,
|
||||
// Comment
|
||||
COM = 0xfe,
|
||||
// Temporary
|
||||
TEM = 0x01,
|
||||
}
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
namespace Umbraco.Web.Media.Exif
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the memory view of a JPEG section.
|
||||
/// A JPEG section is the data between markers of the JPEG file.
|
||||
/// </summary>
|
||||
internal class JPEGSection
|
||||
{
|
||||
#region Properties
|
||||
/// <summary>
|
||||
/// The marker byte representing the section.
|
||||
/// </summary>
|
||||
public JPEGMarker Marker { get; private set; }
|
||||
/// <summary>
|
||||
/// Section header as a byte array. This is different from the header
|
||||
/// definition in JPEG specification in that it does not include the
|
||||
/// two byte section length.
|
||||
/// </summary>
|
||||
public byte[] Header { get; set; }
|
||||
/// <summary>
|
||||
/// For the SOS and RST markers, this contains the entropy coded data.
|
||||
/// </summary>
|
||||
public byte[] EntropyData { get; set; }
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
/// <summary>
|
||||
/// Constructs a JPEGSection represented by the marker byte and containing
|
||||
/// the given data.
|
||||
/// </summary>
|
||||
/// <param name="marker">The marker byte representing the section.</param>
|
||||
/// <param name="data">Section data.</param>
|
||||
/// <param name="entropydata">Entropy coded data.</param>
|
||||
public JPEGSection(JPEGMarker marker, byte[] data, byte[] entropydata)
|
||||
{
|
||||
Marker = marker;
|
||||
Header = data;
|
||||
EntropyData = entropydata;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a JPEGSection represented by the marker byte.
|
||||
/// </summary>
|
||||
/// <param name="marker">The marker byte representing the section.</param>
|
||||
public JPEGSection(JPEGMarker marker)
|
||||
: this(marker, new byte[0], new byte[0])
|
||||
{
|
||||
;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Instance Methods
|
||||
/// <summary>
|
||||
/// Returns a string representation of the current section.
|
||||
/// </summary>
|
||||
/// <returns>A System.String that represents the current section.</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("{0} => Header: {1} bytes, Entropy Data: {2} bytes", Marker, Header.Length, EntropyData.Length);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,37 +0,0 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Umbraco.Web.Media.Exif
|
||||
{
|
||||
internal class SvgFile : ImageFile
|
||||
{
|
||||
public SvgFile(Stream fileStream)
|
||||
{
|
||||
fileStream.Position = 0;
|
||||
|
||||
var document = XDocument.Load(fileStream); //if it throws an exception the ugly try catch in MediaFileSystem will catch it
|
||||
|
||||
var width = document.Root?.Attributes().Where(x => x.Name == "width").Select(x => x.Value).FirstOrDefault();
|
||||
var height = document.Root?.Attributes().Where(x => x.Name == "height").Select(x => x.Value).FirstOrDefault();
|
||||
|
||||
Properties.Add(new ExifSInt(ExifTag.PixelYDimension,
|
||||
height == null ? Core.Constants.Conventions.Media.DefaultSize : int.Parse(height)));
|
||||
Properties.Add(new ExifSInt(ExifTag.PixelXDimension,
|
||||
width == null ? Core.Constants.Conventions.Media.DefaultSize : int.Parse(width)));
|
||||
|
||||
Format = ImageFileFormat.SVG;
|
||||
}
|
||||
|
||||
public override void Save(Stream stream)
|
||||
{
|
||||
}
|
||||
|
||||
public override Image ToImage()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,177 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
|
||||
namespace Umbraco.Web.Media.Exif
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the binary view of a TIFF file.
|
||||
/// </summary>
|
||||
internal class TIFFFile : ImageFile
|
||||
{
|
||||
#region Properties
|
||||
/// <summary>
|
||||
/// Gets the TIFF header.
|
||||
/// </summary>
|
||||
public TIFFHeader TIFFHeader { get; private set; }
|
||||
/// <summary>
|
||||
/// Gets the image file directories.
|
||||
/// </summary>
|
||||
public List<ImageFileDirectory> IFDs { get; private set; }
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="JPEGFile"/> class from the
|
||||
/// specified data stream.
|
||||
/// </summary>
|
||||
/// <param name="stream">A <see cref="Sytem.IO.Stream"/> that contains image data.</param>
|
||||
/// <param name="encoding">The encoding to be used for text metadata when the source encoding is unknown.</param>
|
||||
protected internal TIFFFile(Stream stream, System.Text.Encoding encoding)
|
||||
{
|
||||
Format = ImageFileFormat.TIFF;
|
||||
IFDs = new List<ImageFileDirectory>();
|
||||
Encoding = encoding;
|
||||
|
||||
// Read the entire stream
|
||||
byte[] data = Utility.GetStreamBytes(stream);
|
||||
|
||||
// Read the TIFF header
|
||||
TIFFHeader = TIFFHeader.FromBytes(data, 0);
|
||||
uint nextIFDOffset = TIFFHeader.IFDOffset;
|
||||
if (nextIFDOffset == 0)
|
||||
throw new NotValidTIFFileException("The first IFD offset is zero.");
|
||||
|
||||
// Read IFDs in order
|
||||
while (nextIFDOffset != 0)
|
||||
{
|
||||
ImageFileDirectory ifd = ImageFileDirectory.FromBytes(data, nextIFDOffset, TIFFHeader.ByteOrder);
|
||||
nextIFDOffset = ifd.NextIFDOffset;
|
||||
IFDs.Add(ifd);
|
||||
}
|
||||
|
||||
// Process IFDs
|
||||
// TODO: Add support for multiple frames
|
||||
foreach (ImageFileDirectoryEntry field in IFDs[0].Fields)
|
||||
{
|
||||
Properties.Add(ExifPropertyFactory.Get(field.Tag, field.Type, field.Count, field.Data, BitConverterEx.SystemByteOrder, IFD.Zeroth, Encoding));
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Instance Methods
|
||||
/// <summary>
|
||||
/// Saves the <see cref="ImageFile"/> to the given stream.
|
||||
/// </summary>
|
||||
/// <param name="stream">The data stream used to save the image.</param>
|
||||
public override void Save(Stream stream)
|
||||
{
|
||||
BitConverterEx conv = BitConverterEx.SystemEndian;
|
||||
|
||||
// Write TIFF header
|
||||
uint ifdoffset = 8;
|
||||
// Byte order
|
||||
stream.Write((BitConverterEx.SystemByteOrder == BitConverterEx.ByteOrder.LittleEndian ? new byte[] { 0x49, 0x49 } : new byte[] { 0x4D, 0x4D }), 0, 2);
|
||||
// TIFF ID
|
||||
stream.Write(conv.GetBytes((ushort)42), 0, 2);
|
||||
// Offset to 0th IFD, will be corrected below
|
||||
stream.Write(conv.GetBytes(ifdoffset), 0, 4);
|
||||
|
||||
// Write IFD sections
|
||||
for (int i = 0; i < IFDs.Count; i++)
|
||||
{
|
||||
ImageFileDirectory ifd = IFDs[i];
|
||||
|
||||
// Save the location of IFD offset
|
||||
long ifdLocation = stream.Position - 4;
|
||||
|
||||
// Write strips first
|
||||
byte[] stripOffsets = new byte[4 * ifd.Strips.Count];
|
||||
byte[] stripLengths = new byte[4 * ifd.Strips.Count];
|
||||
uint stripOffset = ifdoffset;
|
||||
for (int j = 0; j < ifd.Strips.Count; j++)
|
||||
{
|
||||
byte[] stripData = ifd.Strips[j].Data;
|
||||
byte[] oBytes = BitConverter.GetBytes(stripOffset);
|
||||
byte[] lBytes = BitConverter.GetBytes((uint)stripData.Length);
|
||||
Array.Copy(oBytes, 0, stripOffsets, 4 * j, 4);
|
||||
Array.Copy(lBytes, 0, stripLengths, 4 * j, 4);
|
||||
stream.Write(stripData, 0, stripData.Length);
|
||||
stripOffset += (uint)stripData.Length;
|
||||
}
|
||||
|
||||
// Remove old strip tags
|
||||
for (int j = ifd.Fields.Count - 1; j > 0; j--)
|
||||
{
|
||||
ushort tag = ifd.Fields[j].Tag;
|
||||
if (tag == 273 || tag == 279)
|
||||
ifd.Fields.RemoveAt(j);
|
||||
}
|
||||
// Write new strip tags
|
||||
ifd.Fields.Add(new ImageFileDirectoryEntry(273, 4, (uint)ifd.Strips.Count, stripOffsets));
|
||||
ifd.Fields.Add(new ImageFileDirectoryEntry(279, 4, (uint)ifd.Strips.Count, stripLengths));
|
||||
|
||||
// Write fields after strips
|
||||
ifdoffset = stripOffset;
|
||||
|
||||
// Correct IFD offset
|
||||
long currentLocation = stream.Position;
|
||||
stream.Seek(ifdLocation, SeekOrigin.Begin);
|
||||
stream.Write(conv.GetBytes(ifdoffset), 0, 4);
|
||||
stream.Seek(currentLocation, SeekOrigin.Begin);
|
||||
|
||||
// Offset to field data
|
||||
uint dataOffset = ifdoffset + 2 + (uint)ifd.Fields.Count * 12 + 4;
|
||||
|
||||
// Field count
|
||||
stream.Write(conv.GetBytes((ushort)ifd.Fields.Count), 0, 2);
|
||||
|
||||
// Fields
|
||||
foreach (ImageFileDirectoryEntry field in ifd.Fields)
|
||||
{
|
||||
// Tag
|
||||
stream.Write(conv.GetBytes(field.Tag), 0, 2);
|
||||
// Type
|
||||
stream.Write(conv.GetBytes(field.Type), 0, 2);
|
||||
// Count
|
||||
stream.Write(conv.GetBytes(field.Count), 0, 4);
|
||||
|
||||
// Field data
|
||||
byte[] data = field.Data;
|
||||
if (data.Length <= 4)
|
||||
{
|
||||
stream.Write(data, 0, data.Length);
|
||||
for (int j = data.Length; j < 4; j++)
|
||||
stream.WriteByte(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
stream.Write(conv.GetBytes(dataOffset), 0, 4);
|
||||
long currentOffset = stream.Position;
|
||||
stream.Seek(dataOffset, SeekOrigin.Begin);
|
||||
stream.Write(data, 0, data.Length);
|
||||
dataOffset += (uint)data.Length;
|
||||
stream.Seek(currentOffset, SeekOrigin.Begin);
|
||||
}
|
||||
}
|
||||
|
||||
// Offset to next IFD
|
||||
ifdoffset = dataOffset;
|
||||
stream.Write(conv.GetBytes(i == IFDs.Count - 1 ? 0 : ifdoffset), 0, 4);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the <see cref="ImageFile"/> to a <see cref="System.Drawing.Image"/>.
|
||||
/// </summary>
|
||||
/// <returns>Returns a <see cref="System.Drawing.Image"/> containing image data.</returns>
|
||||
public override Image ToImage()
|
||||
{
|
||||
MemoryStream stream = new MemoryStream();
|
||||
Save(stream);
|
||||
return Image.FromStream(stream);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
namespace Umbraco.Web.Media.Exif
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a TIFF Header.
|
||||
/// </summary>
|
||||
internal struct TIFFHeader
|
||||
{
|
||||
/// <summary>
|
||||
/// The byte order of the image file.
|
||||
/// </summary>
|
||||
public BitConverterEx.ByteOrder ByteOrder;
|
||||
/// <summary>
|
||||
/// TIFF ID. This value should always be 42.
|
||||
/// </summary>
|
||||
public byte ID;
|
||||
/// <summary>
|
||||
/// The offset to the first IFD section from the
|
||||
/// start of the TIFF header.
|
||||
/// </summary>
|
||||
public uint IFDOffset;
|
||||
/// <summary>
|
||||
/// The byte order of the TIFF header itself.
|
||||
/// </summary>
|
||||
public BitConverterEx.ByteOrder TIFFHeaderByteOrder;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TIFFHeader"/> struct.
|
||||
/// </summary>
|
||||
/// <param name="byteOrder">The byte order.</param>
|
||||
/// <param name="id">The TIFF ID. This value should always be 42.</param>
|
||||
/// <param name="ifdOffset">The offset to the first IFD section from the
|
||||
/// start of the TIFF header.</param>
|
||||
/// <param name="headerByteOrder">The byte order of the TIFF header itself.</param>
|
||||
public TIFFHeader(BitConverterEx.ByteOrder byteOrder, byte id, uint ifdOffset, BitConverterEx.ByteOrder headerByteOrder)
|
||||
{
|
||||
if (id != 42)
|
||||
throw new NotValidTIFFHeader();
|
||||
|
||||
ByteOrder = byteOrder;
|
||||
ID = id;
|
||||
IFDOffset = ifdOffset;
|
||||
TIFFHeaderByteOrder = headerByteOrder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a <see cref="TIFFHeader"/> initialized from the given byte data.
|
||||
/// </summary>
|
||||
/// <param name="data">The data.</param>
|
||||
/// <param name="offset">The offset into <paramref name="data"/>.</param>
|
||||
/// <returns>A <see cref="TIFFHeader"/> initialized from the given byte data.</returns>
|
||||
public static TIFFHeader FromBytes(byte[] data, int offset)
|
||||
{
|
||||
TIFFHeader header = new TIFFHeader();
|
||||
|
||||
// TIFF header
|
||||
if (data[offset] == 0x49 && data[offset + 1] == 0x49)
|
||||
header.ByteOrder = BitConverterEx.ByteOrder.LittleEndian;
|
||||
else if (data[offset] == 0x4D && data[offset + 1] == 0x4D)
|
||||
header.ByteOrder = BitConverterEx.ByteOrder.BigEndian;
|
||||
else
|
||||
throw new NotValidTIFFHeader();
|
||||
|
||||
// TIFF header may have a different byte order
|
||||
if (BitConverterEx.LittleEndian.ToUInt16(data, offset + 2) == 42)
|
||||
header.TIFFHeaderByteOrder = BitConverterEx.ByteOrder.LittleEndian;
|
||||
else if (BitConverterEx.BigEndian.ToUInt16(data, offset + 2) == 42)
|
||||
header.TIFFHeaderByteOrder = BitConverterEx.ByteOrder.BigEndian;
|
||||
else
|
||||
throw new NotValidTIFFHeader();
|
||||
header.ID = 42;
|
||||
|
||||
// IFD offset
|
||||
header.IFDOffset = BitConverterEx.ToUInt32(data, offset + 4, header.TIFFHeaderByteOrder, BitConverterEx.SystemByteOrder);
|
||||
|
||||
return header;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace Umbraco.Web.Media.Exif
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a strip of compressed image data in a TIFF file.
|
||||
/// </summary>
|
||||
internal class TIFFStrip
|
||||
{
|
||||
/// <summary>
|
||||
/// Compressed image data contained in this strip.
|
||||
/// </summary>
|
||||
public byte[] Data { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TIFFStrip"/> class.
|
||||
/// </summary>
|
||||
/// <param name="data">The byte array to copy strip from.</param>
|
||||
/// <param name="offset">The offset to the beginning of strip.</param>
|
||||
/// <param name="length">The length of strip.</param>
|
||||
public TIFFStrip(byte[] data, uint offset, uint length)
|
||||
{
|
||||
Data = new byte[length];
|
||||
Array.Copy(data, offset, Data, 0, length);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
using System.IO;
|
||||
|
||||
namespace Umbraco.Web.Media.Exif
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains utility functions.
|
||||
/// </summary>
|
||||
internal class Utility
|
||||
{
|
||||
/// <summary>
|
||||
/// Reads the entire stream and returns its contents as a byte array.
|
||||
/// </summary>
|
||||
/// <param name="stream">The <see cref="System.IO.Stream"/> to read.</param>
|
||||
/// <returns>Contents of the <paramref name="stream"/> as a byte array.</returns>
|
||||
public static byte[] GetStreamBytes(Stream stream)
|
||||
{
|
||||
using (MemoryStream mem = new MemoryStream())
|
||||
{
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
byte[] b = new byte[32768];
|
||||
int r;
|
||||
while ((r = stream.Read(b, 0, b.Length)) > 0)
|
||||
mem.Write(b, 0, r);
|
||||
|
||||
return mem.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Configuration.UmbracoSettings;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Media;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Web.Composing;
|
||||
using Umbraco.Web.Media.Exif;
|
||||
|
||||
namespace Umbraco.Web.Media
|
||||
{
|
||||
public static class ImageHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the dimensions of an image.
|
||||
/// </summary>
|
||||
/// <param name="stream">A stream containing the image bytes.</param>
|
||||
/// <returns>The dimension of the image.</returns>
|
||||
/// <remarks>First try with EXIF as it is faster and does not load the entire image
|
||||
/// in memory. Fallback to GDI which means loading the image in memory and thus
|
||||
/// use potentially large amounts of memory.</remarks>
|
||||
public static Size GetDimensions(Stream stream)
|
||||
{
|
||||
try
|
||||
{
|
||||
//Try to load with exif
|
||||
var jpgInfo = ImageFile.FromStream(stream);
|
||||
|
||||
if (jpgInfo.Format != ImageFileFormat.Unknown
|
||||
&& jpgInfo.Properties.ContainsKey(ExifTag.PixelYDimension)
|
||||
&& jpgInfo.Properties.ContainsKey(ExifTag.PixelXDimension))
|
||||
{
|
||||
var height = Convert.ToInt32(jpgInfo.Properties[ExifTag.PixelYDimension].Value);
|
||||
var width = Convert.ToInt32(jpgInfo.Properties[ExifTag.PixelXDimension].Value);
|
||||
if (height > 0 && width > 0)
|
||||
{
|
||||
return new Size(width, height);
|
||||
}
|
||||
}
|
||||
|
||||
//we have no choice but to try to read in via GDI
|
||||
using (var image = Image.FromStream(stream))
|
||||
{
|
||||
|
||||
var fileWidth = image.Width;
|
||||
var fileHeight = image.Height;
|
||||
return new Size(fileWidth, fileHeight);
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
//We will just swallow, just means we can't read exif data, we don't want to log an error either
|
||||
return new Size(Constants.Conventions.Media.DefaultSize, Constants.Conventions.Media.DefaultSize);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
96
src/Umbraco.Web/Media/ImageUrl.cs
Normal file
96
src/Umbraco.Web/Media/ImageUrl.cs
Normal file
@@ -0,0 +1,96 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Specialized;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Cache;
|
||||
using Umbraco.Core.Media;
|
||||
using umbraco;
|
||||
|
||||
namespace Umbraco.Web.Media
|
||||
{
|
||||
[Obsolete("This is no longer used and will be removed in future versions")]
|
||||
public class ImageUrl
|
||||
{
|
||||
[Obsolete("Use TryGetImageUrl() instead")]
|
||||
public static string GetImageUrl(string specifiedSrc, string field, string provider, string parameters, int? nodeId = null)
|
||||
{
|
||||
string url;
|
||||
var found = TryGetImageUrl(specifiedSrc, field, provider, parameters, nodeId, out url);
|
||||
|
||||
return found ? url : string.Empty;
|
||||
}
|
||||
|
||||
public static bool TryGetImageUrl(string specifiedSrc, string field, string provider, string parameters, int? nodeId, out string url)
|
||||
{
|
||||
var imageUrlProvider = GetProvider(provider);
|
||||
|
||||
var parsedParameters = string.IsNullOrEmpty(parameters) ? new NameValueCollection() : HttpUtility.ParseQueryString(parameters);
|
||||
|
||||
var queryValues = parsedParameters.Keys.Cast<string>().ToDictionary(key => key, key => parsedParameters[key]);
|
||||
|
||||
if (string.IsNullOrEmpty(field))
|
||||
{
|
||||
url = imageUrlProvider.GetImageUrlFromFileName(specifiedSrc, queryValues);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
var fieldValue = string.Empty;
|
||||
if (nodeId.HasValue)
|
||||
{
|
||||
var contentFromCache = GetContentFromCache(nodeId.GetValueOrDefault(), field);
|
||||
if (contentFromCache != null)
|
||||
{
|
||||
fieldValue = contentFromCache.ToString();
|
||||
}
|
||||
else
|
||||
{
|
||||
var p = UmbracoContext.Current.ContentCache.GetById(nodeId.GetValueOrDefault());
|
||||
var v = p.GetPropertyValue(field);
|
||||
fieldValue = v == null ? string.Empty : v.ToString();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var context = HttpContext.Current;
|
||||
if (context != null)
|
||||
{
|
||||
var elements = context.Items["pageElements"] as Hashtable;
|
||||
if (elements != null)
|
||||
{
|
||||
var value = elements[field];
|
||||
fieldValue = value != null ? value.ToString() : string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(fieldValue))
|
||||
{
|
||||
int mediaId;
|
||||
url = int.TryParse(fieldValue, out mediaId)
|
||||
? imageUrlProvider.GetImageUrlFromMedia(mediaId, queryValues)
|
||||
: imageUrlProvider.GetImageUrlFromFileName(fieldValue, queryValues);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
url = string.Empty;
|
||||
return false;
|
||||
}
|
||||
|
||||
private static IImageUrlProvider GetProvider(string provider)
|
||||
{
|
||||
return ImageUrlProviderResolver.Current.GetProvider(provider);
|
||||
}
|
||||
|
||||
private static object GetContentFromCache(int nodeIdInt, string field)
|
||||
{
|
||||
var content = ApplicationContext.Current.ApplicationCache.RuntimeCache.GetCacheItem<object>(
|
||||
string.Format("{0}{1}_{2}", CacheKeys.ContentItemCacheKey, nodeIdInt.ToString(CultureInfo.InvariantCulture), field));
|
||||
return content;
|
||||
}
|
||||
}
|
||||
}
|
||||
26
src/Umbraco.Web/Media/ImageUrlProviderResolver.cs
Normal file
26
src/Umbraco.Web/Media/ImageUrlProviderResolver.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Media;
|
||||
using Umbraco.Core.ObjectResolution;
|
||||
using Umbraco.Web.Media.ImageUrlProviders;
|
||||
|
||||
namespace Umbraco.Web.Media
|
||||
{
|
||||
[Obsolete("IImageUrlProvider is no longer used and will be removed in future versions")]
|
||||
internal sealed class ImageUrlProviderResolver : LazyManyObjectsResolverBase<ImageUrlProviderResolver, IImageUrlProvider>
|
||||
{
|
||||
internal ImageUrlProviderResolver(IServiceProvider serviceProvider, ILogger logger, Func<IEnumerable<Type>> value)
|
||||
: base(serviceProvider, logger, value)
|
||||
{
|
||||
}
|
||||
|
||||
public IImageUrlProvider GetProvider(string provider)
|
||||
{
|
||||
return string.IsNullOrEmpty(provider) ?
|
||||
Values.First(v => v.Name.Equals(ImageUrlProvider.DefaultName, StringComparison.InvariantCultureIgnoreCase)) :
|
||||
Values.First(v => v.Name.Equals(provider, StringComparison.InvariantCultureIgnoreCase));
|
||||
}
|
||||
}
|
||||
}
|
||||
95
src/Umbraco.Web/Media/ImageUrlProviders/ImageUrlProvider.cs
Normal file
95
src/Umbraco.Web/Media/ImageUrlProviders/ImageUrlProvider.cs
Normal file
@@ -0,0 +1,95 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Xml.XPath;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Media;
|
||||
using umbraco;
|
||||
using Umbraco.Core;
|
||||
|
||||
namespace Umbraco.Web.Media.ImageUrlProviders
|
||||
{
|
||||
[Obsolete("IImageUrlProvider is no longer used and will be removed in future versions")]
|
||||
public class ImageUrlProvider : IImageUrlProvider
|
||||
{
|
||||
public const string DefaultName = "umbracoUpload";
|
||||
|
||||
public string Name
|
||||
{
|
||||
get { return DefaultName; }
|
||||
}
|
||||
|
||||
public string GetImageUrlFromMedia(int mediaId, IDictionary<string, string> parameters)
|
||||
{
|
||||
var url = string.Empty;
|
||||
|
||||
var nodeIterator = library.GetMedia(mediaId, false);
|
||||
|
||||
if (nodeIterator.Current != null)
|
||||
{
|
||||
var filename = GetProperty(nodeIterator, Constants.Conventions.Media.File);
|
||||
var withThumb = AddThumbInfo(filename, parameters);
|
||||
url = AddCropInfo(withThumb, parameters);
|
||||
}
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
public string GetImageUrlFromFileName(string specifiedSrc, IDictionary<string, string> parameters)
|
||||
{
|
||||
var withThumb = AddThumbInfo(specifiedSrc, parameters);
|
||||
return AddCropInfo(withThumb, parameters);
|
||||
}
|
||||
|
||||
private static string AddThumbInfo(string filename, IDictionary<string, string> parameters)
|
||||
{
|
||||
var thumb = string.Empty;
|
||||
if (parameters.ContainsKey("thumb"))
|
||||
thumb = parameters["thumb"];
|
||||
|
||||
if (!string.IsNullOrEmpty(thumb) && filename.Contains("."))
|
||||
{
|
||||
var lastIndexOf = filename.LastIndexOf('.');
|
||||
var name = filename.Substring(0, lastIndexOf);
|
||||
var extension = filename.Substring(lastIndexOf, filename.Length - lastIndexOf);
|
||||
return string.Format("{0}_thumb_{1}{2}", name, thumb, extension);
|
||||
}
|
||||
return filename;
|
||||
}
|
||||
|
||||
private static string AddCropInfo(string filename, IDictionary<string, string> parameters)
|
||||
{
|
||||
var crop = string.Empty;
|
||||
if (parameters.ContainsKey("crop"))
|
||||
crop = parameters["crop"];
|
||||
|
||||
if (!string.IsNullOrEmpty(crop) && filename.Contains("."))
|
||||
{
|
||||
var lastIndexOf = filename.LastIndexOf('.');
|
||||
var name = filename.Substring(0, lastIndexOf);
|
||||
|
||||
//var extension = filename.Substring(lastIndexOf, filename.Length - lastIndexOf);
|
||||
//Built in cropper currently always uses jpg as an extension
|
||||
|
||||
const string extension = ".jpg";
|
||||
return string.Format("{0}_{1}{2}", name, crop, extension);
|
||||
}
|
||||
|
||||
return filename;
|
||||
}
|
||||
|
||||
|
||||
private static string GetProperty(XPathNodeIterator nodeIterator, string fileProp)
|
||||
{
|
||||
var xpath = UmbracoConfig.For.UmbracoSettings().Content.UseLegacyXmlSchema
|
||||
? string.Format(".//data[@alias = '{0}']", fileProp)
|
||||
: string.Format(".//{0}", fileProp);
|
||||
|
||||
var file = string.Empty;
|
||||
var selectSingleNode = nodeIterator.Current.SelectSingleNode(xpath);
|
||||
if (selectSingleNode != null)
|
||||
file = selectSingleNode.InnerXml;
|
||||
|
||||
return file;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Media;
|
||||
|
||||
namespace Umbraco.Web.Media.ThumbnailProviders
|
||||
{
|
||||
public abstract class AbstractThumbnailProvider : IThumbnailProvider
|
||||
{
|
||||
protected abstract IEnumerable<string> SupportedExtensions { get; }
|
||||
|
||||
public bool CanProvideThumbnail(string fileUrl)
|
||||
{
|
||||
string thumbUrl;
|
||||
return TryGetThumbnailUrl(fileUrl, out thumbUrl);
|
||||
}
|
||||
|
||||
public string GetThumbnailUrl(string fileUrl)
|
||||
{
|
||||
string thumbUrl;
|
||||
TryGetThumbnailUrl(fileUrl, out thumbUrl);
|
||||
return thumbUrl;
|
||||
}
|
||||
|
||||
protected bool IsSupportedExtension(string ext)
|
||||
{
|
||||
return SupportedExtensions.InvariantContains(ext) ||
|
||||
SupportedExtensions.InvariantContains("*");
|
||||
}
|
||||
|
||||
protected abstract bool TryGetThumbnailUrl(string fileUrl, out string thumbUrl);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Umbraco.Core.ObjectResolution;
|
||||
using Umbraco.Core.IO;
|
||||
|
||||
namespace Umbraco.Web.Media.ThumbnailProviders
|
||||
{
|
||||
[Weight(2000)]
|
||||
public class FileExtensionIconThumbnailProvider : AbstractThumbnailProvider
|
||||
{
|
||||
protected override IEnumerable<string> SupportedExtensions
|
||||
{
|
||||
get { return new List<string> { "*" }; }
|
||||
}
|
||||
|
||||
protected override bool TryGetThumbnailUrl(string fileUrl, out string thumbUrl)
|
||||
{
|
||||
// Set thumbnail url to empty strin initially
|
||||
thumbUrl = string.Empty;
|
||||
|
||||
// Make sure file has an extension
|
||||
var ext = Path.GetExtension(fileUrl);
|
||||
if (string.IsNullOrEmpty(ext))
|
||||
return false;
|
||||
|
||||
// Make sure it has a supported file extension
|
||||
if (!IsSupportedExtension(ext))
|
||||
return false;
|
||||
|
||||
// Make sure the thumbnail exists
|
||||
var tmpThumbUrl = IOHelper.ResolveUrl(SystemDirectories.Umbraco + "/images/mediaThumbnails/"+ ext.TrimStart('.') +".png");
|
||||
if (!File.Exists(IOHelper.MapPath(tmpThumbUrl)))
|
||||
return false;
|
||||
|
||||
// We've got this far, so thumbnail must exist
|
||||
thumbUrl = tmpThumbUrl;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.ObjectResolution;
|
||||
|
||||
namespace Umbraco.Web.Media.ThumbnailProviders
|
||||
{
|
||||
[Weight(1000)]
|
||||
public class ImageThumbnailProvider : AbstractThumbnailProvider
|
||||
{
|
||||
protected override IEnumerable<string> SupportedExtensions
|
||||
{
|
||||
get { return new List<string> { ".jpeg", ".jpg", ".gif", ".bmp", ".png", ".tiff", ".tif" }; }
|
||||
}
|
||||
|
||||
protected override bool TryGetThumbnailUrl(string fileUrl, out string thumbUrl)
|
||||
{
|
||||
// Set thumbnail url to empty strin initially
|
||||
thumbUrl = string.Empty;
|
||||
|
||||
// Make sure file has an extension
|
||||
var ext = Path.GetExtension(fileUrl);
|
||||
if (string.IsNullOrEmpty(ext))
|
||||
return false;
|
||||
|
||||
// Make sure it has a supported file extension
|
||||
if (!IsSupportedExtension(ext))
|
||||
return false;
|
||||
|
||||
// Make sure the thumbnail exists
|
||||
var tmpThumbUrl = fileUrl.Replace(ext, "_thumb" + ext);
|
||||
|
||||
try
|
||||
{
|
||||
var fs = FileSystemProviderManager.Current.GetFileSystemProvider<MediaFileSystem>();
|
||||
var relativeThumbPath = fs.GetRelativePath(tmpThumbUrl);
|
||||
if (!fs.FileExists(relativeThumbPath))
|
||||
return false;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// If something odd happens, just return false and move on
|
||||
return false;
|
||||
}
|
||||
|
||||
// We've got this far, so thumbnail must exist
|
||||
thumbUrl = tmpThumbUrl;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Umbraco.Core.ObjectResolution;
|
||||
using Umbraco.Core.IO;
|
||||
|
||||
namespace Umbraco.Web.Media.ThumbnailProviders
|
||||
{
|
||||
[Weight(3000)]
|
||||
public class MediaTypeIconThumbnailProvider : AbstractThumbnailProvider
|
||||
{
|
||||
|
||||
protected override IEnumerable<string> SupportedExtensions
|
||||
{
|
||||
get { return new List<string> { "*" }; }
|
||||
}
|
||||
|
||||
protected override bool TryGetThumbnailUrl(string fileUrl, out string thumbUrl)
|
||||
{
|
||||
// Set thumbnail url to empty strin initially
|
||||
thumbUrl = string.Empty;
|
||||
|
||||
// Make sure file has an extension
|
||||
var ext = Path.GetExtension(fileUrl);
|
||||
if (string.IsNullOrEmpty(ext))
|
||||
return false;
|
||||
|
||||
// Make sure it has a supported file extension
|
||||
if (!IsSupportedExtension(ext))
|
||||
return false;
|
||||
|
||||
// Make sure the thumbnail exists
|
||||
var tmpThumbUrl = IOHelper.ResolveUrl(SystemDirectories.Umbraco + "/images/mediaThumbnails/"+ ext.TrimStart('.') +".png");
|
||||
if (!File.Exists(IOHelper.MapPath(tmpThumbUrl)))
|
||||
return false;
|
||||
|
||||
// We've got this far, so thumbnail must exist
|
||||
thumbUrl = tmpThumbUrl;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Web;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Media;
|
||||
using Umbraco.Core.ObjectResolution;
|
||||
using umbraco.BusinessLogic;
|
||||
using umbraco.BusinessLogic.Utils;
|
||||
|
||||
namespace Umbraco.Web.Media.ThumbnailProviders
|
||||
{
|
||||
[Obsolete("Thumbnails are generated by ImageProcessor, use that instead")]
|
||||
public sealed class ThumbnailProvidersResolver : LazyManyObjectsResolverBase<ThumbnailProvidersResolver, IThumbnailProvider>
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
/// <param name="serviceProvider"></param>
|
||||
/// <param name="logger"></param>
|
||||
/// <param name="providers"></param>
|
||||
internal ThumbnailProvidersResolver(IServiceProvider serviceProvider, ILogger logger, Func<IEnumerable<Type>> providers)
|
||||
: base(serviceProvider, logger, providers)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return the providers
|
||||
/// </summary>
|
||||
public IEnumerable<IThumbnailProvider> Providers
|
||||
{
|
||||
get { return GetSortedValues(); }
|
||||
}
|
||||
|
||||
public string GetThumbnailUrl(string fileUrl)
|
||||
{
|
||||
var provider = Providers.FirstOrDefault(x => x.CanProvideThumbnail(fileUrl));
|
||||
return provider != null ? provider.GetThumbnailUrl(fileUrl) : string.Empty;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
using System.IO;
|
||||
|
||||
namespace Umbraco.Web.Media.TypeDetector
|
||||
{
|
||||
public class JpegDetector : RasterizedTypeDetector
|
||||
{
|
||||
public static bool IsOfType(Stream fileStream)
|
||||
{
|
||||
var header = GetFileHeader(fileStream);
|
||||
|
||||
return header[0] == 0xff && header[1] == 0xD8;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
using System.IO;
|
||||
|
||||
namespace Umbraco.Web.Media.TypeDetector
|
||||
{
|
||||
public abstract class RasterizedTypeDetector
|
||||
{
|
||||
public static byte[] GetFileHeader(Stream fileStream)
|
||||
{
|
||||
fileStream.Seek(0, SeekOrigin.Begin);
|
||||
byte[] header = new byte[8];
|
||||
fileStream.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
return header;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
using System.IO;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Umbraco.Web.Media.TypeDetector
|
||||
{
|
||||
public class SvgDetector
|
||||
{
|
||||
public static bool IsOfType(Stream fileStream)
|
||||
{
|
||||
var document = new XDocument();
|
||||
|
||||
try
|
||||
{
|
||||
document = XDocument.Load(fileStream);
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return document.Root?.Name.LocalName == "svg";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace Umbraco.Web.Media.TypeDetector
|
||||
{
|
||||
public class TIFFDetector
|
||||
{
|
||||
public static bool IsOfType(Stream fileStream)
|
||||
{
|
||||
string tiffHeader = GetFileHeader(fileStream);
|
||||
|
||||
return tiffHeader == "MM\x00\x2a" || tiffHeader == "II\x2a\x00";
|
||||
}
|
||||
|
||||
public static string GetFileHeader(Stream fileStream)
|
||||
{
|
||||
var header = RasterizedTypeDetector.GetFileHeader(fileStream);
|
||||
|
||||
string tiffHeader = Encoding.ASCII.GetString(header, 0, 4);
|
||||
|
||||
return tiffHeader;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,146 +0,0 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Configuration.UmbracoSettings;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models;
|
||||
|
||||
namespace Umbraco.Web.Media
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides methods to manage auto-fill properties for upload fields.
|
||||
/// </summary>
|
||||
internal class UploadAutoFillProperties
|
||||
{
|
||||
private readonly MediaFileSystem _mediaFileSystem;
|
||||
private readonly ILogger _logger;
|
||||
private readonly IContentSection _contentSection;
|
||||
|
||||
public UploadAutoFillProperties(MediaFileSystem mediaFileSystem, ILogger logger, IContentSection contentSection)
|
||||
{
|
||||
_mediaFileSystem = mediaFileSystem ?? throw new ArgumentNullException(nameof(mediaFileSystem));
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
_contentSection = contentSection ?? throw new ArgumentNullException(nameof(contentSection));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets the auto-fill properties of a content item, for a specified auto-fill configuration.
|
||||
/// </summary>
|
||||
/// <param name="content">The content item.</param>
|
||||
/// <param name="autoFillConfig">The auto-fill configuration.</param>
|
||||
/// <param name="culture">Variation language.</param>
|
||||
/// <param name="segment">Variation segment.</param>
|
||||
public void Reset(IContentBase content, IImagingAutoFillUploadField autoFillConfig, string culture, string segment)
|
||||
{
|
||||
if (content == null) throw new ArgumentNullException(nameof(content));
|
||||
if (autoFillConfig == null) throw new ArgumentNullException(nameof(autoFillConfig));
|
||||
|
||||
ResetProperties(content, autoFillConfig, culture, segment);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Populates the auto-fill properties of a content item, for a specified auto-fill configuration.
|
||||
/// </summary>
|
||||
/// <param name="content">The content item.</param>
|
||||
/// <param name="autoFillConfig">The auto-fill configuration.</param>
|
||||
/// <param name="filepath">The filesystem path to the uploaded file.</param>
|
||||
/// <remarks>The <paramref name="filepath"/> parameter is the path relative to the filesystem.</remarks>
|
||||
/// <param name="culture">Variation language.</param>
|
||||
/// <param name="segment">Variation segment.</param>
|
||||
public void Populate(IContentBase content, IImagingAutoFillUploadField autoFillConfig, string filepath, string culture, string segment)
|
||||
{
|
||||
if (content == null) throw new ArgumentNullException(nameof(content));
|
||||
if (autoFillConfig == null) throw new ArgumentNullException(nameof(autoFillConfig));
|
||||
|
||||
// no file = reset, file = auto-fill
|
||||
if (filepath.IsNullOrWhiteSpace())
|
||||
{
|
||||
ResetProperties(content, autoFillConfig, culture, segment);
|
||||
}
|
||||
else
|
||||
{
|
||||
// if anything goes wrong, just reset the properties
|
||||
try
|
||||
{
|
||||
using (var filestream = _mediaFileSystem.OpenFile(filepath))
|
||||
{
|
||||
var extension = (Path.GetExtension(filepath) ?? "").TrimStart('.');
|
||||
var size = _contentSection.IsImageFile(extension) ? (Size?)ImageHelper.GetDimensions(filestream) : null;
|
||||
SetProperties(content, autoFillConfig, size, filestream.Length, extension, culture, segment);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error(typeof(UploadAutoFillProperties), ex, "Could not populate upload auto-fill properties for file '{File}'.", filepath);
|
||||
ResetProperties(content, autoFillConfig, culture, segment);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Populates the auto-fill properties of a content item.
|
||||
/// </summary>
|
||||
/// <param name="content">The content item.</param>
|
||||
/// <param name="autoFillConfig"></param>
|
||||
/// <param name="filepath">The filesystem-relative filepath, or null to clear properties.</param>
|
||||
/// <param name="filestream">The stream containing the file data.</param>
|
||||
/// <param name="culture">Variation language.</param>
|
||||
/// <param name="segment">Variation segment.</param>
|
||||
public void Populate(IContentBase content, IImagingAutoFillUploadField autoFillConfig, string filepath, Stream filestream, string culture, string segment)
|
||||
{
|
||||
if (content == null) throw new ArgumentNullException(nameof(content));
|
||||
if (autoFillConfig == null) throw new ArgumentNullException(nameof(autoFillConfig));
|
||||
|
||||
// no file = reset, file = auto-fill
|
||||
if (filepath.IsNullOrWhiteSpace() || filestream == null)
|
||||
{
|
||||
ResetProperties(content, autoFillConfig, culture, segment);
|
||||
}
|
||||
else
|
||||
{
|
||||
var extension = (Path.GetExtension(filepath) ?? "").TrimStart('.');
|
||||
var size = _contentSection.IsImageFile(extension) ? (Size?)ImageHelper.GetDimensions(filestream) : null;
|
||||
SetProperties(content, autoFillConfig, size, filestream.Length, extension, culture, segment);
|
||||
}
|
||||
}
|
||||
|
||||
private static void SetProperties(IContentBase content, IImagingAutoFillUploadField autoFillConfig, Size? size, long length, string extension, string culture, string segment)
|
||||
{
|
||||
if (content == null) throw new ArgumentNullException(nameof(content));
|
||||
if (autoFillConfig == null) throw new ArgumentNullException(nameof(autoFillConfig));
|
||||
|
||||
if (content.Properties.Contains(autoFillConfig.WidthFieldAlias))
|
||||
content.Properties[autoFillConfig.WidthFieldAlias].SetValue(size.HasValue ? size.Value.Width.ToInvariantString() : string.Empty, culture, segment);
|
||||
|
||||
if (content.Properties.Contains(autoFillConfig.HeightFieldAlias))
|
||||
content.Properties[autoFillConfig.HeightFieldAlias].SetValue(size.HasValue ? size.Value.Height.ToInvariantString() : string.Empty, culture, segment);
|
||||
|
||||
if (content.Properties.Contains(autoFillConfig.LengthFieldAlias))
|
||||
content.Properties[autoFillConfig.LengthFieldAlias].SetValue(length, culture, segment);
|
||||
|
||||
if (content.Properties.Contains(autoFillConfig.ExtensionFieldAlias))
|
||||
content.Properties[autoFillConfig.ExtensionFieldAlias].SetValue(extension, culture, segment);
|
||||
}
|
||||
|
||||
private static void ResetProperties(IContentBase content, IImagingAutoFillUploadField autoFillConfig, string culture, string segment)
|
||||
{
|
||||
if (content == null) throw new ArgumentNullException(nameof(content));
|
||||
if (autoFillConfig == null) throw new ArgumentNullException(nameof(autoFillConfig));
|
||||
|
||||
if (content.Properties.Contains(autoFillConfig.WidthFieldAlias))
|
||||
content.Properties[autoFillConfig.WidthFieldAlias].SetValue(string.Empty, culture, segment);
|
||||
|
||||
if (content.Properties.Contains(autoFillConfig.HeightFieldAlias))
|
||||
content.Properties[autoFillConfig.HeightFieldAlias].SetValue(string.Empty, culture, segment);
|
||||
|
||||
if (content.Properties.Contains(autoFillConfig.LengthFieldAlias))
|
||||
content.Properties[autoFillConfig.LengthFieldAlias].SetValue(string.Empty, culture, segment);
|
||||
|
||||
if (content.Properties.Contains(autoFillConfig.ExtensionFieldAlias))
|
||||
content.Properties[autoFillConfig.ExtensionFieldAlias].SetValue(string.Empty, culture, segment);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user