New e-d-s backend which indexes all local addressbooks and calendars.
[beagle.git] / Util / ExifData.cs
blobf0f2bba039e65aaea9567aa0a2ad1a5be266f037
1 //
2 // Exif.cs : LibExif wrapper for FSpot
3 //
4 // Author:
5 // Larry Ewing (lewing@novell.com)
6 // Ravi Pratap (ravi@ximian.com)
7 // Miguel de Icaza (miguel@ximian.com)
8 //
9 // (C) 2002, 2004, 2005 Novell, Inc.
12 using System;
13 using System.Collections;
14 using System.Runtime.InteropServices;
16 namespace Beagle.Util {
17 public enum ExifTag {
18 InteroperabilityIndex = 0x0001,
19 InteroperabilityVersion = 0x0002,
20 ImageWidth = 0x0100,
21 ImageLength = 0x0101,
22 BitsPersample = 0x0102,
23 Compression = 0x0103,
24 PhotometricInterpretation = 0x0106,
25 FillOrder = 0x010a,
26 DocumentName = 0x010d,
27 ImageDescription = 0x010e,
28 Make = 0x010f,
29 Model = 0x0110,
30 StripOffsets = 0x0111,
31 Orientation = 0x0112,
32 SamplesPerPixel = 0x0115,
33 RowsPerStrip = 0x0116,
34 StripByteCounts = 0x0117,
35 XResolution = 0x011a,
36 YResolution = 0x011b,
37 PlanarConfiguration = 0x011c,
38 ResolutionUnit = 0x0128,
39 TransferFunction = 0x012d,
40 Software = 0x0131,
41 DateTime = 0x0132,
42 Artist = 0x013b,
43 WhitePoint = 0x013e,
44 PrimaryChromaticities = 0x013f,
45 TransferRange = 0x0156,
46 JPEGProc = 0x0200,
47 JPEGInterchangeFormat = 0x0201,
48 JPEGInterchangeFormatLength = 0x0202,
49 YCBCRCoefficients = 0x0211,
50 YCBCRSubSampling = 0x0212,
51 YCBCRPositioning = 0x0213,
52 ReferenceBlackWhite = 0x0214,
53 RelatedImageFileFormat = 0x1000,
54 RelatedImageWidth = 0x1001,
55 RelatedImageLength = 0x1002,
56 CFARepeatPatternDim = 0x828d,
57 CFAPattern = 0x828e,
58 BatteryLevel = 0x828f,
59 Copyright = 0x8298,
60 ExposureTime = 0x829a,
61 FNumber = 0x829d,
62 IPTCNAA = 0x83bb,
63 ExifIfdPointer = 0x8769,
64 InterColorProfile = 0x8773,
65 ExposureProgram = 0x8822,
66 SpectralSensitivity = 0x8824,
67 GPSInfoIfdPointer = 0x8825,
68 ISOSpeedRatings = 0x8827,
69 OECF = 0x8828,
70 ExifVersion = 0x9000,
71 DateTimeOriginal = 0x9003,
72 DateTimeDigitized = 0x9004,
73 ComponentsConfiguration = 0x9101,
74 CompressedBitsPerPixel = 0x9102,
75 ShutterSpeedValue = 0x9201,
76 ApertureValue = 0x9202,
77 BrightnessValue = 0x9203,
78 ExposureBiasValue = 0x9204,
79 MaxApertureValue = 0x9205,
80 SubjectDistance = 0x9206,
81 MeteringMode = 0x9207,
82 LightSource = 0x9208,
83 Flash = 0x9209,
84 FocalLength = 0x920a,
85 SubjectArea = 0x9214,
86 MakerNote = 0x927c,
87 UserComment = 0x9286,
88 SubSecTime = 0x9290,
89 SubSecTimeOriginal = 0x9291,
90 SubSecTimeDigitized = 0x9292,
91 FlashPixVersion = 0xa000,
92 ColorSpace = 0xa001,
93 PixelXDimension = 0xa002,
94 PixelYDimension = 0xa003,
95 RelatedSoundFile = 0xa004,
96 InteroperabilityIfdPointer = 0xa005,
97 FlashEnergy = 0xa20b,
98 SpatialFrequencyResponse = 0xa20c,
99 FocalPlaneXResolution = 0xa20e,
100 FocalPlaneYResolution = 0xa20f,
101 FocalPlaneResolutionUnit = 0xa210,
102 SubjectLocation = 0xa214,
103 ExposureIndex = 0xa215,
104 SensingMethod = 0xa217,
105 FileSource = 0xa300,
106 SceneType = 0xa301,
107 NewCFAPattern = 0xa302,
108 CustomRendered = 0xa401,
109 ExposureMode = 0xa402,
110 WhiteBalance = 0xa403,
111 DigitalZoomRatio = 0xa404,
112 FocalLengthIn35mmFilm = 0xa405,
113 SceneCaptureType = 0xa406,
114 GainControl = 0xa407,
115 Contrast = 0xa408,
116 Saturation = 0xa409,
117 Sharpness = 0xa40a,
118 DeviceSettingDescription = 0xa40b,
119 SubjectDistanceRange = 0xa40c,
120 ImageUniqueId = 0xa420,
122 // The Following IDs are not described the EXIF spec
124 // The XMP spec declares that XMP data should live 0x2bc when
125 // embedded in tiff images.
126 XMP = 0x02bc,
128 // Print Image Matching data
129 PimIfdPointer = 0xc4a5
132 public enum ExifByteOrder {
133 Motorola,
134 Intel
137 public enum ExifFormat {
138 Byte = 1,
139 Ascii = 2,
140 Short = 3,
141 Long = 4,
142 Rational = 5,
143 Undefined = 7,
144 Slong = 9,
145 SRational = 10
148 public enum ExifIfd {
149 Zero = 0,
150 One,
151 Exif,
152 Gps,
153 InterOperability,
154 Count
157 public class ExifUtil {
159 [DllImport ("libexif.dll")]
160 static extern IntPtr exif_tag_get_name (ExifTag tag);
162 [DllImport ("libexif.dll")]
163 static extern IntPtr exif_tag_get_title (ExifTag tag);
165 [DllImport ("libexif.dll")]
166 static extern IntPtr exif_tag_get_description (ExifTag tag);
168 [DllImport ("libexif.dll")]
169 static extern IntPtr exif_byte_order_get_name (ExifByteOrder order);
171 [DllImport ("libexif.dll")]
172 static extern IntPtr exif_format_get_name (ExifFormat format);
174 [DllImport ("libexif.dll")]
175 static extern char exif_format_get_size (ExifFormat format);
177 [DllImport ("libexif.dll")]
178 static extern IntPtr exif_ifd_get_name (ExifIfd ifd);
180 public static string GetTagName (ExifTag tag)
183 IntPtr raw_ret = exif_tag_get_name (tag);
184 return Marshal.PtrToStringAnsi (raw_ret);
187 public static string GetTagTitle (ExifTag tag)
189 IntPtr raw_ret = exif_tag_get_title (tag);
190 return Marshal.PtrToStringAnsi (raw_ret);
193 public static string GetTagDescription (ExifTag tag)
195 IntPtr raw_ret = exif_tag_get_description (tag);
196 return Marshal.PtrToStringAnsi (raw_ret);
199 public static string GetByteOrderName (ExifByteOrder order)
201 IntPtr raw_ret = exif_byte_order_get_name (order);
202 return Marshal.PtrToStringAnsi (raw_ret);
205 public static string GetFormatName (ExifFormat format)
207 IntPtr raw_ret = exif_format_get_name (format);
208 return Marshal.PtrToStringAnsi (raw_ret);
211 public static char GetFormatSize (ExifFormat format)
213 return exif_format_get_size (format);
216 public static string GetIfdName (ExifIfd ifd)
218 IntPtr raw_ret = exif_ifd_get_name (ifd);
219 return Marshal.PtrToStringAnsi (raw_ret);
222 public static string GetIfdNameExtended (ExifIfd ifd)
224 switch (ifd) {
225 case ExifIfd.Zero:
226 return Mono.Posix.Catalog.GetString ("Image Directory");
227 case ExifIfd.One:
228 return Mono.Posix.Catalog.GetString ("Thumbnail Directory");
229 case ExifIfd.Exif:
230 return Mono.Posix.Catalog.GetString ("Exif Directory");
231 case ExifIfd.Gps:
232 return Mono.Posix.Catalog.GetString ("GPS Directory");
233 case ExifIfd.InterOperability:
234 return Mono.Posix.Catalog.GetString ("InterOperability Directory");
235 default:
236 return Mono.Posix.Catalog.GetString ("Unknown Directory");
240 public static DateTime DateTimeFromString(string dt)
242 // Exif DateTime strings are formatted as
243 // "YYYY:MM:DD HH:MM:SS"
245 string delimiters = " :";
246 string[] dt_data = dt.Split ( delimiters.ToCharArray(), 6 );
247 DateTime result;
248 result = new DateTime (Int32.Parse(dt_data[0]), Int32.Parse(dt_data[1]), Int32.Parse(dt_data[2]),
249 Int32.Parse(dt_data[3]), Int32.Parse(dt_data[4]), Int32.Parse(dt_data[5]));
251 return result;
256 public abstract class ExifObject : IDisposable {
257 protected HandleRef handle;
259 public HandleRef Handle {
260 get {
261 return handle;
265 public ExifObject () {}
267 public ExifObject (IntPtr ptr)
269 handle = new HandleRef (this, ptr);
272 protected abstract void Cleanup ();
274 public void Dispose () {
275 Cleanup ();
276 System.GC.SuppressFinalize (this);
279 ~ExifObject ()
281 Cleanup ();
285 [StructLayout(LayoutKind.Sequential)]
286 internal unsafe struct _ExifContent {
287 IntPtr entries;
288 uint count;
289 IntPtr parent;
291 IntPtr priv;
294 public class ExifContent : ExifObject {
295 ExifData parent;
296 public ExifData Parent {
297 get {
298 return parent;
302 System.Collections.ArrayList entries;
304 internal ExifContent (ExifData parent, IntPtr handle) : base (handle)
306 this.parent = parent;
307 exif_content_ref (this.handle);
310 [DllImport ("libexif.dll")]
311 static extern void exif_content_ref (HandleRef handle);
313 [DllImport ("libexif.dll")]
314 static extern void exif_content_unref (HandleRef handle);
316 protected override void Cleanup ()
318 exif_content_unref (handle);
321 [DllImport ("libexif.dll")]
322 internal static extern void exif_content_remove_entry (HandleRef content, HandleRef entry);
324 [DllImport ("libexif.dll")]
325 internal static extern void exif_content_add_entry (HandleRef content, HandleRef entry);
327 public ExifEntry Lookup (ExifTag tag)
329 Assemble ();
331 foreach (ExifEntry entry in entries) {
332 if (entry.Tag == tag) {
333 return entry;
337 return null;
340 public bool Contains (ExifEntry entry)
342 Assemble ();
344 return entries.Contains (entry);
347 public ExifEntry GetEntry (ExifTag tag)
349 Assemble ();
351 ExifEntry entry = Lookup (tag);
352 if (entry == null)
353 entry = new ExifEntry (this, tag);
355 return entry;
358 public void Add (ExifEntry entry)
360 Assemble ();
362 entries.Add (entry);
363 // This call can recurse into this function but it protects
364 // itself by checking if it the content already contains the entry
365 entry.SetParent (this);
366 exif_content_add_entry (this.handle, entry.Handle);
369 public void Remove (ExifEntry entry)
371 Assemble ();
373 entries.Remove (entry);
374 // This call can recurse into this function but it protects
375 // itself by checking if it the content already contains the entry
376 entry.SetParent (null);
377 exif_content_remove_entry (this.handle, entry.Handle);
380 public ExifEntry [] GetEntries ()
382 Assemble ();
384 return (ExifEntry [])entries.ToArray (typeof (ExifEntry));
387 [DllImport ("libexif.dll")]
388 internal static unsafe extern IntPtr exif_content_foreach_entry (HandleRef content,
389 ExifContentForeachEntryFunc func,
390 IntPtr data);
392 internal delegate void ExifContentForeachEntryFunc (IntPtr entry_ptr, IntPtr data);
394 void AssembleEntry (IntPtr entry, IntPtr data)
396 entries.Add (new ExifEntry (this, entry));
399 ExifContentForeachEntryFunc func;
401 public void Assemble ()
403 if (entries == null) {
404 entries = new System.Collections.ArrayList ();
406 func = new ExifContentForeachEntryFunc (AssembleEntry);
407 exif_content_foreach_entry (this.Handle, func, IntPtr.Zero);
413 [StructLayout(LayoutKind.Sequential)]
414 internal struct _ExifEntry {
415 public ExifTag tag;
416 public int format;
417 public uint components;
418 public IntPtr data;
419 public uint size;
421 public IntPtr parent;
423 IntPtr priv;
427 public class ExifEntry : ExifObject {
428 ExifContent parent;
429 public ExifContent Parent {
430 get {
431 unsafe {
432 if (_handle->parent != parent.Handle.Handle)
433 throw new Exception ("Invalid Object State");
435 return parent;
439 // Don't use this unless you know exactly what you are doing
440 internal void SetParent (ExifContent adoptor) {
441 // NOTE this api is ugly but the check prevent the parent state
442 // from getting confused. See ExifContent Add and Remove for the
443 // other half.
444 if (parent != null && parent.Contains (this))
445 parent.Remove (this);
447 if (adoptor != null && !adoptor.Contains (this))
448 adoptor.Add (this);
450 parent = adoptor;
453 internal ExifEntry (ExifContent parent, IntPtr native) : base (native)
455 this.handle = new HandleRef (this, native);
456 this.parent = parent;
457 exif_entry_ref (this.handle);
460 [DllImport ("libexif.dll")]
461 internal static extern IntPtr exif_entry_new ();
463 [DllImport ("libexif.dll")]
464 internal static extern void exif_entry_initialize (HandleRef handle, ExifTag tag);
466 public ExifEntry (ExifContent parent, ExifTag tag)
468 handle = new HandleRef (this, exif_entry_new ());
469 parent.Add (this);
470 this.Reset (tag);
473 public void Reset (ExifTag tag)
475 unsafe {
476 // Free any exsting data so that _initialize will actually set the data
477 if (_handle->data != IntPtr.Zero)
478 ExifData.free (_handle->data);
479 _handle->data = IntPtr.Zero;
482 exif_entry_initialize (handle, tag);
484 //FIXME the month string in time fields in libexif ix currently broken so we do our own.
485 if (tag == ExifTag.DateTime
486 || tag == ExifTag.DateTimeOriginal
487 || tag == ExifTag.DateTimeDigitized)
488 this.SetData (System.DateTime.Now);
493 public void Reset ()
495 Reset (Tag);
498 protected override void Cleanup ()
500 exif_entry_unref (this.handle);
503 private unsafe _ExifEntry *_handle {
504 get {
505 return (_ExifEntry *)handle.Handle;
509 public ExifTag Tag {
510 get {
511 unsafe {
512 return _handle->tag;
517 public ExifFormat Format {
518 get {
519 unsafe {
520 return (ExifFormat) _handle->format;
525 public byte [] Data {
526 get {
527 unsafe {
528 byte [] data = new byte [_handle->size];
529 Marshal.Copy (_handle->data, data, 0, (int)_handle->size);
530 return data;
535 public void SetData (byte [] data, bool check_type)
537 unsafe {
538 if (data == null || data.Length == 0)
539 throw new System.Exception ("Invalid Length");
541 if (_handle->data != IntPtr.Zero)
542 ExifData.free (_handle->data);
544 _handle->data = ExifData.malloc ((uint)data.Length);
545 Marshal.Copy (data, 0, _handle->data, data.Length);
547 _handle->size = (uint) data.Length;
548 // This needs to be set per type as well but
549 // we do it here as well
550 _handle->components = (uint) data.Length;
554 public void SetData (byte []data)
556 SetData (data, true);
559 public void SetData (ushort [] data)
564 public void SetData (short [] data)
569 public void SetData (string value)
571 int len = System.Text.Encoding.UTF8.GetByteCount (value);
572 byte [] tmp = new byte [len + 1];
573 System.Text.Encoding.UTF8.GetBytes (value, 0, value.Length, tmp, 0);
574 tmp[len] = 0;
575 System.Console.WriteLine ("value = {0} len = {1}", value, len);
576 SetData (tmp, false);
579 public void SetData (System.DateTime time)
581 SetData (time.ToString ("yyyy:MM:dd HH:mm:ss"));
584 private unsafe void PutBytes (byte *dest, byte *src, int count)
586 int i = 0;
587 if (System.BitConverter.IsLittleEndian == (this.ByteOrder == ExifByteOrder.Intel)) {
588 for (i = 0; i < count; i++) {
589 //System.Console.WriteLine ("Copying normal byte [{0}]= {1}", i, src[i]);
590 dest [i] = src [i];
592 } else {
593 for (i = 0; i < count; i++) {
594 //System.Console.WriteLine ("Copying swapped byte [{0}]= {1}", i, src[i]);
595 dest [i] = src [count - i -1];
600 private unsafe uint ToUInt (byte *src)
602 uint value;
603 PutBytes ((byte *)&value, (byte *)src, 4);
604 return value;
607 private unsafe ushort ToUShort (byte *src)
609 ushort value;
610 PutBytes ((byte *)&value, (byte *)src, 2);
611 return value;
614 public uint [] GetDataUInt () {
615 unsafe {
616 uint [] result = new uint [_handle->components];
617 uint *src = (uint *)_handle->data;
618 //System.Console.WriteLine ("copying {0} components", result.Length);
619 for (int i = 0; i < result.Length; i++) {
620 result [i] = ToUInt ((byte *)src);
621 //System.Console.WriteLine ("value[{0}] = {1}", i, result [i]);
622 src += i;
625 return result;
629 public ushort [] GetDataUShort () {
630 unsafe {
631 ushort [] result = new ushort [_handle->components];
632 ushort *src = (ushort *)_handle->data;
633 //System.Console.WriteLine ("copying {0} components", result.Length);
634 for (int i = 0; i < result.Length; i++) {
635 result [i] = ToUShort ((byte *)src);
636 //System.Console.WriteLine ("value[{0}] = {1}", i, result [i]);
637 src += i;
640 return result;
645 public int [] GetDataInt () {
646 return null;
649 public ExifByteOrder ByteOrder
651 get {
652 return parent.Parent.GetByteOrder ();
656 public string Description
658 get {
659 return ExifUtil.GetTagDescription (Tag);
663 public string Name
665 get {
666 return ExifUtil.GetTagName (Tag);
670 public string Title
672 get {
673 return ExifUtil.GetTagTitle (Tag);
677 static int fallback = 0;
679 // FIXME this version is only valid in libexif 0.5
680 [DllImport ("libexif.dll")]
681 internal static extern IntPtr exif_entry_get_value (HandleRef handle);
682 [DllImport ("libexif.dll")]
683 internal static extern IntPtr exif_entry_get_value_brief (HandleRef handle);
685 // FIXME this version is only valid in libexif 0.6
686 [DllImport ("libexif.dll")]
687 internal static extern IntPtr exif_entry_get_value (HandleRef handle, byte [] value, int maxlen);
689 public string Value
691 get {
692 if (fallback == 0) {
693 try {
694 exif_entry_get_value_brief (this.Handle);
695 fallback = 1;
696 } catch (System.Exception e) {
697 fallback = -1;
701 if (fallback > 0)
702 return Marshal.PtrToStringAnsi (exif_entry_get_value (this.Handle));
703 else {
704 byte [] value = new byte [1024];
705 exif_entry_get_value (this.Handle, value, value.Length);
707 int i;
708 for (i = 0; i < value.Length; i++) {
709 if (value [i] == 0)
710 break;
712 int len = System.Math.Max (i, 0);
713 if (len == 0)
714 return null;
716 return System.Text.Encoding.UTF8.GetString (value, 0, len);
721 [DllImport ("libexif.dll")]
722 internal static extern void exif_entry_ref (HandleRef handle);
724 [DllImport ("libexif.dll")]
725 internal static extern void exif_entry_unref (HandleRef handle);
728 [StructLayout(LayoutKind.Sequential)]
729 internal struct _ExifData {
730 IntPtr ifd0;
731 IntPtr ifd1;
732 IntPtr ifd_exif;
733 IntPtr ifd_gps;
734 IntPtr ifd_interop;
736 internal IntPtr data;
737 internal int size;
739 IntPtr priv;
742 public class ExifData : ExifObject {
743 System.Collections.ArrayList ifds;
745 [DllImport ("libexif.dll")]
746 internal static extern IntPtr exif_data_new ();
748 public ExifData ()
750 handle = new HandleRef (this, exif_data_new ());
753 [DllImport ("libexif.dll")]
754 internal static extern IntPtr exif_data_new_from_file (string path);
756 public ExifData (string filename)
758 handle = new HandleRef (this, exif_data_new_from_file (filename));
761 [DllImport ("libexif.dll")]
762 internal static extern IntPtr exif_data_new_from_data (byte [] data, uint size);
764 public ExifData (byte [] data, uint size)
766 handle = new HandleRef (this, exif_data_new_from_data (data, size));
769 [DllImport ("libc")]
770 internal static extern void free (IntPtr address);
772 [DllImport ("libc")]
773 internal static extern IntPtr malloc (uint size);
775 [DllImport ("libexif.dll")]
776 private static extern void exif_data_save_data (HandleRef handle, out IntPtr content, out uint size);
777 public byte [] Save ()
779 Byte [] content = null;
780 uint size;
781 IntPtr data;
782 unsafe {
783 exif_data_save_data (handle, out data, out size);
785 content = new byte [size];
786 Marshal.Copy (data, content, 0, (int)size);
787 free (data);
790 System.Console.WriteLine ("Saved {0} bytes", content.Length);
791 return content;
794 [DllImport ("libexif.dll")]
795 internal static extern void exif_data_unref (HandleRef data);
797 [DllImport ("libexif.dll")]
798 internal static extern void exif_data_free (HandleRef data);
800 protected override void Cleanup ()
802 exif_data_unref (handle);
805 public ExifContent GetContents (ExifIfd ifd)
807 Assemble ();
809 return (ExifContent) ifds [(int)ifd];
812 public ExifContent [] GetContents ()
814 Assemble ();
816 return (ExifContent []) ifds.ToArray (typeof (ExifContent));
819 [DllImport("libexif.dll")]
820 internal static extern ExifByteOrder exif_data_get_byte_order (HandleRef handle);
822 public ExifByteOrder GetByteOrder ()
824 return exif_data_get_byte_order (handle);
827 internal delegate void ExifDataForeachContentFunc (IntPtr content, IntPtr data);
829 [DllImport ("libexif.dll")]
830 internal unsafe static extern void exif_data_foreach_content(HandleRef handle, ExifDataForeachContentFunc func, IntPtr data);
832 unsafe void AssembleIfds (IntPtr content, IntPtr data)
834 ifds.Add (new ExifContent (this, content));
837 public ExifEntry LookupFirst (ExifTag tag)
839 Assemble ();
840 foreach (ExifContent content in ifds) {
841 if (content == null)
842 continue;
844 ExifEntry entry = content.Lookup (tag);
845 if (entry != null)
846 return entry;
848 return null;
851 public string LookupFirstValue (ExifTag tag)
853 ExifEntry entry = LookupFirst (tag);
854 if (entry != null) {
855 return entry.Value;
857 return null;
860 public void Assemble ()
862 if (ifds == null) {
863 ifds = new System.Collections.ArrayList ();
865 if (handle.Handle != IntPtr.Zero)
866 exif_data_foreach_content (handle, new ExifDataForeachContentFunc (AssembleIfds), IntPtr.Zero);
870 byte [] empty = new byte [0];
871 public byte [] Data {
872 get {
873 unsafe {
874 _ExifData * obj = (_ExifData *) Handle.Handle;
875 byte [] result;
877 if (obj == null || obj->data == (IntPtr) 0)
878 result = empty;
879 else {
880 result = new byte [obj->size];
881 Marshal.Copy (obj->data, result, 0, obj->size);
884 return result;
887 set {
888 unsafe {
889 _ExifData * obj = (_ExifData *) Handle.Handle;
890 if (value.Length > 65533)
891 throw new System.Exception ("Thumbnail too large");
893 if (obj->data != IntPtr.Zero)
894 free (obj->data);
896 obj->data = malloc ((uint)value.Length);
897 Marshal.Copy (value, 0, obj->data, value.Length);