2
// Exif.cs : LibExif wrapper for FSpot
5
// Larry Ewing (lewing@novell.com)
6
// Ravi Pratap (ravi@ximian.com)
7
// Miguel de Icaza (miguel@ximian.com)
9
// (C) 2002, 2004, 2005 Novell, Inc.
13
using System.Collections;
14
using System.Runtime.InteropServices;
20
InteroperabilityIndex = 0x0001,
21
InteroperabilityVersion = 0x0002,
24
BitsPersample = 0x0102,
26
PhotometricInterpretation = 0x0106,
28
DocumentName = 0x010d,
29
ImageDescription = 0x010e,
32
StripOffsets = 0x0111,
34
SamplesPerPixel = 0x0115,
35
RowsPerStrip = 0x0116,
36
StripByteCounts = 0x0117,
39
PlanarConfiguration = 0x011c,
40
ResolutionUnit = 0x0128,
41
TransferFunction = 0x012d,
46
PrimaryChromaticities = 0x013f,
47
TransferRange = 0x0156,
49
JPEGInterchangeFormat = 0x0201,
50
JPEGInterchangeFormatLength = 0x0202,
51
YCBCRCoefficients = 0x0211,
52
YCBCRSubSampling = 0x0212,
53
YCBCRPositioning = 0x0213,
54
ReferenceBlackWhite = 0x0214,
55
RelatedImageFileFormat = 0x1000,
56
RelatedImageWidth = 0x1001,
57
RelatedImageHeight = 0x1002,
58
CFARepeatPatternDim = 0x828d,
60
BatteryLevel = 0x828f,
62
ExposureTime = 0x829a,
65
ExifIfdPointer = 0x8769,
66
InterColorProfile = 0x8773,
67
ExposureProgram = 0x8822,
68
SpectralSensitivity = 0x8824,
69
GPSInfoIfdPointer = 0x8825,
70
ISOSpeedRatings = 0x8827,
73
DateTimeOriginal = 0x9003,
74
DateTimeDigitized = 0x9004,
75
ComponentsConfiguration = 0x9101,
76
CompressedBitsPerPixel = 0x9102,
77
ShutterSpeedValue = 0x9201,
78
ApertureValue = 0x9202,
79
BrightnessValue = 0x9203,
80
ExposureBiasValue = 0x9204,
81
MaxApertureValue = 0x9205,
82
SubjectDistance = 0x9206,
83
MeteringMode = 0x9207,
91
SubSecTimeOriginal = 0x9291,
92
SubSecTimeDigitized = 0x9292,
93
FlashPixVersion = 0xa000,
95
PixelXDimension = 0xa002,
96
PixelYDimension = 0xa003,
97
RelatedSoundFile = 0xa004,
98
InteroperabilityIfdPointer = 0xa005,
100
SpatialFrequencyResponse = 0xa20c,
101
FocalPlaneXResolution = 0xa20e,
102
FocalPlaneYResolution = 0xa20f,
103
FocalPlaneResolutionUnit = 0xa210,
104
SubjectLocation = 0xa214,
105
ExposureIndex = 0xa215,
106
SensingMethod = 0xa217,
109
NewCFAPattern = 0xa302,
110
CustomRendered = 0xa401,
111
ExposureMode = 0xa402,
112
WhiteBalance = 0xa403,
113
DigitalZoomRatio = 0xa404,
114
FocalLengthIn35mmFilm = 0xa405,
115
SceneCaptureType = 0xa406,
116
GainControl = 0xa407,
120
DeviceSettingDescription = 0xa40b,
121
SubjectDistanceRange = 0xa40c,
122
ImageUniqueId = 0xa420,
124
// The Following IDs are not described the EXIF spec
126
// The XMP spec declares that XMP data should live 0x2bc when
127
// embedded in tiff images.
130
// Print Image Matching data
131
PimIfdPointer = 0xc4a5
134
public enum ByteOrder {
139
public enum ExifFormat {
159
public class ExifUtil {
161
[DllImport ("libexif.dll")]
162
static extern IntPtr exif_tag_get_name (Tag tag);
164
[DllImport ("libexif.dll")]
165
static extern IntPtr exif_tag_get_title (Tag tag);
167
[DllImport ("libexif.dll")]
168
static extern IntPtr exif_tag_get_description (Tag tag);
170
[DllImport ("libexif.dll")]
171
static extern IntPtr exif_byte_order_get_name (ByteOrder order);
173
[DllImport ("libexif.dll")]
174
static extern IntPtr exif_format_get_name (ExifFormat format);
176
[DllImport ("libexif.dll")]
177
static extern char exif_format_get_size (ExifFormat format);
179
[DllImport ("libexif.dll")]
180
static extern IntPtr exif_ifd_get_name (Ifd ifd);
182
public static string GetTagName (Tag tag)
185
IntPtr raw_ret = exif_tag_get_name (tag);
186
return Marshal.PtrToStringAnsi (raw_ret);
189
public static string GetTagTitle (Tag tag)
191
IntPtr raw_ret = exif_tag_get_title (tag);
192
return Marshal.PtrToStringAnsi (raw_ret);
195
public static string GetTagDescription (Tag tag)
197
IntPtr raw_ret = exif_tag_get_description (tag);
198
return Marshal.PtrToStringAnsi (raw_ret);
201
public static string GetByteOrderName (ByteOrder order)
203
IntPtr raw_ret = exif_byte_order_get_name (order);
204
return Marshal.PtrToStringAnsi (raw_ret);
207
public static string GetFormatName (ExifFormat format)
209
IntPtr raw_ret = exif_format_get_name (format);
210
return Marshal.PtrToStringAnsi (raw_ret);
213
public static char GetFormatSize (ExifFormat format)
215
return exif_format_get_size (format);
218
public static string GetIfdName (Ifd ifd)
220
IntPtr raw_ret = exif_ifd_get_name (ifd);
221
return Marshal.PtrToStringAnsi (raw_ret);
224
public static string GetIfdNameExtended (Ifd ifd)
228
return Catalog.GetString ("Image Directory");
230
return Catalog.GetString ("Thumbnail Directory");
232
return Catalog.GetString ("Exif Directory");
234
return Catalog.GetString ("GPS Directory");
235
case Ifd.InterOperability:
236
return Catalog.GetString ("InterOperability Directory");
238
return Catalog.GetString ("Unknown Directory");
242
public static DateTime DateTimeFromString(string dt)
244
// Exif DateTime strings are formatted as
245
// "YYYY:MM:DD HH:MM:SS"
247
string delimiters = " :";
248
string[] dt_data = dt.Split ( delimiters.ToCharArray(), 6 );
252
result = new DateTime (Int32.Parse(dt_data[0]), Int32.Parse(dt_data[1]), Int32.Parse(dt_data[2]),
253
Int32.Parse(dt_data[3]), Int32.Parse(dt_data[4]), Int32.Parse(dt_data[5]), DateTimeKind.Local);
254
} catch (FormatException) {
255
result = DateTime.MinValue;
263
public abstract class ExifObject : IDisposable {
264
protected HandleRef handle;
266
public HandleRef Handle {
272
public ExifObject () {}
274
public ExifObject (IntPtr ptr)
276
handle = new HandleRef (this, ptr);
279
protected abstract void Cleanup ();
281
public void Dispose () {
283
System.GC.SuppressFinalize (this);
292
[StructLayout(LayoutKind.Sequential)]
293
internal unsafe struct _ExifContent {
294
public IntPtr entries;
296
public IntPtr parent;
301
public class ExifContent : ExifObject {
303
public ExifData Parent {
309
System.Collections.ArrayList entries;
311
internal ExifContent (ExifData parent, IntPtr handle) : base (handle)
313
this.parent = parent;
314
exif_content_ref (this.handle);
317
[DllImport ("libexif.dll")]
318
static extern void exif_content_ref (HandleRef handle);
320
[DllImport ("libexif.dll")]
321
static extern void exif_content_unref (HandleRef handle);
323
protected override void Cleanup ()
325
exif_content_unref (handle);
328
[DllImport ("libexif.dll")]
329
internal static extern void exif_content_remove_entry (HandleRef content, HandleRef entry);
331
[DllImport ("libexif.dll")]
332
internal static extern void exif_content_add_entry (HandleRef content, HandleRef entry);
334
public ExifEntry Lookup (Tag tag)
338
foreach (ExifEntry entry in entries) {
339
if (entry.Tag == tag) {
347
public bool Contains (ExifEntry entry)
351
return entries.Contains (entry);
354
public ExifEntry GetEntry (Tag tag)
358
ExifEntry entry = Lookup (tag);
360
entry = new ExifEntry (this, tag);
365
public void Add (ExifEntry entry)
370
// This call can recurse into this function but it protects
371
// itself by checking if it the content already contains the entry
372
entry.SetParent (this);
373
exif_content_add_entry (this.handle, entry.Handle);
376
public void Remove (ExifEntry entry)
380
entries.Remove (entry);
381
// This call can recurse into this function but it protects
382
// itself by checking if it the content already contains the entry
383
entry.SetParent (null);
384
exif_content_remove_entry (this.handle, entry.Handle);
387
public ExifEntry [] GetEntries ()
391
return (ExifEntry [])entries.ToArray (typeof (ExifEntry));
394
[DllImport ("libexif.dll")]
395
internal static unsafe extern IntPtr exif_content_foreach_entry (HandleRef content,
396
ExifContentForeachEntryFunc func,
399
internal delegate void ExifContentForeachEntryFunc (IntPtr entry_ptr, IntPtr data);
401
void AssembleEntry (IntPtr entry, IntPtr data)
403
entries.Add (new ExifEntry (this, entry));
406
ExifContentForeachEntryFunc func;
408
public void Assemble ()
410
if (entries == null) {
411
entries = new System.Collections.ArrayList ();
413
func = new ExifContentForeachEntryFunc (AssembleEntry);
414
exif_content_foreach_entry (this.Handle, func, IntPtr.Zero);
420
[StructLayout(LayoutKind.Sequential)]
421
internal struct _ExifEntry {
424
public uint components;
428
public IntPtr parent;
434
public class ExifEntry : ExifObject {
436
public ExifContent Parent {
439
if (_handle->parent != parent.Handle.Handle)
440
throw new Exception ("Invalid Object State");
446
// Don't use this unless you know exactly what you are doing
447
internal void SetParent (ExifContent adoptor) {
448
// NOTE this api is ugly but the check prevent the parent state
449
// from getting confused. See ExifContent Add and Remove for the
451
if (parent != null && parent.Contains (this))
452
parent.Remove (this);
454
if (adoptor != null && !adoptor.Contains (this))
460
internal ExifEntry (ExifContent parent, IntPtr native) : base (native)
462
this.handle = new HandleRef (this, native);
463
this.parent = parent;
464
exif_entry_ref (this.handle);
467
[DllImport ("libexif.dll")]
468
internal static extern IntPtr exif_entry_new ();
470
[DllImport ("libexif.dll")]
471
internal static extern void exif_entry_initialize (HandleRef handle, Tag tag);
473
public ExifEntry (ExifContent parent, Tag tag)
475
handle = new HandleRef (this, exif_entry_new ());
480
public void Reset (Tag tag)
483
// Free any exsting data so that _initialize will actually set the data
484
if (_handle->data != IntPtr.Zero)
485
ExifData.free (_handle->data);
486
_handle->data = IntPtr.Zero;
489
exif_entry_initialize (handle, tag);
491
//FIXME the month string in time fields in libexif ix currently broken so we do our own.
492
if (tag == Tag.DateTime
493
|| tag == Tag.DateTimeOriginal
494
|| tag == Tag.DateTimeDigitized)
495
this.SetData (System.DateTime.Now);
505
protected override void Cleanup ()
507
exif_entry_unref (this.handle);
510
private unsafe _ExifEntry *_handle {
512
return (_ExifEntry *)handle.Handle;
524
public ExifFormat Format {
527
return (ExifFormat) _handle->format;
532
public byte [] Data {
535
byte [] data = new byte [_handle->size];
538
Marshal.Copy (_handle->data, data, 0, (int)_handle->size);
545
public void SetData (byte [] data, int size)
548
if (data == null || data.Length == 0)
549
throw new System.Exception ("Invalid Length");
551
if (_handle->data != IntPtr.Zero)
552
ExifData.free (_handle->data);
554
_handle->data = ExifData.malloc ((uint)data.Length);
555
Marshal.Copy (data, 0, _handle->data, data.Length);
557
_handle->size = (uint) data.Length;
558
// This needs to be set per type as well but
559
// we do it here as well
560
_handle->components = (uint) (data.Length / size);
564
public void SetData (byte []data)
569
public void SetData (uint s)
571
this.SetData (FSpot.BitConverter.GetBytes (s, this.ByteOrder == ByteOrder.Intel), 4);
574
public void SetData (ushort s)
576
this.SetData (FSpot.BitConverter.GetBytes (s, this.ByteOrder == ByteOrder.Intel), 2);
579
public void SetData (string value)
581
int len = System.Text.Encoding.UTF8.GetByteCount (value);
582
byte [] tmp = new byte [len + 1];
583
System.Text.Encoding.UTF8.GetBytes (value, 0, value.Length, tmp, 0);
585
//System.Console.WriteLine ("value = {0} len = {1}", value, len);
589
public void SetData (System.DateTime time)
591
SetData (time.ToString ("yyyy:MM:dd HH:mm:ss"));
594
private unsafe void PutBytes (byte *dest, byte *src, int count)
597
if (System.BitConverter.IsLittleEndian == (this.ByteOrder == ByteOrder.Intel)) {
598
for (i = 0; i < count; i++) {
599
//System.Console.WriteLine ("Copying normal byte [{0}]= {1}", i, src[i]);
603
for (i = 0; i < count; i++) {
604
//System.Console.WriteLine ("Copying swapped byte [{0}]= {1}", i, src[i]);
605
dest [i] = src [count - i -1];
610
private unsafe uint ToUInt (byte *src)
613
PutBytes ((byte *)&value, (byte *)src, 4);
617
private unsafe ushort ToUShort (byte *src)
620
PutBytes ((byte *)&value, (byte *)src, 2);
624
public uint [] GetDataUInt () {
626
uint [] result = new uint [_handle->components];
627
uint *src = (uint *)_handle->data;
628
//System.Console.WriteLine ("copying {0} components", result.Length);
629
for (int i = 0; i < result.Length; i++) {
630
result [i] = ToUInt ((byte *)src);
631
//System.Console.WriteLine ("value[{0}] = {1}", i, result [i]);
639
public ushort [] GetDataUShort () {
641
ushort [] result = new ushort [_handle->components];
642
ushort *src = (ushort *)_handle->data;
643
//System.Console.WriteLine ("copying {0} components", result.Length);
644
for (int i = 0; i < result.Length; i++) {
645
result [i] = ToUShort ((byte *)src);
646
//System.Console.WriteLine ("value[{0}] = {1}", i, result [i]);
655
public int [] GetDataInt () {
659
public ByteOrder ByteOrder
662
return parent.Parent.GetByteOrder ();
666
public string Description
669
return ExifUtil.GetTagDescription (Tag);
676
return ExifUtil.GetTagName (Tag);
683
return ExifUtil.GetTagTitle (Tag);
687
static int fallback = 0;
689
// FIXME this version is only valid in libexif 0.5
690
[DllImport ("libexif.dll")]
691
internal static extern IntPtr exif_entry_get_value (HandleRef handle);
692
[DllImport ("libexif.dll")]
693
internal static extern IntPtr exif_entry_get_value_brief (HandleRef handle);
695
// FIXME this version is only valid in libexif 0.6
696
[DllImport ("libexif.dll")]
697
internal static extern IntPtr exif_entry_get_value (HandleRef handle, byte [] value, int maxlen);
704
exif_entry_get_value_brief (this.Handle);
706
} catch (EntryPointNotFoundException) {
712
return Marshal.PtrToStringAnsi (exif_entry_get_value (this.Handle));
714
byte [] value = new byte [1024];
715
exif_entry_get_value (this.Handle, value, value.Length);
718
for (i = 0; i < value.Length; i++) {
722
int len = System.Math.Max (i, 0);
726
return System.Text.Encoding.UTF8.GetString (value, 0, len);
731
[DllImport ("libexif.dll")]
732
internal static extern void exif_entry_ref (HandleRef handle);
734
[DllImport ("libexif.dll")]
735
internal static extern void exif_entry_unref (HandleRef handle);
738
[StructLayout(LayoutKind.Sequential)]
739
internal struct _ExifData {
740
internal IntPtr ifd0;
741
internal IntPtr ifd1;
742
internal IntPtr ifd_exif;
743
internal IntPtr ifd_gps;
744
internal IntPtr ifd_interop;
746
internal IntPtr data;
749
internal IntPtr priv;
752
public class ExifData : ExifObject {
753
System.Collections.ArrayList ifds;
755
[DllImport ("libexif.dll")]
756
internal static extern IntPtr exif_data_new ();
760
handle = new HandleRef (this, exif_data_new ());
763
[DllImport ("libexif.dll")]
764
internal static extern IntPtr exif_data_new_from_file (string path);
766
public ExifData (string filename)
768
handle = new HandleRef (this, exif_data_new_from_file (filename));
771
[DllImport ("libexif.dll")]
772
internal static extern IntPtr exif_data_new_from_data (byte [] data, uint size);
774
public ExifData (byte [] data)
776
handle = new HandleRef (this, exif_data_new_from_data (data, (uint) data.Length));
779
public ExifData (byte [] data, uint size)
781
handle = new HandleRef (this, exif_data_new_from_data (data, size));
785
internal static extern void free (IntPtr address);
788
internal static extern IntPtr malloc (uint size);
790
[DllImport ("libexif.dll")]
791
private static extern void exif_data_save_data (HandleRef handle, out IntPtr content, out uint size);
792
public byte [] Save ()
794
Byte [] content = null;
798
exif_data_save_data (handle, out data, out size);
800
content = new byte [size];
801
Marshal.Copy (data, content, 0, (int)size);
805
//System.Console.WriteLine ("Saved {0} bytes", content.Length);
809
[DllImport ("libexif.dll")]
810
internal static extern void exif_data_unref (HandleRef data);
812
[DllImport ("libexif.dll")]
813
internal static extern void exif_data_free (HandleRef data);
815
protected override void Cleanup ()
817
exif_data_unref (handle);
820
[DllImport ("libexif.dll")]
821
internal static extern void exif_data_dump (HandleRef data);
825
exif_data_dump (handle);
828
public ExifContent GetContents (Ifd ifd)
832
return (ExifContent) ifds [(int)ifd];
835
public ExifContent [] GetContents ()
839
return (ExifContent []) ifds.ToArray (typeof (ExifContent));
842
[DllImport("libexif.dll")]
843
internal static extern ByteOrder exif_data_get_byte_order (HandleRef handle);
845
public ByteOrder GetByteOrder ()
847
return exif_data_get_byte_order (handle);
850
internal delegate void ExifDataForeachContentFunc (IntPtr content, IntPtr data);
852
[DllImport ("libexif.dll")]
853
internal unsafe static extern void exif_data_foreach_content(HandleRef handle, ExifDataForeachContentFunc func, IntPtr data);
855
unsafe void AssembleIfds (IntPtr content, IntPtr data)
857
ifds.Add (new ExifContent (this, content));
860
public ExifEntry LookupFirst (Tag tag)
863
foreach (ExifContent content in ifds) {
867
ExifEntry entry = content.Lookup (tag);
874
public string LookupFirstValue (Tag tag)
876
ExifEntry entry = LookupFirst (tag);
883
public void Assemble ()
886
ifds = new System.Collections.ArrayList ();
888
if (handle.Handle != IntPtr.Zero)
889
exif_data_foreach_content (handle, new ExifDataForeachContentFunc (AssembleIfds), IntPtr.Zero);
893
byte [] empty = new byte [0];
894
public byte [] Data {
897
_ExifData * obj = (_ExifData *) Handle.Handle;
900
if (obj == null || obj->data == (IntPtr) 0)
903
result = new byte [obj->size];
904
Marshal.Copy (obj->data, result, 0, obj->size);
912
_ExifData * obj = (_ExifData *) Handle.Handle;
913
if (value.Length > 65533)
914
throw new System.Exception ("Thumbnail too large");
916
if (obj->data != IntPtr.Zero)
919
obj->data = malloc ((uint)value.Length);
920
Marshal.Copy (value, 0, obj->data, value.Length);