~ubuntu-branches/ubuntu/jaunty/beagle/jaunty-security

« back to all changes in this revision

Viewing changes to Util/F-Spot/Imaging/Tiff.cs

  • Committer: Bazaar Package Importer
  • Author(s): Stefan Ebner
  • Date: 2008-05-04 00:31:32 UTC
  • mfrom: (1.1.21 upstream)
  • Revision ID: james.westby@ubuntu.com-20080504003132-2tkm5o8moo5952ri
Tags: 0.3.7-2ubuntu1
 * Merge from Debian unstable. (LP: #225746) Remaining Ubuntu changes:
  - debian/control:
    + Rename ice{weasel,dove}-beagle to {mozilla,thunderbird}-beagle and
      and update the dependencies accordingly.
    + Change Maintainer to Ubuntu Mono Team.
  - debian/rules:
    + Install the mozilla-beagle and thunderbird-beagle extensions.
  - ice{dove,weasel}.dirs:
    + Renamed to {mozilla,thunderbird}-beagle.dirs.
    + Fixed paths to point to usr/lib/{firefox,thunderbird}

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
//#define DEBUG_LOADER
 
2
using FSpot;
 
3
using SemWeb;
 
4
using System;
 
5
using System.IO;
 
6
using System.Collections.Generic;
 
7
 
 
8
#if ENABLE_NUNIT
 
9
using NUnit.Framework;
 
10
#endif
 
11
 
 
12
namespace FSpot.Tiff {
 
13
 
 
14
        // This is primarily to preserve the names from the specification
 
15
        // because they differ from the tiff standard names
 
16
        public enum NiffId : ushort {
 
17
                SubfileType                     = 0x00fe,
 
18
                PelPathLength                   = 0x0100,
 
19
                LineProgressionLength           = 257,
 
20
                BitsPerSample                   = 0x0101,
 
21
                PhotometricInterpretation       = 0x0106,
 
22
                DataOffset                      = 0x0111,
 
23
                SamplesPerPixel                 = 0x0115,
 
24
                DataByteCounts                  = 0x0117,
 
25
                PelPathResolution               = 0x011a,
 
26
                LineProgressionResolution       = 0x011b,
 
27
                ResolutionUnit                  = 0x0128,
 
28
                ColumnsPerPelPath               = 322,
 
29
                RowsPerLineProgression          = 323,
 
30
                Rotation                        = 33465,
 
31
                NavyCompression                 = 33466,
 
32
                TileIndex                       = 33467
 
33
        }
 
34
 
 
35
        public enum TagGPS : ushort {
 
36
                GPSVersionID                    = 0x0000,
 
37
                GPSLatitudeRef                  = 0x0001,
 
38
                GPSLatitude                     = 0x0002,
 
39
                        GPSLongitudeRef                    = 0x0003,
 
40
                        GPSLongitue                        = 0x0004,
 
41
                        GPSAltitudeRef                     = 0x0005,
 
42
                        GPSAltitude                        = 0x0006,
 
43
                        GPSTimeStamp                       = 0x0007,
 
44
                        GPSSatellites                      = 0x0008,
 
45
                        GPSStatus                          = 0x0009,
 
46
                        GPSMeasureMode                     = 0x000a,
 
47
                        GPSDOP                             = 0x000b,
 
48
                        GPSSpeedRef                        = 0x000c,
 
49
                        GPSSpeed                           = 0x000d,
 
50
                        GPSTrackRef                        = 0x000e,
 
51
                        GPSTrack                           = 0x000f,
 
52
                        GPSImgDirectionRef                 = 0x0010,
 
53
                        GPSImgDirection                    = 0x0011,
 
54
                        GPSMapDatum                        = 0x0012,
 
55
                        GPSDestLatitudeRef                 = 0x0013,
 
56
                        GPSDestLatitude                    = 0x0014,
 
57
                        GPSDestLongitudeRef                = 0x0015,
 
58
                        GPSDestLongitude                   = 0x0016,
 
59
                        GPSDestBearingRef                  = 0x0017,
 
60
                        GPSDestBearing                     = 0x0018,
 
61
                        GPSDestDistanceRef                 = 0x0019,
 
62
                        GPSDestDistance                    = 0x001a,
 
63
                        GPSProcessingMethod                = 0x001b,
 
64
                        GPSAreaInformation                 = 0x001c,
 
65
                        GPSDateStamp                       = 0x001d,
 
66
                        GPSDifferential                    = 0x001e
 
67
        }
 
68
 
 
69
        public enum TagId : ushort {
 
70
                InteroperabilityIndex           = 0x0001,
 
71
                InteroperabilityVersion         = 0x0002,
 
72
                
 
73
                NewSubfileType                  = 0x00fe,
 
74
                SubfileType                     = 0x00ff,
 
75
                
 
76
                ImageWidth                      = 0x0100,
 
77
                ImageLength                     = 0x0101,
 
78
                BitsPerSample                   = 0x0102,
 
79
                Compression                     = 0x0103,
 
80
                PhotometricInterpretation       = 0x0106,
 
81
                FillOrder                       = 0x010a,
 
82
                DocumentName                    = 0x010d,
 
83
                ImageDescription                = 0x010e,
 
84
                Make                            = 0x010f,
 
85
                Model                           = 0x0110,
 
86
                StripOffsets                    = 0x0111,
 
87
                Orientation                     = 0x0112,
 
88
                SamplesPerPixel                 = 0x0115,
 
89
                RowsPerStrip                    = 0x0116,
 
90
                StripByteCounts                 = 0x0117,
 
91
                XResolution                     = 0x011a,
 
92
                YResolution                     = 0x011b,
 
93
                PlanarConfiguration             = 0x011c,
 
94
 
 
95
                T4Options                       = 0x0124,
 
96
                T6Options                       = 0x0125,
 
97
 
 
98
                ResolutionUnit                  = 0x0128,
 
99
                TransferFunction                = 0x012d,
 
100
                Software                        = 0x0131,
 
101
                DateTime                        = 0x0132,
 
102
                Artist                          = 0x013b,
 
103
                WhitePoint                      = 0x013e,
 
104
                PrimaryChromaticities           = 0x013f,
 
105
                        
 
106
                HalftoneHints                   = 0x0141,
 
107
                // Tiled images
 
108
                TileWidth                       = 0x0142,
 
109
                TileLength                      = 0x0143,
 
110
                TileOffsets                     = 0x0144,
 
111
                TileByteCounts                  = 0x0145,
 
112
 
 
113
                SubIFDs                         = 0x014a, // TIFF-EP
 
114
 
 
115
                // CMYK images
 
116
                InkSet                          = 0x014c,
 
117
                NumberOfInks                    = 0x014e,
 
118
                InkNames                        = 0x014d,
 
119
                DotRange                        = 0x0150,
 
120
                TargetPrinter                   = 0x0151,
 
121
                ExtraSamples                    = 0x0152,
 
122
                SampleFormat                    = 0x0153,
 
123
                SMinSampleValue                 = 0x0154,
 
124
                SMaxSampleValue                 = 0x0155,
 
125
                
 
126
                TransferRange                   = 0x0156,
 
127
                
 
128
                ClipPath                        = 0x0157, // TIFF PageMaker Technote #2.
 
129
                
 
130
                JPEGTables                      = 0x015b, // TIFF-EP
 
131
                
 
132
                JPEGProc                        = 0x0200,
 
133
                JPEGInterchangeFormat           = 0x0201,
 
134
                JPEGInterchangeFormatLength     = 0x0202,
 
135
                JPEGRestartInterval             = 0x0203,
 
136
                JPEGLosslessPredictors          = 0x0205,
 
137
                JPEGPointTransforms             = 0x0206,
 
138
                JPEGQTables                     = 0x0207,
 
139
                JPEGDCTables                    = 0x0208,
 
140
                JPEGACTables                    = 0x0209,
 
141
 
 
142
                YCbCrCoefficients               = 0x0211,
 
143
                YCbCrSubSampling                = 0x0212,
 
144
                YCbCrPositioning                = 0x0213,
 
145
 
 
146
                ReferenceBlackWhite             = 0x0214,
 
147
                RelatedImageFileFormat          = 0x1000,
 
148
                RelatedImageWidth               = 0x1001,
 
149
                RelatedImageLength              = 0x1002,
 
150
                CFARepeatPatternDim             = 0x828d,
 
151
                CFAPattern                      = 0x828e,
 
152
                BatteryLevel                    = 0x828f,
 
153
                Copyright                       = 0x8298,
 
154
                ExposureTime                    = 0x829a,
 
155
                FNumber                         = 0x829d,
 
156
 
 
157
                // These are from the NIFF spec and only really valid when the header begins with IIN1
 
158
                // see the NiffTag enum for the specifcation specific names
 
159
                        Rotation                        = 0x82b9,
 
160
                        NavyCompression                 = 0x82ba,
 
161
                        TileIndex                       = 0x82bb,
 
162
                // end NIFF specific
 
163
                        
 
164
                IPTCNAA                         = 0x83bb,
 
165
 
 
166
                PhotoshopPrivate                = 0x8649,
 
167
 
 
168
                ExifIfdPointer                  = 0x8769,
 
169
                InterColorProfile               = 0x8773,
 
170
                ExposureProgram                 = 0x8822,
 
171
                SpectralSensitivity             = 0x8824,
 
172
                GPSInfoIfdPointer               = 0x8825,
 
173
                ISOSpeedRatings                 = 0x8827,
 
174
                OECF                            = 0x8828,
 
175
                ExifVersion                     = 0x9000,
 
176
                DateTimeOriginal                = 0x9003,
 
177
                DateTimeDigitized               = 0x9004,
 
178
                ComponentsConfiguration         = 0x9101,
 
179
                CompressedBitsPerPixel          = 0x9102,
 
180
                ShutterSpeedValue               = 0x9201,
 
181
                ApertureValue                   = 0x9202,
 
182
                BrightnessValue                 = 0x9203,
 
183
                ExposureBiasValue               = 0x9204,
 
184
                MaxApertureValue                = 0x9205,
 
185
                SubjectDistance                 = 0x9206,
 
186
                MeteringMode                    = 0x9207,
 
187
                LightSource                     = 0x9208,
 
188
                Flash                           = 0x9209,
 
189
                FocalLength                     = 0x920a,
 
190
                        
 
191
                FlashEnergy_TIFFEP              = 0x920b,// TIFF-EP 
 
192
                SpacialFrequencyResponse        = 0x920c,// TIFF-EP 
 
193
                Noise                           = 0x920d,// TIFF-EP 
 
194
                FocalPlaneXResolution_TIFFEP    = 0x920e,// TIFF-EP 
 
195
                FocalPlaneYResolution_TIFFEP    = 0x920f,// TIFF-EP 
 
196
                FocalPlaneResolutionUnit_TIFFEP = 0x9210,// TIFF-EP 
 
197
                ImageName                       = 0x9211,// TIFF-EP 
 
198
                SecurityClassification          = 0x9212,// TIFF-EP 
 
199
                
 
200
                ImageHistory                    = 0x9213, // TIFF-EP null separated list
 
201
 
 
202
                SubjectArea                     = 0x9214,
 
203
 
 
204
                ExposureIndex_TIFFEP            = 0x9215, // TIFF-EP
 
205
                TIFFEPStandardID                = 0x9216, // TIFF-EP
 
206
                SensingMethod_TIFFEP            = 0x9217, // TIFF-EP
 
207
                        
 
208
                MakerNote                       = 0x927c,
 
209
                UserComment                     = 0x9286,
 
210
                SubSecTime                      = 0x9290,
 
211
                SubSecTimeOriginal              = 0x9291,
 
212
                SubSecTimeDigitized             = 0x9292,
 
213
                FlashPixVersion                 = 0xa000,
 
214
                ColorSpace                      = 0xa001,
 
215
                PixelXDimension                 = 0xa002,
 
216
                PixelYDimension                 = 0xa003,
 
217
                RelatedSoundFile                = 0xa004,
 
218
                InteroperabilityIfdPointer      = 0xa005,
 
219
                FlashEnergy                     = 0xa20b,
 
220
                SpatialFrequencyResponse        = 0xa20c,
 
221
                FocalPlaneXResolution           = 0xa20e,
 
222
                FocalPlaneYResolution           = 0xa20f,
 
223
                FocalPlaneResolutionUnit        = 0xa210,
 
224
                SubjectLocation                 = 0xa214,
 
225
                ExposureIndex                   = 0xa215,
 
226
                SensingMethod                   = 0xa217,
 
227
                FileSource                      = 0xa300,
 
228
                SceneType                       = 0xa301,
 
229
                ExifCFAPattern                  = 0xa302,
 
230
                CustomRendered                  = 0xa401,
 
231
                ExposureMode                    = 0xa402,
 
232
                WhiteBalance                    = 0xa403,
 
233
                DigitalZoomRatio                = 0xa404,
 
234
                FocalLengthIn35mmFilm           = 0xa405,
 
235
                SceneCaptureType                = 0xa406,
 
236
                GainControl                     = 0xa407,
 
237
                Contrast                        = 0xa408,
 
238
                Saturation                      = 0xa409,
 
239
                Sharpness                       = 0xa40a,
 
240
                DeviceSettingDescription        = 0xa40b,
 
241
                SubjectDistanceRange            = 0xa40c,
 
242
                ImageUniqueId                   = 0xa420,
 
243
 
 
244
                // The Following IDs are not described the EXIF spec
 
245
                Gamma                           = 0xa500,
 
246
 
 
247
                // The XMP spec declares that XMP data should live 0x2bc when
 
248
                // embedded in tiff images.
 
249
                XMP                             = 0x02bc,
 
250
                
 
251
                // from the dng spec
 
252
                DNGVersion                      = 0xc612, // Ifd0
 
253
                DNGBackwardVersion              = 0xc613, // Ifd0
 
254
                UniqueCameraModel               = 0xc614, // Ifd0
 
255
                LocalizedCameraModel            = 0xc615, // Ifd0
 
256
                CFAPlaneColor                   = 0xc616, // RawIfd
 
257
                CFALayout                       = 0xc617, // RawIfd
 
258
                LinearizationTable              = 0xc618, // RawIfd
 
259
                BlackLevelRepeatDim             = 0xc619, // RawIfd
 
260
                BlackLevel                      = 0xc61a, // RawIfd
 
261
                BlackLevelDeltaH                = 0xc61b, // RawIfd
 
262
                BlackLevelDeltaV                = 0xc61c, // RawIfd
 
263
                WhiteLevel                      = 0xc61d, // RawIfd
 
264
                DefaultScale                    = 0xc61e, // RawIfd             
 
265
                DefaultCropOrigin               = 0xc61f, // RawIfd
 
266
                DefaultCropSize                 = 0xc620, // RawIfd
 
267
                ColorMatrix1                    = 0xc621, // Ifd0
 
268
                ColorMatrix2                    = 0xc622, // Ifd0
 
269
                CameraCalibration1              = 0xc623, // Ifd0
 
270
                CameraCalibration2              = 0xc624, // Ifd0
 
271
                ReductionMatrix1                = 0xc625, // Ifd0
 
272
                ReductionMatrix2                = 0xc626, // Ifd0
 
273
                AnalogBalance                   = 0xc627, // Ifd0
 
274
                AsShotNetural                   = 0xc628, // Ifd0
 
275
                AsShotWhiteXY                   = 0xc629, // Ifd0
 
276
                BaselineExposure                = 0xc62a, // Ifd0
 
277
                BaselineNoise                   = 0xc62b, // Ifd0
 
278
                BaselineSharpness               = 0xc62c, // Ifd0
 
279
                BayerGreeSpit                   = 0xc62d, // Ifd0
 
280
                LinearResponseLimit             = 0xc62e, // Ifd0
 
281
                CameraSerialNumber              = 0xc62f, // Ifd0
 
282
                LensInfo                        = 0xc630, // Ifd0
 
283
                ChromaBlurRadius                = 0xc631, // RawIfd
 
284
                AntiAliasStrength               = 0xc632, // RawIfd
 
285
                DNGPrivateData                  = 0xc634, // Ifd0
 
286
                
 
287
                MakerNoteSafety                 = 0xc635, // Ifd0
 
288
 
 
289
                // The Spec says BestQualityScale is 0xc635 but it appears to be wrong
 
290
                //BestQualityScale                = 0xc635, // RawIfd 
 
291
                BestQualityScale                = 0xc63c, // RawIfd  this looks like the correct value
 
292
 
 
293
                CalibrationIlluminant1          = 0xc65a, // Ifd0
 
294
                CalibrationIlluminant2          = 0xc65b, // Ifd0
 
295
                
 
296
                // Print Image Matching data
 
297
                PimIfdPointer                   = 0xc4a5
 
298
        }
 
299
 
 
300
        public struct SRational {
 
301
                public int Numerator;
 
302
                public int Denominator;
 
303
                
 
304
                public SRational (byte [] raw_data, int offset, bool little)
 
305
                {
 
306
                        Numerator = BitConverter.ToInt32 (raw_data, offset, little);
 
307
                        Denominator = BitConverter.ToInt32 (raw_data, offset, little);
 
308
                }
 
309
 
 
310
                public SRational (int numerator, int denominator)
 
311
                {
 
312
                        Numerator = numerator;
 
313
                        Denominator = denominator;
 
314
                }
 
315
 
 
316
                public static SRational BitwiseCopy (Rational rational)
 
317
                {
 
318
                        SRational result;
 
319
 
 
320
                        result.Numerator = unchecked ((int) rational.Numerator);
 
321
                        result.Denominator = unchecked ((int) rational.Denominator);
 
322
                        return result;
 
323
                }
 
324
 
 
325
                public override string ToString ()
 
326
                {
 
327
                        if (Numerator == 0 || Denominator == 0)
 
328
                                return String.Format ("{0}/{1}", Numerator, Denominator);
 
329
                        else if (Numerator % Denominator == 0)
 
330
                                return String.Format ("{0}", Numerator / Denominator);
 
331
                        else if (Denominator % Numerator == 0)
 
332
                                return String.Format ("1/{0}", Denominator / Numerator);
 
333
 
 
334
                        return String.Format ("{0}/{1}", Numerator, Denominator);
 
335
                }
 
336
                
 
337
                public double Value {
 
338
                        get {
 
339
                                return Numerator / (double)Denominator;
 
340
                        }
 
341
                }
 
342
        }
 
343
 
 
344
        public struct Rational {
 
345
                public uint Numerator;
 
346
                public uint Denominator;
 
347
 
 
348
                public Rational (uint numerator, uint denominator)
 
349
                {
 
350
                        Numerator = numerator;
 
351
                        Denominator = denominator;
 
352
                }
 
353
                
 
354
                public Rational (string value)
 
355
                {
 
356
                        string [] vals = value.Split ('/');
 
357
                        if (vals.Length == 2) {
 
358
                                this.Numerator = UInt32.Parse (vals [0]);
 
359
                                this.Denominator = UInt32.Parse (vals [1]);
 
360
                                return;
 
361
                        } if (vals.Length == 1) {
 
362
                                double tmp = Double.Parse (value);
 
363
                                this.Numerator = (uint) (tmp * 100000);
 
364
                                this.Denominator = 100000;
 
365
                        } else
 
366
                                throw new ParseException ("unable to parse rational value");
 
367
                }
 
368
 
 
369
                public override string ToString ()
 
370
                {
 
371
                        if (Numerator == 0 || Denominator == 0)
 
372
                                return String.Format ("{0}/{1}", Numerator, Denominator);
 
373
                        else if (Numerator % Denominator == 0)
 
374
                                return String.Format ("{0}", Numerator / Denominator);
 
375
                        else if (Denominator % Numerator == 0)
 
376
                                return String.Format ("1/{0}", Denominator / Numerator);
 
377
 
 
378
                        return String.Format ("{0}/{1}", Numerator, Denominator);
 
379
                }
 
380
                
 
381
                public double Value {
 
382
                        get {
 
383
                                return Numerator / (double)Denominator;
 
384
                        }
 
385
                }
 
386
        }
 
387
 
 
388
        struct UserComment {
 
389
                string Charset;
 
390
                public string Value;
 
391
 
 
392
                public UserComment (string value)
 
393
                {
 
394
                        Charset = null;
 
395
                        Value = value;
 
396
                }
 
397
 
 
398
                public UserComment (byte [] raw_data, bool little)
 
399
                {
 
400
                        if (raw_data.Length == 8 || raw_data.Length == 0) { 
 
401
                                Charset = null;
 
402
                                Value = String.Empty;
 
403
                                return;
 
404
                        } else if (raw_data.Length < 8) {
 
405
                                throw new Exception ("Invalid UserComment value, no charset found");
 
406
                        }
 
407
 
 
408
                        string charset = System.Text.Encoding.ASCII.GetString (raw_data, 0, 8);
 
409
                        System.Text.Encoding enc;
 
410
 
 
411
                        switch (charset) {
 
412
                        case "ASCII\0\0\0":
 
413
                                enc = System.Text.Encoding.ASCII;
 
414
                                break;
 
415
                        case "UNICODE\0":
 
416
                        case "Unicode\0":
 
417
                                enc = new System.Text.UnicodeEncoding (! little, true);
 
418
                                break;
 
419
                        case "JIS\0\0\0\0\0":
 
420
                                // FIXME this requires mono locale extras
 
421
                                try {
 
422
                                        enc = System.Text.Encoding.GetEncoding ("euc-jp");
 
423
                                } catch {
 
424
                                        //System.Console.WriteLine ("missing jis0208 encoding");
 
425
                                        enc = System.Text.Encoding.Default;
 
426
                                }
 
427
                                break;
 
428
                        case "\0\0\0\0\0\0\0\0":
 
429
                                // FIXME the spec says to use the local encoding in this case, we could probably
 
430
                                // do something smarter, but whatever.
 
431
                                enc = System.Text.Encoding.Default;
 
432
                                break;
 
433
                        default:
 
434
                                enc = null;
 
435
                                throw new ParseException (System.String.Format ("Invalid charset name: {0}", charset));
 
436
                        }
 
437
 
 
438
                        Charset = charset;
 
439
                        // for (int i = 0; i < raw_data.Length; i++)
 
440
                        //      System.Console.WriteLine ("{0} - \"{1}\"", raw_data [i].ToString ("x"), raw_data [i]);
 
441
                        Value = enc.GetString (raw_data, 8, raw_data.Length - 8).Trim('\0');
 
442
                }
 
443
 
 
444
                public byte [] GetBytes (bool is_little)
 
445
                {
 
446
                        bool ascii = true;
 
447
                        string description = Value;
 
448
                        System.Text.Encoding enc;
 
449
                        string heading;
 
450
 
 
451
                        for (int i = 0; i < description.Length; i++) {
 
452
                                if (description [i] > 127) {
 
453
                                        ascii = false;
 
454
                                        break;
 
455
                                }
 
456
                        }
 
457
 
 
458
                        if (ascii) {
 
459
                                heading = "ASCII\0\0\0";
 
460
                                enc = new System.Text.ASCIIEncoding ();
 
461
                        } else {
 
462
                                heading = "Unicode\0";
 
463
                                enc = new System.Text.UnicodeEncoding (! is_little, true);
 
464
                        }
 
465
                        
 
466
                        int len = enc.GetByteCount (description);
 
467
                        byte [] data = new byte [len + heading.Length];
 
468
                        System.Text.Encoding.ASCII.GetBytes (heading, 0, heading.Length, data, 0);
 
469
                        enc.GetBytes (Value, 0, Value.Length, data, heading.Length);
 
470
                        
 
471
                        UserComment c = new UserComment (data, is_little);
 
472
                        //System.Console.WriteLine ("old = \"{0}\" new = \"{1}\" heading = \"{2}\"", c.Value, description, heading);
 
473
                        return data;
 
474
                }
 
475
 
 
476
                public override string ToString ()
 
477
                {
 
478
                        return String.Format ("({0},charset={1})", Value, Charset);
 
479
                }
 
480
        }
 
481
 
 
482
        public struct CFAPattern {
 
483
                public ushort Rows;
 
484
                public ushort Columns;
 
485
                public byte [] Values;
 
486
 
 
487
                public CFAPattern (byte [] raw_data, bool little)
 
488
                {
 
489
                        Columns = BitConverter.ToUInt16 (raw_data, 0, little);
 
490
                        Rows = BitConverter.ToUInt16 (raw_data, 2, little);
 
491
 
 
492
                        Values = new byte [Rows * Columns];
 
493
                        // FIXME the contents here may differ from what we
 
494
                        // expect, but for now we won't throw an exception
 
495
                        // since the entry still may be a valid structure.
 
496
                        if (raw_data.Length -4 < Values.Length)
 
497
                                System.Array.Copy (raw_data, 4, Values, 0, Values.Length);
 
498
                }
 
499
 
 
500
                /* 
 
501
                 * Note the Exif spec defines a CFA pattern tag that includes the row and column counts as shorts
 
502
                 * inside the first four bytes of the entry.  The Tiff-EP standard define the CFARepeatPattern tag
 
503
                 * that contains the row and column counts presumably since the Exif version wouldn't allow you to
 
504
                 * alter the endian of the file without knowing the tag layout.
 
505
                 */
 
506
                
 
507
                public CFAPattern (ushort rows, ushort cols, byte [] raw_data,  bool little)
 
508
                {
 
509
                        Columns = rows;
 
510
                        Rows = cols;
 
511
                        Values = new byte [rows * cols];
 
512
                        System.Array.Copy (raw_data, 0, Values, 0, Values.Length);
 
513
                }
 
514
        }
 
515
 
 
516
        public struct OECFTable {
 
517
                public ushort Rows;
 
518
                public ushort Columns;
 
519
                public string [] Names;
 
520
                public SRational [] Values;
 
521
                
 
522
                public OECFTable (byte [] raw_data, bool little)
 
523
                {
 
524
                        Columns = BitConverter.ToUInt16 (raw_data, 0, little);
 
525
                        Rows = BitConverter.ToUInt16 (raw_data, 2, little);
 
526
                        Names = new string [Columns];
 
527
                        Values = new SRational [Columns * Rows];
 
528
 
 
529
                        int pos = 2;
 
530
                        int i;
 
531
                        int type_size = DirectoryEntry.GetTypeSize (EntryType.SRational);
 
532
 
 
533
                        for (i = 0; i < Names.Length; i++)
 
534
                                Names [i] = ReadString (raw_data, ref pos);
 
535
                        
 
536
                        for (i = 0; i < Values.Length; i++)
 
537
                                Values [i] = new SRational (raw_data, pos + i * type_size, little);
 
538
                        
 
539
                }
 
540
 
 
541
                public string ReadString (byte [] data, ref int pos)
 
542
                {
 
543
                        int start = pos;
 
544
                        for (; pos < data.Length; pos++) {
 
545
                                if (data [pos] == 0)
 
546
                                        break;
 
547
                        }       
 
548
                        return System.Text.Encoding.ASCII.GetString (data, start, pos - start);
 
549
                }
 
550
        }
 
551
 
 
552
        public enum ExtraSamples {
 
553
                Unspecified = 0,
 
554
                AssociatedAlpha = 1,
 
555
                UnassociatedAlpa = 2
 
556
        }
 
557
 
 
558
        public enum FileSource {
 
559
                DCF = 3,
 
560
        }
 
561
 
 
562
        public enum PhotometricInterpretation : ushort {
 
563
                WhiteIsZero = 0,
 
564
                BlackIsZero = 1,
 
565
                RGB = 2,
 
566
                PaletteColor = 3,
 
567
                TransparencyMask = 4,
 
568
                Separated = 5,  // CMYK
 
569
                YCbCr = 6,
 
570
                CIELab = 8,
 
571
                ICCLab = 9,
 
572
                ITULab = 10,
 
573
                LogL = 32844, // Log Luminance
 
574
                LogLUV = 32845,
 
575
                ColorFilterArray = 32803,  // ColorFilterArray... the good stuff
 
576
                LinearRaw = 34892  // DBG LinearRaw
 
577
        }
 
578
 
 
579
        public enum PlanarConfiguration {
 
580
                Chunky = 1,
 
581
                Planar = 2
 
582
        }
 
583
        
 
584
        public enum Compression {
 
585
                Packed = 1,
 
586
                Huffman = 2,
 
587
                T4 = 3,
 
588
                T6 = 4,
 
589
                LZW = 5,
 
590
                JPEG = 6,
 
591
                JPEGStream = 7,  // TIFF-EP stores full jpeg stream 
 
592
                Deflate = 8,
 
593
                JBIG = 9,
 
594
                JBIG_MRC,
 
595
                PackBits = 32773,
 
596
                NikonCompression = 34713,
 
597
                Deflate_experimental = 0x80b2
 
598
        }
 
599
 
 
600
        public enum JPEGProc {
 
601
                BaselineSequencial = 1,
 
602
                LosslessHuffman = 14,
 
603
        }
 
604
 
 
605
        public enum SubfileType {
 
606
                FullResolution = 1,
 
607
                ReducedResolution = 2,
 
608
                PageOfMultipage = 3
 
609
        }
 
610
        
 
611
        public enum ExposureProgram {
 
612
                NotDefined = 0,
 
613
                Manual = 1,
 
614
                NormalProgram = 2,  // Normal Program
 
615
                AperturePriority = 3, // Aperture priority
 
616
                ShutterPriorty = 4, // Shutter priority
 
617
                CreativeProgram = 5, // Creative program
 
618
                ActionProgram = 6, // Action program
 
619
                PortraitMode = 7, // Portrait mode
 
620
                LandscapeMode = 8 // Landscape mode
 
621
        }
 
622
 
 
623
        public enum ExposureMode {
 
624
                Auto = 0,
 
625
                Manual = 1,
 
626
                AutoBracket = 2
 
627
        }
 
628
 
 
629
        public enum CustomRendered : ushort {
 
630
                Normal = 0,
 
631
                Custom = 1
 
632
        }
 
633
 
 
634
        public enum SceneType {
 
635
                DirectlyPhotographed = 1
 
636
        }
 
637
 
 
638
        public enum MeteringMode {
 
639
                Uknown = 0,
 
640
                Average = 1,
 
641
                CenterWeightedAverage = 2,
 
642
                Spot = 3,
 
643
                MulitSpot = 4,
 
644
                Pattern = 5,
 
645
                Partial = 6,
 
646
        }
 
647
 
 
648
        public enum SceneCaptureType : ushort {
 
649
                Standard = 0,
 
650
                Landscape = 1,
 
651
                Portrait = 2,
 
652
                NightScene = 3
 
653
        }
 
654
 
 
655
        public enum GainControl : ushort {
 
656
                None = 0,
 
657
                LowGainUp = 1,
 
658
                HighGainUp = 2,
 
659
                LowGainDown = 3,
 
660
                HighGainDown = 4
 
661
        }
 
662
 
 
663
        public enum Contrast : ushort {
 
664
                Normal = 0,
 
665
                Soft = 1,
 
666
                Hard = 2
 
667
        }
 
668
        
 
669
        public enum Saturation : ushort {
 
670
                Normal = 0,
 
671
                Low = 1,
 
672
                High = 2
 
673
        }
 
674
 
 
675
        public enum WhiteBalance : ushort {
 
676
                Auto = 0,
 
677
                Manual = 1
 
678
        }
 
679
 
 
680
        public enum Sharpness : ushort {
 
681
                Normal = 0,
 
682
                Soft = 1,
 
683
                Hard = 2
 
684
        }
 
685
 
 
686
        public enum LightSource {
 
687
                Unknown = 0,
 
688
                Daylight = 1,
 
689
                Fluorescent = 2,
 
690
                Tungsten = 3,
 
691
                Fash = 4,
 
692
                FineWeather = 9,
 
693
                CloudyWeather = 10,
 
694
                Shade = 11,
 
695
                DaylightFluorescent = 12,
 
696
                DaylightWhiteFluorescent = 13,
 
697
                CoolWhiteFluorescent = 14,
 
698
                WhiteFluorescent = 15,
 
699
                StandardLightA = 17,
 
700
                StandardLightB = 18,
 
701
                StandardLightC = 19,
 
702
                D55 = 20,
 
703
                D65 = 21,
 
704
                D75 = 22,
 
705
                D50 = 23,
 
706
                ISOStudioTungsten = 24,
 
707
                OtherSource = 255
 
708
        }
 
709
 
 
710
        public enum ColorSpace {
 
711
                StandardRGB = 1,  // sRGB
 
712
                AdobeRGB = 2,
 
713
                Uncalibrated = 0xffff
 
714
        }
 
715
 
 
716
        public enum ComponentsConfiguration {
 
717
                DoesNotExist = 0,
 
718
                Y = 1,
 
719
                Cb = 2,
 
720
                Cr = 3,
 
721
                R = 4,
 
722
                G = 6,
 
723
        }
 
724
 
 
725
        public enum ResolutionUnit : ushort {
 
726
                Uncalibrated = 1,
 
727
                Inch = 2,
 
728
                Centimeter = 3
 
729
        }
 
730
        
 
731
        public enum SensingMethod : short {
 
732
                NotDefined = 1,
 
733
                OneChipColorAreaSensor = 2,
 
734
                TwoChipColorAreaSensor = 3,
 
735
                ThreeChipColorAreaSensor = 4,
 
736
                ColorSequentialAreaSensor = 5,
 
737
                TrilinearSensor = 7,
 
738
                ColorSequentialLinearSensor = 8
 
739
        }
 
740
 
 
741
        [System.Flags]
 
742
        public enum NewSubfileType : uint {
 
743
                ReducedResolution = 1,
 
744
                PageOfMultipage= 1 << 1,
 
745
                TransparencyMask = 1 << 2
 
746
        }
 
747
 
 
748
        public enum EntryType {
 
749
                Byte = 1,
 
750
                Ascii,
 
751
                Short,
 
752
                Long,
 
753
                Rational,
 
754
                SByte,
 
755
                Undefined,
 
756
                SShort,
 
757
                SLong,
 
758
                SRational,
 
759
                Float,
 
760
                Double,
 
761
                Ifd // TIFF-EP - TIFF PageMaker TechnicalNote 2
 
762
        }
 
763
        
 
764
        public class Tag {
 
765
                public ushort Id;
 
766
                public EntryType Type;
 
767
                public int Count;
 
768
                public string Name;
 
769
                public string Description;
 
770
        }
 
771
 
 
772
        public class CanonTag : Tag {
 
773
                // http://www.gvsoft.homedns.org/exif/makernote-canon.html
 
774
                
 
775
                public enum CanonId {
 
776
                        Unknown1           = 0x0000,
 
777
                        CameraSettings1    = 0x0001,
 
778
                        Unknown2           = 0x0003,
 
779
                        CameraSettings2    = 0x0004,
 
780
                        ImageType          = 0x0006,
 
781
                        FirmwareVersion    = 0x0007,
 
782
                        ImageNumber        = 0x0008,
 
783
                        OwnerName          = 0x0009,
 
784
                        Unknown3           = 0x000a,
 
785
                        CameraSerialNumber = 0x000c,
 
786
                        Unknown4           = 0x000d,
 
787
                        CustomFunctions    = 0x000f
 
788
                }
 
789
                
 
790
                public CanonTag (CanonId id, EntryType type, int count, string name, string description)
 
791
                {
 
792
                        this.Id = (ushort)id;
 
793
                        this.Type = type;
 
794
                        this.Count = count;
 
795
                        this.Name = name;
 
796
                        this.Description = description;
 
797
                }
 
798
 
 
799
                public static System.Collections.Hashtable Tags;
 
800
 
 
801
                static CanonTag () {
 
802
                        CanonTag [] tags = { 
 
803
                                new CanonTag (CanonId.Unknown1, EntryType.Short, 6, null, null),
 
804
                                new CanonTag (CanonId.CameraSettings1, EntryType.Short, -1, "Camera Settings 1", "First Canon MakerNote settings section"),
 
805
                                new CanonTag (CanonId.Unknown2, EntryType.Short, 4, null, null),                                
 
806
                                new CanonTag (CanonId.CameraSettings2, EntryType.Short, -1, "Camera Settings 2", "Second Canon MakerNote settings section"),
 
807
                                new CanonTag (CanonId.ImageType, EntryType.Ascii, 32, "Image Type", null), // FIXME description
 
808
                                new CanonTag (CanonId.FirmwareVersion, EntryType.Ascii, 24, "Firmware Version", "Version of the firmware installed on the camera"),
 
809
                                new CanonTag (CanonId.ImageNumber, EntryType.Long, 1, "Image Number", null), // FIXME description
 
810
                                new CanonTag (CanonId.OwnerName, EntryType.Long, 32, "Owner Name", "Name of the Camera Owner"), // FIXME description
 
811
                                new CanonTag (CanonId.Unknown4, EntryType.Short, -1, null, null),                               
 
812
                                new CanonTag (CanonId.CameraSerialNumber, EntryType.Short, 1, "Serial Number", null), //FIXME description
 
813
                                new CanonTag (CanonId.Unknown4, EntryType.Short, -1, null, null),                               
 
814
                                new CanonTag (CanonId.CustomFunctions, EntryType.Short, -1, "Custom Functions", "Camera Custom Functions")
 
815
                        };
 
816
                                         
 
817
                        foreach (CanonTag tag in tags)
 
818
                                Tags [tag.Id] = tag;
 
819
                }
 
820
 
 
821
        }
 
822
        
 
823
        public enum Endian {
 
824
                Big,
 
825
                Little
 
826
        }
 
827
 
 
828
        public class ParseException : System.Exception 
 
829
        {
 
830
                public ParseException (string msg) : base (msg)
 
831
                {
 
832
                }
 
833
        }
 
834
 
 
835
        public class ShortReadException : ParseException 
 
836
        {
 
837
                public ShortReadException () : base ("Short Read")
 
838
                {
 
839
                }
 
840
        }
 
841
 
 
842
        public class Converter {
 
843
                public static uint ReadUInt (System.IO.Stream stream, Endian endian)
 
844
                {
 
845
                        byte [] tmp = new byte [4];
 
846
 
 
847
                        if (stream.Read (tmp, 0, tmp.Length) < 4) {
 
848
#if DEBUG_LOADER
 
849
                                System.Console.WriteLine ("short read XXXXXXXXXXXXXXXXXXXXXXx");
 
850
#endif
 
851
                                throw new ShortReadException ();
 
852
                        }
 
853
                        return BitConverter.ToUInt32 (tmp, 0, endian == Endian.Little);
 
854
                }
 
855
 
 
856
                public static ushort ReadUShort (System.IO.Stream stream, Endian endian)
 
857
                {
 
858
                        byte [] tmp = new byte [2];
 
859
 
 
860
                        if (stream.Read (tmp, 0, tmp.Length) < 2) {
 
861
#if DEBUG_LOADER
 
862
                                System.Console.WriteLine ("Short read");
 
863
#endif
 
864
                                throw new ShortReadException ();
 
865
                        }
 
866
 
 
867
                        return BitConverter.ToUInt16 (tmp, 0, endian == Endian.Little);
 
868
                }
 
869
        }
 
870
 
 
871
        public class Header : SemWeb.StatementSource {
 
872
                public Endian endian;
 
873
 
 
874
                private uint directory_offset;
 
875
                public ImageDirectory Directory;
 
876
 
 
877
                // false seems a safe default
 
878
                public bool Distinct {
 
879
                        get { return false; }
 
880
                }
 
881
 
 
882
                public Header (System.IO.Stream stream)
 
883
                {
 
884
                        //using (new Timer ("new Tiff.Header")) {
 
885
                        byte [] data = new byte [8];
 
886
                        stream.Read (data, 0, data.Length);
 
887
                        if (data [0] == 'M' && data [1] == 'M')
 
888
                                endian = Endian.Big;
 
889
                        else if (data [0] == 'I' && data [1] == 'I')
 
890
                                endian = Endian.Little;
 
891
 
 
892
                        ushort marker = BitConverter.ToUInt16 (data, 2, endian == Endian.Little);
 
893
                        switch (marker) {
 
894
                        case 42:
 
895
                                //System.Console.WriteLine ("Found Standard Tiff Marker {0}", marker);
 
896
                                break;
 
897
                        case 0x4f52:
 
898
                                //System.Console.WriteLine ("Found Olympus Tiff Marker {0}", marker.ToString ("x"));
 
899
                                break;
 
900
                        case 0x4e31:
 
901
                                //System.Console.WriteLine ("Found Navy Interchange File Format Tiff Marker {0}", marker.ToString ("x")); 
 
902
                                break;
 
903
                        default:
 
904
                                //System.Console.WriteLine ("Found Unknown Tiff Marker {0}", marker.ToString ("x"));
 
905
                                break;
 
906
                        }
 
907
 
 
908
                        //System.Console.WriteLine ("Converting Something");
 
909
                        directory_offset = BitConverter.ToUInt32 (data, 4, endian == Endian.Little);
 
910
                        
 
911
                        if (directory_offset < 8)
 
912
                                throw new ParseException ("Invalid IFD0 Offset [" + directory_offset.ToString () + "]"); 
 
913
                        
 
914
#if DEBUG_LOADER
 
915
                        System.Console.WriteLine ("Reading First IFD");
 
916
#endif
 
917
                        Directory = new ImageDirectory (stream, directory_offset, endian); 
 
918
                        //}
 
919
                }
 
920
                
 
921
                
 
922
                public void Select (SemWeb.StatementSink sink)
 
923
                {
 
924
                        //using (new Timer ("Tiff.Header.Select")) {
 
925
                                SelectDirectory (Directory, sink);
 
926
                        //}
 
927
                }
 
928
 
 
929
                public void SelectDirectory (ImageDirectory dir, StatementSink sink)
 
930
                {
 
931
                        foreach (DirectoryEntry e in dir.Entries) {
 
932
#if DEBUG_LOADER
 
933
                                System.Console.WriteLine ("{0}", e.Id);
 
934
#endif
 
935
                                switch (e.Id) {
 
936
                                case TagId.IPTCNAA:
 
937
                                        System.IO.Stream iptcstream = new System.IO.MemoryStream (e.RawData);
 
938
                                        FSpot.Iptc.IptcFile iptc = new FSpot.Iptc.IptcFile (iptcstream);
 
939
                                        iptc.Select (sink);
 
940
                                        break;
 
941
                                case TagId.PhotoshopPrivate:
 
942
                                        System.IO.Stream bimstream = new System.IO.MemoryStream (e.RawData);
 
943
                                        FSpot.Bim.BimFile bim = new FSpot.Bim.BimFile (bimstream);
 
944
                                        bim.Select (sink);
 
945
                                        break;
 
946
                                case TagId.XMP:
 
947
                                        System.IO.Stream xmpstream = new System.IO.MemoryStream (e.RawData);
 
948
                                        FSpot.Xmp.XmpFile xmp = new FSpot.Xmp.XmpFile (xmpstream);
 
949
                                        xmp.Select (sink);
 
950
                                        break;
 
951
                                case TagId.ImageDescription:
 
952
                                        MetadataStore.AddLiteral (sink, "dc:description", "rdf:Alt", 
 
953
                                                                  new SemWeb.Literal (e.ValueAsString [0], "x-default", null));
 
954
                                        break;
 
955
                                case TagId.UserComment:
 
956
                                        MetadataStore.AddLiteral (sink, "exif:UserComment", "rdf:Alt", 
 
957
                                                                  new SemWeb.Literal (e.ValueAsString [0], "x-default", null));
 
958
                                        break;
 
959
                                case TagId.Copyright:
 
960
                                        MetadataStore.AddLiteral (sink, "dc:rights", "rdf:Alt", 
 
961
                                                                  new SemWeb.Literal (e.ValueAsString [0], "x-default", null));
 
962
                                        break;
 
963
                                case TagId.Artist:
 
964
                                        MetadataStore.Add (sink, "dc:creator", "rdf:Seq", e.ValueAsString);
 
965
                                        break;
 
966
                                case TagId.ExifIfdPointer:
 
967
                                        try {
 
968
                                                ImageDirectory sub = ((SubdirectoryEntry)e).Directory [0];
 
969
                                                SelectDirectory (sub, sink);
 
970
                                        } catch (System.Exception exc) {
 
971
                                                //System.Console.WriteLine (exc);
 
972
                                        }
 
973
                                        break;
 
974
                                case TagId.Software:
 
975
                                        MetadataStore.AddLiteral (sink, "xmp:CreatorTool", e.ValueAsString [0]);
 
976
                                        break;
 
977
                                case TagId.DateTime:
 
978
                                        try {
 
979
 
 
980
                                        MetadataStore.AddLiteral (sink, "xmp:ModifyDate", 
 
981
                                                                  e.ValueAsDate.ToString ("yyyy-MM-ddThh:mm:ss"));
 
982
                                        } catch (System.Exception ex) {
 
983
                                                //System.Console.WriteLine (String.Format ("error parsing {0}{2}{1}", e.ValueAsString[0], ex, Environment.NewLine));
 
984
                                        }
 
985
 
 
986
                                        break;
 
987
                                case TagId.DateTimeOriginal:
 
988
                                case TagId.DateTimeDigitized:
 
989
                                        // FIXME subsectime needs to be included in these values
 
990
                                        // FIXME shouldn't DateTimeOriginal be xmp:CreateDate? the spec says no but wtf?
 
991
                                        try {
 
992
                                                MetadataStore.AddLiteral (sink, "exif:" + e.Id.ToString (), 
 
993
                                                                          e.ValueAsDate.ToString ("yyyy-MM-ddThh:mm:ss"));
 
994
                                        } catch (System.Exception ex) {
 
995
                                                //System.Console.WriteLine (String.Format ("error parsing {0}{2}{1}", e.ValueAsString[0], ex, Environment.NewLine));
 
996
                                        }
 
997
                                        break;
 
998
                                        //case TagId.SpatialFrequencyResponse
 
999
                                case TagId.ExifCFAPattern:
 
1000
                                        CFAPattern pattern = new CFAPattern (e.RawData, e.IsLittle);
 
1001
                                        Entity empty = new BNode ();
 
1002
                                        Statement top = new Statement (MetadataStore.FSpotXMPBase, 
 
1003
                                                                       (Entity)MetadataStore.Namespaces.Resolve ("exif:" + e.Id.ToString ()),
 
1004
                                                                       empty);
 
1005
                                        
 
1006
                                        Statement cols = new Statement (empty, 
 
1007
                                                                        (Entity) MetadataStore.Namespaces.Resolve ("exif:Columns"),
 
1008
                                                                        new SemWeb.Literal (pattern.Columns.ToString (), null, null));
 
1009
                                        sink.Add (cols);
 
1010
                                        Statement rows = new Statement (empty, 
 
1011
                                                                        (Entity) MetadataStore.Namespaces.Resolve ("exif:Rows"),
 
1012
                                                                        new SemWeb.Literal (pattern.Rows.ToString (), null, null));
 
1013
                                        sink.Add (rows);
 
1014
                                        string [] vals = e.ArrayToString (pattern.Values);
 
1015
                                        MetadataStore.Add (sink, empty, "exif:Values", "rdf:Seq", vals);
 
1016
                                        sink.Add (top);
 
1017
                                        break;
 
1018
                                case TagId.ExifVersion:
 
1019
                                case TagId.FlashPixVersion:
 
1020
                                case TagId.ColorSpace:
 
1021
                                case TagId.CompressedBitsPerPixel:
 
1022
                                case TagId.PixelYDimension:
 
1023
                                case TagId.PixelXDimension:
 
1024
                                case TagId.RelatedSoundFile:
 
1025
                                case TagId.ExposureTime:
 
1026
                                case TagId.FNumber:
 
1027
                                case TagId.ExposureProgram:
 
1028
                                case TagId.SpectralSensitivity:
 
1029
                                case TagId.ShutterSpeedValue:
 
1030
                                case TagId.ApertureValue:
 
1031
                                case TagId.BrightnessValue:
 
1032
                                case TagId.ExposureBiasValue:
 
1033
                                case TagId.MaxApertureValue:
 
1034
                                case TagId.SubjectDistance:
 
1035
                                case TagId.MeteringMode:
 
1036
                                case TagId.LightSource:
 
1037
                                case TagId.FocalLength:
 
1038
                                case TagId.FlashEnergy:
 
1039
                                case TagId.FocalPlaneXResolution:
 
1040
                                case TagId.FocalPlaneYResolution:
 
1041
                                case TagId.FocalPlaneResolutionUnit:
 
1042
                                case TagId.ExposureIndex:
 
1043
                                case TagId.SensingMethod:
 
1044
                                case TagId.FileSource:
 
1045
                                case TagId.SceneType:
 
1046
                                case TagId.CustomRendered:
 
1047
                                case TagId.ExposureMode:
 
1048
                                case TagId.WhiteBalance:
 
1049
                                case TagId.DigitalZoomRatio:
 
1050
                                case TagId.FocalLengthIn35mmFilm:
 
1051
                                case TagId.SceneCaptureType:
 
1052
                                case TagId.GainControl:
 
1053
                                case TagId.Contrast:
 
1054
                                case TagId.Saturation:
 
1055
                                case TagId.Sharpness:
 
1056
                                        MetadataStore.AddLiteral (sink, "exif:" + e.Id.ToString (), e.ValueAsString [0]);
 
1057
                                        break;
 
1058
                                case TagId.ComponentsConfiguration:
 
1059
                                case TagId.ISOSpeedRatings:
 
1060
                                case TagId.SubjectArea:
 
1061
                                case TagId.SubjectLocation:
 
1062
                                        MetadataStore.Add (sink, "exif:" + e.Id.ToString (), "rdf:Seq", e.ValueAsString);
 
1063
                                        break;
 
1064
                                case TagId.TransferFunction:
 
1065
                                case TagId.YCbCrSubSampling:
 
1066
                                case TagId.WhitePoint:
 
1067
                                case TagId.PrimaryChromaticities:
 
1068
                                case TagId.YCbCrCoefficients:
 
1069
                                case TagId.ReferenceBlackWhite:
 
1070
                                case TagId.BitsPerSample:
 
1071
                                        MetadataStore.Add (sink, "tiff:" + e.Id.ToString (), "rdf:Seq", e.ValueAsString);
 
1072
                                        break;
 
1073
                                case TagId.Orientation:
 
1074
                                case TagId.Compression:
 
1075
                                case TagId.PhotometricInterpretation:                                   
 
1076
                                case TagId.SamplesPerPixel:
 
1077
                                case TagId.PlanarConfiguration:
 
1078
                                case TagId.YCbCrPositioning:
 
1079
                                case TagId.ResolutionUnit:
 
1080
                                case TagId.ImageWidth:
 
1081
                                case TagId.ImageLength:
 
1082
                                case TagId.Model:
 
1083
                                case TagId.Make:
 
1084
                                        try {
 
1085
                                                MetadataStore.AddLiteral (sink, "tiff:" + e.Id.ToString (), e.ValueAsString [0]);
 
1086
                                        } catch (System.Exception ex) {
 
1087
                                                //System.Console.WriteLine (String.Format ("error parsing {0}{2}{1}", e.Id, ex, Environment.NewLine));
 
1088
                                        }
 
1089
                                        break;
 
1090
                                }
 
1091
                        }
 
1092
                }
 
1093
 
 
1094
                public void Save (System.IO.Stream out_stream)
 
1095
                {
 
1096
                        OrderedWriter writer = new OrderedWriter (out_stream, endian == Endian.Little);
 
1097
                        
 
1098
                        /* Header */
 
1099
                        if (endian == Endian.Little) {
 
1100
                                writer.Write ((byte)'I');
 
1101
                                writer.Write ((byte)'I');
 
1102
                        } else {
 
1103
                                writer.Write ((byte)'M');
 
1104
                                writer.Write ((byte)'M');
 
1105
                        }
 
1106
                        
 
1107
                        writer.Write ((ushort)42);
 
1108
                        
 
1109
                        /* First IFD */
 
1110
                        Directory.Save (writer, 8);
 
1111
                }
 
1112
 
 
1113
                public void Dump (string name)
 
1114
                {
 
1115
                        ImageDirectory ifd = Directory;
 
1116
                        for (int i = 0; ifd != null; i++) {
 
1117
                                ifd.Dump (System.String.Format ("IFD[{0}]:", i));
 
1118
                                ifd = ifd.NextDirectory;
 
1119
                        }
 
1120
                }
 
1121
        }
 
1122
 
 
1123
        public class ImageDirectory {
 
1124
                protected Endian endian;
 
1125
                protected ushort num_entries;
 
1126
                protected List<DirectoryEntry> entries;
 
1127
                protected uint orig_position;
 
1128
 
 
1129
                protected uint next_directory_offset;
 
1130
                ImageDirectory next_directory;
 
1131
                
 
1132
                protected bool has_header;
 
1133
                protected bool has_footer;
 
1134
 
 
1135
                public ImageDirectory (System.IO.Stream stream, uint start_position, Endian endian)
 
1136
                {
 
1137
                        this.endian = endian;
 
1138
                        orig_position = start_position;
 
1139
                        Load (stream);
 
1140
                }
 
1141
                
 
1142
                public uint Save (OrderedWriter writer, uint position)
 
1143
                {
 
1144
                        writer.Write (position);
 
1145
                        writer.Stream.Position = position;
 
1146
 
 
1147
                        writer.Write ((ushort)entries.Count);
 
1148
 
 
1149
                        position += 2;
 
1150
                        uint  value_position = (uint) (position + 12 * entries.Count + 4);
 
1151
 
 
1152
                        for (int i = 0; i < entries.Count; i++) {
 
1153
                                writer.Stream.Position = position + (12 * i);
 
1154
                                value_position = (uint)Entries[i].Save (writer, value_position);
 
1155
                        }
 
1156
                                                        
 
1157
                        writer.Stream.Position = position + (12 * entries.Count);
 
1158
                        if (next_directory != null)
 
1159
                                value_position = next_directory.Save (writer, value_position);
 
1160
                        else 
 
1161
                                writer.Write ((uint) 0);
 
1162
 
 
1163
                        return value_position;
 
1164
                }
 
1165
 
 
1166
                protected void Load (System.IO.Stream stream)
 
1167
                {
 
1168
                        ReadHeader (stream);                    
 
1169
                        ReadEntries (stream);
 
1170
                        ReadFooter (stream);
 
1171
                        
 
1172
                        LoadEntries (stream);
 
1173
                        LoadNextDirectory (stream);
 
1174
                }
 
1175
 
 
1176
                public virtual bool ReadHeader (System.IO.Stream stream)
 
1177
                {
 
1178
                        stream.Seek ((long)orig_position, System.IO.SeekOrigin.Begin);
 
1179
                        return true;
 
1180
                }
 
1181
 
 
1182
                protected virtual void ReadEntries (System.IO.Stream stream) 
 
1183
                {
 
1184
                        num_entries = Converter.ReadUShort (stream, endian);
 
1185
#if DEBUG_LOADER
 
1186
                        System.Console.WriteLine ("reading {0} entries", num_entries);
 
1187
#endif                  
 
1188
                        entries = new List<DirectoryEntry> (num_entries);
 
1189
                        int entry_length = num_entries * 12;
 
1190
                        byte [] content = new byte [entry_length];
 
1191
                        
 
1192
                        if (stream.Read (content, 0, content.Length) < content.Length) {
 
1193
#if DEBUG_LOADER
 
1194
                                System.Console.WriteLine ("short read XXXXXXXXXXXXXXXXXXXXXXx");
 
1195
#endif
 
1196
                                throw new ShortReadException ();
 
1197
                        }
 
1198
 
 
1199
 
 
1200
                        for (int pos = 0; pos < entry_length; pos += 12) {
 
1201
                                DirectoryEntry entry = CreateEntry (this, content, pos, this.endian);
 
1202
                                entries.Add (entry);            
 
1203
#if DEBUG_LOADER
 
1204
                                System.Console.WriteLine ("Added Entry {0} {1} - {2} * {3}", entry.Id.ToString (), entry.Id.ToString ("x"), entry.Type, entry.Count);
 
1205
#endif
 
1206
                                if (entry.Id == TagId.NewSubfileType) {
 
1207
                                        
 
1208
                                }
 
1209
                        }
 
1210
                }
 
1211
 
 
1212
                protected virtual void ReadFooter (System.IO.Stream stream)
 
1213
                {
 
1214
                        next_directory_offset = Converter.ReadUInt (stream, this.endian);
 
1215
                }
 
1216
 
 
1217
                protected void LoadEntries (System.IO.Stream stream)
 
1218
                {
 
1219
                        foreach (DirectoryEntry entry in entries) {
 
1220
                                entry.LoadExternal (stream);
 
1221
                        }
 
1222
                }
 
1223
                
 
1224
                protected void LoadNextDirectory (System.IO.Stream stream)
 
1225
                {
 
1226
#if DEBUG_LOADER
 
1227
                        System.Console.WriteLine ("start_position = {1} next_directory_offset = {0}",
 
1228
                                                  next_directory_offset, orig_position);
 
1229
#endif
 
1230
                        next_directory = null;
 
1231
                        try {
 
1232
                                if (next_directory_offset != 0 && next_directory_offset != orig_position)
 
1233
                                        next_directory = new ImageDirectory (stream, next_directory_offset, this.endian);
 
1234
                                
 
1235
                        } catch (System.Exception) {
 
1236
                                //System.Console.WriteLine ("Error loading directory {0}", e.ToString ());
 
1237
                                next_directory = null;
 
1238
                                next_directory_offset = 0;
 
1239
                        }               
 
1240
                }
 
1241
 
 
1242
                public ImageDirectory NextDirectory {
 
1243
                        get {
 
1244
                                return next_directory;
 
1245
                        }
 
1246
                }
 
1247
 
 
1248
                public List<DirectoryEntry> Entries {
 
1249
                        get { 
 
1250
                                return entries;
 
1251
                        }
 
1252
                }
 
1253
 
 
1254
                public DirectoryEntry Lookup (TagId id) 
 
1255
                {
 
1256
                        foreach (DirectoryEntry entry in entries)
 
1257
                                if (entry.Id == id)
 
1258
                                        return entry;
 
1259
                        
 
1260
 
 
1261
                        return null;
 
1262
                }
 
1263
 
 
1264
                private DirectoryEntry GetEntry (int i)
 
1265
                {
 
1266
                        if (i < Entries.Count)
 
1267
                                return Entries [i];
 
1268
                        else
 
1269
                                return null;
 
1270
                }
 
1271
 
 
1272
                public DirectoryEntry Lookup (uint id) 
 
1273
                {
 
1274
                        foreach (DirectoryEntry entry in entries)
 
1275
                                if ((uint)entry.Id == id)
 
1276
                                        return entry;
 
1277
 
 
1278
                        return null;
 
1279
                }
 
1280
 
 
1281
                public static DirectoryEntry CreateEntry (ImageDirectory parent, byte [] input, int start, Endian header_endian)
 
1282
                {
 
1283
                        TagId tagid;
 
1284
                        EntryType type;
 
1285
 
 
1286
                        DirectoryEntry.ParseHeader (input, start, out tagid, out type, header_endian);
 
1287
                        //ConstructorFunc ctor = ctors[tagid];                  
 
1288
                        //if (ctor == null) {
 
1289
                        //      return ctor (input, header_endian);                             
 
1290
                        //}
 
1291
                        
 
1292
                        switch (tagid) {
 
1293
                        case TagId.ExifIfdPointer:
 
1294
                        case TagId.GPSInfoIfdPointer:
 
1295
                        case TagId.InteroperabilityIfdPointer:
 
1296
                        case TagId.SubIFDs:
 
1297
                                return new SubdirectoryEntry (input, start, header_endian);
 
1298
                                //case TagId.MakerNote:
 
1299
                                //return new MakerNoteEntry (input, start, header_endian);
 
1300
                                //case TagId.PimIfdPointer:
 
1301
                                //return new 
 
1302
                                //case TagId.MakerNote:
 
1303
                                //return new MakerNoteEntry (input, start, header_endian);
 
1304
                        }
 
1305
                        
 
1306
                        switch (type) {
 
1307
                        case EntryType.Ifd:
 
1308
                                //System.Console.WriteLine ("Trying to load {0} {1}", tagid, tagid.ToString ("x"));
 
1309
                                return new SubdirectoryEntry (input, start, header_endian);
 
1310
                        case EntryType.Byte:
 
1311
                                return new ByteEntry (input, start, header_endian);
 
1312
                        case EntryType.Long:
 
1313
                                return new LongEntry (input, start, header_endian);
 
1314
                        case EntryType.Short:
 
1315
                                return new ShortEntry (input, start, header_endian);
 
1316
                        }
 
1317
 
 
1318
                        return new DirectoryEntry (input, start, header_endian);
 
1319
                }
 
1320
                
 
1321
#if false
 
1322
                public Cms.Profile GetProfile ()
 
1323
                {
 
1324
                        Cms.ColorCIExyY whitepoint = new Cms.ColorCIExyY (0, 0, 0);
 
1325
                        Cms.ColorCIExyYTriple primaries = new Cms.ColorCIExyYTriple (whitepoint, whitepoint, whitepoint);
 
1326
                        Cms.GammaTable [] transfer = null;
 
1327
                        int bits_per_sample = 8;
 
1328
                        double gamma = 2.2;
 
1329
                        
 
1330
                        foreach (DirectoryEntry e in entries) {
 
1331
                                switch (e.Id) {
 
1332
                                case TagId.InterColorProfile:
 
1333
                                        try {
 
1334
                                                return new Cms.Profile (e.RawData);
 
1335
                                        } catch (System.Exception ex) {
 
1336
                                                //System.Console.WriteLine (ex);
 
1337
                                        }
 
1338
                                        break;
 
1339
                                case TagId.ColorSpace:
 
1340
                                        switch ((ColorSpace)e.ValueAsLong [0]) {
 
1341
                                        case ColorSpace.StandardRGB:
 
1342
                                                return Cms.Profile.CreateStandardRgb ();
 
1343
                                        case ColorSpace.AdobeRGB:
 
1344
                                                return Cms.Profile.CreateAlternateRgb ();
 
1345
                                        case ColorSpace.Uncalibrated:
 
1346
                                                //System.Console.WriteLine ("Uncalibrated colorspace");
 
1347
                                                break;
 
1348
                                        }
 
1349
                                        break;
 
1350
                                case TagId.WhitePoint:
 
1351
                                        Rational [] white = e.RationalValue;
 
1352
                                        whitepoint.x = white [0].Value;
 
1353
                                        whitepoint.y = white [1].Value;
 
1354
                                        whitepoint.Y = 1.0;
 
1355
                                        break;
 
1356
                                case TagId.PrimaryChromaticities:
 
1357
                                        Rational [] colors = e.RationalValue;
 
1358
                                        primaries.Red.x = colors [0].Value;
 
1359
                                        primaries.Red.y = colors [1].Value;
 
1360
                                        primaries.Red.Y = 1.0;
 
1361
 
 
1362
                                        primaries.Green.x = colors [2].Value;
 
1363
                                        primaries.Green.y = colors [3].Value;
 
1364
                                        primaries.Green.Y = 1.0;
 
1365
 
 
1366
                                        primaries.Blue.x = colors [4].Value;
 
1367
                                        primaries.Blue.y = colors [5].Value;
 
1368
                                        primaries.Blue.Y = 1.0;
 
1369
                                        break;
 
1370
                                case TagId.TransferFunction:
 
1371
                                        ushort [] trns = e.ShortValue;
 
1372
                                        ushort gamma_count = (ushort) (1 << bits_per_sample);
 
1373
                                        Cms.GammaTable [] tables = new Cms.GammaTable [3];
 
1374
                                        //System.Console.WriteLine ("Parsing transfer function: count = {0}", trns.Length);
 
1375
 
 
1376
                                        // FIXME we should use the TransferRange here
 
1377
                                        // FIXME we should use bits per sample here
 
1378
                                        for (int c = 0; c < 3; c++) {
 
1379
                                                tables [c] = new Cms.GammaTable (trns, c * gamma_count, gamma_count);
 
1380
                                        }
 
1381
 
 
1382
                                        transfer = tables;
 
1383
                                        break;
 
1384
                                case TagId.ExifIfdPointer:
 
1385
                                        SubdirectoryEntry exif = (SubdirectoryEntry) e;
 
1386
                                        DirectoryEntry ee = exif.Directory [0].Lookup ((int)TagId.Gamma);
 
1387
                                        
 
1388
                                        if (ee == null)
 
1389
                                                break;
 
1390
 
 
1391
                                        Rational rgamma = ee.RationalValue [0];
 
1392
                                        gamma = rgamma.Value;
 
1393
                                        break;
 
1394
                                }
 
1395
                        }
 
1396
 
 
1397
                        if (transfer == null) {
 
1398
                                Cms.GammaTable basic = new Cms.GammaTable (1 << bits_per_sample, gamma);
 
1399
                                transfer = new Cms.GammaTable [] { basic, basic, basic };
 
1400
                        }
 
1401
 
 
1402
                        // if we didn't get a white point or primaries, give up
 
1403
                        if (whitepoint.Y != 1.0 || primaries.Red.Y != 1.0)
 
1404
                                return null;
 
1405
                                
 
1406
                        return new Cms.Profile (whitepoint, primaries, transfer);
 
1407
                }
 
1408
#endif
 
1409
 
 
1410
                public void Dump (string name) 
 
1411
                {
 
1412
                        System.Console.WriteLine ("Starting {0}", name);
 
1413
                        foreach (DirectoryEntry e in this.Entries)
 
1414
                                e.Dump (name);
 
1415
                        System.Console.WriteLine ("Ending {0}", name);
 
1416
                }
 
1417
                
 
1418
                public string Dump2 ()
 
1419
                {
 
1420
                        System.Text.StringBuilder builder = new System.Text.StringBuilder ();
 
1421
                        builder.Append ("Dummping IFD");
 
1422
                        foreach (DirectoryEntry entry in entries) {
 
1423
                                builder.Append (entry.ToString ()+ Environment.NewLine);
 
1424
 
 
1425
                                if (entry is SubdirectoryEntry)
 
1426
                                        builder.Append ("Found SUBDIRECTORYENTRY" + Environment.NewLine);
 
1427
                        }
 
1428
                        
 
1429
                        if (next_directory != null) {
 
1430
                                builder.Append ("Dummping Next IFD");
 
1431
                                builder.Append (next_directory.Dump2 ());
 
1432
                        }
 
1433
 
 
1434
                        return builder.ToString ();
 
1435
                }
 
1436
        }
 
1437
        
 
1438
        public class MakerNoteEntry : SubdirectoryEntry {
 
1439
                public MakerNoteEntry (byte [] data, int offset, Endian endian) : base (data, offset, endian)
 
1440
                {
 
1441
                
 
1442
                }
 
1443
 
 
1444
                public override uint GetEntryCount ()
 
1445
                {
 
1446
                        return 1;
 
1447
                }
 
1448
 
 
1449
                public override void LoadExternal (System.IO.Stream stream)
 
1450
                {
 
1451
                }
 
1452
        }
 
1453
 
 
1454
 
 
1455
        public class SubdirectoryEntry : DirectoryEntry {
 
1456
                public uint directory_offset;
 
1457
                public ImageDirectory [] Directory;
 
1458
                
 
1459
                public SubdirectoryEntry (byte [] data, int offset, Endian endian) : base (data, offset, endian)
 
1460
                {
 
1461
                        if (this.GetEntryCount () > 1) {
 
1462
                                //System.Console.WriteLine ("Count is greater than 1 ({1}) on Subdirectory {0} interesting", tagid, count);
 
1463
                        }
 
1464
                }
 
1465
 
 
1466
                public override uint Save (OrderedWriter writer, uint position)
 
1467
                {
 
1468
#if DEBUG_LOADER                        
 
1469
                        Console.WriteLine ("writing entry {0} {1} {2} - value offset = {3}", Id, Type, Count, position);
 
1470
#endif
 
1471
 
 
1472
                        writer.Write ((ushort)Id);
 
1473
                        writer.Write ((ushort)Type);
 
1474
                        writer.Write ((uint)Count);
 
1475
 
 
1476
 
 
1477
                        if (Directory.Length == 1) {
 
1478
                                writer.Write ((uint)position);
 
1479
                                position = Directory [0].Save (writer, position);
 
1480
                        } else if (Directory.Length > 1) {
 
1481
                                writer.Write ((uint)position);
 
1482
                                uint value_position = (uint) (position + Directory.Length * 4);
 
1483
                                for (int i = 0; i < Directory.Length; i++) {
 
1484
                                        writer.Stream.Position = position + i * 4;
 
1485
                                        value_position = Directory [i].Save (writer, value_position);
 
1486
                                }
 
1487
                                return value_position;
 
1488
                        } else
 
1489
                                writer.Write ((uint) 0);
 
1490
 
 
1491
                        return position;
 
1492
                }
 
1493
 
 
1494
                public virtual uint GetEntryCount ()
 
1495
                {
 
1496
                        return count;
 
1497
                }
 
1498
 
 
1499
                public override void LoadExternal (System.IO.Stream stream)
 
1500
                {
 
1501
                        uint entry_count = GetEntryCount ();
 
1502
                        Directory = new ImageDirectory [entry_count];
 
1503
 
 
1504
                        base.LoadExternal (stream);
 
1505
 
 
1506
                        for (int i = 0; i <  entry_count; i++) {
 
1507
                                try {
 
1508
                                        directory_offset = BitConverter.ToUInt32 (raw_data, i * 4, endian == Endian.Little);
 
1509
                                        if (directory_offset == offset_origin + 8)
 
1510
                                                throw new Exception ("recursive ifd");
 
1511
                                        Directory [i] = new ImageDirectory (stream, directory_offset, endian);
 
1512
                                } catch (System.Exception e) {
 
1513
                                        //System.Console.WriteLine ("Error loading Subdirectory {0} at {2} of {3}bytes:{4}{1}", 
 
1514
                                        //                        this.Id, e, directory_offset, stream.Length, Environment.NewLine);
 
1515
                                }
 
1516
                                        
 
1517
                        }
 
1518
                }
 
1519
 
 
1520
                public override void Dump (string name)
 
1521
                {
 
1522
                        for (int i = 0; i < GetEntryCount (); i++) {
 
1523
                                string subdirname = System.String.Format ("{0}{1}[{2}]({3})]", name, tagid, i, directory_offset);
 
1524
 
 
1525
                                try {
 
1526
                                        if (Directory [i] != null)
 
1527
                                                Directory [i].Dump (subdirname);
 
1528
                                } catch (System.Exception e) {
 
1529
                                        System.Console.WriteLine (e);
 
1530
                                }
 
1531
                        }
 
1532
                }
 
1533
        }
 
1534
        
 
1535
        public class ShortEntry : DirectoryEntry {
 
1536
                public ShortEntry (byte [] data, int offset, Endian endian) : base (data, offset, endian)
 
1537
                {
 
1538
                }
 
1539
        }
 
1540
        
 
1541
        public class LongEntry : DirectoryEntry {
 
1542
                public LongEntry (byte [] data, int offset, Endian endian) : base (data, offset, endian)
 
1543
                {
 
1544
                        if (type != EntryType.Long)
 
1545
                                throw new ParseException (System.String.Format ("Invalid Settings At Birth {0}", tagid));
 
1546
                }
 
1547
        }
 
1548
 
 
1549
        public class ByteEntry : DirectoryEntry {
 
1550
                public ByteEntry (byte [] data, int offset, Endian endian) : base (data, offset, endian)
 
1551
                {
 
1552
                        if (type != EntryType.Byte)
 
1553
                                throw new ParseException ("Invalid Settings At Birth");
 
1554
                }
 
1555
        }
 
1556
        
 
1557
#if false
 
1558
        public class ImageLoader {
 
1559
                int width;
 
1560
                int length;
 
1561
                int [] bps;
 
1562
                PhotometricInterpretation interpretation;
 
1563
                Compression compression;
 
1564
                uint [] offsets;
 
1565
                uint [] strip_byte_counts;
 
1566
                uint rows_per_strip;
 
1567
                byte [] strip;
 
1568
 
 
1569
                public ImageLoader (ImageDirectory directory) 
 
1570
                {
 
1571
                        width = directory.Lookup (TagId.ImageWidth).ValueAsLong [0];
 
1572
                        length = directory.Lookup (TagId.ImageLength).ValueAsLong [0];
 
1573
                        bps = directory.Lookup (TagId.BitsPerSample).ValueAsLong;
 
1574
                        
 
1575
                        compression = (Compression) directory.Lookup (TagId.Compression).ValueAsLong [0];
 
1576
                        interpretation = (PhotometricInterpretation) directory.Lookup (TagId.PhotometricInterpretation).ValueAsLong [0];
 
1577
                        
 
1578
                        offsets = directory.Lookup (TagId.StripOffsets).ValueAsLong;
 
1579
                        strip_byte_counts = directory.Lookup (TagId.StripByteCounts).ValueAsLong;
 
1580
                        rows_per_strip = directory.Lookup (TagId.RowsPerStrip).ValueAsLong [0];
 
1581
 
 
1582
                        if (interpretation != 
 
1583
                }
 
1584
 
 
1585
 
 
1586
#if false
 
1587
                public Gdk.Pixbuf LoadPixbuf (System.IO.Stream stream) 
 
1588
                {
 
1589
                        Gdk.Pixbuf dest = new Gdk.Pixbuf (Gdk.Colorspace.Rgb, false, width, height);
 
1590
                        strip = new byte [strip_byte_counts];
 
1591
                        int row;
 
1592
                        for (int i = 0; i < offsets.Length; i++) {
 
1593
                                strip = new byte [strip_byte_counts [i]];
 
1594
                                stream.Read (strip, 0, strip.Length);
 
1595
                                switch (compression) {
 
1596
                                        case Compression.Notice
 
1597
 
 
1598
                                }
 
1599
                        }
 
1600
                }
 
1601
#endif
 
1602
        }
 
1603
#endif
 
1604
 
 
1605
        public class DirectoryEntry {
 
1606
                protected TagId  tagid;
 
1607
                protected EntryType type;
 
1608
                protected uint count;
 
1609
                protected uint offset_origin;
 
1610
                protected uint data_offset;
 
1611
 
 
1612
                protected byte [] raw_data;
 
1613
                protected Endian endian;
 
1614
 
 
1615
                public TagId Id {
 
1616
                        get {
 
1617
                                return tagid;
 
1618
                        }
 
1619
                }
 
1620
 
 
1621
                public EntryType Type {
 
1622
                        get {
 
1623
                                return type;
 
1624
                        }
 
1625
                }
 
1626
                
 
1627
                public uint Count {
 
1628
                        get {
 
1629
                                return count;
 
1630
                        }
 
1631
                }
 
1632
 
 
1633
                public virtual uint Save (OrderedWriter writer, uint position)
 
1634
                {
 
1635
#if DEBUG_LOADER                        
 
1636
                        Console.WriteLine ("writing entry {0} {1} {2}", Id, Type, Count);
 
1637
#endif
 
1638
                        writer.Write ((ushort)Id);
 
1639
                        writer.Write ((ushort)Type);
 
1640
                        writer.Write ((uint)Count);
 
1641
                        if (Length > 4) {
 
1642
                                writer.Write ((uint)position);
 
1643
                                writer.Stream.Position = position;
 
1644
                                writer.Stream.Write (RawData, 0, RawData.Length);
 
1645
                                return (uint) (position + RawData.Length);
 
1646
                        } else {
 
1647
                                writer.Stream.Write (RawData, 0, RawData.Length);
 
1648
                                for (int i = 0; i < 4 - RawData.Length; i++)
 
1649
                                        writer.Write ((byte)0);
 
1650
                        }
 
1651
                        return position;
 
1652
                }
 
1653
 
 
1654
                public void SetOrigin (uint pos)
 
1655
                {
 
1656
                        offset_origin = pos;
 
1657
                }
 
1658
 
 
1659
                public uint Position {
 
1660
                        get {
 
1661
                                return offset_origin + data_offset;
 
1662
                        }
 
1663
                }
 
1664
 
 
1665
                public uint Length {
 
1666
                        get { return (uint)(Count * GetTypeSize ()); }
 
1667
                }
 
1668
 
 
1669
                public virtual int GetTypeSize ()
 
1670
                {
 
1671
                        return GetTypeSize (type);
 
1672
                }
 
1673
 
 
1674
                public static int GetTypeSize (EntryType type)
 
1675
                {
 
1676
                        switch (type) {
 
1677
                        case EntryType.Byte:
 
1678
                        case EntryType.SByte:
 
1679
                        case EntryType.Undefined:
 
1680
                        case EntryType.Ascii:
 
1681
                                return 1;
 
1682
                        case EntryType.Short:
 
1683
                        case EntryType.SShort:
 
1684
                                return 2;
 
1685
                        case EntryType.Long:
 
1686
                        case EntryType.SLong:
 
1687
                        case EntryType.Float:
 
1688
                                return 4;
 
1689
                        case EntryType.Double:
 
1690
                        case EntryType.Rational:
 
1691
                        case EntryType.SRational:
 
1692
                                return 8;
 
1693
                        default:
 
1694
                                return 1;
 
1695
                        }
 
1696
                }
 
1697
 
 
1698
                public bool IsLittle {
 
1699
                        get {
 
1700
                                return (endian == Endian.Little);
 
1701
                        }
 
1702
                }
 
1703
 
 
1704
                public static int ParseHeader (byte [] data, int start, out TagId tagid, out EntryType type, Endian endian)
 
1705
                {
 
1706
                        tagid = (TagId) BitConverter.ToUInt16 (data, start, endian == Endian.Little);
 
1707
                        type = (EntryType) BitConverter.ToUInt16 (data, start + 2, endian == Endian.Little);
 
1708
                        return 4;
 
1709
                }
 
1710
                
 
1711
                public DirectoryEntry (byte [] data, int start, Endian endian)
 
1712
                {
 
1713
                        this.endian = endian;
 
1714
 
 
1715
                        start += ParseHeader (data, start, out this.tagid, out this.type, endian);
 
1716
                        ParseStream (data, start);
 
1717
                }
 
1718
 
 
1719
                public virtual void LoadExternal (System.IO.Stream stream)
 
1720
                {
 
1721
                        if (data_offset != 0) {
 
1722
                                stream.Seek ((long)Position, System.IO.SeekOrigin.Begin);
 
1723
                                byte [] data = new byte [count * GetTypeSize ()];
 
1724
                                if (stream.Read (data, 0, data.Length) < data.Length) {
 
1725
#if DEBUG_LOADER
 
1726
                                        System.Console.WriteLine ("Short read");
 
1727
#endif
 
1728
                                        throw new ShortReadException ();
 
1729
                                }
 
1730
                                raw_data = data;
 
1731
                        }
 
1732
 
 
1733
#if DEBUG_LOADER
 
1734
                        switch ((int)this.Id) {
 
1735
                        case (int)TagId.NewSubfileType:
 
1736
                                //System.Console.WriteLine ("XXXXXXXXXXXXXXXXXXXXX new NewSubFileType {0}", (NewSubfileType) this.ValueAsLong [0]);
 
1737
                                break;
 
1738
                        case (int)TagId.SubfileType:
 
1739
                                //System.Console.WriteLine ("XXXXXXXXXXXXXXXXXXXXX new SubFileType {0}", (SubfileType) this.ValueAsLong [0]);
 
1740
                                break;
 
1741
                        case (int)TagId.Compression:
 
1742
                                //System.Console.WriteLine ("XXXXXXXXXXXXXXXXXXXXX new Compression {0}", (Compression) this.ValueAsLong [0]);
 
1743
                                
 
1744
                                break;
 
1745
                        case (int)TagId.JPEGProc:
 
1746
                                //System.Console.WriteLine ("XXXXXXXXXXXXXXXXXXXXX new JPEGProc {0}", (JPEGProc) this.ValueAsLong [0]);
 
1747
                                
 
1748
                                break;
 
1749
                        case (int)TagId.PhotometricInterpretation:
 
1750
                                //System.Console.WriteLine ("XXXXXXXXXXXXXXXXXXXXX new PhotometricInterpretation {0}", (PhotometricInterpretation) this.ValueAsLong [0]);
 
1751
                                break;
 
1752
                        case (int)TagId.ImageWidth:
 
1753
                        case (int)TagId.ImageLength:
 
1754
                                //System.Console.WriteLine ("XXXXXXXXXXXXXXXXXXXXX new {1} {0}", this.ValueAsLong [0], this.Id);
 
1755
                                break;
 
1756
                        case 50648:
 
1757
                        case 50656:
 
1758
                        case 50752:
 
1759
                                //System.Console.WriteLine ("XXXXXXXXXXXXXXXXXXXXX {0}({1}) - {2} {3}", this.Id, this.Id.ToString ("x"), this.type, raw_data.Length);
 
1760
                                //System.Console.WriteLine ("XXXX ", System.Text.Encoding.ASCII.GetString (raw_data));
 
1761
                                switch (this.type) {
 
1762
                                case EntryType.Long:
 
1763
                                        foreach (uint val in ((LongEntry)this).Value)
 
1764
                                                System.Console.Write (" {0}", val);
 
1765
                                        break;
 
1766
                                case EntryType.Short:
 
1767
                                        foreach (ushort val in ((ShortEntry)this).ShortValue)
 
1768
                                                System.Console.Write (" {0}", val);
 
1769
                                        break;
 
1770
                                case EntryType.Byte:
 
1771
                                        foreach (byte val in this.RawData)
 
1772
                                                System.Console.Write (" {0}", val);
 
1773
                                        break;
 
1774
                                }
 
1775
                                //System.Console.WriteLine (String.Empty);
 
1776
                                break;
 
1777
                        }
 
1778
#endif
 
1779
                }
 
1780
 
 
1781
                public virtual void Dump (string name)
 
1782
                {
 
1783
                        switch (this.Type) {
 
1784
                        case EntryType.Short:
 
1785
                        case EntryType.Long:
 
1786
                                uint [] vals = this.ValueAsLong;
 
1787
                                System.Console.Write ("{3}{1}({2}) [{0}] (", vals.Length, this.Id, this.Type, name);
 
1788
                                for (int i = 0; i < System.Math.Min (15, vals.Length); i++) {
 
1789
                                        System.Console.Write (" {0}", vals [i]);
 
1790
                                }
 
1791
                                System.Console.WriteLine (")");
 
1792
                                break;
 
1793
                        case EntryType.Ascii:
 
1794
                                System.Console.WriteLine ("{3}{1}({2}) (\"{0}\")", this.StringValue, this.Id, this.Type, name);
 
1795
                                break;
 
1796
                        default:
 
1797
                                System.Console.WriteLine ("{3}{1}({2}) [{0}]", this.Count, this.Id, this.Type, name);
 
1798
                                break;
 
1799
                        }
 
1800
                }
 
1801
                
 
1802
                protected void ParseStream (byte [] data, int start)
 
1803
                {
 
1804
                        int i = start;
 
1805
 
 
1806
                        count = BitConverter.ToUInt32 (data, i, endian == Endian.Little); 
 
1807
                        i += 4;
 
1808
                        int size = (int)count * GetTypeSize ();
 
1809
                        if (size > 4)
 
1810
                                data_offset = BitConverter.ToUInt32 (data, i, endian == Endian.Little);
 
1811
                        else {
 
1812
                                data_offset = 0;
 
1813
                                raw_data = new byte [size];
 
1814
                                System.Array.Copy (data, i, raw_data, 0, size);
 
1815
                        }
 
1816
                }
 
1817
 
 
1818
                public void SetData (string value)
 
1819
                {
 
1820
                        int len = System.Text.Encoding.UTF8.GetByteCount (value);
 
1821
                        byte [] tmp = new byte [len + 1];
 
1822
                        System.Text.Encoding.UTF8.GetBytes (value, 0, value.Length, tmp, 0);
 
1823
                        tmp[len] = 0;
 
1824
                        //System.Console.WriteLine ("SetData: value = {0} len = {1}", value, len);
 
1825
                        SetData (tmp);
 
1826
                }
 
1827
        
 
1828
                public static System.DateTime DateTimeFromString (string dt)
 
1829
                {
 
1830
                        // Exif DateTime strings are formatted as
 
1831
                        //      "YYYY:MM:DD HH:MM:SS"
 
1832
                        
 
1833
                        string delimiters = " :";
 
1834
                        string[] dt_data = dt.Split ( delimiters.ToCharArray(), 6 );
 
1835
                        System.DateTime result;
 
1836
                        result = new System.DateTime (System.Int32.Parse(dt_data[0]), 
 
1837
                                                      System.Int32.Parse(dt_data[1]), 
 
1838
                                                      System.Int32.Parse(dt_data[2]),
 
1839
                                                      System.Int32.Parse(dt_data[3]), 
 
1840
                                                      System.Int32.Parse(dt_data[4]), 
 
1841
                                                      System.Int32.Parse(dt_data[5]));
 
1842
                        
 
1843
                        return result;
 
1844
                }
 
1845
                
 
1846
                public void SetData (byte [] data)
 
1847
                {
 
1848
                        raw_data = data;
 
1849
                        count = (uint)raw_data.Length / (uint)GetTypeSize ();
 
1850
                }
 
1851
 
 
1852
#if false               
 
1853
                public object  GetValue () {
 
1854
                        switch (Type) {
 
1855
                        case EntryType.Short:
 
1856
                                return ShortValue;
 
1857
                        case EntryType.Long:
 
1858
                                return LongValue;
 
1859
                        case  EntryType.Rational:
 
1860
                                return RationalValue;
 
1861
                        case EntryType.SRational:
 
1862
                                return SRationalValue;
 
1863
                        case EntryType.Ascii:
 
1864
                                return StringValue.Split ('\0');
 
1865
                                break;
 
1866
                        default:
 
1867
                                //System.Console.WriteLine ("{1}({2}) [{0}]", this.Count, this.Id, this.Type);
 
1868
                                break;
 
1869
 
 
1870
                                }
 
1871
                        }
 
1872
                }
 
1873
#endif
 
1874
 
 
1875
                public byte [] Value {
 
1876
                        get {
 
1877
                                return raw_data;
 
1878
                        }
 
1879
                }
 
1880
 
 
1881
                public byte [] RawData {
 
1882
                        get { 
 
1883
                                return raw_data;
 
1884
                        }
 
1885
                }
 
1886
 
 
1887
                public string [] ValueAsString {
 
1888
                        get {
 
1889
                                switch (this.Type) {
 
1890
                                case EntryType.Short:
 
1891
                                case EntryType.Long:
 
1892
                                        return ArrayToString (this.ValueAsLong);
 
1893
                                case EntryType.Rational:
 
1894
                                        return ArrayToString (this.RationalValue);
 
1895
                                case EntryType.SRational:
 
1896
                                        return ArrayToString (this.SRationalValue);
 
1897
                                case EntryType.Undefined:
 
1898
                                        switch (Id) {
 
1899
                                        case TagId.UserComment:
 
1900
                                                return new string [] { UserCommentValue };
 
1901
                                        case TagId.FlashPixVersion:
 
1902
                                        case TagId.ExifVersion:
 
1903
                                                return new string [] { StringValue };
 
1904
                                        case TagId.FileSource:
 
1905
                                        case TagId.SceneType:
 
1906
                                                return ArrayToString (this.RawData);
 
1907
                                        case TagId.ComponentsConfiguration:
 
1908
                                                return ArrayToString (ValueAsLong);
 
1909
                                        default:
 
1910
                                                //System.Console.WriteLine ("Cannot convert type \"{0}\" to string", Id);
 
1911
                                                break;
 
1912
                                        }
 
1913
                                        break;
 
1914
                                case EntryType.Ascii:
 
1915
                                        return StringValue.Split ('\0');
 
1916
                                }
 
1917
                                return null;
 
1918
                        }
 
1919
                }
 
1920
 
 
1921
                public string [] ArrayToString (System.Array array)
 
1922
                {
 
1923
                        string [] vals = new string [array.Length];
 
1924
                        for (int i = 0; i < array.Length; i++)
 
1925
                                vals [i] = array.GetValue (i).ToString ();
 
1926
 
 
1927
                        return vals;
 
1928
                }
 
1929
 
 
1930
                public uint [] ValueAsLong {
 
1931
                        get {
 
1932
                                uint [] data = new uint [this.Count];
 
1933
                                for (int i = 0; i < this.Count; i++) {
 
1934
                                        switch (this.Type) {
 
1935
                                        case EntryType.Long:
 
1936
                                                data [i] = BitConverter.ToUInt32 (raw_data, i * GetTypeSize (), endian == Endian.Little);
 
1937
                                                break;
 
1938
                                        case EntryType.Short:
 
1939
                                                data [i] = BitConverter.ToUInt16 (raw_data, i * GetTypeSize (), endian == Endian.Little);
 
1940
                                                break;
 
1941
                                        case EntryType.Undefined:
 
1942
                                        case EntryType.Byte:
 
1943
                                                data [i] = raw_data [i];
 
1944
                                                break;
 
1945
                                        default:
 
1946
                                                throw new ParseException ("Invalid conversion");
 
1947
                                        }
 
1948
                                }
 
1949
                                return data;
 
1950
                        }
 
1951
                }
 
1952
                
 
1953
                // The following methods are usded to convert the data 
 
1954
                // to the various type regardless of the entry
 
1955
                // type, they are used internally in processing the data
 
1956
                // Use at your own risk.
 
1957
 
 
1958
                public string StringValue {
 
1959
                        get {
 
1960
                                return System.Text.Encoding.ASCII.GetString (raw_data);
 
1961
                        }
 
1962
                }
 
1963
 
 
1964
                public System.DateTime ValueAsDate {
 
1965
                        get {
 
1966
                                return DirectoryEntry.DateTimeFromString (StringValue);
 
1967
                        }
 
1968
                }
 
1969
 
 
1970
                public string UserCommentValue {
 
1971
                        get {
 
1972
                                UserComment comment = new UserComment (raw_data, IsLittle);
 
1973
                                return comment.Value;
 
1974
                        }
 
1975
                }
 
1976
 
 
1977
                public SRational [] SRationalValue {
 
1978
                        get {
 
1979
                                Rational [] vals = RationalValue;
 
1980
                                SRational [] data = new SRational [vals.Length];
 
1981
                                
 
1982
                                for (int i = 0; i < vals.Length; i++)
 
1983
                                        data [i] = SRational.BitwiseCopy (vals [i]);
 
1984
 
 
1985
                                return data;
 
1986
                        }
 
1987
                }
 
1988
 
 
1989
                public Rational [] RationalValue {
 
1990
                        get {
 
1991
                                uint [] vals = LongValue;
 
1992
                                Rational [] data = new Rational [vals.Length / 2];
 
1993
 
 
1994
                                for (int i = 0; i < vals.Length; i += 2)
 
1995
                                        data [i/2] = new Rational (vals [i], vals [i + 1]);
 
1996
                                
 
1997
                                return data;
 
1998
                        }
 
1999
                        
 
2000
                }
 
2001
 
 
2002
                public uint [] LongValue {
 
2003
                        get {
 
2004
                                uint [] data = new uint [raw_data.Length / 4];
 
2005
                                for (int i = 0; i < raw_data.Length; i+= 4)
 
2006
                                        data [i/4] = BitConverter.ToUInt32 (raw_data, i, endian == Endian.Little);
 
2007
 
 
2008
                                return data;
 
2009
                        }
 
2010
                }
 
2011
 
 
2012
                public ushort [] ShortValue {
 
2013
                        get {
 
2014
                                ushort [] data = new ushort [raw_data.Length];
 
2015
                                for (int i = 0; i < raw_data.Length; i+= 2) {
 
2016
                                        data [i] = BitConverter.ToUInt16 (raw_data, i, endian == Endian.Little);
 
2017
                                }
 
2018
                                return data;
 
2019
                        }
 
2020
                }
 
2021
        }
 
2022
 
 
2023
 
 
2024
        public class TiffFile : ImageFile, SemWeb.StatementSource {
 
2025
                public Header Header;
 
2026
 
 
2027
                // false seems a safe default
 
2028
                public bool Distinct {
 
2029
                        get { return false; }
 
2030
                }
 
2031
 
 
2032
                public TiffFile (string path) : base (path)
 
2033
                {
 
2034
                        try {
 
2035
                                using (System.IO.Stream input = Open ()) {
 
2036
                                        this.Header = new Header (input);
 
2037
                                }
 
2038
 
 
2039
#if DEBUG_LOADER
 
2040
                                Header.Dump (this.ToString () + ":");
 
2041
#endif
 
2042
                        } catch (System.Exception e) {
 
2043
                                Beagle.Util.Log.Error (e, "Error loading TIFF file {0}", path);
 
2044
                        }
 
2045
                }
 
2046
 
 
2047
                public TiffFile (Uri uri) : base (uri)
 
2048
                {
 
2049
                        try {
 
2050
                                using (System.IO.Stream input = Open ()) {
 
2051
                                        this.Header = new Header (input);
 
2052
                                }
 
2053
 
 
2054
#if DEBUG_LOADER
 
2055
                                Header.Dump (this.ToString () + ":");
 
2056
#endif
 
2057
                        } catch (System.Exception e) {
 
2058
                                Beagle.Util.Log.Error (e, "Error loading TIFF file {0}", uri);
 
2059
                        }
 
2060
                }
 
2061
 
 
2062
                public virtual void Select (SemWeb.StatementSink sink)
 
2063
                {
 
2064
                        Header.SelectDirectory (Header.Directory, sink);
 
2065
                }
 
2066
 
 
2067
                public override System.DateTime Date {
 
2068
                        get {
 
2069
                                SubdirectoryEntry sub = (SubdirectoryEntry) this.Header.Directory.Lookup (TagId.ExifIfdPointer);
 
2070
                                DirectoryEntry e;
 
2071
 
 
2072
                                if (sub != null) {
 
2073
                                        e = sub.Directory [0].Lookup (TagId.DateTimeOriginal);
 
2074
                                        
 
2075
                                        if (e != null)
 
2076
                                                return DirectoryEntry.DateTimeFromString (e.StringValue).ToUniversalTime ();
 
2077
                                }
 
2078
 
 
2079
                                e = this.Header.Directory.Lookup (TagId.DateTime);
 
2080
 
 
2081
                                if (e != null)
 
2082
                                        return DirectoryEntry.DateTimeFromString (e.StringValue).ToUniversalTime ();
 
2083
                                else
 
2084
                                        return base.Date;
 
2085
                        }
 
2086
                }
 
2087
                
 
2088
                public override System.IO.Stream PixbufStream ()
 
2089
                {
 
2090
                        return Open ();
 
2091
                }
 
2092
 
 
2093
                public override PixbufOrientation GetOrientation ()
 
2094
                {
 
2095
                        ShortEntry e = (ShortEntry)(this.Header.Directory.Lookup (TagId.Orientation));
 
2096
                        if (e != null) 
 
2097
                                return (PixbufOrientation)(e.ShortValue[0]);
 
2098
                        else
 
2099
                                return PixbufOrientation.TopLeft;
 
2100
                }
 
2101
 
 
2102
                public System.IO.Stream LookupJpegSubstream (ImageDirectory directory)
 
2103
                {
 
2104
                        uint offset = directory.Lookup (TagId.JPEGInterchangeFormat).ValueAsLong [0];
 
2105
                        
 
2106
                        System.IO.Stream file = Open ();
 
2107
                        file.Position = offset;
 
2108
                        return file;
 
2109
                }
 
2110
 
 
2111
#if false
 
2112
                public Gdk.Pixbuf LoadJpegInterchangeFormat (ImageDirectory directory)
 
2113
                {
 
2114
                        uint offset = directory.Lookup (TagId.JPEGInterchangeFormat).ValueAsLong [0];
 
2115
                        uint length = directory.Lookup (TagId.JPEGInterchangeFormatLength).ValueAsLong [0];
 
2116
                           
 
2117
                        using (System.IO.Stream file = Open ()) {
 
2118
                                file.Position = offset;
 
2119
                                
 
2120
                                byte [] data = new byte [32768];
 
2121
                                int read;
 
2122
 
 
2123
                                Gdk.PixbufLoader loader = new Gdk.PixbufLoader ();
 
2124
                                
 
2125
                                while (length > 0) {
 
2126
                                        read = file.Read (data, 0, (int)System.Math.Min ((int)data.Length, length));
 
2127
                                        if (read <= 0)
 
2128
                                                break;
 
2129
 
 
2130
                                        loader.Write (data, (ulong)read);
 
2131
                                        length -= (uint) read;
 
2132
                                }
 
2133
                                Gdk.Pixbuf result = loader.Pixbuf;
 
2134
                                loader.Close ();
 
2135
                                return result; 
 
2136
                        }
 
2137
                }
 
2138
#endif
 
2139
        }
 
2140
 
 
2141
        public class DngFile : TiffFile {
 
2142
                public DngFile (string path) : base (path) 
 
2143
                {
 
2144
                }
 
2145
 
 
2146
                public DngFile (System.Uri uri) : base (uri) 
 
2147
                {
 
2148
                }
 
2149
 
 
2150
                public override System.IO.Stream PixbufStream ()
 
2151
                {
 
2152
                        try {
 
2153
                                SubdirectoryEntry sub = (SubdirectoryEntry) Header.Directory.Lookup (TagId.SubIFDs);
 
2154
                                ImageDirectory directory = sub.Directory [sub.Directory.Length - 1];
 
2155
 
 
2156
                                uint offset = directory.Lookup (TagId.StripOffsets).ValueAsLong [0];
 
2157
                                System.IO.Stream file = Open ();
 
2158
                                file.Position = offset;
 
2159
                                return file;
 
2160
                        } catch {
 
2161
                                return DCRawFile.RawPixbufStream (uri);
 
2162
                        }
 
2163
                }
 
2164
 
 
2165
                public override void Select (SemWeb.StatementSink sink)
 
2166
                {
 
2167
                        
 
2168
                        /* this is just a sanity pass, if the first ifd is not a subfile use the normal
 
2169
                         * tiff path 
 
2170
                         */
 
2171
                        DirectoryEntry e = Header.Directory.Lookup (TagId.NewSubfileType);
 
2172
                        if (e == null) {
 
2173
                                base.Select (sink);
 
2174
                                return;
 
2175
                        }
 
2176
 
 
2177
                        /*
 
2178
                         * Even though Ifd0 doesn't have the full resolution image
 
2179
                         * it would have the XMP data so we look for it
 
2180
                         */
 
2181
                        e = Header.Directory.Lookup (TagId.XMP);
 
2182
                        if (e != null) {
 
2183
                                System.IO.Stream xmpstream = new System.IO.MemoryStream (e.RawData);
 
2184
                                FSpot.Xmp.XmpFile xmp = new FSpot.Xmp.XmpFile (xmpstream);
 
2185
                                xmp.Select (sink);
 
2186
                        }
 
2187
 
 
2188
                        /* 
 
2189
                         * Ifd0 will also have the exif directory
 
2190
                         */
 
2191
                        ImageDirectory dir = Header.Directory;
 
2192
                        SubdirectoryEntry sub = (SubdirectoryEntry) dir.Lookup (TagId.ExifIfdPointer);
 
2193
                        if (sub != null)
 
2194
                                Header.SelectDirectory (sub.Directory [0], sink);
 
2195
                        
 
2196
                        /*
 
2197
                         * now we lookup subifd0 (we should probably scan the newsubfile types here)
 
2198
                         * and load the metadata we are interested in from it.
 
2199
                         */
 
2200
                        sub = (SubdirectoryEntry) Header.Directory.Lookup (TagId.SubIFDs);      
 
2201
 
 
2202
                        if (e == null)
 
2203
                                e = dir.Lookup (TagId.NewSubfileType);
 
2204
 
 
2205
                        int i = 0;
 
2206
                        do {
 
2207
                                uint dirtype = e.ValueAsLong [0];
 
2208
                                if (dirtype == 0) {
 
2209
                                        Header.SelectDirectory (dir, sink);
 
2210
                                        break;
 
2211
                                }
 
2212
                                        
 
2213
                                if (sub == null)
 
2214
                                        break;
 
2215
 
 
2216
                                dir = sub.Directory [i];
 
2217
                                e = dir.Lookup (TagId.NewSubfileType);
 
2218
                                i++;
 
2219
                        } while (i < sub.Directory.Length);
 
2220
 
 
2221
                        
 
2222
                }
 
2223
        }       
 
2224
        
 
2225
        public class NefFile : TiffFile, IThumbnailContainer {
 
2226
                public NefFile (string path) : base (path) 
 
2227
                {
 
2228
                }
 
2229
 
 
2230
                public NefFile (Uri uri) : base (uri)
 
2231
                {
 
2232
                }
 
2233
 
 
2234
                public override void Select (SemWeb.StatementSink sink)
 
2235
                {
 
2236
                        DirectoryEntry e = Header.Directory.Lookup (TagId.NewSubfileType);
 
2237
 
 
2238
                        if (e == null) {
 
2239
                                base.Select (sink);
 
2240
                                return;
 
2241
                        }
 
2242
 
 
2243
                        ImageDirectory dir = Header.Directory;
 
2244
                        SubdirectoryEntry sub = (SubdirectoryEntry) dir.Lookup (TagId.ExifIfdPointer);
 
2245
                        
 
2246
                        if (sub != null)
 
2247
                                Header.SelectDirectory (sub.Directory [0], sink);
 
2248
                        
 
2249
                        sub = (SubdirectoryEntry) Header.Directory.Lookup (TagId.SubIFDs);      
 
2250
 
 
2251
                        int i = 0;
 
2252
                        do {
 
2253
                                uint dirtype = e.ValueAsLong [0];
 
2254
                                if (dirtype == 0) {
 
2255
                                        Header.SelectDirectory (dir, sink);
 
2256
                                        break;
 
2257
                                }
 
2258
                                        
 
2259
                                if (sub == null)
 
2260
                                        break;
 
2261
 
 
2262
                                dir = sub.Directory [i];
 
2263
                                if (dir != null)
 
2264
                                        e = dir.Lookup (TagId.NewSubfileType);
 
2265
                                i++;
 
2266
                        } while (i < sub.Directory.Length);
 
2267
                }
 
2268
 
 
2269
#if false
 
2270
                public Gdk.Pixbuf GetEmbeddedThumbnail ()
 
2271
                {
 
2272
                        using (System.IO.Stream stream = Open ()) {
 
2273
                                return TransformAndDispose (new Gdk.Pixbuf (stream));
 
2274
                        }
 
2275
                }
 
2276
 
 
2277
                public override System.IO.Stream PixbufStream ()
 
2278
                {
 
2279
                        try {
 
2280
                                SubdirectoryEntry sub = (SubdirectoryEntry) Header.Directory.Lookup (TagId.SubIFDs);
 
2281
                                ImageDirectory jpeg_directory = sub.Directory [0];
 
2282
                                return LookupJpegSubstream (jpeg_directory);
 
2283
                        } catch (System.Exception) {
 
2284
                                return DCRawFile.RawPixbufStream (uri);
 
2285
                        }
 
2286
                }
 
2287
#endif
 
2288
        }
 
2289
                
 
2290
 
 
2291
        public class Cr2File : TiffFile, IThumbnailContainer {
 
2292
                public Cr2File (string path) : base (path) 
 
2293
                {
 
2294
                }
 
2295
 
 
2296
                public Cr2File (Uri uri) : base (uri)
 
2297
                {
 
2298
                }
 
2299
 
 
2300
                /*
 
2301
                public override PixbufOrientation GetOrientation ()
 
2302
                {
 
2303
                        return PixbufOrientation.TopLeft;
 
2304
                }
 
2305
                */
 
2306
 
 
2307
#if false
 
2308
                public Gdk.Pixbuf GetEmbeddedThumbnail ()
 
2309
                {
 
2310
                        ImageDirectory directory;
 
2311
                        directory = Header.Directory.NextDirectory;
 
2312
                        return TransformAndDispose (LoadJpegInterchangeFormat (directory));
 
2313
                }
 
2314
#endif
 
2315
 
 
2316
                public override System.IO.Stream PixbufStream ()
 
2317
                {
 
2318
                        uint offset = Header.Directory.Lookup (TagId.StripOffsets).ValueAsLong [0];
 
2319
                        System.IO.Stream file = Open ();
 
2320
                        file.Position = offset;
 
2321
                        return file;
 
2322
                }
 
2323
        }
 
2324
 
 
2325
#if ENABLE_NUNIT
 
2326
        [TestFixture]
 
2327
        public class Tests {
 
2328
                public Tests ()
 
2329
                {
 
2330
                        Gnome.Vfs.Vfs.Initialize ();
 
2331
                        Gtk.Application.Init ();
 
2332
                }
 
2333
                
 
2334
                [Test]
 
2335
                public void Save ()
 
2336
                {
 
2337
                        string desc = "this is an example description";
 
2338
                        string desc2 = "\x00a9 Novell Inc.";
 
2339
                        PixbufOrientation orient = PixbufOrientation.TopRight;
 
2340
                        Gdk.Pixbuf test = new Gdk.Pixbuf (null, "f-spot-32.png");
 
2341
                        string path = ImageFile.TempPath ("joe.jpg");
 
2342
                        
 
2343
                        PixbufUtils.SaveJpeg (test, path, 75, new Exif.ExifData ());
 
2344
                        JpegFile jimg = new JpegFile (path);
 
2345
                        jimg.SetDescription (desc);
 
2346
                        jimg.SetOrientation (orient);
 
2347
                        jimg.SaveMetaData (path);
 
2348
                        JpegFile mod = new JpegFile (path);
 
2349
                        Assert.AreEqual (mod.Orientation, orient);
 
2350
                        Assert.AreEqual (mod.Description, desc);
 
2351
                        jimg.SetDescription (desc2);
 
2352
                        jimg.SaveMetaData (path);
 
2353
                        mod = new JpegFile (path);
 
2354
                        Assert.AreEqual (mod.Description, desc2);
 
2355
                        
 
2356
                        Header header = mod.ExifHeader;
 
2357
#if USE_TEST_FILE
 
2358
                        string tmp = "/home/lewing/test.tiff";
 
2359
                        if (File.Exists (tmp))
 
2360
                                File.Delete (tmp);
 
2361
                        Stream stream = File.Open (tmp, FileMode.Create, FileAccess.ReadWrite);
 
2362
                        Console.WriteLine ("XXXX saving tiff {0}", tmp);
 
2363
#else
 
2364
                        System.IO.MemoryStream stream = new System.IO.MemoryStream ();
 
2365
#endif
 
2366
 
 
2367
                        header.Dump ("source");
 
2368
                        header.Save (stream);
 
2369
                        stream.Position = 0;
 
2370
                        //System.Console.WriteLine ("----------------------------------------------LOADING TIFF");
 
2371
                        Header loader = new Header (stream);
 
2372
                        loader.Dump ("loader");
 
2373
                        
 
2374
                        CompareDirectories (header.Directory, loader.Directory);
 
2375
 
 
2376
                        System.IO.File.Delete (path);   
 
2377
                }
 
2378
 
 
2379
                private void CompareDirectories (ImageDirectory olddir, ImageDirectory newdir)
 
2380
                {
 
2381
                        Assert.AreEqual (olddir.Entries.Count, newdir.Entries.Count);
 
2382
                        for (int i = 0; i < olddir.Entries.Count; i++) {
 
2383
                                Assert.AreEqual (olddir.Entries [i].Id, newdir.Entries [i].Id);
 
2384
                                Assert.AreEqual (olddir.Entries [i].Type, newdir.Entries [i].Type);
 
2385
                                Assert.AreEqual (olddir.Entries [i].Count, newdir.Entries [i].Count);
 
2386
                                Assert.AreEqual (olddir.Entries [i].Length, newdir.Entries [i].Length);
 
2387
                                if (olddir.Entries [i] is SubdirectoryEntry) {
 
2388
                                        SubdirectoryEntry oldsub = olddir.Entries [i] as SubdirectoryEntry;
 
2389
                                        SubdirectoryEntry newsub = newdir.Entries [i] as SubdirectoryEntry;
 
2390
                                        
 
2391
                                        for (int j = 0; j < oldsub.Directory.Length; j++)
 
2392
                                                CompareDirectories (oldsub.Directory [j], newsub.Directory [j]);
 
2393
                                }
 
2394
                        }
 
2395
                }
 
2396
        }
 
2397
#endif
 
2398
}
 
2399