diff --git a/README.md b/README.md
index 28c377037..a1c9bca99 100644
--- a/README.md
+++ b/README.md
@@ -155,6 +155,7 @@ Core Team
- [Dirk Lemstra](https://github.com/dlemstra)
- [Anton Firsov](https://github.com/antonfirsov)
- [Scott Williams](https://github.com/tocsoft)
+- [Brian Popow](https://github.com/brianpopow)
### Backers
diff --git a/src/ImageSharp/Common/Helpers/EnumUtils.cs b/src/ImageSharp/Common/Helpers/EnumUtils.cs
new file mode 100644
index 000000000..a98b7f84c
--- /dev/null
+++ b/src/ImageSharp/Common/Helpers/EnumUtils.cs
@@ -0,0 +1,50 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using System;
+using System.Runtime.CompilerServices;
+
+namespace SixLabors.ImageSharp
+{
+ ///
+ /// Common utility methods for working with enums.
+ ///
+ internal static class EnumUtils
+ {
+ ///
+ /// Converts the numeric representation of the enumerated constants to an equivalent enumerated object.
+ ///
+ /// The type of enum
+ /// The value to parse
+ /// The default value to return.
+ /// The .
+ public static TEnum Parse(int value, TEnum defaultValue)
+ where TEnum : Enum
+ {
+ foreach (TEnum enumValue in Enum.GetValues(typeof(TEnum)))
+ {
+ TEnum current = enumValue;
+ if (value == Unsafe.As(ref current))
+ {
+ return enumValue;
+ }
+ }
+
+ return defaultValue;
+ }
+
+ ///
+ /// Returns a value indicating whether the given enum has a flag of the given value.
+ ///
+ /// The type of enum.
+ /// The value.
+ /// The flag.
+ /// The .
+ public static bool HasFlag(TEnum value, TEnum flag)
+ where TEnum : Enum
+ {
+ uint flagValue = Unsafe.As(ref flag);
+ return (Unsafe.As(ref value) & flagValue) == flagValue;
+ }
+ }
+}
diff --git a/src/ImageSharp/Common/Helpers/TolerantMath.cs b/src/ImageSharp/Common/Helpers/TolerantMath.cs
index bef7eb161..62b564472 100644
--- a/src/ImageSharp/Common/Helpers/TolerantMath.cs
+++ b/src/ImageSharp/Common/Helpers/TolerantMath.cs
@@ -1,4 +1,4 @@
-// Copyright (c) Six Labors and contributors.
+// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
@@ -103,4 +103,4 @@ namespace SixLabors.ImageSharp
return Math.Floor(a);
}
}
-}
\ No newline at end of file
+}
diff --git a/src/ImageSharp/Common/Helpers/UnitConverter.cs b/src/ImageSharp/Common/Helpers/UnitConverter.cs
index 75dbb032c..9e4306170 100644
--- a/src/ImageSharp/Common/Helpers/UnitConverter.cs
+++ b/src/ImageSharp/Common/Helpers/UnitConverter.cs
@@ -1,4 +1,4 @@
-// Copyright (c) Six Labors and contributors.
+// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Runtime.CompilerServices;
@@ -86,9 +86,10 @@ namespace SixLabors.ImageSharp.Common.Helpers
[MethodImpl(InliningOptions.ShortMethod)]
public static PixelResolutionUnit ExifProfileToResolutionUnit(ExifProfile profile)
{
- return profile.TryGetValue(ExifTag.ResolutionUnit, out ExifValue resolution)
- ? (PixelResolutionUnit)(byte)(((ushort)resolution.Value) - 1) // EXIF is 1, 2, 3
- : default;
+ IExifValue resolution = profile.GetValue(ExifTag.ResolutionUnit);
+
+ // EXIF is 1, 2, 3 so we minus "1" off the result.
+ return resolution is null ? default : (PixelResolutionUnit)(byte)(resolution.Value - 1);
}
}
}
diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs
index 60709ed00..ec9cca8c8 100644
--- a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs
+++ b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs
@@ -465,24 +465,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
}
}
- private double GetExifResolutionValue(ExifTag tag)
+ private double GetExifResolutionValue(ExifTag tag)
{
- if (!this.Metadata.ExifProfile.TryGetValue(tag, out ExifValue exifValue))
- {
- return 0;
- }
+ IExifValue resolution = this.Metadata.ExifProfile.GetValue(tag);
- switch (exifValue.DataType)
- {
- case ExifDataType.Rational:
- return ((Rational)exifValue.Value).ToDouble();
- case ExifDataType.Long:
- return (uint)exifValue.Value;
- case ExifDataType.DoubleFloat:
- return (double)exifValue.Value;
- default:
- return 0;
- }
+ return resolution is null ? 0 : resolution.Value.ToDouble();
}
///
diff --git a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs
index bc9bb9543..cd3c19aa3 100644
--- a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs
+++ b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs
@@ -1,4 +1,4 @@
-// Copyright (c) Six Labors and contributors.
+// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
@@ -647,7 +647,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
///
private void WriteExifProfile(ExifProfile exifProfile)
{
- if (exifProfile is null)
+ if (exifProfile is null || exifProfile.Values.Count == 0)
{
return;
}
@@ -655,9 +655,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
const int MaxBytesApp1 = 65533; // 64k - 2 padding bytes
const int MaxBytesWithExifId = 65527; // Max - 6 bytes for EXIF header.
- byte[] data = exifProfile?.ToByteArray();
+ byte[] data = exifProfile.ToByteArray();
- if (data is null || data.Length == 0)
+ if (data.Length == 0)
{
return;
}
@@ -998,4 +998,4 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
this.outputStream.Write(this.buffer, 0, 4);
}
}
-}
\ No newline at end of file
+}
diff --git a/src/ImageSharp/Formats/Png/PngEncoderCore.cs b/src/ImageSharp/Formats/Png/PngEncoderCore.cs
index 82796c570..22e3f252d 100644
--- a/src/ImageSharp/Formats/Png/PngEncoderCore.cs
+++ b/src/ImageSharp/Formats/Png/PngEncoderCore.cs
@@ -622,11 +622,13 @@ namespace SixLabors.ImageSharp.Formats.Png
/// The image metadata.
private void WriteExifChunk(Stream stream, ImageMetadata meta)
{
- if (meta.ExifProfile?.Values.Count > 0)
+ if (meta.ExifProfile is null || meta.ExifProfile.Values.Count == 0)
{
- meta.SyncProfiles();
- this.WriteChunk(stream, PngChunkType.Exif, meta.ExifProfile.ToByteArray());
+ return;
}
+
+ meta.SyncProfiles();
+ this.WriteChunk(stream, PngChunkType.Exif, meta.ExifProfile.ToByteArray());
}
///
diff --git a/src/ImageSharp/Metadata/Profiles/Exif/DC-008-Translation-2019-E.pdf b/src/ImageSharp/Metadata/Profiles/Exif/DC-008-Translation-2019-E.pdf
new file mode 100644
index 000000000..9be0c8402
Binary files /dev/null and b/src/ImageSharp/Metadata/Profiles/Exif/DC-008-Translation-2019-E.pdf differ
diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifDataType.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifDataType.cs
index 83e7f7fe8..74c86f721 100644
--- a/src/ImageSharp/Metadata/Profiles/Exif/ExifDataType.cs
+++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifDataType.cs
@@ -1,4 +1,4 @@
-// Copyright (c) Six Labors and contributors.
+// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
@@ -20,6 +20,10 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
///
/// An 8-bit byte containing one 7-bit ASCII code. The final byte is terminated with NULL.
+ ///
+ /// Although the standard defines ASCII this has commonly been ignored as
+ /// ASCII cannot properly encode text in many languages.
+ ///
///
Ascii = 2,
@@ -64,13 +68,13 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
SignedRational = 10,
///
- /// A 32-bit floating point value.
+ /// A 32-bit single precision floating point value.
///
SingleFloat = 11,
///
- /// A 64-bit floating point value.
+ /// A 64-bit double precision floating point value.
///
DoubleFloat = 12
}
-}
\ No newline at end of file
+}
diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifDataTypes.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifDataTypes.cs
new file mode 100644
index 000000000..d94dc5640
--- /dev/null
+++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifDataTypes.cs
@@ -0,0 +1,45 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using System;
+
+namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
+{
+ internal static class ExifDataTypes
+ {
+ ///
+ /// Gets the size in bytes of the given data type.
+ ///
+ /// The data type.
+ ///
+ /// The .
+ ///
+ ///
+ /// Thrown if the type is unsupported.
+ ///
+ public static uint GetSize(ExifDataType dataType)
+ {
+ switch (dataType)
+ {
+ case ExifDataType.Ascii:
+ case ExifDataType.Byte:
+ case ExifDataType.SignedByte:
+ case ExifDataType.Undefined:
+ return 1;
+ case ExifDataType.Short:
+ case ExifDataType.SignedShort:
+ return 2;
+ case ExifDataType.Long:
+ case ExifDataType.SignedLong:
+ case ExifDataType.SingleFloat:
+ return 4;
+ case ExifDataType.DoubleFloat:
+ case ExifDataType.Rational:
+ case ExifDataType.SignedRational:
+ return 8;
+ default:
+ throw new NotSupportedException(dataType.ToString());
+ }
+ }
+ }
+}
diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifParts.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifParts.cs
index d22dc730f..6405a7ff2 100644
--- a/src/ImageSharp/Metadata/Profiles/Exif/ExifParts.cs
+++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifParts.cs
@@ -1,4 +1,4 @@
-// Copyright (c) Six Labors and contributors.
+// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
@@ -29,11 +29,11 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
///
/// GPSTags
///
- GPSTags = 8,
+ GpsTags = 8,
///
/// All
///
- All = IfdTags | ExifTags | GPSTags
+ All = IfdTags | ExifTags | GpsTags
}
-}
\ No newline at end of file
+}
diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifProfile.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifProfile.cs
index 3d90cb3a9..f98a1f3c7 100644
--- a/src/ImageSharp/Metadata/Profiles/Exif/ExifProfile.cs
+++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifProfile.cs
@@ -1,4 +1,4 @@
-// Copyright (c) Six Labors and contributors.
+// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
@@ -22,7 +22,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
///
/// The collection of EXIF values
///
- private List values;
+ private List values;
///
/// The thumbnail offset position in the byte stream
@@ -70,9 +70,9 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
if (other.values != null)
{
- this.values = new List(other.Values.Count);
+ this.values = new List(other.Values.Count);
- foreach (ExifValue value in other.Values)
+ foreach (IExifValue value in other.Values)
{
this.values.Add(value.DeepClone());
}
@@ -98,7 +98,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
///
/// Gets the values of this EXIF profile.
///
- public IReadOnlyList Values
+ public IReadOnlyList Values
{
get
{
@@ -138,46 +138,13 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
///
/// Returns the value with the specified tag.
///
- /// The tag of the EXIF value.
- ///
- /// The .
- ///
- public ExifValue GetValue(ExifTag tag)
- {
- foreach (ExifValue exifValue in this.Values)
- {
- if (exifValue.Tag == tag)
- {
- return exifValue;
- }
- }
-
- return null;
- }
-
- ///
- /// Conditionally returns the value of the tag if it exists.
- ///
- /// The tag of the EXIF value.
- /// The value of the tag, if found.
- ///
- /// The .
- ///
- public bool TryGetValue(ExifTag tag, out ExifValue value)
+ /// The tag of the exif value.
+ /// The value with the specified tag.
+ /// The data type of the tag.
+ public IExifValue GetValue(ExifTag tag)
{
- foreach (ExifValue exifValue in this.Values)
- {
- if (exifValue.Tag == tag)
- {
- value = exifValue;
-
- return true;
- }
- }
-
- value = default;
-
- return false;
+ IExifValue value = this.GetValueInternal(tag);
+ return value is null ? null : (IExifValue)value;
}
///
@@ -206,24 +173,11 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
///
/// Sets the value of the specified tag.
///
- /// The tag of the EXIF value.
+ /// The tag of the exif value.
/// The value.
- public void SetValue(ExifTag tag, object value)
- {
- for (int i = 0; i < this.Values.Count; i++)
- {
- if (this.values[i].Tag == tag)
- {
- this.values[i] = this.values[i].WithValue(value);
-
- return;
- }
- }
-
- var newExifValue = ExifValue.Create(tag, value);
-
- this.values.Add(newExifValue);
- }
+ /// The data type of the tag.
+ public void SetValue(ExifTag tag, TValueType value)
+ => this.SetValueInternal(tag, value);
///
/// Converts this instance to a byte array.
@@ -238,7 +192,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
if (this.values.Count == 0)
{
- return null;
+ return Array.Empty();
}
var writer = new ExifWriter(this.values, this.Parts);
@@ -248,6 +202,50 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
///
public ExifProfile DeepClone() => new ExifProfile(this);
+ ///
+ /// Returns the value with the specified tag.
+ ///
+ /// The tag of the exif value.
+ /// The value with the specified tag.
+ internal IExifValue GetValueInternal(ExifTag tag)
+ {
+ foreach (IExifValue exifValue in this.Values)
+ {
+ if (exifValue.Tag == tag)
+ {
+ return exifValue;
+ }
+ }
+
+ return null;
+ }
+
+ ///
+ /// Sets the value of the specified tag.
+ ///
+ /// The tag of the exif value.
+ /// The value.
+ internal void SetValueInternal(ExifTag tag, object value)
+ {
+ foreach (IExifValue exifValue in this.Values)
+ {
+ if (exifValue.Tag == tag)
+ {
+ exifValue.TrySetValue(value);
+ return;
+ }
+ }
+
+ ExifValue newExifValue = ExifValues.Create(tag);
+ if (newExifValue is null)
+ {
+ throw new NotSupportedException();
+ }
+
+ newExifValue.TrySetValue(value);
+ this.values.Add(newExifValue);
+ }
+
///
/// Synchronizes the profiles with the specified metadata.
///
@@ -258,9 +256,9 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
this.SyncResolution(ExifTag.YResolution, metadata.VerticalResolution);
}
- private void SyncResolution(ExifTag tag, double resolution)
+ private void SyncResolution(ExifTag tag, double resolution)
{
- ExifValue value = this.GetValue(tag);
+ IExifValue value = this.GetValue(tag);
if (value is null)
{
@@ -285,7 +283,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
if (this.data is null)
{
- this.values = new List();
+ this.values = new List();
return;
}
@@ -301,4 +299,4 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
this.thumbnailLength = (int)reader.ThumbnailLength;
}
}
-}
\ No newline at end of file
+}
diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs
index 77c1cf2ea..00410fb59 100644
--- a/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs
+++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs
@@ -5,7 +5,6 @@ using System;
using System.Buffers.Binary;
using System.Collections.Generic;
using System.Collections.ObjectModel;
-using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using SixLabors.ImageSharp.Primitives;
@@ -68,12 +67,12 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
///
/// The .
///
- public List ReadValues()
+ public List ReadValues()
{
- var values = new List();
+ var values = new List();
// II == 0x4949
- this.isBigEndian = !(this.ReadUInt16() == 0x4949);
+ this.isBigEndian = this.ReadUInt16() != 0x4949;
if (this.ReadUInt16() != 0x002A)
{
@@ -101,7 +100,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
private static TDataType[] ToArray(ExifDataType dataType, ReadOnlySpan data, ConverterMethod converter)
{
- int dataTypeSize = (int)ExifValue.GetSize(dataType);
+ int dataTypeSize = (int)ExifDataTypes.GetSize(dataType);
int length = data.Length / dataTypeSize;
var result = new TDataType[length];
@@ -135,7 +134,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
///
/// The values.
/// The index.
- private void AddValues(List values, uint index)
+ private void AddValues(List values, uint index)
{
if (index > (uint)this.exifData.Length)
{
@@ -153,9 +152,9 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
}
bool duplicate = false;
- foreach (ExifValue val in values)
+ foreach (IExifValue val in values)
{
- if (val.Tag == value.Tag)
+ if (val == value)
{
duplicate = true;
break;
@@ -167,19 +166,13 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
continue;
}
- if (value.Tag == ExifTag.SubIFDOffset)
+ if (value == ExifTag.SubIFDOffset)
{
- if (value.DataType == ExifDataType.Long)
- {
- this.exifOffset = (uint)value.Value;
- }
+ this.exifOffset = ((ExifLong)value).Value;
}
- else if (value.Tag == ExifTag.GPSIFDOffset)
+ else if (value == ExifTag.GPSIFDOffset)
{
- if (value.DataType == ExifDataType.Long)
- {
- this.gpsOffset = (uint)value.Value;
- }
+ this.gpsOffset = ((ExifLong)value).Value;
}
else
{
@@ -285,30 +278,24 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
private bool TryReadValue(out ExifValue exifValue)
{
+ exifValue = default;
+
// 2 | 2 | 4 | 4
// tag | type | count | value offset
if (this.RemainingLength < 12)
{
- exifValue = default;
-
return false;
}
- ExifTag tag = this.ToEnum(this.ReadUInt16(), ExifTag.Unknown);
- uint type = this.ReadUInt16();
+ var tag = (ExifTagValue)this.ReadUInt16();
+ ExifDataType dataType = EnumUtils.Parse(this.ReadUInt16(), ExifDataType.Unknown);
// Ensure that the data type is valid
- if (type == 0 || type > 12)
+ if (dataType == ExifDataType.Unknown)
{
- exifValue = new ExifValue(tag, ExifDataType.Unknown, null, false);
-
- return true;
+ return false;
}
- var dataType = (ExifDataType)type;
-
- object value;
-
uint numberOfComponents = this.ReadUInt32();
// Issue #132: ExifDataType == Undefined is treated like a byte array.
@@ -318,23 +305,20 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
numberOfComponents = 4;
}
- uint size = numberOfComponents * ExifValue.GetSize(dataType);
+ uint size = numberOfComponents * ExifDataTypes.GetSize(dataType);
this.TryReadSpan(4, out ReadOnlySpan offsetBuffer);
+ object value;
if (size > 4)
{
int oldIndex = this.position;
-
uint newIndex = this.ConvertToUInt32(offsetBuffer);
// Ensure that the new index does not overrun the data
if (newIndex > int.MaxValue)
{
- this.AddInvalidTag(tag);
-
- exifValue = default;
-
+ this.AddInvalidTag(new UnkownExifTag(tag));
return false;
}
@@ -342,12 +326,9 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
if (this.RemainingLength < size)
{
- this.AddInvalidTag(tag);
+ this.AddInvalidTag(new UnkownExifTag(tag));
this.position = oldIndex;
-
- exifValue = default;
-
return false;
}
@@ -361,33 +342,25 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
value = this.ConvertValue(dataType, offsetBuffer, numberOfComponents);
}
- exifValue = new ExifValue(tag, dataType, value, isArray: value != null && numberOfComponents != 1);
-
- return true;
- }
+ exifValue = ExifValues.Create(tag) ?? ExifValues.Create(tag, dataType, numberOfComponents);
- private void AddInvalidTag(ExifTag tag)
- {
- if (this.invalidTags is null)
+ if (exifValue is null)
{
- this.invalidTags = new List();
+ this.AddInvalidTag(new UnkownExifTag(tag));
+ return false;
}
- this.invalidTags.Add(tag);
- }
-
- [MethodImpl(InliningOptions.ShortMethod)]
- private TEnum ToEnum(int value, TEnum defaultValue)
- where TEnum : struct, Enum
- {
- if (EnumHelper.IsDefined(value))
+ if (!exifValue.TrySetValue(value))
{
- return Unsafe.As(ref value);
+ return false;
}
- return defaultValue;
+ return true;
}
+ private void AddInvalidTag(ExifTag tag)
+ => (this.invalidTags ?? (this.invalidTags = new List())).Add(tag);
+
private bool TryReadSpan(int length, out ReadOnlySpan span)
{
if (this.RemainingLength < length)
@@ -421,18 +394,18 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
private void GetThumbnail(uint offset)
{
- var values = new List();
+ var values = new List();
this.AddValues(values, offset);
foreach (ExifValue value in values)
{
- if (value.Tag == ExifTag.JPEGInterchangeFormat && (value.DataType == ExifDataType.Long))
+ if (value == ExifTag.JPEGInterchangeFormat)
{
- this.ThumbnailOffset = (uint)value.Value;
+ this.ThumbnailOffset = ((ExifLong)value).Value;
}
- else if (value.Tag == ExifTag.JPEGInterchangeFormatLength && value.DataType == ExifDataType.Long)
+ else if (value == ExifTag.JPEGInterchangeFormatLength)
{
- this.ThumbnailLength = (uint)value.Value;
+ this.ThumbnailLength = ((ExifLong)value).Value;
}
}
}
@@ -541,18 +514,5 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
? BinaryPrimitives.ReadInt16BigEndian(buffer)
: BinaryPrimitives.ReadInt16LittleEndian(buffer);
}
-
- private sealed class EnumHelper
- where TEnum : struct, Enum
- {
- private static readonly int[] Values = Enum.GetValues(typeof(TEnum)).Cast()
- .Select(e => Convert.ToInt32(e)).OrderBy(e => e).ToArray();
-
- [MethodImpl(InliningOptions.ShortMethod)]
- public static bool IsDefined(int value)
- {
- return Array.BinarySearch(Values, value) >= 0;
- }
- }
}
}
diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifTagDescriptionAttribute.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifTagDescriptionAttribute.cs
index 845e4ee73..b9bb2ee05 100644
--- a/src/ImageSharp/Metadata/Profiles/Exif/ExifTagDescriptionAttribute.cs
+++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifTagDescriptionAttribute.cs
@@ -1,4 +1,4 @@
-// Copyright (c) Six Labors and contributors.
+// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
@@ -31,7 +31,8 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
///
public static string GetDescription(ExifTag tag, object value)
{
- FieldInfo field = tag.GetType().GetTypeInfo().GetDeclaredField(tag.ToString());
+ var tagValue = (ExifTagValue)(ushort)tag;
+ FieldInfo field = tagValue.GetType().GetTypeInfo().GetDeclaredField(tagValue.ToString());
if (field is null)
{
@@ -42,7 +43,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
{
object attributeValue = customAttribute.ConstructorArguments[0].Value;
- if (object.Equals(attributeValue, value))
+ if (Equals(attributeValue, value))
{
return (string)customAttribute.ConstructorArguments[1].Value;
}
diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifTags.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifTags.cs
index 0ed3a43b7..13fff5b6a 100644
--- a/src/ImageSharp/Metadata/Profiles/Exif/ExifTags.cs
+++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifTags.cs
@@ -1,281 +1,275 @@
-// Copyright (c) Six Labors and contributors.
+// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
-using static SixLabors.ImageSharp.Metadata.Profiles.Exif.ExifTag;
-
namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
{
internal static class ExifTags
{
- ///
- /// The collection if Image File Directory tags
- ///
- public static readonly ExifTag[] Ifd =
+ public static ExifParts GetPart(ExifTag tag)
{
- SubfileType,
- OldSubfileType,
- ImageWidth,
- ImageLength,
- BitsPerSample,
- Compression,
- PhotometricInterpretation,
- Thresholding,
- CellWidth,
- CellLength,
- FillOrder,
- DocumentName,
- ImageDescription,
- Make,
- Model,
- StripOffsets,
- Orientation,
- SamplesPerPixel,
- RowsPerStrip,
- StripByteCounts,
- MinSampleValue,
- MaxSampleValue,
- XResolution,
- YResolution,
- PlanarConfiguration,
- PageName,
- XPosition,
- YPosition,
- FreeOffsets,
- FreeByteCounts,
- GrayResponseUnit,
- GrayResponseCurve,
- T4Options,
- T6Options,
- ResolutionUnit,
- PageNumber,
- ColorResponseUnit,
- TransferFunction,
- Software,
- DateTime,
- Artist,
- HostComputer,
- Predictor,
- WhitePoint,
- PrimaryChromaticities,
- ColorMap,
- HalftoneHints,
- TileWidth,
- TileLength,
- TileOffsets,
- TileByteCounts,
- BadFaxLines,
- CleanFaxData,
- ConsecutiveBadFaxLines,
- InkSet,
- InkNames,
- NumberOfInks,
- DotRange,
- TargetPrinter,
- ExtraSamples,
- SampleFormat,
- SMinSampleValue,
- SMaxSampleValue,
- TransferRange,
- ClipPath,
- XClipPathUnits,
- YClipPathUnits,
- Indexed,
- JPEGTables,
- OPIProxy,
- ProfileType,
- FaxProfile,
- CodingMethods,
- VersionYear,
- ModeNumber,
- Decode,
- DefaultImageColor,
- T82ptions,
- JPEGProc,
- JPEGInterchangeFormat,
- JPEGInterchangeFormatLength,
- JPEGRestartInterval,
- JPEGLosslessPredictors,
- JPEGPointTransforms,
- JPEGQTables,
- JPEGDCTables,
- JPEGACTables,
- YCbCrCoefficients,
- YCbCrSubsampling,
- YCbCrSubsampling,
- YCbCrPositioning,
- ReferenceBlackWhite,
- StripRowCounts,
- XMP,
- Rating,
- RatingPercent,
- ImageID,
- CFARepeatPatternDim,
- CFAPattern2,
- BatteryLevel,
- Copyright,
- MDFileTag,
- MDScalePixel,
- MDLabName,
- MDSampleInfo,
- MDPrepDate,
- MDPrepTime,
- MDFileUnits,
- PixelScale,
- IntergraphPacketData,
- IntergraphRegisters,
- IntergraphMatrix,
- ModelTiePoint,
- SEMInfo,
- ModelTransform,
- ImageLayer,
- FaxRecvParams,
- FaxSubaddress,
- FaxRecvTime,
- ImageSourceData,
- XPTitle,
- XPComment,
- XPAuthor,
- XPKeywords,
- XPSubject,
- GDALMetadata,
- GDALNoData
- };
+ switch ((ExifTagValue)(ushort)tag)
+ {
+ case ExifTagValue.SubfileType:
+ case ExifTagValue.OldSubfileType:
+ case ExifTagValue.ImageWidth:
+ case ExifTagValue.ImageLength:
+ case ExifTagValue.BitsPerSample:
+ case ExifTagValue.Compression:
+ case ExifTagValue.PhotometricInterpretation:
+ case ExifTagValue.Thresholding:
+ case ExifTagValue.CellWidth:
+ case ExifTagValue.CellLength:
+ case ExifTagValue.FillOrder:
+ case ExifTagValue.DocumentName:
+ case ExifTagValue.ImageDescription:
+ case ExifTagValue.Make:
+ case ExifTagValue.Model:
+ case ExifTagValue.StripOffsets:
+ case ExifTagValue.Orientation:
+ case ExifTagValue.SamplesPerPixel:
+ case ExifTagValue.RowsPerStrip:
+ case ExifTagValue.StripByteCounts:
+ case ExifTagValue.MinSampleValue:
+ case ExifTagValue.MaxSampleValue:
+ case ExifTagValue.XResolution:
+ case ExifTagValue.YResolution:
+ case ExifTagValue.PlanarConfiguration:
+ case ExifTagValue.PageName:
+ case ExifTagValue.XPosition:
+ case ExifTagValue.YPosition:
+ case ExifTagValue.FreeOffsets:
+ case ExifTagValue.FreeByteCounts:
+ case ExifTagValue.GrayResponseUnit:
+ case ExifTagValue.GrayResponseCurve:
+ case ExifTagValue.T4Options:
+ case ExifTagValue.T6Options:
+ case ExifTagValue.ResolutionUnit:
+ case ExifTagValue.PageNumber:
+ case ExifTagValue.ColorResponseUnit:
+ case ExifTagValue.TransferFunction:
+ case ExifTagValue.Software:
+ case ExifTagValue.DateTime:
+ case ExifTagValue.Artist:
+ case ExifTagValue.HostComputer:
+ case ExifTagValue.Predictor:
+ case ExifTagValue.WhitePoint:
+ case ExifTagValue.PrimaryChromaticities:
+ case ExifTagValue.ColorMap:
+ case ExifTagValue.HalftoneHints:
+ case ExifTagValue.TileWidth:
+ case ExifTagValue.TileLength:
+ case ExifTagValue.TileOffsets:
+ case ExifTagValue.TileByteCounts:
+ case ExifTagValue.BadFaxLines:
+ case ExifTagValue.CleanFaxData:
+ case ExifTagValue.ConsecutiveBadFaxLines:
+ case ExifTagValue.InkSet:
+ case ExifTagValue.InkNames:
+ case ExifTagValue.NumberOfInks:
+ case ExifTagValue.DotRange:
+ case ExifTagValue.TargetPrinter:
+ case ExifTagValue.ExtraSamples:
+ case ExifTagValue.SampleFormat:
+ case ExifTagValue.SMinSampleValue:
+ case ExifTagValue.SMaxSampleValue:
+ case ExifTagValue.TransferRange:
+ case ExifTagValue.ClipPath:
+ case ExifTagValue.XClipPathUnits:
+ case ExifTagValue.YClipPathUnits:
+ case ExifTagValue.Indexed:
+ case ExifTagValue.JPEGTables:
+ case ExifTagValue.OPIProxy:
+ case ExifTagValue.ProfileType:
+ case ExifTagValue.FaxProfile:
+ case ExifTagValue.CodingMethods:
+ case ExifTagValue.VersionYear:
+ case ExifTagValue.ModeNumber:
+ case ExifTagValue.Decode:
+ case ExifTagValue.DefaultImageColor:
+ case ExifTagValue.T82ptions:
+ case ExifTagValue.JPEGProc:
+ case ExifTagValue.JPEGInterchangeFormat:
+ case ExifTagValue.JPEGInterchangeFormatLength:
+ case ExifTagValue.JPEGRestartInterval:
+ case ExifTagValue.JPEGLosslessPredictors:
+ case ExifTagValue.JPEGPointTransforms:
+ case ExifTagValue.JPEGQTables:
+ case ExifTagValue.JPEGDCTables:
+ case ExifTagValue.JPEGACTables:
+ case ExifTagValue.YCbCrCoefficients:
+ case ExifTagValue.YCbCrPositioning:
+ case ExifTagValue.YCbCrSubsampling:
+ case ExifTagValue.ReferenceBlackWhite:
+ case ExifTagValue.StripRowCounts:
+ case ExifTagValue.XMP:
+ case ExifTagValue.Rating:
+ case ExifTagValue.RatingPercent:
+ case ExifTagValue.ImageID:
+ case ExifTagValue.CFARepeatPatternDim:
+ case ExifTagValue.CFAPattern2:
+ case ExifTagValue.BatteryLevel:
+ case ExifTagValue.Copyright:
+ case ExifTagValue.MDFileTag:
+ case ExifTagValue.MDScalePixel:
+ case ExifTagValue.MDLabName:
+ case ExifTagValue.MDSampleInfo:
+ case ExifTagValue.MDPrepDate:
+ case ExifTagValue.MDPrepTime:
+ case ExifTagValue.MDFileUnits:
+ case ExifTagValue.PixelScale:
+ case ExifTagValue.IntergraphPacketData:
+ case ExifTagValue.IntergraphRegisters:
+ case ExifTagValue.IntergraphMatrix:
+ case ExifTagValue.ModelTiePoint:
+ case ExifTagValue.SEMInfo:
+ case ExifTagValue.ModelTransform:
+ case ExifTagValue.ImageLayer:
+ case ExifTagValue.FaxRecvParams:
+ case ExifTagValue.FaxSubaddress:
+ case ExifTagValue.FaxRecvTime:
+ case ExifTagValue.ImageSourceData:
+ case ExifTagValue.XPTitle:
+ case ExifTagValue.XPComment:
+ case ExifTagValue.XPAuthor:
+ case ExifTagValue.XPKeywords:
+ case ExifTagValue.XPSubject:
+ case ExifTagValue.GDALMetadata:
+ case ExifTagValue.GDALNoData:
+ return ExifParts.IfdTags;
- ///
- /// The collection of Exif tags
- ///
- public static readonly ExifTag[] Exif =
- {
- ExposureTime,
- FNumber,
- ExposureProgram,
- SpectralSensitivity,
- ISOSpeedRatings,
- OECF,
- Interlace,
- TimeZoneOffset,
- SelfTimerMode,
- SensitivityType,
- StandardOutputSensitivity,
- RecommendedExposureIndex,
- ISOSpeed,
- ISOSpeedLatitudeyyy,
- ISOSpeedLatitudezzz,
- ExifVersion,
- DateTimeOriginal,
- DateTimeDigitized,
- OffsetTime,
- OffsetTimeOriginal,
- OffsetTimeDigitized,
- ComponentsConfiguration,
- CompressedBitsPerPixel,
- ShutterSpeedValue,
- ApertureValue,
- BrightnessValue,
- ExposureBiasValue,
- MaxApertureValue,
- SubjectDistance,
- MeteringMode,
- LightSource,
- Flash,
- FocalLength,
- FlashEnergy2,
- SpatialFrequencyResponse2,
- Noise,
- FocalPlaneXResolution2,
- FocalPlaneYResolution2,
- FocalPlaneResolutionUnit2,
- ImageNumber,
- SecurityClassification,
- ImageHistory,
- SubjectArea,
- ExposureIndex2,
- TIFFEPStandardID,
- SensingMethod2,
- MakerNote,
- UserComment,
- SubsecTime,
- SubsecTimeOriginal,
- SubsecTimeDigitized,
- AmbientTemperature,
- Humidity,
- Pressure,
- WaterDepth,
- Acceleration,
- CameraElevationAngle,
- FlashpixVersion,
- ColorSpace,
- PixelXDimension,
- PixelYDimension,
- RelatedSoundFile,
- FlashEnergy,
- SpatialFrequencyResponse,
- FocalPlaneXResolution,
- FocalPlaneYResolution,
- FocalPlaneResolutionUnit,
- SubjectLocation,
- ExposureIndex,
- SensingMethod,
- FileSource,
- SceneType,
- CFAPattern,
- CustomRendered,
- ExposureMode,
- WhiteBalance,
- DigitalZoomRatio,
- FocalLengthIn35mmFilm,
- SceneCaptureType,
- GainControl,
- Contrast,
- Saturation,
- Sharpness,
- DeviceSettingDescription,
- SubjectDistanceRange,
- ImageUniqueID,
- OwnerName,
- SerialNumber,
- LensInfo,
- LensMake,
- LensModel,
- LensSerialNumber
- };
+ case ExifTagValue.ExposureTime:
+ case ExifTagValue.FNumber:
+ case ExifTagValue.ExposureProgram:
+ case ExifTagValue.SpectralSensitivity:
+ case ExifTagValue.ISOSpeedRatings:
+ case ExifTagValue.OECF:
+ case ExifTagValue.Interlace:
+ case ExifTagValue.TimeZoneOffset:
+ case ExifTagValue.SelfTimerMode:
+ case ExifTagValue.SensitivityType:
+ case ExifTagValue.StandardOutputSensitivity:
+ case ExifTagValue.RecommendedExposureIndex:
+ case ExifTagValue.ISOSpeed:
+ case ExifTagValue.ISOSpeedLatitudeyyy:
+ case ExifTagValue.ISOSpeedLatitudezzz:
+ case ExifTagValue.ExifVersion:
+ case ExifTagValue.DateTimeOriginal:
+ case ExifTagValue.DateTimeDigitized:
+ case ExifTagValue.OffsetTime:
+ case ExifTagValue.OffsetTimeOriginal:
+ case ExifTagValue.OffsetTimeDigitized:
+ case ExifTagValue.ComponentsConfiguration:
+ case ExifTagValue.CompressedBitsPerPixel:
+ case ExifTagValue.ShutterSpeedValue:
+ case ExifTagValue.ApertureValue:
+ case ExifTagValue.BrightnessValue:
+ case ExifTagValue.ExposureBiasValue:
+ case ExifTagValue.MaxApertureValue:
+ case ExifTagValue.SubjectDistance:
+ case ExifTagValue.MeteringMode:
+ case ExifTagValue.LightSource:
+ case ExifTagValue.Flash:
+ case ExifTagValue.FocalLength:
+ case ExifTagValue.FlashEnergy2:
+ case ExifTagValue.SpatialFrequencyResponse2:
+ case ExifTagValue.Noise:
+ case ExifTagValue.FocalPlaneXResolution2:
+ case ExifTagValue.FocalPlaneYResolution2:
+ case ExifTagValue.FocalPlaneResolutionUnit2:
+ case ExifTagValue.ImageNumber:
+ case ExifTagValue.SecurityClassification:
+ case ExifTagValue.ImageHistory:
+ case ExifTagValue.SubjectArea:
+ case ExifTagValue.ExposureIndex2:
+ case ExifTagValue.TIFFEPStandardID:
+ case ExifTagValue.SensingMethod2:
+ case ExifTagValue.MakerNote:
+ case ExifTagValue.UserComment:
+ case ExifTagValue.SubsecTime:
+ case ExifTagValue.SubsecTimeOriginal:
+ case ExifTagValue.SubsecTimeDigitized:
+ case ExifTagValue.AmbientTemperature:
+ case ExifTagValue.Humidity:
+ case ExifTagValue.Pressure:
+ case ExifTagValue.WaterDepth:
+ case ExifTagValue.Acceleration:
+ case ExifTagValue.CameraElevationAngle:
+ case ExifTagValue.FlashpixVersion:
+ case ExifTagValue.ColorSpace:
+ case ExifTagValue.PixelXDimension:
+ case ExifTagValue.PixelYDimension:
+ case ExifTagValue.RelatedSoundFile:
+ case ExifTagValue.FlashEnergy:
+ case ExifTagValue.SpatialFrequencyResponse:
+ case ExifTagValue.FocalPlaneXResolution:
+ case ExifTagValue.FocalPlaneYResolution:
+ case ExifTagValue.FocalPlaneResolutionUnit:
+ case ExifTagValue.SubjectLocation:
+ case ExifTagValue.ExposureIndex:
+ case ExifTagValue.SensingMethod:
+ case ExifTagValue.FileSource:
+ case ExifTagValue.SceneType:
+ case ExifTagValue.CFAPattern:
+ case ExifTagValue.CustomRendered:
+ case ExifTagValue.ExposureMode:
+ case ExifTagValue.WhiteBalance:
+ case ExifTagValue.DigitalZoomRatio:
+ case ExifTagValue.FocalLengthIn35mmFilm:
+ case ExifTagValue.SceneCaptureType:
+ case ExifTagValue.GainControl:
+ case ExifTagValue.Contrast:
+ case ExifTagValue.Saturation:
+ case ExifTagValue.Sharpness:
+ case ExifTagValue.DeviceSettingDescription:
+ case ExifTagValue.SubjectDistanceRange:
+ case ExifTagValue.ImageUniqueID:
+ case ExifTagValue.OwnerName:
+ case ExifTagValue.SerialNumber:
+ case ExifTagValue.LensInfo:
+ case ExifTagValue.LensMake:
+ case ExifTagValue.LensModel:
+ case ExifTagValue.LensSerialNumber:
+ return ExifParts.ExifTags;
- ///
- /// The collection of GPS tags
- ///
- public static readonly ExifTag[] Gps =
- {
- GPSVersionID,
- GPSLatitudeRef,
- GPSLatitude,
- GPSLongitudeRef,
- GPSLongitude,
- GPSAltitudeRef,
- GPSAltitude,
- GPSTimestamp,
- GPSSatellites,
- GPSStatus,
- GPSMeasureMode,
- GPSDOP,
- GPSSpeedRef,
- GPSSpeed,
- GPSTrackRef,
- GPSTrack,
- GPSImgDirectionRef,
- GPSImgDirection,
- GPSMapDatum,
- GPSDestLatitudeRef,
- GPSDestLatitude,
- GPSDestLongitudeRef,
- GPSDestLongitude,
- GPSDestBearingRef,
- GPSDestBearing,
- GPSDestDistanceRef,
- GPSDestDistance,
- GPSProcessingMethod,
- GPSAreaInformation,
- GPSDateStamp,
- GPSDifferential
- };
+ case ExifTagValue.GPSVersionID:
+ case ExifTagValue.GPSLatitudeRef:
+ case ExifTagValue.GPSLatitude:
+ case ExifTagValue.GPSLongitudeRef:
+ case ExifTagValue.GPSLongitude:
+ case ExifTagValue.GPSAltitudeRef:
+ case ExifTagValue.GPSAltitude:
+ case ExifTagValue.GPSTimestamp:
+ case ExifTagValue.GPSSatellites:
+ case ExifTagValue.GPSStatus:
+ case ExifTagValue.GPSMeasureMode:
+ case ExifTagValue.GPSDOP:
+ case ExifTagValue.GPSSpeedRef:
+ case ExifTagValue.GPSSpeed:
+ case ExifTagValue.GPSTrackRef:
+ case ExifTagValue.GPSTrack:
+ case ExifTagValue.GPSImgDirectionRef:
+ case ExifTagValue.GPSImgDirection:
+ case ExifTagValue.GPSMapDatum:
+ case ExifTagValue.GPSDestLatitudeRef:
+ case ExifTagValue.GPSDestLatitude:
+ case ExifTagValue.GPSDestLongitudeRef:
+ case ExifTagValue.GPSDestLongitude:
+ case ExifTagValue.GPSDestBearingRef:
+ case ExifTagValue.GPSDestBearing:
+ case ExifTagValue.GPSDestDistanceRef:
+ case ExifTagValue.GPSDestDistance:
+ case ExifTagValue.GPSProcessingMethod:
+ case ExifTagValue.GPSAreaInformation:
+ case ExifTagValue.GPSDateStamp:
+ case ExifTagValue.GPSDifferential:
+ return ExifParts.GpsTags;
+
+ case ExifTagValue.Unknown:
+ case ExifTagValue.SubIFDOffset:
+ case ExifTagValue.GPSIFDOffset:
+ default:
+ return ExifParts.None;
+ }
+ }
}
-}
\ No newline at end of file
+}
diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifValue.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifValue.cs
deleted file mode 100644
index 05a9f35c9..000000000
--- a/src/ImageSharp/Metadata/Profiles/Exif/ExifValue.cs
+++ /dev/null
@@ -1,721 +0,0 @@
-// Copyright (c) Six Labors and contributors.
-// Licensed under the Apache License, Version 2.0.
-
-using System;
-using System.Globalization;
-using System.Text;
-using SixLabors.ImageSharp.Primitives;
-
-namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
-{
- ///
- /// Represent the value of the EXIF profile.
- ///
- public sealed class ExifValue : IEquatable, IDeepCloneable
- {
- ///
- /// Initializes a new instance of the class.
- ///
- /// The tag.
- /// The data type.
- /// The value.
- /// Whether the value is an array.
- internal ExifValue(ExifTag tag, ExifDataType dataType, object value, bool isArray)
- {
- this.Tag = tag;
- this.DataType = dataType;
- this.IsArray = isArray && dataType != ExifDataType.Ascii;
- this.Value = value;
- }
-
- ///
- /// Initializes a new instance of the class
- /// by making a copy from another exif value.
- ///
- /// The other exif value, where the clone should be made from.
- /// is null.
- private ExifValue(ExifValue other)
- {
- Guard.NotNull(other, nameof(other));
-
- this.DataType = other.DataType;
- this.IsArray = other.IsArray;
- this.Tag = other.Tag;
-
- if (!other.IsArray)
- {
- // All types are value types except for string which is immutable so safe to simply assign.
- this.Value = other.Value;
- }
- else
- {
- // All array types are value types so Clone() is sufficient here.
- var array = (Array)other.Value;
- this.Value = array.Clone();
- }
- }
-
- ///
- /// Gets the data type of the exif value.
- ///
- public ExifDataType DataType { get; }
-
- ///
- /// Gets a value indicating whether the value is an array.
- ///
- public bool IsArray { get; }
-
- ///
- /// Gets the tag of the exif value.
- ///
- public ExifTag Tag { get; }
-
- ///
- /// Gets the value.
- ///
- public object Value { get; }
-
- ///
- /// Gets a value indicating whether the EXIF value has a value.
- ///
- internal bool HasValue
- {
- get
- {
- if (this.Value is null)
- {
- return false;
- }
-
- if (this.DataType == ExifDataType.Ascii)
- {
- return ((string)this.Value).Length > 0;
- }
-
- return true;
- }
- }
-
- ///
- /// Gets the length of the EXIF value
- ///
- internal int Length
- {
- get
- {
- if (this.Value is null)
- {
- return 4;
- }
-
- int size = (int)(GetSize(this.DataType) * this.NumberOfComponents);
-
- return size < 4 ? 4 : size;
- }
- }
-
- ///
- /// Gets the number of components.
- ///
- internal int NumberOfComponents
- {
- get
- {
- if (this.DataType == ExifDataType.Ascii)
- {
- return Encoding.UTF8.GetBytes((string)this.Value).Length;
- }
-
- if (this.IsArray)
- {
- return ((Array)this.Value).Length;
- }
-
- return 1;
- }
- }
-
- ///
- /// Compares two objects for equality.
- ///
- ///
- /// The on the left side of the operand.
- ///
- ///
- /// The on the right side of the operand.
- ///
- ///
- /// True if the parameter is equal to the parameter; otherwise, false.
- ///
- public static bool operator ==(ExifValue left, ExifValue right) => ReferenceEquals(left, right) || left.Equals(right);
-
- ///
- /// Compares two objects for equality.
- ///
- ///
- /// The on the left side of the operand.
- ///
- ///
- /// The on the right side of the operand.
- ///
- ///
- /// True if the parameter is not equal to the parameter; otherwise, false.
- ///
- public static bool operator !=(ExifValue left, ExifValue right) => !(left == right);
-
- ///
- public override bool Equals(object obj) => obj is ExifValue other && this.Equals(other);
-
- ///
- public bool Equals(ExifValue other)
- {
- if (other is null)
- {
- return false;
- }
-
- if (ReferenceEquals(this, other))
- {
- return true;
- }
-
- return
- this.Tag == other.Tag
- && this.DataType == other.DataType
- && object.Equals(this.Value, other.Value);
- }
-
- ///
- /// Clones the current value, overwriting the value.
- ///
- /// The value to overwrite.
- ///
- public ExifValue WithValue(object value)
- {
- this.CheckValue(value);
-
- return new ExifValue(this.Tag, this.DataType, value, this.IsArray);
- }
-
- ///
- public override int GetHashCode()
- {
- return HashCode.Combine(this.Tag, this.DataType, this.Value);
- }
-
- ///
- public override string ToString()
- {
- if (this.Value is null)
- {
- return null;
- }
-
- if (this.DataType == ExifDataType.Ascii)
- {
- return (string)this.Value;
- }
-
- if (!this.IsArray)
- {
- return this.ToString(this.Value);
- }
-
- var sb = new StringBuilder();
- foreach (object value in (Array)this.Value)
- {
- sb.Append(this.ToString(value));
- sb.Append(' ');
- }
-
- return sb.ToString();
- }
-
- ///
- public ExifValue DeepClone() => new ExifValue(this);
-
- ///
- /// Creates a new
- ///
- /// The tag.
- /// The value.
- ///
- /// The .
- ///
- ///
- /// Thrown if the tag is not supported.
- ///
- internal static ExifValue Create(ExifTag tag, object value)
- {
- Guard.IsFalse(tag == ExifTag.Unknown, nameof(tag), "Invalid Tag");
-
- switch (tag)
- {
- case ExifTag.ImageDescription:
- case ExifTag.Make:
- case ExifTag.Model:
- case ExifTag.Software:
- case ExifTag.DateTime:
- case ExifTag.Artist:
- case ExifTag.HostComputer:
- case ExifTag.Copyright:
- case ExifTag.DocumentName:
- case ExifTag.PageName:
- case ExifTag.InkNames:
- case ExifTag.TargetPrinter:
- case ExifTag.ImageID:
- case ExifTag.MDLabName:
- case ExifTag.MDSampleInfo:
- case ExifTag.MDPrepDate:
- case ExifTag.MDPrepTime:
- case ExifTag.MDFileUnits:
- case ExifTag.SEMInfo:
- case ExifTag.SpectralSensitivity:
- case ExifTag.DateTimeOriginal:
- case ExifTag.DateTimeDigitized:
- case ExifTag.SubsecTime:
- case ExifTag.SubsecTimeOriginal:
- case ExifTag.SubsecTimeDigitized:
- case ExifTag.FaxSubaddress:
- case ExifTag.OffsetTime:
- case ExifTag.OffsetTimeOriginal:
- case ExifTag.OffsetTimeDigitized:
- case ExifTag.SecurityClassification:
- case ExifTag.ImageHistory:
- case ExifTag.ImageUniqueID:
- case ExifTag.OwnerName:
- case ExifTag.SerialNumber:
- case ExifTag.LensMake:
- case ExifTag.LensModel:
- case ExifTag.LensSerialNumber:
- case ExifTag.GDALMetadata:
- case ExifTag.GDALNoData:
- case ExifTag.GPSLatitudeRef:
- case ExifTag.GPSLongitudeRef:
- case ExifTag.GPSSatellites:
- case ExifTag.GPSStatus:
- case ExifTag.GPSMeasureMode:
- case ExifTag.GPSSpeedRef:
- case ExifTag.GPSTrackRef:
- case ExifTag.GPSImgDirectionRef:
- case ExifTag.GPSMapDatum:
- case ExifTag.GPSDestLatitudeRef:
- case ExifTag.GPSDestLongitudeRef:
- case ExifTag.GPSDestBearingRef:
- case ExifTag.GPSDestDistanceRef:
- case ExifTag.GPSDateStamp:
- return new ExifValue(tag, ExifDataType.Ascii, value, true);
-
- case ExifTag.ClipPath:
- case ExifTag.VersionYear:
- case ExifTag.XMP:
- case ExifTag.CFAPattern2:
- case ExifTag.TIFFEPStandardID:
- case ExifTag.XPTitle:
- case ExifTag.XPComment:
- case ExifTag.XPAuthor:
- case ExifTag.XPKeywords:
- case ExifTag.XPSubject:
- case ExifTag.GPSVersionID:
- return new ExifValue(tag, ExifDataType.Byte, value, true);
-
- case ExifTag.FaxProfile:
- case ExifTag.ModeNumber:
- case ExifTag.GPSAltitudeRef:
- return new ExifValue(tag, ExifDataType.Byte, value, false);
-
- case ExifTag.FreeOffsets:
- case ExifTag.FreeByteCounts:
- case ExifTag.ColorResponseUnit:
- case ExifTag.TileOffsets:
- case ExifTag.SMinSampleValue:
- case ExifTag.SMaxSampleValue:
- case ExifTag.JPEGQTables:
- case ExifTag.JPEGDCTables:
- case ExifTag.JPEGACTables:
- case ExifTag.StripRowCounts:
- case ExifTag.IntergraphRegisters:
- case ExifTag.TimeZoneOffset:
- return new ExifValue(tag, ExifDataType.Long, value, true);
- case ExifTag.SubfileType:
- case ExifTag.SubIFDOffset:
- case ExifTag.GPSIFDOffset:
- case ExifTag.T4Options:
- case ExifTag.T6Options:
- case ExifTag.XClipPathUnits:
- case ExifTag.YClipPathUnits:
- case ExifTag.ProfileType:
- case ExifTag.CodingMethods:
- case ExifTag.T82ptions:
- case ExifTag.JPEGInterchangeFormat:
- case ExifTag.JPEGInterchangeFormatLength:
- case ExifTag.MDFileTag:
- case ExifTag.StandardOutputSensitivity:
- case ExifTag.RecommendedExposureIndex:
- case ExifTag.ISOSpeed:
- case ExifTag.ISOSpeedLatitudeyyy:
- case ExifTag.ISOSpeedLatitudezzz:
- case ExifTag.FaxRecvParams:
- case ExifTag.FaxRecvTime:
- case ExifTag.ImageNumber:
- return new ExifValue(tag, ExifDataType.Long, value, false);
-
- case ExifTag.WhitePoint:
- case ExifTag.PrimaryChromaticities:
- case ExifTag.YCbCrCoefficients:
- case ExifTag.ReferenceBlackWhite:
- case ExifTag.PixelScale:
- case ExifTag.IntergraphMatrix:
- case ExifTag.ModelTiePoint:
- case ExifTag.ModelTransform:
- case ExifTag.GPSLatitude:
- case ExifTag.GPSLongitude:
- case ExifTag.GPSTimestamp:
- case ExifTag.GPSDestLatitude:
- case ExifTag.GPSDestLongitude:
- return new ExifValue(tag, ExifDataType.Rational, value, true);
-
- case ExifTag.XPosition:
- case ExifTag.YPosition:
- case ExifTag.XResolution:
- case ExifTag.YResolution:
- case ExifTag.BatteryLevel:
- case ExifTag.ExposureTime:
- case ExifTag.FNumber:
- case ExifTag.MDScalePixel:
- case ExifTag.CompressedBitsPerPixel:
- case ExifTag.ApertureValue:
- case ExifTag.MaxApertureValue:
- case ExifTag.SubjectDistance:
- case ExifTag.FocalLength:
- case ExifTag.FlashEnergy2:
- case ExifTag.FocalPlaneXResolution2:
- case ExifTag.FocalPlaneYResolution2:
- case ExifTag.ExposureIndex2:
- case ExifTag.Humidity:
- case ExifTag.Pressure:
- case ExifTag.Acceleration:
- case ExifTag.FlashEnergy:
- case ExifTag.FocalPlaneXResolution:
- case ExifTag.FocalPlaneYResolution:
- case ExifTag.ExposureIndex:
- case ExifTag.DigitalZoomRatio:
- case ExifTag.LensInfo:
- case ExifTag.GPSAltitude:
- case ExifTag.GPSDOP:
- case ExifTag.GPSSpeed:
- case ExifTag.GPSTrack:
- case ExifTag.GPSImgDirection:
- case ExifTag.GPSDestBearing:
- case ExifTag.GPSDestDistance:
- return new ExifValue(tag, ExifDataType.Rational, value, false);
-
- case ExifTag.BitsPerSample:
- case ExifTag.MinSampleValue:
- case ExifTag.MaxSampleValue:
- case ExifTag.GrayResponseCurve:
- case ExifTag.ColorMap:
- case ExifTag.ExtraSamples:
- case ExifTag.PageNumber:
- case ExifTag.TransferFunction:
- case ExifTag.Predictor:
- case ExifTag.HalftoneHints:
- case ExifTag.SampleFormat:
- case ExifTag.TransferRange:
- case ExifTag.DefaultImageColor:
- case ExifTag.JPEGLosslessPredictors:
- case ExifTag.JPEGPointTransforms:
- case ExifTag.YCbCrSubsampling:
- case ExifTag.CFARepeatPatternDim:
- case ExifTag.IntergraphPacketData:
- case ExifTag.ISOSpeedRatings:
- case ExifTag.SubjectArea:
- case ExifTag.SubjectLocation:
- return new ExifValue(tag, ExifDataType.Short, value, true);
-
- case ExifTag.OldSubfileType:
- case ExifTag.Compression:
- case ExifTag.PhotometricInterpretation:
- case ExifTag.Thresholding:
- case ExifTag.CellWidth:
- case ExifTag.CellLength:
- case ExifTag.FillOrder:
- case ExifTag.Orientation:
- case ExifTag.SamplesPerPixel:
- case ExifTag.PlanarConfiguration:
- case ExifTag.GrayResponseUnit:
- case ExifTag.ResolutionUnit:
- case ExifTag.CleanFaxData:
- case ExifTag.InkSet:
- case ExifTag.NumberOfInks:
- case ExifTag.DotRange:
- case ExifTag.Indexed:
- case ExifTag.OPIProxy:
- case ExifTag.JPEGProc:
- case ExifTag.JPEGRestartInterval:
- case ExifTag.YCbCrPositioning:
- case ExifTag.Rating:
- case ExifTag.RatingPercent:
- case ExifTag.ExposureProgram:
- case ExifTag.Interlace:
- case ExifTag.SelfTimerMode:
- case ExifTag.SensitivityType:
- case ExifTag.MeteringMode:
- case ExifTag.LightSource:
- case ExifTag.FocalPlaneResolutionUnit2:
- case ExifTag.SensingMethod2:
- case ExifTag.Flash:
- case ExifTag.ColorSpace:
- case ExifTag.FocalPlaneResolutionUnit:
- case ExifTag.SensingMethod:
- case ExifTag.CustomRendered:
- case ExifTag.ExposureMode:
- case ExifTag.WhiteBalance:
- case ExifTag.FocalLengthIn35mmFilm:
- case ExifTag.SceneCaptureType:
- case ExifTag.GainControl:
- case ExifTag.Contrast:
- case ExifTag.Saturation:
- case ExifTag.Sharpness:
- case ExifTag.SubjectDistanceRange:
- case ExifTag.GPSDifferential:
- return new ExifValue(tag, ExifDataType.Short, value, false);
-
- case ExifTag.Decode:
- return new ExifValue(tag, ExifDataType.SignedRational, value, true);
-
- case ExifTag.ShutterSpeedValue:
- case ExifTag.BrightnessValue:
- case ExifTag.ExposureBiasValue:
- case ExifTag.AmbientTemperature:
- case ExifTag.WaterDepth:
- case ExifTag.CameraElevationAngle:
- return new ExifValue(tag, ExifDataType.SignedRational, value, false);
-
- case ExifTag.JPEGTables:
- case ExifTag.OECF:
- case ExifTag.ExifVersion:
- case ExifTag.ComponentsConfiguration:
- case ExifTag.MakerNote:
- case ExifTag.UserComment:
- case ExifTag.FlashpixVersion:
- case ExifTag.SpatialFrequencyResponse:
- case ExifTag.SpatialFrequencyResponse2:
- case ExifTag.Noise:
- case ExifTag.CFAPattern:
- case ExifTag.DeviceSettingDescription:
- case ExifTag.ImageSourceData:
- case ExifTag.GPSProcessingMethod:
- case ExifTag.GPSAreaInformation:
- return new ExifValue(tag, ExifDataType.Undefined, value, true);
-
- case ExifTag.FileSource:
- case ExifTag.SceneType:
- return new ExifValue(tag, ExifDataType.Undefined, value, false);
-
- case ExifTag.StripOffsets:
- case ExifTag.TileByteCounts:
- case ExifTag.ImageLayer:
- return CreateNumber(tag, value, true);
-
- case ExifTag.ImageWidth:
- case ExifTag.ImageLength:
- case ExifTag.TileWidth:
- case ExifTag.TileLength:
- case ExifTag.BadFaxLines:
- case ExifTag.ConsecutiveBadFaxLines:
- case ExifTag.PixelXDimension:
- case ExifTag.PixelYDimension:
- return CreateNumber(tag, value, false);
-
- default:
- throw new NotSupportedException();
- }
- }
-
- ///
- /// Gets the size in bytes of the given data type.
- ///
- /// The data type.
- ///
- /// The .
- ///
- ///
- /// Thrown if the type is unsupported.
- ///
- internal static uint GetSize(ExifDataType dataType)
- {
- switch (dataType)
- {
- case ExifDataType.Ascii:
- case ExifDataType.Byte:
- case ExifDataType.SignedByte:
- case ExifDataType.Undefined:
- return 1;
- case ExifDataType.Short:
- case ExifDataType.SignedShort:
- return 2;
- case ExifDataType.Long:
- case ExifDataType.SignedLong:
- case ExifDataType.SingleFloat:
- return 4;
- case ExifDataType.DoubleFloat:
- case ExifDataType.Rational:
- case ExifDataType.SignedRational:
- return 8;
- default:
- throw new NotSupportedException(dataType.ToString());
- }
- }
-
- ///
- /// Returns an EXIF value with a numeric type for the given tag.
- ///
- /// The tag.
- /// The value.
- /// Whether the value is an array.
- ///
- /// The .
- ///
- private static ExifValue CreateNumber(ExifTag tag, object value, bool isArray)
- {
- Type type = value?.GetType();
- if (type?.IsArray == true)
- {
- type = type.GetElementType();
- }
-
- if (type is null || type == typeof(ushort))
- {
- return new ExifValue(tag, ExifDataType.Short, value, isArray);
- }
-
- if (type == typeof(short))
- {
- return new ExifValue(tag, ExifDataType.SignedShort, value, isArray);
- }
-
- if (type == typeof(uint))
- {
- return new ExifValue(tag, ExifDataType.Long, value, isArray);
- }
-
- return new ExifValue(tag, ExifDataType.SignedLong, value, isArray);
- }
-
- ///
- /// Checks the value type of the given object.
- ///
- /// The value to check.
- ///
- /// Thrown if the object type is not supported.
- ///
- private void CheckValue(object value)
- {
- if (value is null)
- {
- return;
- }
-
- Type type = value.GetType();
-
- if (this.DataType == ExifDataType.Ascii)
- {
- Guard.IsTrue(type == typeof(string), nameof(value), "Value should be a string.");
- return;
- }
-
- if (type.IsArray)
- {
- Guard.IsTrue(this.IsArray, nameof(value), "Value should not be an array.");
- type = type.GetElementType();
- }
- else
- {
- Guard.IsFalse(this.IsArray, nameof(value), "Value should not be an array.");
- }
-
- switch (this.DataType)
- {
- case ExifDataType.Byte:
- Guard.IsTrue(type == typeof(byte), nameof(value), $"Value should be a byte{(this.IsArray ? " array." : ".")}");
- break;
- case ExifDataType.DoubleFloat:
- Guard.IsTrue(type == typeof(double), nameof(value), $"Value should be a double{(this.IsArray ? " array." : ".")}");
- break;
- case ExifDataType.Long:
- Guard.IsTrue(type == typeof(uint), nameof(value), $"Value should be an unsigned int{(this.IsArray ? " array." : ".")}");
- break;
- case ExifDataType.Rational:
- Guard.IsTrue(type == typeof(Rational), nameof(value), $"Value should be a Rational{(this.IsArray ? " array." : ".")}");
- break;
- case ExifDataType.Short:
- Guard.IsTrue(type == typeof(ushort), nameof(value), $"Value should be an unsigned short{(this.IsArray ? " array." : ".")}");
- break;
- case ExifDataType.SignedByte:
- Guard.IsTrue(type == typeof(sbyte), nameof(value), $"Value should be a signed byte{(this.IsArray ? " array." : ".")}");
- break;
- case ExifDataType.SignedLong:
- Guard.IsTrue(type == typeof(int), nameof(value), $"Value should be an int{(this.IsArray ? " array." : ".")}");
- break;
- case ExifDataType.SignedRational:
- Guard.IsTrue(type == typeof(SignedRational), nameof(value), $"Value should be a SignedRational{(this.IsArray ? " array." : ".")}");
- break;
- case ExifDataType.SignedShort:
- Guard.IsTrue(type == typeof(short), nameof(value), $"Value should be a short{(this.IsArray ? " array." : ".")}");
- break;
- case ExifDataType.SingleFloat:
- Guard.IsTrue(type == typeof(float), nameof(value), $"Value should be a float{(this.IsArray ? " array." : ".")}");
- break;
- case ExifDataType.Undefined:
- Guard.IsTrue(type == typeof(byte), nameof(value), "Value should be a byte array.");
- break;
- default:
- throw new NotSupportedException();
- }
- }
-
- ///
- /// Converts the object value of this instance to its equivalent string representation
- ///
- /// The value
- /// The
- private string ToString(object value)
- {
- if (ExifTagDescriptionAttribute.GetDescription(this.Tag, value) is string description)
- {
- return description;
- }
-
- switch (this.DataType)
- {
- case ExifDataType.Ascii:
- return (string)value;
- case ExifDataType.Byte:
- return ((byte)value).ToString("X2", CultureInfo.InvariantCulture);
- case ExifDataType.DoubleFloat:
- return ((double)value).ToString(CultureInfo.InvariantCulture);
- case ExifDataType.Long:
- return ((uint)value).ToString(CultureInfo.InvariantCulture);
- case ExifDataType.Rational:
- return ((Rational)value).ToString(CultureInfo.InvariantCulture);
- case ExifDataType.Short:
- return ((ushort)value).ToString(CultureInfo.InvariantCulture);
- case ExifDataType.SignedByte:
- return ((sbyte)value).ToString("X2", CultureInfo.InvariantCulture);
- case ExifDataType.SignedLong:
- return ((int)value).ToString(CultureInfo.InvariantCulture);
- case ExifDataType.SignedRational:
- return ((Rational)value).ToString(CultureInfo.InvariantCulture);
- case ExifDataType.SignedShort:
- return ((short)value).ToString(CultureInfo.InvariantCulture);
- case ExifDataType.SingleFloat:
- return ((float)value).ToString(CultureInfo.InvariantCulture);
- case ExifDataType.Undefined:
- return ((byte)value).ToString("X2", CultureInfo.InvariantCulture);
- default:
- throw new NotSupportedException();
- }
- }
- }
-}
\ No newline at end of file
diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifWriter.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifWriter.cs
index 67c1b2b65..48b0fddca 100644
--- a/src/ImageSharp/Metadata/Profiles/Exif/ExifWriter.cs
+++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifWriter.cs
@@ -18,24 +18,24 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
/// Which parts will be written.
///
private readonly ExifParts allowedParts;
- private readonly IList values;
+ private readonly IList values;
private List dataOffsets;
- private readonly List ifdIndexes;
- private readonly List exifIndexes;
- private readonly List gpsIndexes;
+ private readonly List ifdValues;
+ private readonly List exifValues;
+ private readonly List gpsValues;
///
/// Initializes a new instance of the class.
///
/// The values.
/// The allowed parts.
- public ExifWriter(IList values, ExifParts allowedParts)
+ public ExifWriter(IList values, ExifParts allowedParts)
{
this.values = values;
this.allowedParts = allowedParts;
- this.ifdIndexes = this.GetIndexes(ExifParts.IfdTags, ExifTags.Ifd);
- this.exifIndexes = this.GetIndexes(ExifParts.ExifTags, ExifTags.Exif);
- this.gpsIndexes = this.GetIndexes(ExifParts.GPSTags, ExifTags.Gps);
+ this.ifdValues = this.GetPartValues(ExifParts.IfdTags);
+ this.exifValues = this.GetPartValues(ExifParts.ExifTags);
+ this.gpsValues = this.GetPartValues(ExifParts.GpsTags);
}
///
@@ -46,43 +46,29 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
///
public byte[] GetData()
{
- uint startIndex = 0;
+ const uint startIndex = 0;
uint length;
- int exifIndex = -1;
- int gpsIndex = -1;
- if (this.exifIndexes.Count > 0)
- {
- exifIndex = this.GetIndex(this.ifdIndexes, ExifTag.SubIFDOffset);
- }
-
- if (this.gpsIndexes.Count > 0)
- {
- gpsIndex = this.GetIndex(this.ifdIndexes, ExifTag.GPSIFDOffset);
- }
-
- uint ifdLength = 2 + this.GetLength(this.ifdIndexes) + 4;
- uint exifLength = this.GetLength(this.exifIndexes);
- uint gpsLength = this.GetLength(this.gpsIndexes);
+ IExifValue exifOffset = GetOffsetValue(this.ifdValues, this.exifValues, ExifTag.SubIFDOffset);
+ IExifValue gpsOffset = GetOffsetValue(this.ifdValues, this.gpsValues, ExifTag.GPSIFDOffset);
- if (exifLength > 0)
+ if (this.ifdValues.Count == 0 && this.exifValues.Count == 0 && this.gpsValues.Count == 0)
{
- exifLength += 2;
+ return Array.Empty();
}
- if (gpsLength > 0)
- {
- gpsLength += 2;
- }
+ uint ifdLength = this.GetLength(this.ifdValues) + 4U;
+ uint exifLength = this.GetLength(this.exifValues);
+ uint gpsLength = this.GetLength(this.gpsValues);
length = ifdLength + exifLength + gpsLength;
- if (length == 6)
+ if (length == 4U)
{
- return null;
+ return Array.Empty();
}
- // two bytes for the byte Order marker 'II', followed by the number 42 (0x2A) and a 0, making 4 bytes total
+ // two bytes for the byte Order marker 'II' or 'MM', followed by the number 42 (0x2A) and a 0, making 4 bytes total
length += (uint)ExifConstants.LittleEndianByteOrderMarker.Length;
length += 4 + 2;
@@ -91,38 +77,31 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
int i = 0;
- // the byte order marker for little-endian, followed by the number 42 and a 0
+ // The byte order marker for little-endian, followed by the number 42 and a 0
ExifConstants.LittleEndianByteOrderMarker.AsSpan().CopyTo(result.AsSpan(start: i));
i += ExifConstants.LittleEndianByteOrderMarker.Length;
- uint ifdOffset = ((uint)i - startIndex) + 4;
+ uint ifdOffset = ((uint)i - startIndex) + 4U;
uint thumbnailOffset = ifdOffset + ifdLength + exifLength + gpsLength;
- if (exifLength > 0)
- {
- this.values[exifIndex] = this.values[exifIndex].WithValue(ifdOffset + ifdLength);
- }
-
- if (gpsLength > 0)
- {
- this.values[gpsIndex] = this.values[gpsIndex].WithValue(ifdOffset + ifdLength + exifLength);
- }
+ exifOffset?.TrySetValue(ifdOffset + ifdLength);
+ gpsOffset?.TrySetValue(ifdOffset + ifdLength + exifLength);
i = WriteUInt32(ifdOffset, result, i);
- i = this.WriteHeaders(this.ifdIndexes, result, i);
+ i = this.WriteHeaders(this.ifdValues, result, i);
i = WriteUInt32(thumbnailOffset, result, i);
- i = this.WriteData(startIndex, this.ifdIndexes, result, i);
+ i = this.WriteData(startIndex, this.ifdValues, result, i);
if (exifLength > 0)
{
- i = this.WriteHeaders(this.exifIndexes, result, i);
- i = this.WriteData(startIndex, this.exifIndexes, result, i);
+ i = this.WriteHeaders(this.exifValues, result, i);
+ i = this.WriteData(startIndex, this.exifValues, result, i);
}
if (gpsLength > 0)
{
- i = this.WriteHeaders(this.gpsIndexes, result, i);
- i = this.WriteData(startIndex, this.gpsIndexes, result, i);
+ i = this.WriteHeaders(this.gpsValues, result, i);
+ i = this.WriteData(startIndex, this.gpsValues, result, i);
}
WriteUInt16(0, result, i);
@@ -179,79 +158,137 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
return offset + 4;
}
- private int GetIndex(IList indexes, ExifTag tag)
+ private static IExifValue GetOffsetValue(List ifdValues, List values, ExifTag offset)
{
- foreach (int index in indexes)
+ int index = -1;
+
+ for (int i = 0; i < ifdValues.Count; i++)
{
- if (this.values[index].Tag == tag)
+ if (ifdValues[i].Tag == offset)
{
- return index;
+ index = i;
}
}
- int newIndex = this.values.Count;
- indexes.Add(newIndex);
- this.values.Add(ExifValue.Create(tag, null));
- return newIndex;
+ if (values.Count > 0)
+ {
+ if (index != -1)
+ {
+ return ifdValues[index];
+ }
+
+ ExifValue result = ExifValues.Create(offset);
+ ifdValues.Add(result);
+
+ return result;
+ }
+ else if (index != -1)
+ {
+ ifdValues.RemoveAt(index);
+ }
+
+ return null;
}
- private List GetIndexes(ExifParts part, ExifTag[] tags)
+ private List GetPartValues(ExifParts part)
{
- if (((int)this.allowedParts & (int)part) == 0)
+ var result = new List();
+
+ if (!EnumUtils.HasFlag(this.allowedParts, part))
{
- return new List();
+ return result;
}
- var result = new List();
- for (int i = 0; i < this.values.Count; i++)
+ foreach (IExifValue value in this.values)
{
- ExifValue value = this.values[i];
-
- if (!value.HasValue)
+ if (!HasValue(value))
{
continue;
}
- int index = Array.IndexOf(tags, value.Tag);
- if (index > -1)
+ if (ExifTags.GetPart(value.Tag) == part)
{
- result.Add(i);
+ result.Add(value);
}
}
return result;
}
- private uint GetLength(IList indexes)
+ private static bool HasValue(IExifValue exifValue)
+ {
+ object value = exifValue.GetValue();
+ if (value is null)
+ {
+ return false;
+ }
+
+ if (exifValue.DataType == ExifDataType.Ascii)
+ {
+ string stringValue = (string)value;
+ return stringValue.Length > 0;
+ }
+
+ if (value is Array arrayValue)
+ {
+ return arrayValue.Length > 0;
+ }
+
+ return true;
+ }
+
+ private uint GetLength(IList values)
{
- uint length = 0;
+ if (values.Count == 0)
+ {
+ return 0;
+ }
- foreach (int index in indexes)
+ uint length = 2;
+
+ foreach (IExifValue value in values)
{
- uint valueLength = (uint)this.values[index].Length;
+ uint valueLength = GetLength(value);
+
+ length += 2 + 2 + 4 + 4;
if (valueLength > 4)
{
- length += 12 + valueLength;
- }
- else
- {
- length += 12;
+ length += valueLength;
}
}
return length;
}
- private int WriteArray(ExifValue value, Span destination, int offset)
+ private static uint GetLength(IExifValue value) => GetNumberOfComponents(value) * ExifDataTypes.GetSize(value.DataType);
+
+ private static uint GetNumberOfComponents(IExifValue exifValue)
+ {
+ object value = exifValue.GetValue();
+
+ if (exifValue.DataType == ExifDataType.Ascii)
+ {
+ return (uint)Encoding.UTF8.GetBytes((string)value).Length + 1;
+ }
+
+ if (value is Array arrayValue)
+ {
+ return (uint)arrayValue.Length;
+ }
+
+ return 1;
+ }
+
+ private int WriteArray(IExifValue value, Span destination, int offset)
{
if (value.DataType == ExifDataType.Ascii)
{
- return this.WriteValue(ExifDataType.Ascii, value.Value, destination, offset);
+ return this.WriteValue(ExifDataType.Ascii, value.GetValue(), destination, offset);
}
int newOffset = offset;
- foreach (object obj in (Array)value.Value)
+ foreach (object obj in (Array)value.GetValue())
{
newOffset = this.WriteValue(value.DataType, obj, destination, newOffset);
}
@@ -259,7 +296,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
return newOffset;
}
- private int WriteData(uint startIndex, List indexes, Span destination, int offset)
+ private int WriteData(uint startIndex, List values, Span destination, int offset)
{
if (this.dataOffsets.Count == 0)
{
@@ -269,10 +306,9 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
int newOffset = offset;
int i = 0;
- foreach (int index in indexes)
+ foreach (IExifValue value in values)
{
- ExifValue value = this.values[index];
- if (value.Length > 4)
+ if (GetLength(value) > 4)
{
WriteUInt32((uint)(newOffset - startIndex), destination, this.dataOffsets[i++]);
newOffset = this.WriteValue(value, destination, newOffset);
@@ -282,25 +318,25 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
return newOffset;
}
- private int WriteHeaders(List indexes, Span destination, int offset)
+ private int WriteHeaders(List values, Span destination, int offset)
{
this.dataOffsets = new List();
- int newOffset = WriteUInt16((ushort)indexes.Count, destination, offset);
+ int newOffset = WriteUInt16((ushort)values.Count, destination, offset);
- if (indexes.Count == 0)
+ if (values.Count == 0)
{
return newOffset;
}
- foreach (int index in indexes)
+ foreach (IExifValue value in values)
{
- ExifValue value = this.values[index];
newOffset = WriteUInt16((ushort)value.Tag, destination, newOffset);
newOffset = WriteUInt16((ushort)value.DataType, destination, newOffset);
- newOffset = WriteUInt32((uint)value.NumberOfComponents, destination, newOffset);
+ newOffset = WriteUInt32(GetNumberOfComponents(value), destination, newOffset);
- if (value.Length > 4)
+ uint length = GetLength(value);
+ if (length > 4)
{
this.dataOffsets.Add(newOffset);
}
@@ -332,7 +368,9 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
switch (dataType)
{
case ExifDataType.Ascii:
- return Write(Encoding.UTF8.GetBytes((string)value), destination, offset);
+ offset = Write(Encoding.UTF8.GetBytes((string)value), destination, offset);
+ destination[offset] = 0;
+ return offset + 1;
case ExifDataType.Byte:
case ExifDataType.Undefined:
destination[offset] = (byte)value;
@@ -340,8 +378,18 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
case ExifDataType.DoubleFloat:
return WriteDouble((double)value, destination, offset);
case ExifDataType.Short:
+ if (value is Number shortNumber)
+ {
+ return WriteUInt16((ushort)shortNumber, destination, offset);
+ }
+
return WriteUInt16((ushort)value, destination, offset);
case ExifDataType.Long:
+ if (value is Number longNumber)
+ {
+ return WriteUInt32((uint)longNumber, destination, offset);
+ }
+
return WriteUInt32((uint)value, destination, offset);
case ExifDataType.Rational:
WriteRational(destination.Slice(offset, 8), (Rational)value);
@@ -363,14 +411,14 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
}
}
- private int WriteValue(ExifValue value, Span destination, int offset)
+ private int WriteValue(IExifValue value, Span destination, int offset)
{
if (value.IsArray && value.DataType != ExifDataType.Ascii)
{
return this.WriteArray(value, destination, offset);
}
- return this.WriteValue(value.DataType, value.Value, destination, offset);
+ return this.WriteValue(value.DataType, value.GetValue(), destination, offset);
}
}
}
diff --git a/src/ImageSharp/Metadata/Profiles/Exif/README.md b/src/ImageSharp/Metadata/Profiles/Exif/README.md
index b6e27b70c..7901527e1 100644
--- a/src/ImageSharp/Metadata/Profiles/Exif/README.md
+++ b/src/ImageSharp/Metadata/Profiles/Exif/README.md
@@ -1,3 +1,3 @@
-Adapted from Magick.NET:
+Adapted from Magick.NET:
-https://github.com/dlemstra/Magick.NET/tree/784e23b1f5c824fc03d4b95d3387b3efe1ed510b/Magick.NET/Core/Profiles/Exif
\ No newline at end of file
+https://github.com/dlemstra/Magick.NET
diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Byte.cs b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Byte.cs
new file mode 100644
index 000000000..dc33fd8b0
--- /dev/null
+++ b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Byte.cs
@@ -0,0 +1,24 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
+{
+ ///
+ public abstract partial class ExifTag
+ {
+ ///
+ /// Gets the FaxProfile exif tag.
+ ///
+ public static ExifTag FaxProfile { get; } = new ExifTag(ExifTagValue.FaxProfile);
+
+ ///
+ /// Gets the ModeNumber exif tag.
+ ///
+ public static ExifTag ModeNumber { get; } = new ExifTag(ExifTagValue.ModeNumber);
+
+ ///
+ /// Gets the GPSAltitudeRef exif tag.
+ ///
+ public static ExifTag GPSAltitudeRef { get; } = new ExifTag(ExifTagValue.GPSAltitudeRef);
+ }
+}
diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.ByteArray.cs b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.ByteArray.cs
new file mode 100644
index 000000000..2bfa8ff21
--- /dev/null
+++ b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.ByteArray.cs
@@ -0,0 +1,64 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
+{
+ ///
+ public abstract partial class ExifTag
+ {
+ ///
+ /// Gets the ClipPath exif tag.
+ ///
+ public static ExifTag ClipPath => new ExifTag(ExifTagValue.ClipPath);
+
+ ///
+ /// Gets the VersionYear exif tag.
+ ///
+ public static ExifTag VersionYear => new ExifTag(ExifTagValue.VersionYear);
+
+ ///
+ /// Gets the XMP exif tag.
+ ///
+ public static ExifTag XMP => new ExifTag(ExifTagValue.XMP);
+
+ ///
+ /// Gets the CFAPattern2 exif tag.
+ ///
+ public static ExifTag CFAPattern2 => new ExifTag(ExifTagValue.CFAPattern2);
+
+ ///
+ /// Gets the TIFFEPStandardID exif tag.
+ ///
+ public static ExifTag TIFFEPStandardID => new ExifTag(ExifTagValue.TIFFEPStandardID);
+
+ ///
+ /// Gets the XPTitle exif tag.
+ ///
+ public static ExifTag XPTitle => new ExifTag(ExifTagValue.XPTitle);
+
+ ///
+ /// Gets the XPComment exif tag.
+ ///
+ public static ExifTag XPComment => new ExifTag(ExifTagValue.XPComment);
+
+ ///
+ /// Gets the XPAuthor exif tag.
+ ///
+ public static ExifTag XPAuthor => new ExifTag(ExifTagValue.XPAuthor);
+
+ ///
+ /// Gets the XPKeywords exif tag.
+ ///
+ public static ExifTag XPKeywords => new ExifTag(ExifTagValue.XPKeywords);
+
+ ///
+ /// Gets the XPSubject exif tag.
+ ///
+ public static ExifTag XPSubject => new ExifTag(ExifTagValue.XPSubject);
+
+ ///
+ /// Gets the GPSVersionID exif tag.
+ ///
+ public static ExifTag GPSVersionID => new ExifTag(ExifTagValue.GPSVersionID);
+ }
+}
diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.DoubleArray.cs b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.DoubleArray.cs
new file mode 100644
index 000000000..6cbae4c55
--- /dev/null
+++ b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.DoubleArray.cs
@@ -0,0 +1,29 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
+{
+ ///
+ public abstract partial class ExifTag
+ {
+ ///
+ /// Gets the PixelScale exif tag.
+ ///
+ public static ExifTag PixelScale { get; } = new ExifTag(ExifTagValue.PixelScale);
+
+ ///
+ /// Gets the IntergraphMatrix exif tag.
+ ///
+ public static ExifTag IntergraphMatrix { get; } = new ExifTag(ExifTagValue.IntergraphMatrix);
+
+ ///
+ /// Gets the ModelTiePoint exif tag.
+ ///
+ public static ExifTag ModelTiePoint { get; } = new ExifTag(ExifTagValue.ModelTiePoint);
+
+ ///
+ /// Gets the ModelTransform exif tag.
+ ///
+ public static ExifTag ModelTransform { get; } = new ExifTag(ExifTagValue.ModelTransform);
+ }
+}
diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Long.cs b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Long.cs
new file mode 100644
index 000000000..571b50efb
--- /dev/null
+++ b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Long.cs
@@ -0,0 +1,114 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
+{
+ ///
+ public abstract partial class ExifTag
+ {
+ ///
+ /// Gets the SubfileType exif tag.
+ ///
+ public static ExifTag SubfileType { get; } = new ExifTag(ExifTagValue.SubfileType);
+
+ ///
+ /// Gets the SubIFDOffset exif tag.
+ ///
+ public static ExifTag SubIFDOffset { get; } = new ExifTag(ExifTagValue.SubIFDOffset);
+
+ ///
+ /// Gets the GPSIFDOffset exif tag.
+ ///
+ public static ExifTag GPSIFDOffset { get; } = new ExifTag(ExifTagValue.GPSIFDOffset);
+
+ ///
+ /// Gets the T4Options exif tag.
+ ///
+ public static ExifTag T4Options { get; } = new ExifTag(ExifTagValue.T4Options);
+
+ ///
+ /// Gets the T6Options exif tag.
+ ///
+ public static ExifTag T6Options { get; } = new ExifTag(ExifTagValue.T6Options);
+
+ ///
+ /// Gets the XClipPathUnits exif tag.
+ ///
+ public static ExifTag XClipPathUnits { get; } = new ExifTag(ExifTagValue.XClipPathUnits);
+
+ ///
+ /// Gets the YClipPathUnits exif tag.
+ ///
+ public static ExifTag YClipPathUnits { get; } = new ExifTag(ExifTagValue.YClipPathUnits);
+
+ ///
+ /// Gets the ProfileType exif tag.
+ ///
+ public static ExifTag ProfileType { get; } = new ExifTag(ExifTagValue.ProfileType);
+
+ ///
+ /// Gets the CodingMethods exif tag.
+ ///
+ public static ExifTag CodingMethods { get; } = new ExifTag(ExifTagValue.CodingMethods);
+
+ ///
+ /// Gets the T82ptions exif tag.
+ ///
+ public static ExifTag T82ptions { get; } = new ExifTag(ExifTagValue.T82ptions);
+
+ ///
+ /// Gets the JPEGInterchangeFormat exif tag.
+ ///
+ public static ExifTag JPEGInterchangeFormat { get; } = new ExifTag(ExifTagValue.JPEGInterchangeFormat);
+
+ ///
+ /// Gets the JPEGInterchangeFormatLength exif tag.
+ ///
+ public static ExifTag JPEGInterchangeFormatLength { get; } = new ExifTag(ExifTagValue.JPEGInterchangeFormatLength);
+
+ ///
+ /// Gets the MDFileTag exif tag.
+ ///
+ public static ExifTag MDFileTag { get; } = new ExifTag(ExifTagValue.MDFileTag);
+
+ ///
+ /// Gets the StandardOutputSensitivity exif tag.
+ ///
+ public static ExifTag StandardOutputSensitivity { get; } = new ExifTag(ExifTagValue.StandardOutputSensitivity);
+
+ ///
+ /// Gets the RecommendedExposureIndex exif tag.
+ ///
+ public static ExifTag RecommendedExposureIndex { get; } = new ExifTag(ExifTagValue.RecommendedExposureIndex);
+
+ ///
+ /// Gets the ISOSpeed exif tag.
+ ///
+ public static ExifTag ISOSpeed { get; } = new ExifTag(ExifTagValue.ISOSpeed);
+
+ ///
+ /// Gets the ISOSpeedLatitudeyyy exif tag.
+ ///
+ public static ExifTag ISOSpeedLatitudeyyy { get; } = new ExifTag(ExifTagValue.ISOSpeedLatitudeyyy);
+
+ ///
+ /// Gets the ISOSpeedLatitudezzz exif tag.
+ ///
+ public static ExifTag ISOSpeedLatitudezzz { get; } = new ExifTag(ExifTagValue.ISOSpeedLatitudezzz);
+
+ ///
+ /// Gets the FaxRecvParams exif tag.
+ ///
+ public static ExifTag FaxRecvParams { get; } = new ExifTag(ExifTagValue.FaxRecvParams);
+
+ ///
+ /// Gets the FaxRecvTime exif tag.
+ ///
+ public static ExifTag FaxRecvTime { get; } = new ExifTag(ExifTagValue.FaxRecvTime);
+
+ ///
+ /// Gets the ImageNumber exif tag.
+ ///
+ public static ExifTag ImageNumber { get; } = new ExifTag(ExifTagValue.ImageNumber);
+ }
+}
diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.LongArray.cs b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.LongArray.cs
new file mode 100644
index 000000000..120f2dab0
--- /dev/null
+++ b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.LongArray.cs
@@ -0,0 +1,69 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
+{
+ ///
+ public abstract partial class ExifTag
+ {
+ ///
+ /// Gets the FreeOffsets exif tag.
+ ///
+ public static ExifTag FreeOffsets { get; } = new ExifTag(ExifTagValue.FreeOffsets);
+
+ ///
+ /// Gets the FreeByteCounts exif tag.
+ ///
+ public static ExifTag FreeByteCounts { get; } = new ExifTag(ExifTagValue.FreeByteCounts);
+
+ ///
+ /// Gets the ColorResponseUnit exif tag.
+ ///
+ public static ExifTag ColorResponseUnit { get; } = new ExifTag(ExifTagValue.ColorResponseUnit);
+
+ ///
+ /// Gets the TileOffsets exif tag.
+ ///
+ public static ExifTag TileOffsets { get; } = new ExifTag(ExifTagValue.TileOffsets);
+
+ ///
+ /// Gets the SMinSampleValue exif tag.
+ ///
+ public static ExifTag SMinSampleValue { get; } = new ExifTag(ExifTagValue.SMinSampleValue);
+
+ ///
+ /// Gets the SMaxSampleValue exif tag.
+ ///
+ public static ExifTag SMaxSampleValue { get; } = new ExifTag(ExifTagValue.SMaxSampleValue);
+
+ ///
+ /// Gets the JPEGQTables exif tag.
+ ///
+ public static ExifTag JPEGQTables { get; } = new ExifTag(ExifTagValue.JPEGQTables);
+
+ ///
+ /// Gets the JPEGDCTables exif tag.
+ ///
+ public static ExifTag JPEGDCTables { get; } = new ExifTag(ExifTagValue.JPEGDCTables);
+
+ ///
+ /// Gets the JPEGACTables exif tag.
+ ///
+ public static ExifTag JPEGACTables { get; } = new ExifTag(ExifTagValue.JPEGACTables);
+
+ ///
+ /// Gets the StripRowCounts exif tag.
+ ///
+ public static ExifTag StripRowCounts { get; } = new ExifTag(ExifTagValue.StripRowCounts);
+
+ ///
+ /// Gets the IntergraphRegisters exif tag.
+ ///
+ public static ExifTag IntergraphRegisters { get; } = new ExifTag(ExifTagValue.IntergraphRegisters);
+
+ ///
+ /// Gets the TimeZoneOffset exif tag.
+ ///
+ public static ExifTag TimeZoneOffset { get; } = new ExifTag(ExifTagValue.TimeZoneOffset);
+ }
+}
diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Number.cs b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Number.cs
new file mode 100644
index 000000000..7f6be3c4d
--- /dev/null
+++ b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Number.cs
@@ -0,0 +1,51 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using SixLabors.ImageSharp.Primitives;
+
+namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
+{
+ ///
+ public abstract partial class ExifTag
+ {
+ ///
+ /// Gets the ImageWidth exif tag.
+ ///
+ public static ExifTag ImageWidth { get; } = new ExifTag(ExifTagValue.ImageWidth);
+
+ ///
+ /// Gets the ImageLength exif tag.
+ ///
+ public static ExifTag ImageLength { get; } = new ExifTag(ExifTagValue.ImageLength);
+
+ ///
+ /// Gets the TileWidth exif tag.
+ ///
+ public static ExifTag TileWidth { get; } = new ExifTag(ExifTagValue.TileWidth);
+
+ ///
+ /// Gets the TileLength exif tag.
+ ///
+ public static ExifTag TileLength { get; } = new ExifTag(ExifTagValue.TileLength);
+
+ ///
+ /// Gets the BadFaxLines exif tag.
+ ///
+ public static ExifTag BadFaxLines { get; } = new ExifTag(ExifTagValue.BadFaxLines);
+
+ ///
+ /// Gets the ConsecutiveBadFaxLines exif tag.
+ ///
+ public static ExifTag ConsecutiveBadFaxLines { get; } = new ExifTag(ExifTagValue.ConsecutiveBadFaxLines);
+
+ ///
+ /// Gets the PixelXDimension exif tag.
+ ///
+ public static ExifTag PixelXDimension { get; } = new ExifTag(ExifTagValue.PixelXDimension);
+
+ ///
+ /// Gets the PixelYDimension exif tag.
+ ///
+ public static ExifTag PixelYDimension { get; } = new ExifTag(ExifTagValue.PixelYDimension);
+ }
+}
diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.NumberArray.cs b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.NumberArray.cs
new file mode 100644
index 000000000..b4feba056
--- /dev/null
+++ b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.NumberArray.cs
@@ -0,0 +1,26 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using SixLabors.ImageSharp.Primitives;
+
+namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
+{
+ ///
+ public abstract partial class ExifTag
+ {
+ ///
+ /// Gets the StripOffsets exif tag.
+ ///
+ public static ExifTag StripOffsets { get; } = new ExifTag(ExifTagValue.StripOffsets);
+
+ ///
+ /// Gets the TileByteCounts exif tag.
+ ///
+ public static ExifTag TileByteCounts { get; } = new ExifTag(ExifTagValue.TileByteCounts);
+
+ ///
+ /// Gets the ImageLayer exif tag.
+ ///
+ public static ExifTag ImageLayer { get; } = new ExifTag(ExifTagValue.ImageLayer);
+ }
+}
diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Rational.cs b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Rational.cs
new file mode 100644
index 000000000..db52bbb22
--- /dev/null
+++ b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Rational.cs
@@ -0,0 +1,176 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using SixLabors.ImageSharp.Primitives;
+
+namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
+{
+ ///
+ public abstract partial class ExifTag
+ {
+ ///
+ /// Gets the XPosition exif tag.
+ ///
+ public static ExifTag XPosition { get; } = new ExifTag(ExifTagValue.XPosition);
+
+ ///
+ /// Gets the YPosition exif tag.
+ ///
+ public static ExifTag YPosition { get; } = new ExifTag(ExifTagValue.YPosition);
+
+ ///
+ /// Gets the XResolution exif tag.
+ ///
+ public static ExifTag XResolution { get; } = new ExifTag(ExifTagValue.XResolution);
+
+ ///
+ /// Gets the YResolution exif tag.
+ ///
+ public static ExifTag YResolution { get; } = new ExifTag(ExifTagValue.YResolution);
+
+ ///
+ /// Gets the BatteryLevel exif tag.
+ ///
+ public static ExifTag BatteryLevel { get; } = new ExifTag(ExifTagValue.BatteryLevel);
+
+ ///
+ /// Gets the ExposureTime exif tag.
+ ///
+ public static ExifTag ExposureTime { get; } = new ExifTag(ExifTagValue.ExposureTime);
+
+ ///
+ /// Gets the FNumber exif tag.
+ ///
+ public static ExifTag FNumber { get; } = new ExifTag(ExifTagValue.FNumber);
+
+ ///
+ /// Gets the MDScalePixel exif tag.
+ ///
+ public static ExifTag MDScalePixel { get; } = new ExifTag(ExifTagValue.MDScalePixel);
+
+ ///
+ /// Gets the CompressedBitsPerPixel exif tag.
+ ///
+ public static ExifTag CompressedBitsPerPixel { get; } = new ExifTag(ExifTagValue.CompressedBitsPerPixel);
+
+ ///
+ /// Gets the ApertureValue exif tag.
+ ///
+ public static ExifTag ApertureValue { get; } = new ExifTag(ExifTagValue.ApertureValue);
+
+ ///
+ /// Gets the MaxApertureValue exif tag.
+ ///
+ public static ExifTag MaxApertureValue { get; } = new ExifTag(ExifTagValue.MaxApertureValue);
+
+ ///
+ /// Gets the SubjectDistance exif tag.
+ ///
+ public static ExifTag SubjectDistance { get; } = new ExifTag(ExifTagValue.SubjectDistance);
+
+ ///
+ /// Gets the FocalLength exif tag.
+ ///
+ public static ExifTag FocalLength { get; } = new ExifTag(ExifTagValue.FocalLength);
+
+ ///
+ /// Gets the FlashEnergy2 exif tag.
+ ///
+ public static ExifTag FlashEnergy2 { get; } = new ExifTag(ExifTagValue.FlashEnergy2);
+
+ ///
+ /// Gets the FocalPlaneXResolution2 exif tag.
+ ///
+ public static ExifTag FocalPlaneXResolution2 { get; } = new ExifTag(ExifTagValue.FocalPlaneXResolution2);
+
+ ///
+ /// Gets the FocalPlaneYResolution2 exif tag.
+ ///
+ public static ExifTag FocalPlaneYResolution2 { get; } = new ExifTag(ExifTagValue.FocalPlaneYResolution2);
+
+ ///
+ /// Gets the ExposureIndex2 exif tag.
+ ///
+ public static ExifTag ExposureIndex2 { get; } = new ExifTag(ExifTagValue.ExposureIndex2);
+
+ ///
+ /// Gets the Humidity exif tag.
+ ///
+ public static ExifTag Humidity { get; } = new ExifTag(ExifTagValue.Humidity);
+
+ ///
+ /// Gets the Pressure exif tag.
+ ///
+ public static ExifTag Pressure { get; } = new ExifTag(ExifTagValue.Pressure);
+
+ ///
+ /// Gets the Acceleration exif tag.
+ ///
+ public static ExifTag Acceleration { get; } = new ExifTag(ExifTagValue.Acceleration);
+
+ ///
+ /// Gets the FlashEnergy exif tag.
+ ///
+ public static ExifTag FlashEnergy { get; } = new ExifTag(ExifTagValue.FlashEnergy);
+
+ ///
+ /// Gets the FocalPlaneXResolution exif tag.
+ ///
+ public static ExifTag FocalPlaneXResolution { get; } = new ExifTag(ExifTagValue.FocalPlaneXResolution);
+
+ ///
+ /// Gets the FocalPlaneYResolution exif tag.
+ ///
+ public static ExifTag FocalPlaneYResolution { get; } = new ExifTag(ExifTagValue.FocalPlaneYResolution);
+
+ ///
+ /// Gets the ExposureIndex exif tag.
+ ///
+ public static ExifTag ExposureIndex { get; } = new ExifTag(ExifTagValue.ExposureIndex);
+
+ ///
+ /// Gets the DigitalZoomRatio exif tag.
+ ///
+ public static ExifTag DigitalZoomRatio { get; } = new ExifTag(ExifTagValue.DigitalZoomRatio);
+
+ ///
+ /// Gets the LensInfo exif tag.
+ ///
+ public static ExifTag LensInfo { get; } = new ExifTag(ExifTagValue.LensInfo);
+
+ ///
+ /// Gets the GPSAltitude exif tag.
+ ///
+ public static ExifTag GPSAltitude { get; } = new ExifTag(ExifTagValue.GPSAltitude);
+
+ ///
+ /// Gets the GPSDOP exif tag.
+ ///
+ public static ExifTag GPSDOP { get; } = new ExifTag(ExifTagValue.GPSDOP);
+
+ ///
+ /// Gets the GPSSpeed exif tag.
+ ///
+ public static ExifTag GPSSpeed { get; } = new ExifTag(ExifTagValue.GPSSpeed);
+
+ ///
+ /// Gets the GPSTrack exif tag.
+ ///
+ public static ExifTag GPSTrack { get; } = new ExifTag(ExifTagValue.GPSTrack);
+
+ ///
+ /// Gets the GPSImgDirection exif tag.
+ ///
+ public static ExifTag GPSImgDirection { get; } = new ExifTag(ExifTagValue.GPSImgDirection);
+
+ ///
+ /// Gets the GPSDestBearing exif tag.
+ ///
+ public static ExifTag GPSDestBearing { get; } = new ExifTag(ExifTagValue.GPSDestBearing);
+
+ ///
+ /// Gets the GPSDestDistance exif tag.
+ ///
+ public static ExifTag GPSDestDistance { get; } = new ExifTag(ExifTagValue.GPSDestDistance);
+ }
+}
diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.RationalArray.cs b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.RationalArray.cs
new file mode 100644
index 000000000..ece06c247
--- /dev/null
+++ b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.RationalArray.cs
@@ -0,0 +1,56 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using SixLabors.ImageSharp.Primitives;
+
+namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
+{
+ ///
+ public abstract partial class ExifTag
+ {
+ ///
+ /// Gets the WhitePoint exif tag.
+ ///
+ public static ExifTag WhitePoint { get; } = new ExifTag(ExifTagValue.WhitePoint);
+
+ ///
+ /// Gets the PrimaryChromaticities exif tag.
+ ///
+ public static ExifTag PrimaryChromaticities { get; } = new ExifTag(ExifTagValue.PrimaryChromaticities);
+
+ ///
+ /// Gets the YCbCrCoefficients exif tag.
+ ///
+ public static ExifTag YCbCrCoefficients { get; } = new ExifTag(ExifTagValue.YCbCrCoefficients);
+
+ ///