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
;
16 namespace Beagle
.Util
{
18 InteroperabilityIndex
= 0x0001,
19 InteroperabilityVersion
= 0x0002,
22 BitsPersample
= 0x0102,
24 PhotometricInterpretation
= 0x0106,
26 DocumentName
= 0x010d,
27 ImageDescription
= 0x010e,
30 StripOffsets
= 0x0111,
32 SamplesPerPixel
= 0x0115,
33 RowsPerStrip
= 0x0116,
34 StripByteCounts
= 0x0117,
37 PlanarConfiguration
= 0x011c,
38 ResolutionUnit
= 0x0128,
39 TransferFunction
= 0x012d,
44 PrimaryChromaticities
= 0x013f,
45 TransferRange
= 0x0156,
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,
58 BatteryLevel
= 0x828f,
60 ExposureTime
= 0x829a,
63 ExifIfdPointer
= 0x8769,
64 InterColorProfile
= 0x8773,
65 ExposureProgram
= 0x8822,
66 SpectralSensitivity
= 0x8824,
67 GPSInfoIfdPointer
= 0x8825,
68 ISOSpeedRatings
= 0x8827,
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,
89 SubSecTimeOriginal
= 0x9291,
90 SubSecTimeDigitized
= 0x9292,
91 FlashPixVersion
= 0xa000,
93 PixelXDimension
= 0xa002,
94 PixelYDimension
= 0xa003,
95 RelatedSoundFile
= 0xa004,
96 InteroperabilityIfdPointer
= 0xa005,
98 SpatialFrequencyResponse
= 0xa20c,
99 FocalPlaneXResolution
= 0xa20e,
100 FocalPlaneYResolution
= 0xa20f,
101 FocalPlaneResolutionUnit
= 0xa210,
102 SubjectLocation
= 0xa214,
103 ExposureIndex
= 0xa215,
104 SensingMethod
= 0xa217,
107 NewCFAPattern
= 0xa302,
108 CustomRendered
= 0xa401,
109 ExposureMode
= 0xa402,
110 WhiteBalance
= 0xa403,
111 DigitalZoomRatio
= 0xa404,
112 FocalLengthIn35mmFilm
= 0xa405,
113 SceneCaptureType
= 0xa406,
114 GainControl
= 0xa407,
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.
128 // Print Image Matching data
129 PimIfdPointer
= 0xc4a5
132 public enum ExifByteOrder
{
137 public enum ExifFormat
{
148 public enum ExifIfd
{
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
)
226 return Mono
.Posix
.Catalog
.GetString ("Image Directory");
228 return Mono
.Posix
.Catalog
.GetString ("Thumbnail Directory");
230 return Mono
.Posix
.Catalog
.GetString ("Exif Directory");
232 return Mono
.Posix
.Catalog
.GetString ("GPS Directory");
233 case ExifIfd
.InterOperability
:
234 return Mono
.Posix
.Catalog
.GetString ("InterOperability Directory");
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 );
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]));
256 public abstract class ExifObject
: IDisposable
{
257 protected HandleRef handle
;
259 public HandleRef Handle
{
265 public ExifObject () {}
267 public ExifObject (IntPtr ptr
)
269 handle
= new HandleRef (this, ptr
);
272 protected abstract void Cleanup ();
274 public void Dispose () {
276 System
.GC
.SuppressFinalize (this);
285 [StructLayout(LayoutKind
.Sequential
)]
286 internal unsafe struct _ExifContent
{
294 public class ExifContent
: ExifObject
{
296 public ExifData 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
)
331 foreach (ExifEntry entry
in entries
) {
332 if (entry
.Tag
== tag
) {
340 public bool Contains (ExifEntry entry
)
344 return entries
.Contains (entry
);
347 public ExifEntry
GetEntry (ExifTag tag
)
351 ExifEntry entry
= Lookup (tag
);
353 entry
= new ExifEntry (this, tag
);
358 public void Add (ExifEntry 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
)
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 ()
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
,
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
{
417 public uint components
;
421 public IntPtr parent
;
427 public class ExifEntry
: ExifObject
{
429 public ExifContent Parent
{
432 if (_handle
->parent
!= parent
.Handle
.Handle
)
433 throw new Exception ("Invalid Object State");
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
444 if (parent
!= null && parent
.Contains (this))
445 parent
.Remove (this);
447 if (adoptor
!= null && !adoptor
.Contains (this))
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 ());
473 public void Reset (ExifTag tag
)
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
);
498 protected override void Cleanup ()
500 exif_entry_unref (this.handle
);
503 private unsafe _ExifEntry
*_handle
{
505 return (_ExifEntry
*)handle
.Handle
;
517 public ExifFormat Format
{
520 return (ExifFormat
) _handle
->format
;
525 public byte [] Data
{
528 byte [] data
= new byte [_handle
->size
];
529 Marshal
.Copy (_handle
->data
, data
, 0, (int)_handle
->size
);
535 public void SetData (byte [] data
, bool check_type
)
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);
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
)
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]);
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
)
603 PutBytes ((byte *)&value, (byte *)src
, 4);
607 private unsafe ushort ToUShort (byte *src
)
610 PutBytes ((byte *)&value, (byte *)src
, 2);
614 public uint [] GetDataUInt () {
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]);
629 public ushort [] GetDataUShort () {
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]);
645 public int [] GetDataInt () {
649 public ExifByteOrder ByteOrder
652 return parent
.Parent
.GetByteOrder ();
656 public string Description
659 return ExifUtil
.GetTagDescription (Tag
);
666 return ExifUtil
.GetTagName (Tag
);
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
);
694 exif_entry_get_value_brief (this.Handle
);
696 } catch (System
.Exception e
) {
702 return Marshal
.PtrToStringAnsi (exif_entry_get_value (this.Handle
));
704 byte [] value = new byte [1024];
705 exif_entry_get_value (this.Handle
, value, value.Length
);
708 for (i
= 0; i
< value.Length
; i
++) {
712 int len
= System
.Math
.Max (i
, 0);
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
{
736 internal IntPtr data
;
742 public class ExifData
: ExifObject
{
743 System
.Collections
.ArrayList ifds
;
745 [DllImport ("libexif.dll")]
746 internal static extern IntPtr
exif_data_new ();
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
));
770 internal static extern void free (IntPtr address
);
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;
783 exif_data_save_data (handle
, out data
, out size
);
785 content
= new byte [size
];
786 Marshal
.Copy (data
, content
, 0, (int)size
);
790 System
.Console
.WriteLine ("Saved {0} bytes", content
.Length
);
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
)
809 return (ExifContent
) ifds
[(int)ifd
];
812 public ExifContent
[] GetContents ()
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
)
840 foreach (ExifContent content
in ifds
) {
844 ExifEntry entry
= content
.Lookup (tag
);
851 public string LookupFirstValue (ExifTag tag
)
853 ExifEntry entry
= LookupFirst (tag
);
860 public void Assemble ()
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
{
874 _ExifData
* obj
= (_ExifData
*) Handle
.Handle
;
877 if (obj
== null || obj
->data
== (IntPtr
) 0)
880 result
= new byte [obj
->size
];
881 Marshal
.Copy (obj
->data
, result
, 0, obj
->size
);
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
)
896 obj
->data
= malloc ((uint)value.Length
);
897 Marshal
.Copy (value, 0, obj
->data
, value.Length
);