Class ExifData
A parsed EXIF block (a TIFF structure, CIPA DC-008): the tags of IFD0, the EXIF, GPS and interoperability sub-IFDs and IFD1 with its thumbnail, with typed getters for the common ones. An unedited block writes back byte for byte; after an edit it is laid out again, keeping the maker note at its old offset when it fits, since maker notes often hold offsets of their own.
public sealed class ExifData
- Inheritance
-
ExifData
- Inherited Members
Examples
using Raster photo = Raster.Load("sideways.jpg");
ExifData exif = photo.Metadata.Exif ?? new ExifData();
// Typed values go in with their EXIF type; fractions are exact numerator / denominator pairs.
exif.Set(ExifKey.ExposureTime, new ExifFraction(1, 250));
exif.Set(ExifKey.Artist, "A. Photographer");
if (exif.TryGet(ExifKey.ExposureTime, out ExifEntry? entry) && entry.Type == ExifType.Rational)
{
Console.WriteLine($"Exposure {entry.AsFraction()} s = {entry.AsFraction().ToDouble()} s, in the {entry.Key.Ifd} IFD");
}
if (exif.Location is GeoPosition where)
{
Console.WriteLine($"Taken at {where.Latitude:F5}, {where.Longitude:F5}");
}
Constructors
ExifData()
Creates an empty block, little-endian, to fill with Set(ExifKey, string) and the other setters.
public ExifData()
Properties
CameraMake
Gets the camera maker, or null.
public string? CameraMake { get; }
Property Value
CameraModel
Gets the camera model, or null.
public string? CameraModel { get; }
Property Value
DateTaken
Gets when the photo was taken: DateTimeOriginal, else DateTime, or null.
public DateTime? DateTaken { get; }
Property Value
Entries
Gets every entry, IFD by IFD (IFD0, EXIF, GPS, interoperability, IFD1), in the order stored.
public IReadOnlyList<ExifEntry> Entries { get; }
Property Value
IsBigEndian
Gets whether values are stored big-endian (Motorola order) rather than little-endian (Intel order).
public bool IsBigEndian { get; }
Property Value
IsDamaged
Gets whether the block was damaged when read: an offset outside it, a loop between IFDs, or more data than allowed. What could be read is available; the original bytes are kept and written back unless the block is edited.
public bool IsDamaged { get; }
Property Value
Location
Gets the GPS position, or null when the block has no complete latitude and longitude.
public GeoPosition? Location { get; }
Property Value
Orientation
Gets or sets the orientation (IFD0 0x0112). Missing or out-of-range values read as Normal.
public ImageOrientation Orientation { get; set; }
Property Value
Exceptions
- ArgumentOutOfRangeException
The value set is not one of the EXIF values 1 to 8.
Thumbnail
Gets the embedded thumbnail (usually a small JPEG), or empty.
public ReadOnlyMemory<byte> Thumbnail { get; }
Property Value
Warnings
Gets what was wrong with a damaged block, and what changed when an edited block was written, as sentences.
public IReadOnlyList<string> Warnings { get; }
Property Value
Methods
Copy()
Returns an independent copy.
public ExifData Copy()
Returns
- ExifData
The copy.
Parse(ReadOnlySpan<byte>, SafetyLimits?)
Reads an EXIF block: a TIFF header and its IFDs, without the "Exif\0\0" prefix JPEG puts before it. Never throws: values past MaxMetadataBytes in all are dropped and the block is marked damaged.
public static ExifData Parse(ReadOnlySpan<byte> tiff, SafetyLimits? limits = null)
Parameters
tiffReadOnlySpan<byte>The block.
limitsSafetyLimitsThe limits whose MaxMetadataBytes caps the values copied. Defaults to Default.
Returns
Examples
// EXIF from somewhere other than an image file (a database column, a sidecar), with a cap on the bytes of values read.
ExifData exif = ExifData.Parse(tiff.Span, SafetyLimits.Default with { MaxMetadataBytes = 64 * 1024 });
foreach (string warning in exif.Warnings)
{
Console.WriteLine($"EXIF: {warning}");
}
ReadThumbnail(EgamiContext?)
Decodes the IFD1 thumbnail with Egami's JPEG reader under the context's safety limits. The pixels are as stored: apply the main image's orientation (Orientation) to show it upright.
public Raster<Rgb8>? ReadThumbnail(EgamiContext? context = null)
Parameters
contextEgamiContextThe context whose limits apply. Defaults to Default.
Returns
- Raster<Rgb8>
The thumbnail, or null when there is no JPEG thumbnail (uncompressed ones are kept as bytes). The caller must dispose it.
Examples
using Raster photo = Raster.Load("sideways.jpg");
// The thumbnail as stored, if the file has one; ReadThumbnail returns null otherwise.
using (Raster<Rgb8>? stored = photo.Metadata.Exif?.ReadThumbnail())
{
Console.WriteLine(stored is null ? "No thumbnail" : $"Thumbnail {stored.Width} x {stored.Height}");
}
// Turning the image drops the thumbnail, which would show sideways; make a new one from the result.
photo.Apply(ops => ops.AutoOrient());
photo.Metadata.Exif?.SetThumbnail(photo);
photo.Save("upright.jpg");
Exceptions
- CorruptDataException
The thumbnail is damaged.
- LimitExceededException
The thumbnail is larger than the context's limits allow.
Remove(ExifKey)
public bool Remove(ExifKey key)
Parameters
keyExifKeyThe tag and IFD.
Returns
- bool
Whether anything was removed.
RemoveThumbnail()
Removes the thumbnail and the rest of IFD1, as operations do once the image no longer matches it.
public bool RemoveThumbnail()
Returns
- bool
Whether there was one.
Set(ExifKey, ExifType, ReadOnlySpan<byte>)
Sets a value of any type from its raw bytes, in this block's byte order.
public void Set(ExifKey key, ExifType type, ReadOnlySpan<byte> value)
Parameters
keyExifKeyThe tag and IFD.
typeExifTypeThe TIFF type.
valueReadOnlySpan<byte>The bytes: a whole number of values of
type.
Exceptions
- ArgumentException
The type is unknown or the bytes are not a whole number of values.
Set(ExifKey, params ReadOnlySpan<ExifFraction>)
Sets one or more fractions: RATIONAL type, or SRATIONAL when any part is negative.
public void Set(ExifKey key, params ReadOnlySpan<ExifFraction> values)
Parameters
keyExifKeyThe tag and IFD.
valuesReadOnlySpan<ExifFraction>The fractions; each part must fit 32 bits.
Exceptions
- ArgumentException
valuesis empty.- ArgumentOutOfRangeException
A numerator or denominator does not fit 32 bits.
Set(ExifKey, string)
Sets a text value: ASCII type, with other characters stored as UTF-8, as many programs do. The Windows XP tags are stored as UTF-16 in BYTE values, as Explorer writes them, and the encoded strings (UserComment, GpsProcessingMethod, GpsAreaInformation) as UNDEFINED with an ASCII prefix when the text is ASCII and a UNICODE one otherwise.
public void Set(ExifKey key, string value)
Parameters
Set(ExifKey, ushort)
Sets a 16-bit unsigned value (SHORT type).
public void Set(ExifKey key, ushort value)
Parameters
Set(ExifKey, uint)
Sets a 32-bit unsigned value (LONG type).
public void Set(ExifKey key, uint value)
Parameters
SetThumbnail(Raster, int, int)
Makes a thumbnail from raster: converted to sRGB, resized to fit maxSide (never
enlarged), encoded as a baseline 4:2:0 JPEG and stored in IFD1 with compression 6. 160 pixels is the DCF thumbnail size.
Operations drop a thumbnail that no longer matches the image; this rebuilds it in one line.
public void SetThumbnail(Raster raster, int maxSide = 160, int quality = 75)
Parameters
rasterRasterThe image, usually the one this block belongs to after its operations. Its first frame is used.
maxSideintThe longest side, in pixels. Defaults to 160.
qualityintThe JPEG quality, 1 to 100. Defaults to 75.
Examples
using Raster photo = Raster.Load("sideways.jpg");
// The thumbnail as stored, if the file has one; ReadThumbnail returns null otherwise.
using (Raster<Rgb8>? stored = photo.Metadata.Exif?.ReadThumbnail())
{
Console.WriteLine(stored is null ? "No thumbnail" : $"Thumbnail {stored.Width} x {stored.Height}");
}
// Turning the image drops the thumbnail, which would show sideways; make a new one from the result.
photo.Apply(ops => ops.AutoOrient());
photo.Metadata.Exif?.SetThumbnail(photo);
photo.Save("upright.jpg");
Exceptions
- ArgumentNullException
rasteris null.- ArgumentOutOfRangeException
maxSideis below 1, orqualityis outside 1 to 100.
ToBytes()
Returns the block as bytes: the bytes it was read from if nothing was edited, else the block laid out again. A maker note that cannot stay at its old offset adds a warning to Warnings.
public ReadOnlyMemory<byte> ToBytes()
Returns
- ReadOnlyMemory<byte>
The TIFF bytes.
TryGet(ExifKey, out ExifEntry?)
Returns the entry for key, if the block has it.
public bool TryGet(ExifKey key, out ExifEntry? entry)
Parameters
Returns
- bool
Whether the block has the tag.