5 namespace Beagle
.Util
.Tiff
{
7 // This is primarily to preserve the names from the specification
8 // because they differ from the tiff standard names
9 public enum NiffId
: ushort {
11 PelPathLength
= 0x0100,
12 LineProgressionLength
= 257,
13 BitsPerSample
= 0x0101,
14 PhotometricInterpretation
= 0x0106,
16 SamplesPerPixel
= 0x0115,
17 DataByteCounts
= 0x0117,
18 PelPathResolution
= 0x011a,
19 LineProgressionResolution
= 0x011b,
20 ResolutionUnit
= 0x0128,
21 ColumnsPerPelPath
= 322,
22 RowsPerLineProgression
= 323,
24 NavyCompression
= 33466,
28 public enum TagId
: ushort {
29 InteroperabilityIndex
= 0x0001,
30 InteroperabilityVersion
= 0x0002,
32 NewSubfileType
= 0x00fe,
37 BitsPerSample
= 0x0102,
39 PhotometricInterpretation
= 0x0106,
41 DocumentName
= 0x010d,
42 ImageDescription
= 0x010e,
45 StripOffsets
= 0x0111,
47 SamplesPerPixel
= 0x0115,
48 RowsPerStrip
= 0x0116,
49 StripByteCounts
= 0x0117,
52 PlanarConfiguration
= 0x011c,
57 ResolutionUnit
= 0x0128,
58 TransferFunction
= 0x012d,
63 PrimaryChromaticities
= 0x013f,
65 HalftoneHints
= 0x0141,
70 TileByteCounts
= 0x0145,
72 SubIFDs
= 0x014a, // TIFF-EP
76 NumberOfInks
= 0x014e,
79 TargetPrinter
= 0x0151,
80 ExtraSamples
= 0x0152,
81 SampleFormat
= 0x0153,
82 SMinSampleValue
= 0x0154,
83 SMaxSampleValue
= 0x0155,
85 TransferRange
= 0x0156,
87 ClipPath
= 0x0157, // TIFF PageMaker Technote #2.
89 JPEGTables
= 0x015b, // TIFF-EP
92 JPEGInterchangeFormat
= 0x0201,
93 JPEGInterchangeFormatLength
= 0x0202,
94 JPEGRestartInterval
= 0x0203,
95 JPEGLosslessPredictors
= 0x0205,
96 JPEGPointTransforms
= 0x0206,
98 JPEGDCTables
= 0x0208,
99 JPEGACTables
= 0x0209,
101 YCbCrCoefficients
= 0x0211,
102 YCbCrSubSampling
= 0x0212,
103 YCbCrPositioning
= 0x0213,
105 ReferenceBlackWhite
= 0x0214,
106 RelatedImageFileFormat
= 0x1000,
107 RelatedImageWidth
= 0x1001,
108 RelatedImageLength
= 0x1002,
109 CFARepeatPatternDim
= 0x828d,
111 BatteryLevel
= 0x828f,
113 ExposureTime
= 0x829a,
116 // These are from the NIFF spec and only really valid when the header begins with IIN1
117 // see the NiffTag enum for the specifcation specific names
119 NavyCompression
= 0x82ba,
125 PhotoshopPrivate
= 0x8649,
127 ExifIfdPointer
= 0x8769,
128 InterColorProfile
= 0x8773,
129 ExposureProgram
= 0x8822,
130 SpectralSensitivity
= 0x8824,
131 GPSInfoIfdPointer
= 0x8825,
132 ISOSpeedRatings
= 0x8827,
134 ExifVersion
= 0x9000,
135 DateTimeOriginal
= 0x9003,
136 DateTimeDigitized
= 0x9004,
137 ComponentsConfiguration
= 0x9101,
138 CompressedBitsPerPixel
= 0x9102,
139 ShutterSpeedValue
= 0x9201,
140 ApertureValue
= 0x9202,
141 BrightnessValue
= 0x9203,
142 ExposureBiasValue
= 0x9204,
143 MaxApertureValue
= 0x9205,
144 SubjectDistance
= 0x9206,
145 MeteringMode
= 0x9207,
146 LightSource
= 0x9208,
148 FocalLength
= 0x920a,
150 FlashEnergy_TIFFEP
= 0x920b,// TIFF-EP
151 SpacialFrequencyResponse
= 0x920c,// TIFF-EP
152 Noise
= 0x920d,// TIFF-EP
153 FocalPlaneXResolution_TIFFEP
= 0x920e,// TIFF-EP
154 FocalPlaneYResolution_TIFFEP
= 0x920f,// TIFF-EP
155 FocalPlaneResolutionUnit_TIFFEP
= 0x9210,// TIFF-EP
156 ImageName
= 0x9211,// TIFF-EP
157 SecurityClassification
= 0x9212,// TIFF-EP
159 ImageHistory
= 0x9213, // TIFF-EP null separated list
161 SubjectArea
= 0x9214,
163 ExposureIndex_TIFFEP
= 0x9215, // TIFF-EP
164 TIFFEPStandardID
= 0x9216, // TIFF-EP
165 SensingMethod_TIFFEP
= 0x9217, // TIFF-EP
168 UserComment
= 0x9286,
170 SubSecTimeOriginal
= 0x9291,
171 SubSecTimeDigitized
= 0x9292,
172 FlashPixVersion
= 0xa000,
174 PixelXDimension
= 0xa002,
175 PixelYDimension
= 0xa003,
176 RelatedSoundFile
= 0xa004,
177 InteroperabilityIfdPointer
= 0xa005,
178 FlashEnergy
= 0xa20b,
179 SpatialFrequencyResponse
= 0xa20c,
180 FocalPlaneXResolution
= 0xa20e,
181 FocalPlaneYResolution
= 0xa20f,
182 FocalPlaneResolutionUnit
= 0xa210,
183 SubjectLocation
= 0xa214,
184 ExposureIndex
= 0xa215,
185 SensingMethod
= 0xa217,
188 ExifCFAPattern
= 0xa302,
189 CustomRendered
= 0xa401,
190 ExposureMode
= 0xa402,
191 WhiteBalance
= 0xa403,
192 DigitalZoomRatio
= 0xa404,
193 FocalLengthIn35mmFilm
= 0xa405,
194 SceneCaptureType
= 0xa406,
195 GainControl
= 0xa407,
199 DeviceSettingDescription
= 0xa40b,
200 SubjectDistanceRange
= 0xa40c,
201 ImageUniqueId
= 0xa420,
203 // The Following IDs are not described the EXIF spec
206 // The XMP spec declares that XMP data should live 0x2bc when
207 // embedded in tiff images.
211 DNGVersion
= 0xc612, // Ifd0
212 DNGBackwardVersion
= 0xc613, // Ifd0
213 UniqueCameraModel
= 0xc614, // Ifd0
214 LocalizedCameraModel
= 0xc615, // Ifd0
215 CFAPlaneColor
= 0xc616, // RawIfd
216 CFALayout
= 0xc617, // RawIfd
217 LinearizationTable
= 0xc618, // RawIfd
218 BlackLevelRepeatDim
= 0xc619, // RawIfd
219 BlackLevel
= 0xc61a, // RawIfd
220 BlackLevelDeltaH
= 0xc61b, // RawIfd
221 BlackLevelDeltaV
= 0xc61c, // RawIfd
222 WhiteLevel
= 0xc61d, // RawIfd
223 DefaultScale
= 0xc61e, // RawIfd
224 DefaultCropOrigin
= 0xc61f, // RawIfd
225 DefaultCropSize
= 0xc620, // RawIfd
226 ColorMatrix1
= 0xc621, // Ifd0
227 ColorMatrix2
= 0xc622, // Ifd0
228 CameraCalibration1
= 0xc623, // Ifd0
229 CameraCalibration2
= 0xc624, // Ifd0
230 ReductionMatrix1
= 0xc625, // Ifd0
231 ReductionMatrix2
= 0xc626, // Ifd0
232 AnalogBalance
= 0xc627, // Ifd0
233 AsShotNetural
= 0xc628, // Ifd0
234 AsShotWhiteXY
= 0xc629, // Ifd0
235 BaselineExposure
= 0xc62a, // Ifd0
236 BaselineNoise
= 0xc62b, // Ifd0
237 BaselineSharpness
= 0xc62c, // Ifd0
238 BayerGreeSpit
= 0xc62d, // Ifd0
239 LinearResponseLimit
= 0xc62e, // Ifd0
240 CameraSerialNumber
= 0xc62f, // Ifd0
241 LensInfo
= 0xc630, // Ifd0
242 ChromaBlurRadius
= 0xc631, // RawIfd
243 AntiAliasStrength
= 0xc632, // RawIfd
244 DNGPrivateData
= 0xc634, // Ifd0
246 MakerNoteSafety
= 0xc635, // Ifd0
248 // The Spec says BestQualityScale is 0xc635 but it appears to be wrong
249 //BestQualityScale = 0xc635, // RawIfd
250 BestQualityScale
= 0xc63c, // RawIfd this looks like the correct value
252 CalibrationIlluminant1
= 0xc65a, // Ifd0
253 CalibrationIlluminant2
= 0xc65b, // Ifd0
255 // Print Image Matching data
256 PimIfdPointer
= 0xc4a5
259 public struct SRational
{
260 public int Numerator
;
261 public int Denominator
;
263 public SRational (byte [] raw_data
, int offset
, bool little
)
265 Numerator
= EndianConverter
.ToInt32 (raw_data
, offset
, little
);
266 Denominator
= EndianConverter
.ToInt32 (raw_data
, offset
, little
);
269 public SRational (int numerator
, int denominator
)
271 Numerator
= numerator
;
272 Denominator
= denominator
;
275 public static SRational
BitwiseCopy (Rational rational
)
279 result
.Numerator
= unchecked ((int) rational
.Numerator
);
280 result
.Denominator
= unchecked ((int) rational
.Denominator
);
284 public override string ToString ()
288 else if (Numerator
% Denominator
== 0)
289 return String
.Format ("{0}", Numerator
/ Denominator
);
290 else if (Denominator
% Numerator
== 0)
291 return String
.Format ("1/{0}", Denominator
/ Numerator
);
293 return String
.Format ("{0}/{1}", Numerator
, Denominator
);
296 public double Value
{
298 return Numerator
/ (double)Denominator
;
303 public struct Rational
{
304 public uint Numerator
;
305 public uint Denominator
;
307 public Rational (uint numerator
, uint denominator
)
309 Numerator
= numerator
;
310 Denominator
= denominator
;
313 public Rational (string value)
315 string [] vals
= value.Split ('/');
316 if (vals
.Length
== 2) {
317 this.Numerator
= UInt32
.Parse (vals
[0]);
318 this.Denominator
= UInt32
.Parse (vals
[1]);
320 } if (vals
.Length
== 1) {
321 double tmp
= Double
.Parse (value);
322 this.Numerator
= (uint) (tmp
* 100000);
323 this.Denominator
= 100000;
325 throw new System
.Exception ("unable to parse rational value");
328 public override string ToString ()
332 else if (Numerator
% Denominator
== 0)
333 return String
.Format ("{0}", Numerator
/ Denominator
);
334 else if (Denominator
% Numerator
== 0)
335 return String
.Format ("1/{0}", Denominator
/ Numerator
);
337 return String
.Format ("{0}/{1}", Numerator
, Denominator
);
340 public double Value
{
342 return Numerator
/ (double)Denominator
;
351 public UserComment (byte [] raw_data
, bool little
)
353 string charset
= System
.Text
.Encoding
.ASCII
.GetString (raw_data
, 0, 8);
354 System
.Text
.Encoding enc
;
358 enc
= System
.Text
.Encoding
.ASCII
;
361 enc
= System
.Text
.Encoding
.BigEndianUnicode
;
364 // FIXME I'm pretty sure this isn't actually the encoding name.
365 enc
= System
.Text
.Encoding
.GetEncoding ("SJIS");
367 case "\0\0\0\0\0\0\0\0":
368 // FIXME the spec says to use the local encoding in this case, we could probably
369 // do something smarter, but whatever.
370 enc
= System
.Text
.Encoding
.Default
;
374 throw new System
.Exception (System
.String
.Format ("Invalid charset name: {0}", charset
));
378 Value
= enc
.GetString (raw_data
, 8, raw_data
.Length
- 8);
382 public struct CFAPattern
{
384 public ushort Columns
;
385 public byte [] Values
;
387 public CFAPattern (byte [] raw_data
, bool little
)
389 Columns
= EndianConverter
.ToUInt16 (raw_data
, 0, little
);
390 Rows
= EndianConverter
.ToUInt16 (raw_data
, 2, little
);
392 Values
= new byte [Rows
* Columns
];
393 System
.Array
.Copy (raw_data
, 4, Values
, 0, Values
.Length
);
397 * Note the Exif spec defines a CFA pattern tag that includes the row and column counts as shorts
398 * inside the first four bytes of the entry. The Tiff-EP standard define the CFARepeatPattern tag
399 * that contains the row and column counts presumably since the Exif version wouldn't allow you to
400 * alter the endian of the file without knowing the tag layout.
403 public CFAPattern (ushort rows
, ushort cols
, byte [] raw_data
, bool little
)
407 Values
= new byte [rows
* cols
];
408 System
.Array
.Copy (raw_data
, 0, Values
, 0, Values
.Length
);
412 public struct OECFTable
{
414 public ushort Columns
;
415 public string [] Names
;
416 public SRational
[] Values
;
418 public OECFTable (byte [] raw_data
, bool little
)
420 Columns
= EndianConverter
.ToUInt16 (raw_data
, 0, little
);
421 Rows
= EndianConverter
.ToUInt16 (raw_data
, 2, little
);
422 Names
= new string [Columns
];
423 Values
= new SRational
[Columns
* Rows
];
427 int type_size
= DirectoryEntry
.GetTypeSize (EntryType
.SRational
);
429 for (i
= 0; i
< Names
.Length
; i
++)
430 Names
[i
] = ReadString (raw_data
, ref pos
);
432 for (i
= 0; i
< Values
.Length
; i
++)
433 Values
[i
] = new SRational (raw_data
, pos
+ i
* type_size
, little
);
437 public string ReadString (byte [] data
, ref int pos
)
440 for (; pos
< data
.Length
; pos
++) {
444 return System
.Text
.Encoding
.ASCII
.GetString (data
, start
, pos
- start
);
448 public enum ExtraSamples
{
454 public enum FileSource
{
458 public enum PhotometricInterpretation
: ushort {
463 TransparencyMask
= 4,
464 Separated
= 5, // CMYK
469 LogL
= 32844, // Log Luminance
471 ColorFilterArray
= 32803, // ColorFilterArray... the good stuff
472 LinearRaw
= 34892 // DBG LinearRaw
475 public enum PlanarConfiguration
{
480 public enum Compression
{
487 JPEGStream
= 7, // TIFF-EP stores full jpeg stream
492 NikonCompression
= 34713,
493 Deflate_experimental
= 0x80b2
496 public enum JPEGProc
{
497 BaselineSequencial
= 1,
498 LosslessHuffman
= 14,
501 public enum SubfileType
{
503 ReducedResolution
= 2,
507 public enum ExposureProgram
{
510 NormalProgram
= 2, // Normal Program
511 AperturePriority
= 3, // Aperture priority
512 ShutterPriorty
= 4, // Shutter priority
513 CreativeProgram
= 5, // Creative program
514 ActionProgram
= 6, // Action program
515 PortraitMode
= 7, // Portrait mode
516 LandscapeMode
= 8 // Landscape mode
519 public enum ExposureMode
{
525 public enum CustomRendered
: ushort {
531 public enum SceneType
{
532 DirectlyPhotographed
= 1
535 public enum MeteringMode
{
538 CenterWeightedAverage
= 2,
545 public enum SceneCaptureType
: ushort {
552 public enum GainControl
: ushort {
560 public enum Contrast
: ushort {
566 public enum Saturation
: ushort {
572 public enum WhiteBalance
: ushort {
577 public enum Sharpness
: ushort {
583 public enum LightSource
{
592 DaylightFluorescent
= 12,
593 DaylightWhiteFluorescent
= 13,
594 CoolWhiteFluorescent
= 14,
595 WhiteFluorescent
= 15,
603 ISOStudioTungsten
= 24,
607 public enum ColorSpace
: ushort {
608 StandardRGB
= 1, // sRGB
610 Uncalibrated
= 0xffff
613 public enum ComponentsConfiguration
{
622 public enum ResolutionUnit
: ushort {
628 public enum SensingMethod
: short {
630 OneChipColorAreaSensor
= 2,
631 TwoChipColorAreaSensor
= 3,
632 ThreeChipColorAreaSensor
= 4,
633 ColorSequentialAreaSensor
= 5,
635 ColorSequentialLinearSensor
= 8
639 public enum NewSubfileType
: uint {
640 ReducedResolution
= 1,
641 PageOfMultipage
= 1 << 1,
642 TransparencyMask
= 1 << 2
645 public enum EntryType
{
658 Ifd
// TIFF-EP - TIFF PageMaker TechnicalNote 2
663 public EntryType Type
;
666 public string Description
;
669 public class CanonTag
: Tag
{
670 // http://www.gvsoft.homedns.org/exif/makernote-canon.html
672 public enum CanonId
{
674 CameraSettings1
= 0x0001,
676 CameraSettings2
= 0x0004,
678 FirmwareVersion
= 0x0007,
679 ImageNumber
= 0x0008,
682 CameraSerialNumber
= 0x000c,
684 CustomFunctions
= 0x000f
687 public CanonTag (CanonId id
, EntryType type
, int count
, string name
, string description
)
689 this.Id
= (ushort)id
;
693 this.Description
= description
;
696 public static System
.Collections
.Hashtable Tags
;
700 new CanonTag (CanonId
.Unknown1
, EntryType
.Short
, 6, null, null),
701 new CanonTag (CanonId
.CameraSettings1
, EntryType
.Short
, -1, "Camera Settings 1", "First Canon MakerNote settings section"),
702 new CanonTag (CanonId
.Unknown2
, EntryType
.Short
, 4, null, null),
703 new CanonTag (CanonId
.CameraSettings2
, EntryType
.Short
, -1, "Camera Settings 2", "Second Canon MakerNote settings section"),
704 new CanonTag (CanonId
.ImageType
, EntryType
.Ascii
, 32, "Image Type", null), // FIXME description
705 new CanonTag (CanonId
.FirmwareVersion
, EntryType
.Ascii
, 24, "Firmware Version", "Version of the firmware installed on the camera"),
706 new CanonTag (CanonId
.ImageNumber
, EntryType
.Long
, 1, "Image Number", null), // FIXME description
707 new CanonTag (CanonId
.OwnerName
, EntryType
.Long
, 32, "Owner Name", "Name of the Camera Owner"), // FIXME description
708 new CanonTag (CanonId
.Unknown4
, EntryType
.Short
, -1, null, null),
709 new CanonTag (CanonId
.CameraSerialNumber
, EntryType
.Short
, 1, "Serial Number", null), //FIXME description
710 new CanonTag (CanonId
.Unknown4
, EntryType
.Short
, -1, null, null),
711 new CanonTag (CanonId
.CustomFunctions
, EntryType
.Short
, -1, "Custom Functions", "Camera Custom Functions")
714 foreach (CanonTag tag
in tags
)
725 public class Converter
{
726 public static uint ReadUInt (System
.IO
.Stream stream
, Endian endian
)
728 byte [] tmp
= new byte [4];
730 if (stream
.Read (tmp
, 0, tmp
.Length
) < 4)
731 throw new System
.Exception ("Short Read");
733 return EndianConverter
.ToUInt32 (tmp
, 0, endian
== Endian
.Little
);
736 public static ushort ReadUShort (System
.IO
.Stream stream
, Endian endian
)
738 byte [] tmp
= new byte [2];
740 if (stream
.Read (tmp
, 0, tmp
.Length
) < 2)
741 throw new System
.Exception ("Short Read");
743 return EndianConverter
.ToUInt16 (tmp
, 0, endian
== Endian
.Little
);
747 public class Header
{
748 public Endian endian
;
750 private uint directory_offset
;
751 public ImageDirectory Directory
;
753 public Header (System
.IO
.Stream stream
)
755 byte [] data
= new byte [8];
756 stream
.Read (data
, 0, data
.Length
);
757 if (data
[0] == 'M' && data
[1] == 'M')
759 else if (data
[0] == 'I' && data
[1] == 'I')
760 endian
= Endian
.Little
;
762 ushort marker
= EndianConverter
.ToUInt16 (data
, 2, endian
== Endian
.Little
);
765 //System.Console.WriteLine ("Found Standard Tiff Marker {0}", marker);
768 System
.Console
.WriteLine ("Found Olympus Tiff Marker {0}", marker
.ToString ("x"));
771 System
.Console
.WriteLine ("Found Navy Interchange File Format Tiff Marker {0}", marker
.ToString ("x"));
774 System
.Console
.WriteLine ("Found Unknown Tiff Marker {0}", marker
.ToString ("x"));
778 //System.Console.WriteLine ("Converting Something");
779 directory_offset
= EndianConverter
.ToUInt32 (data
, 4, endian
== Endian
.Little
);
781 if (directory_offset
< 8)
782 throw new System
.Exception ("Invalid IFD0 Offset [" + directory_offset
.ToString () + "]");
784 //System.Console.WriteLine ("Reading First IFD");
785 Directory
= new ImageDirectory (stream
, directory_offset
, endian
);
790 public void Select (SemWeb
.StatementSink sink
)
792 SelectDirectory (Directory
, sink
);
795 public void SelectDirectory (ImageDirectory dir
, StatementSink sink
)
797 foreach (DirectoryEntry e
in dir
.Entries
) {
798 //System.Console.WriteLine ("{0}", e.Id);
801 System
.IO
.Stream iptcstream
= new System
.IO
.MemoryStream (e
.RawData
);
802 FSpot
.Iptc
.IptcFile iptc
= new FSpot
.Iptc
.IptcFile (iptcstream
);
805 case TagId
.PhotoshopPrivate
:
806 System
.IO
.Stream bimstream
= new System
.IO
.MemoryStream (e
.RawData
);
807 FSpot
.Bim
.BimFile bim
= new FSpot
.Bim
.BimFile (bimstream
);
811 System
.IO
.Stream xmpstream
= new System
.IO
.MemoryStream (e
.RawData
);
812 FSpot
.Xmp
.XmpFile xmp
= new FSpot
.Xmp
.XmpFile (xmpstream
);
815 case TagId
.ImageDescription
:
816 MetadataStore
.AddLiteral (sink
, "dc:description", "rdf:Alt",
817 new Literal (e
.ValueAsString
[0], "x-default", null));
819 case TagId
.UserComment
:
820 MetadataStore
.AddLiteral (sink
, "exif:UserComment", "rdf:Alt",
821 new Literal (e
.ValueAsString
[0], "x-default", null));
823 case TagId
.Copyright
:
824 MetadataStore
.AddLiteral (sink
, "dc:rights", "rdf:Alt",
825 new Literal (e
.ValueAsString
[0], "x-default", null));
828 MetadataStore
.Add (sink
, "dc:creator", "rdf:Seq", e
.ValueAsString
);
830 case TagId
.ExifIfdPointer
:
832 ImageDirectory sub
= ((SubdirectoryEntry
)e
).Directory
[0];
833 SelectDirectory (sub
, sink
);
834 } catch (System
.Exception exc
) {
835 System
.Console
.WriteLine (exc
);
839 MetadataStore
.AddLiteral (sink
, "xmp:CreatorTool", e
.ValueAsString
[0]);
844 MetadataStore
.AddLiteral (sink
, "xmp:ModifyDate",
845 e
.ValueAsDate
.ToString ("yyyy-MM-ddThh:mm:ss"));
846 } catch (System
.Exception ex
) {
847 System
.Console
.WriteLine (String
.Format ("error parsing {0}\n{1}", e
.ValueAsString
[0], ex
));
851 case TagId
.DateTimeOriginal
:
852 case TagId
.DateTimeDigitized
:
853 // FIXME subsectime needs to be included in these values
854 // FIXME shouldn't DateTimeOriginal be xmp:CreateDate? the spec says no but wtf?
856 MetadataStore
.AddLiteral (sink
, "exif:" + e
.Id
.ToString (),
857 e
.ValueAsDate
.ToString ("yyyy-MM-ddThh:mm:ss"));
858 } catch (System
.Exception ex
) {
859 System
.Console
.WriteLine (String
.Format ("error parsing {0}\n{1}", e
.ValueAsString
[0], ex
));
862 //case TagId.SpatialFrequencyResponse
863 case TagId
.ExifCFAPattern
:
864 CFAPattern pattern
= new CFAPattern (e
.RawData
, e
.IsLittle
);
865 Entity empty
= new Entity (null);
866 Statement top
= new Statement ("",
867 (Entity
)MetadataStore
.Namespaces
.Resolve ("exif:" + e
.Id
.ToString ()),
870 Statement cols
= new Statement (empty
,
871 (Entity
) MetadataStore
.Namespaces
.Resolve ("exif:Columns"),
872 new Literal (pattern
.Columns
.ToString (), null, null));
874 Statement rows
= new Statement (empty
,
875 (Entity
) MetadataStore
.Namespaces
.Resolve ("exif:Rows"),
876 new Literal (pattern
.Rows
.ToString (), null, null));
878 string [] vals
= e
.ArrayToString (pattern
.Values
);
879 MetadataStore
.Add (sink
, empty
, "exif:Values", "rdf:Seq", vals
);
882 case TagId
.ExifVersion
:
883 case TagId
.FlashPixVersion
:
884 case TagId
.ColorSpace
:
885 case TagId
.CompressedBitsPerPixel
:
886 case TagId
.PixelYDimension
:
887 case TagId
.PixelXDimension
:
888 case TagId
.RelatedSoundFile
:
889 case TagId
.ExposureTime
:
891 case TagId
.ExposureProgram
:
892 case TagId
.SpectralSensitivity
:
893 case TagId
.ShutterSpeedValue
:
894 case TagId
.ApertureValue
:
895 case TagId
.BrightnessValue
:
896 case TagId
.ExposureBiasValue
:
897 case TagId
.MaxApertureValue
:
898 case TagId
.SubjectDistance
:
899 case TagId
.MeteringMode
:
900 case TagId
.LightSource
:
901 case TagId
.FocalLength
:
902 case TagId
.FlashEnergy
:
903 case TagId
.FocalPlaneXResolution
:
904 case TagId
.FocalPlaneYResolution
:
905 case TagId
.FocalPlaneResolutionUnit
:
906 case TagId
.ExposureIndex
:
907 case TagId
.SensingMethod
:
908 case TagId
.FileSource
:
909 case TagId
.SceneType
:
910 case TagId
.CustomRendered
:
911 case TagId
.ExposureMode
:
912 case TagId
.WhiteBalance
:
913 case TagId
.DigitalZoomRatio
:
914 case TagId
.FocalLengthIn35mmFilm
:
915 case TagId
.SceneCaptureType
:
916 case TagId
.GainControl
:
918 case TagId
.Saturation
:
919 case TagId
.Sharpness
:
920 MetadataStore
.AddLiteral (sink
, "exif:" + e
.Id
.ToString (), e
.ValueAsString
[0]);
922 case TagId
.ComponentsConfiguration
:
923 case TagId
.ISOSpeedRatings
:
924 case TagId
.SubjectArea
:
925 case TagId
.SubjectLocation
:
926 MetadataStore
.Add (sink
, "exif:" + e
.Id
.ToString (), "rdf:Seq", e
.ValueAsString
);
928 case TagId
.TransferFunction
:
929 case TagId
.YCbCrSubSampling
:
930 case TagId
.WhitePoint
:
931 case TagId
.PrimaryChromaticities
:
932 case TagId
.YCbCrCoefficients
:
933 case TagId
.ReferenceBlackWhite
:
934 case TagId
.BitsPerSample
:
935 MetadataStore
.Add (sink
, "tiff:" + e
.Id
.ToString (), "rdf:Seq", e
.ValueAsString
);
937 case TagId
.Orientation
:
938 case TagId
.Compression
:
939 case TagId
.PhotometricInterpretation
:
940 case TagId
.SamplesPerPixel
:
941 case TagId
.PlanarConfiguration
:
942 case TagId
.YCbCrPositioning
:
943 case TagId
.ResolutionUnit
:
944 case TagId
.ImageWidth
:
945 case TagId
.ImageLength
:
948 MetadataStore
.AddLiteral (sink
, "tiff:" + e
.Id
.ToString (), e
.ValueAsString
[0]);
955 public void Dump (string name
)
957 ImageDirectory ifd
= Directory
;
958 for (int i
= 0; ifd
!= null; i
++) {
959 ifd
.Dump (System
.String
.Format ("IFD[{0}]:", i
));
960 ifd
= ifd
.NextDirectory
;
965 public class ImageDirectory
{
966 protected Endian endian
;
967 protected ushort num_entries
;
968 protected System
.Collections
.ArrayList entries
;
969 protected uint orig_position
;
971 protected uint next_directory_offset
;
972 ImageDirectory next_directory
;
974 protected bool has_header
;
975 protected bool has_footer
;
977 public ImageDirectory (System
.IO
.Stream stream
, uint start_position
, Endian endian
)
979 this.endian
= endian
;
980 orig_position
= start_position
;
984 protected void Load (System
.IO
.Stream stream
)
987 ReadEntries (stream
);
990 LoadEntries (stream
);
991 LoadNextDirectory (stream
);
994 public virtual bool ReadHeader (System
.IO
.Stream stream
)
996 stream
.Seek ((long)orig_position
, System
.IO
.SeekOrigin
.Begin
);
1000 protected virtual void ReadEntries (System
.IO
.Stream stream
)
1002 num_entries
= Converter
.ReadUShort (stream
, endian
);
1003 //System.Console.WriteLine ("reading {0} entries", num_entries);
1005 entries
= new System
.Collections
.ArrayList (num_entries
);
1006 int entry_length
= num_entries
* 12;
1007 byte [] content
= new byte [entry_length
];
1009 if (stream
.Read (content
, 0, content
.Length
) < content
.Length
)
1010 throw new System
.Exception ("Short Read");
1012 for (int pos
= 0; pos
< entry_length
; pos
+= 12) {
1013 DirectoryEntry entry
= EntryFactory
.CreateEntry (this, content
, pos
, this.endian
);
1014 entries
.Add (entry
);
1015 //System.Console.WriteLine ("Added Entry {0} {1} - {2} * {3}", entry.Id.ToString (), entry.Id.ToString ("x"), entry.Type, entry.Count);
1016 if (entry
.Id
== TagId
.NewSubfileType
) {
1022 protected virtual void ReadFooter (System
.IO
.Stream stream
)
1024 next_directory_offset
= Converter
.ReadUInt (stream
, this.endian
);
1027 protected void LoadEntries (System
.IO
.Stream stream
)
1029 foreach (DirectoryEntry entry
in entries
) {
1030 entry
.LoadExternal (stream
);
1034 protected void LoadNextDirectory (System
.IO
.Stream stream
)
1036 //System.Console.WriteLine ("next_directory_offset = {0}", next_directory_offset);
1037 next_directory
= null;
1039 if (next_directory_offset
!= 0)
1040 next_directory
= new ImageDirectory (stream
, next_directory_offset
, this.endian
);
1043 //System.Console.WriteLine ("Error loading directory {0}", e.ToString ());
1044 next_directory
= null;
1045 next_directory_offset
= 0;
1049 public ImageDirectory NextDirectory
{
1051 return next_directory
;
1055 public System
.Collections
.ArrayList Entries
{
1061 public DirectoryEntry
Lookup (TagId id
)
1063 foreach (DirectoryEntry entry
in entries
)
1071 public DirectoryEntry
Lookup (uint id
)
1073 foreach (DirectoryEntry entry
in entries
)
1074 if ((uint)entry
.Id
== id
)
1080 public void Dump (string name
)
1082 System
.Console
.WriteLine ("Starting {0}", name
);
1083 foreach (DirectoryEntry e
in this.Entries
)
1085 System
.Console
.WriteLine ("Ending {0}", name
);
1088 public string Dump2 ()
1090 System
.Text
.StringBuilder builder
= new System
.Text
.StringBuilder ();
1091 builder
.Append ("Dummping IFD");
1092 foreach (DirectoryEntry entry
in entries
) {
1093 builder
.Append (entry
.ToString ()+ "\n");
1095 if (entry
is SubdirectoryEntry
)
1096 builder
.Append ("Found SUBDIRECTORYENTRY\n");
1099 if (next_directory
!= null) {
1100 builder
.Append ("Dummping Next IFD");
1101 builder
.Append (next_directory
.Dump2 ());
1104 return builder
.ToString ();
1108 public class EntryFactory
{
1109 //public delegate DirectoryEntry ConstructorFunc (byte [], Endian endian);
1110 //public static System.Collections.Hashtable ctors = new System.Collections.Hashtable ();
1112 public static DirectoryEntry
CreateEntry (ImageDirectory parent
, byte [] input
, int start
, Endian header_endian
)
1117 DirectoryEntry
.ParseHeader (input
, start
, out tagid
, out type
, header_endian
);
1118 //ConstructorFunc ctor = ctors[tagid];
1119 //if (ctor == null) {
1120 // return ctor (input, header_endian);
1124 case TagId
.ExifIfdPointer
:
1125 case TagId
.GPSInfoIfdPointer
:
1126 case TagId
.InteroperabilityIfdPointer
:
1128 return new SubdirectoryEntry (input
, start
, header_endian
);
1129 //case TagId.MakerNote:
1130 //return new MakerNoteEntry (input, start, header_endian);
1131 //case TagId.PimIfdPointer:
1133 //case TagId.MakerNote:
1134 //return new MakerNoteEntry (input, start, header_endian);
1139 //System.Console.WriteLine ("Trying to load {0} {1}", tagid, tagid.ToString ("x"));
1140 return new SubdirectoryEntry (input
, start
, header_endian
);
1141 case EntryType
.Byte
:
1142 return new ByteEntry (input
, start
, header_endian
);
1143 case EntryType
.Long
:
1144 return new LongEntry (input
, start
, header_endian
);
1145 case EntryType
.Short
:
1146 return new ShortEntry (input
, start
, header_endian
);
1149 return new DirectoryEntry (input
, start
, header_endian
);
1153 public class MakerNoteEntry
: SubdirectoryEntry
{
1154 public MakerNoteEntry (byte [] data
, int offset
, Endian endian
) : base (data
, offset
, endian
)
1159 public override uint GetEntryCount ()
1164 public override void LoadExternal (System
.IO
.Stream stream
)
1170 public class SubdirectoryEntry
: DirectoryEntry
{
1171 public uint directory_offset
;
1172 public ImageDirectory
[] Directory
;
1174 public SubdirectoryEntry (byte [] data
, int offset
, Endian endian
) : base (data
, offset
, endian
)
1176 if (this.GetEntryCount () > 1) {
1177 System
.Console
.WriteLine ("Count is greater than 1 ({1}) on Subdirectory {0} interesting", tagid
, count
);
1181 public virtual uint GetEntryCount ()
1186 public override void LoadExternal (System
.IO
.Stream stream
)
1188 uint entry_count
= GetEntryCount ();
1189 Directory
= new ImageDirectory
[entry_count
];
1191 base.LoadExternal (stream
);
1193 for (int i
= 0; i
< entry_count
; i
++) {
1195 directory_offset
= EndianConverter
.ToUInt32 (raw_data
, i
* 4, endian
== Endian
.Little
);
1196 Directory
[i
] = new ImageDirectory (stream
, directory_offset
, endian
);
1197 } catch (System
.Exception e
) {
1198 System
.Console
.WriteLine ("Error loading Subdirectory {0} at {2} of {3}bytes:\n{1}",
1199 this.Id
, e
, directory_offset
, stream
.Length
);
1205 public override void Dump (string name
)
1207 for (int i
= 0; i
< GetEntryCount (); i
++) {
1208 string subdirname
= System
.String
.Format ("{0}{1}[{2}]({3})]", name
, tagid
, i
, directory_offset
);
1211 if (Directory
[i
] != null)
1212 Directory
[i
].Dump (subdirname
);
1213 } catch (System
.Exception e
) {
1214 System
.Console
.WriteLine (e
);
1220 public class ShortEntry
: DirectoryEntry
{
1221 public ShortEntry (byte [] data
, int offset
, Endian endian
) : base (data
, offset
, endian
)
1226 public class LongEntry
: DirectoryEntry
{
1227 public LongEntry (byte [] data
, int offset
, Endian endian
) : base (data
, offset
, endian
)
1229 if (type
!= EntryType
.Long
)
1230 throw new System
.Exception (System
.String
.Format ("Invalid Settings At Birth {0}", tagid
));
1234 public class ByteEntry
: DirectoryEntry
{
1235 public ByteEntry (byte [] data
, int offset
, Endian endian
) : base (data
, offset
, endian
)
1237 if (type
!= EntryType
.Byte
)
1238 throw new System
.Exception ("Invalid Settings At Birth");
1243 public class ImageLoader
{
1247 PhotometricInterpretation interpretation
;
1248 Compression compression
;
1250 uint [] strip_byte_counts
;
1251 uint rows_per_strip
;
1254 public ImageLoader (ImageDirectory directory
)
1256 width
= directory
.Lookup (TagId
.ImageWidth
).ValueAsLong
[0];
1257 length
= directory
.Lookup (TagId
.ImageLength
).ValueAsLong
[0];
1258 bps
= directory
.Lookup (TagId
.BitsPerSample
).ValueAsLong
;
1260 compression
= (Compression
) directory
.Lookup (TagId
.Compression
).ValueAsLong
[0];
1261 interpretation
= (PhotometricInterpretation
) directory
.Lookup (TagId
.PhotometricInterpretation
).ValueAsLong
[0];
1263 offsets
= directory
.Lookup (TagId
.StripOffsets
).ValueAsLong
;
1264 strip_byte_counts
= directory
.Lookup (TagId
.StripByteCounts
).ValueAsLong
;
1265 rows_per_strip
= directory
.Lookup (TagId
.RowsPerStrip
).ValueAsLong
[0];
1267 if (interpretation
!=
1271 public Gdk
.Pixbuf
LoadPixbuf (System
.IO
.Stream stream
)
1273 Gdk
.Pixbuf dest
= new Gdk
.Pixbuf (Gdk
.Colorspace
.Rgb
, false, width
, height
);
1274 strip
= new byte [strip_byte_counts
];
1276 for (int i
= 0; i
< offsets
.Length
; i
++) {
1277 strip
= new byte [strip_byte_counts
[i
]];
1278 stream
.Read (strip
, 0, strip
.Length
);
1279 switch (compression
) {
1280 case Compression
.Notice
1288 public class DirectoryEntry
{
1289 protected TagId tagid
;
1290 protected EntryType type
;
1291 protected uint count
;
1292 protected uint offset_origin
;
1293 protected uint data_offset
;
1295 protected byte [] raw_data
;
1296 protected Endian endian
;
1304 public EntryType Type
{
1316 public void SetOrigin (uint pos
)
1318 offset_origin
= pos
;
1321 public uint Position
{
1323 return offset_origin
+ data_offset
;
1327 public virtual int GetTypeSize ()
1329 return GetTypeSize (type
);
1332 public static int GetTypeSize (EntryType type
)
1335 case EntryType
.Byte
:
1336 case EntryType
.SByte
:
1337 case EntryType
.Undefined
:
1338 case EntryType
.Ascii
:
1340 case EntryType
.Short
:
1341 case EntryType
.SShort
:
1343 case EntryType
.Long
:
1344 case EntryType
.SLong
:
1345 case EntryType
.Float
:
1347 case EntryType
.Double
:
1348 case EntryType
.Rational
:
1349 case EntryType
.SRational
:
1356 public bool IsLittle
{
1358 return (endian
== Endian
.Little
);
1362 public static int ParseHeader (byte [] data
, int start
, out TagId tagid
, out EntryType type
, Endian endian
)
1364 tagid
= (TagId
) EndianConverter
.ToUInt16 (data
, start
, endian
== Endian
.Little
);
1365 type
= (EntryType
) EndianConverter
.ToUInt16 (data
, start
+ 2, endian
== Endian
.Little
);
1369 public DirectoryEntry (byte [] data
, int start
, Endian endian
)
1371 this.endian
= endian
;
1373 start
+= ParseHeader (data
, start
, out this.tagid
, out this.type
, endian
);
1374 ParseStream (data
, start
);
1377 public virtual void LoadExternal (System
.IO
.Stream stream
)
1379 if (data_offset
!= 0) {
1380 stream
.Seek ((long)Position
, System
.IO
.SeekOrigin
.Begin
);
1381 byte [] data
= new byte [count
* GetTypeSize ()];
1382 if (stream
.Read (data
, 0, data
.Length
) < data
.Length
)
1383 throw new System
.Exception ("Short Read");
1388 switch ((int)this.Id
) {
1389 case (int)TagId
.NewSubfileType
:
1390 System
.Console
.WriteLine ("XXXXXXXXXXXXXXXXXXXXX new NewSubFileType {0}", (NewSubfileType
) this.ValueAsLong
[0]);
1392 case (int)TagId
.SubfileType
:
1393 System
.Console
.WriteLine ("XXXXXXXXXXXXXXXXXXXXX new SubFileType {0}", (SubfileType
) this.ValueAsLong
[0]);
1395 case (int)TagId
.Compression
:
1396 //System.Console.WriteLine ("XXXXXXXXXXXXXXXXXXXXX new Compression {0}", (Compression) this.ValueAsLong [0]);
1399 case (int)TagId
.JPEGProc
:
1400 //System.Console.WriteLine ("XXXXXXXXXXXXXXXXXXXXX new JPEGProc {0}", (JPEGProc) this.ValueAsLong [0]);
1403 case (int)TagId
.PhotometricInterpretation
:
1404 //System.Console.WriteLine ("XXXXXXXXXXXXXXXXXXXXX new PhotometricInterpretation {0}", (PhotometricInterpretation) this.ValueAsLong [0]);
1406 case (int)TagId
.ImageWidth
:
1407 case (int)TagId
.ImageLength
:
1408 //System.Console.WriteLine ("XXXXXXXXXXXXXXXXXXXXX new {1} {0}", this.ValueAsLong [0], this.Id);
1413 System
.Console
.WriteLine ("XXXXXXXXXXXXXXXXXXXXX {0}({1}) - {2} {3}", this.Id
, this.Id
.ToString ("x"), this.type
, raw_data
.Length
);
1414 System
.Console
.WriteLine ("XXXX ", System
.Text
.Encoding
.ASCII
.GetString (raw_data
));
1415 switch (this.type
) {
1416 case EntryType
.Long
:
1417 foreach (uint val
in ((LongEntry
)this).Value
)
1418 System
.Console
.Write (" {0}", val
);
1420 case EntryType
.Short
:
1421 foreach (ushort val
in ((ShortEntry
)this).ShortValue
)
1422 System
.Console
.Write (" {0}", val
);
1424 case EntryType
.Byte
:
1425 foreach (byte val
in this.RawData
)
1426 System
.Console
.Write (" {0}", val
);
1429 System
.Console
.WriteLine ("");
1435 public virtual void Dump (string name
)
1437 switch (this.Type
) {
1438 case EntryType
.Short
:
1439 case EntryType
.Long
:
1440 uint [] vals
= this.ValueAsLong
;
1441 System
.Console
.Write ("{3}{1}({2}) [{0}] (", vals
.Length
, this.Id
, this.Type
, name
);
1442 for (int i
= 0; i
< System
.Math
.Min (15, vals
.Length
); i
++) {
1443 System
.Console
.Write (" {0}", vals
[i
]);
1445 System
.Console
.WriteLine (")");
1447 case EntryType
.Ascii
:
1448 System
.Console
.WriteLine ("{3}{1}({2}) (\"{0}\")", this.StringValue
, this.Id
, this.Type
, name
);
1451 System
.Console
.WriteLine ("{3}{1}({2}) [{0}]", this.Count
, this.Id
, this.Type
, name
);
1456 protected void ParseStream (byte [] data
, int start
)
1460 count
= EndianConverter
.ToUInt32 (data
, i
, endian
== Endian
.Little
);
1462 int size
= (int)count
* GetTypeSize ();
1464 data_offset
= EndianConverter
.ToUInt32 (data
, i
, endian
== Endian
.Little
);
1467 raw_data
= new byte [size
];
1468 System
.Array
.Copy (data
, i
, raw_data
, 0, size
);
1472 public void SetData (string value)
1474 int len
= System
.Text
.Encoding
.UTF8
.GetByteCount (value);
1475 byte [] tmp
= new byte [len
+ 1];
1476 System
.Text
.Encoding
.UTF8
.GetBytes (value, 0, value.Length
, tmp
, 0);
1478 System
.Console
.WriteLine ("SetData: value = {0} len = {1}", value, len
);
1482 public static System
.DateTime
DateTimeFromString (string dt
)
1484 // Exif DateTime strings are formatted as
1485 // "YYYY:MM:DD HH:MM:SS"
1487 string delimiters
= " :";
1488 string[] dt_data
= dt
.Split ( delimiters
.ToCharArray(), 6 );
1489 System
.DateTime result
;
1490 result
= new System
.DateTime (System
.Int32
.Parse(dt_data
[0]),
1491 System
.Int32
.Parse(dt_data
[1]),
1492 System
.Int32
.Parse(dt_data
[2]),
1493 System
.Int32
.Parse(dt_data
[3]),
1494 System
.Int32
.Parse(dt_data
[4]),
1495 System
.Int32
.Parse(dt_data
[5]));
1500 public void SetData (byte [] data
)
1503 count
= (uint)raw_data
.Length
/ (uint)GetTypeSize ();
1507 public object GetValue () {
1509 case EntryType
.Short
:
1511 case EntryType
.Long
:
1513 case EntryType
.Rational
:
1514 return RationalValue
;
1515 case EntryType
.SRational
:
1516 return SRationalValue
;
1517 case EntryType
.Ascii
:
1518 return StringValue
.Split ('\0');
1521 System
.Console
.WriteLine ("{1}({2}) [{0}]", this.Count
, this.Id
, this.Type
);
1529 public byte [] Value
{
1535 public byte [] RawData
{
1541 public string [] ValueAsString
{
1543 switch (this.Type
) {
1544 case EntryType
.Short
:
1545 case EntryType
.Long
:
1546 return ArrayToString (this.ValueAsLong
);
1547 case EntryType
.Rational
:
1548 return ArrayToString (this.RationalValue
);
1549 case EntryType
.SRational
:
1550 return ArrayToString (this.SRationalValue
);
1551 case EntryType
.Undefined
:
1553 case TagId
.UserComment
:
1554 return new string [] { UserCommentValue }
;
1555 case TagId
.FlashPixVersion
:
1556 case TagId
.ExifVersion
:
1557 return new string [] { StringValue }
;
1558 case TagId
.FileSource
:
1559 case TagId
.SceneType
:
1560 return ArrayToString (this.RawData
);
1561 case TagId
.ComponentsConfiguration
:
1562 return ArrayToString (ValueAsLong
);
1564 System
.Console
.WriteLine ("Cannot convert type \"{0}\" to string", Id
);
1568 case EntryType
.Ascii
:
1569 return StringValue
.Split ('\0');
1575 public string [] ArrayToString (System
.Array array
)
1577 string [] vals
= new string [array
.Length
];
1578 for (int i
= 0; i
< array
.Length
; i
++)
1579 vals
[i
] = array
.GetValue (i
).ToString ();
1584 public uint [] ValueAsLong
{
1586 uint [] data
= new uint [this.Count
];
1587 for (int i
= 0; i
< this.Count
; i
++) {
1588 switch (this.Type
) {
1589 case EntryType
.Long
:
1590 data
[i
] = EndianConverter
.ToUInt32 (raw_data
, i
* GetTypeSize (), endian
== Endian
.Little
);
1592 case EntryType
.Short
:
1593 data
[i
] = EndianConverter
.ToUInt16 (raw_data
, i
* GetTypeSize (), endian
== Endian
.Little
);
1595 case EntryType
.Undefined
:
1596 case EntryType
.Byte
:
1597 data
[i
] = raw_data
[i
];
1600 throw new System
.Exception ("Invalid conversion");
1607 // The following methods are usded to convert the data
1608 // to the various type regardless of the entry
1609 // type, they are used internally in processing the data
1610 // Use at your own risk.
1612 public string StringValue
{
1614 return System
.Text
.Encoding
.ASCII
.GetString (raw_data
);
1618 public System
.DateTime ValueAsDate
{
1620 return DirectoryEntry
.DateTimeFromString (StringValue
);
1624 public string UserCommentValue
{
1626 UserComment comment
= new UserComment (raw_data
, IsLittle
);
1627 return comment
.Value
;
1631 public SRational
[] SRationalValue
{
1633 Rational
[] vals
= RationalValue
;
1634 SRational
[] data
= new SRational
[vals
.Length
];
1636 for (int i
= 0; i
< vals
.Length
; i
++)
1637 data
[i
] = SRational
.BitwiseCopy (vals
[i
]);
1643 public Rational
[] RationalValue
{
1645 uint [] vals
= LongValue
;
1646 Rational
[] data
= new Rational
[vals
.Length
/ 2];
1648 for (int i
= 0; i
< vals
.Length
; i
+= 2)
1649 data
[i
/2] = new Rational (vals
[i
], vals
[i
+ 1]);
1656 public uint [] LongValue
{
1658 uint [] data
= new uint [raw_data
.Length
/ 4];
1659 for (int i
= 0; i
< raw_data
.Length
; i
+= 4)
1660 data
[i
/4] = EndianConverter
.ToUInt32 (raw_data
, i
, endian
== Endian
.Little
);
1666 public ushort [] ShortValue
{
1668 ushort [] data
= new ushort [raw_data
.Length
];
1669 for (int i
= 0; i
< raw_data
.Length
; i
+= 2) {
1670 data
[i
] = EndianConverter
.ToUInt16 (raw_data
, i
, endian
== Endian
.Little
);
1678 public class TiffFile
: ImageFile
, SemWeb
.StatementSource
{
1679 public Header Header
;
1681 public TiffFile (string path
) : base (path
)
1684 using (System
.IO
.Stream input
= System
.IO
.File
.OpenRead (path
)) {
1685 this.Header
= new Header (input
);
1689 Header
.Dump (this.ToSring () + ":");
1691 } catch (System
.Exception e
) {
1692 System
.Console
.WriteLine (e
.ToString ());
1697 public virtual void Select (SemWeb
.StatementSink sink
)
1699 Header
.SelectDirectory (Header
.Directory
, sink
);
1702 public override System
.DateTime Date
{
1704 SubdirectoryEntry sub
= (SubdirectoryEntry
) this.Header
.Directory
.Lookup (TagId
.ExifIfdPointer
);
1708 e
= sub
.Directory
[0].Lookup (TagId
.DateTimeOriginal
);
1711 return DirectoryEntry
.DateTimeFromString (e
.StringValue
);
1714 e
= this.Header
.Directory
.Lookup (TagId
.DateTime
);
1717 return DirectoryEntry
.DateTimeFromString (e
.StringValue
);
1723 public override System
.IO
.Stream
PixbufStream ()
1728 public override PixbufOrientation
GetOrientation ()
1730 ShortEntry e
= (ShortEntry
)(this.Header
.Directory
.Lookup (TagId
.Orientation
));
1732 return (PixbufOrientation
)(e
.ShortValue
[0]);
1734 return PixbufOrientation
.TopLeft
;
1737 public System
.IO
.Stream
LookupJpegSubstream (ImageDirectory directory
)
1739 uint offset
= directory
.Lookup (TagId
.JPEGInterchangeFormat
).ValueAsLong
[0];
1741 System
.IO
.Stream file
= System
.IO
.File
.OpenRead (this.path
);
1742 file
.Position
= offset
;
1746 public Gdk
.Pixbuf
LoadJpegInterchangeFormat (ImageDirectory directory
)
1748 uint offset
= directory
.Lookup (TagId
.JPEGInterchangeFormat
).ValueAsLong
[0];
1749 uint length
= directory
.Lookup (TagId
.JPEGInterchangeFormatLength
).ValueAsLong
[0];
1751 using (System
.IO
.Stream file
= System
.IO
.File
.OpenRead (this.path
)) {
1752 file
.Position
= offset
;
1754 byte [] data
= new byte [32768];
1757 Gdk
.PixbufLoader loader
= new Gdk
.PixbufLoader ();
1759 while (length
> 0) {
1760 read
= file
.Read (data
, 0, (int)System
.Math
.Min ((int)data
.Length
, length
));
1764 loader
.Write (data
, (ulong)read
);
1765 length
-= (uint) read
;
1767 Gdk
.Pixbuf result
= loader
.Pixbuf
;
1774 public class DngFile
: TiffFile
{
1775 public DngFile (string path
) : base (path
)
1779 public override System
.IO
.Stream
PixbufStream ()
1782 SubdirectoryEntry sub
= (SubdirectoryEntry
) Header
.Directory
.Lookup (TagId
.SubIFDs
);
1783 ImageDirectory directory
= sub
.Directory
[sub
.Directory
.Length
- 1];
1785 uint offset
= directory
.Lookup (TagId
.StripOffsets
).ValueAsLong
[0];
1786 System
.IO
.Stream file
= System
.IO
.File
.OpenRead (this.path
);
1787 file
.Position
= offset
;
1790 return DCRawFile
.RawPixbufStream (path
);
1794 public override Gdk
.Pixbuf
Load ()
1796 return DCRawFile
.Load (this.path
, null);
1799 public override void Select (SemWeb
.StatementSink sink
)
1802 /* this is just a sanity pass, if the first ifd is not a subfile use the normal
1805 DirectoryEntry e
= Header
.Directory
.Lookup (TagId
.NewSubfileType
);
1812 * Even though Ifd0 doesn't have the full resolution image
1813 * it would have the XMP data so we look for it
1815 e
= Header
.Directory
.Lookup (TagId
.XMP
);
1817 System
.IO
.Stream xmpstream
= new System
.IO
.MemoryStream (e
.RawData
);
1818 FSpot
.Xmp
.XmpFile xmp
= new FSpot
.Xmp
.XmpFile (xmpstream
);
1823 * Ifd0 will also have the exif directory
1825 ImageDirectory dir
= Header
.Directory
;
1826 SubdirectoryEntry sub
= (SubdirectoryEntry
) dir
.Lookup (TagId
.ExifIfdPointer
);
1828 Header
.SelectDirectory (sub
.Directory
[0], sink
);
1831 * now we lookup subifd0 (we should probably scan the newsubfile types here)
1832 * and load the metadata we are interested in from it.
1834 sub
= (SubdirectoryEntry
) Header
.Directory
.Lookup (TagId
.SubIFDs
);
1838 uint dirtype
= e
.ValueAsLong
[0];
1840 Header
.SelectDirectory (dir
, sink
);
1847 dir
= sub
.Directory
[i
];
1848 e
= dir
.Lookup (TagId
.NewSubfileType
);
1850 } while (i
< sub
.Directory
.Length
);
1856 public class NefFile
: TiffFile
, IThumbnailContainer
{
1857 public NefFile (string path
) : base (path
)
1861 public override void Select (SemWeb
.StatementSink sink
)
1863 DirectoryEntry e
= Header
.Directory
.Lookup (TagId
.NewSubfileType
);
1870 ImageDirectory dir
= Header
.Directory
;
1871 SubdirectoryEntry sub
= (SubdirectoryEntry
) dir
.Lookup (TagId
.ExifIfdPointer
);
1874 Header
.SelectDirectory (sub
.Directory
[0], sink
);
1876 sub
= (SubdirectoryEntry
) Header
.Directory
.Lookup (TagId
.SubIFDs
);
1880 uint dirtype
= e
.ValueAsLong
[0];
1882 Header
.SelectDirectory (dir
, sink
);
1889 dir
= sub
.Directory
[i
];
1890 e
= dir
.Lookup (TagId
.NewSubfileType
);
1892 } while (i
< sub
.Directory
.Length
);
1895 public Gdk
.Pixbuf
GetEmbeddedThumbnail ()
1897 return TransformAndDispose (new Gdk
.Pixbuf (path
));
1900 public override System
.IO
.Stream
PixbufStream ()
1903 SubdirectoryEntry sub
= (SubdirectoryEntry
) Header
.Directory
.Lookup (TagId
.SubIFDs
);
1904 ImageDirectory jpeg_directory
= sub
.Directory
[0];
1905 return LookupJpegSubstream (jpeg_directory
);
1906 } catch (System
.Exception e
) {
1907 return DCRawFile
.RawPixbufStream (path
);
1911 public override Gdk
.Pixbuf
Load ()
1913 Gdk
.Pixbuf pixbuf
= null;
1914 System
.Console
.WriteLine ("starting load");
1917 SubdirectoryEntry sub
= (SubdirectoryEntry
) Header
.Directory
.Lookup (TagId
.SubIFDs
);
1918 ImageDirectory jpeg_directory
= sub
.Directory
[0];
1920 pixbuf
= LoadJpegInterchangeFormat (jpeg_directory
);
1921 } catch (System
.Exception e
) {
1922 System
.Console
.WriteLine (e
);
1927 return DCRawFile
.Load (this.Path
, null);
1929 return TransformAndDispose (pixbuf
);
1934 public class Cr2File
: TiffFile
, IThumbnailContainer
{
1936 public Cr2File (string path
) : base (path
)
1941 public override PixbufOrientation GetOrientation ()
1943 return PixbufOrientation.TopLeft;
1947 public Gdk
.Pixbuf
GetEmbeddedThumbnail ()
1949 ImageDirectory directory
;
1950 directory
= Header
.Directory
.NextDirectory
;
1951 return TransformAndDispose (LoadJpegInterchangeFormat (directory
));
1955 public override System
.IO
.Stream
PixbufStream ()
1957 uint offset
= Header
.Directory
.Lookup (TagId
.StripOffsets
).ValueAsLong
[0];
1958 System
.IO
.Stream file
= System
.IO
.File
.OpenRead (this.path
);
1959 file
.Position
= offset
;
1961 //return LookupJpegSubstream (Header.Directory.NextDirectory);
1962 //return DCRawFile.RawPixbufStream (path);
1965 public override Gdk
.Pixbuf
Load ()
1967 return DCRawFile
.Load (this.Path
, null);
1970 public override System
.DateTime Date
1973 SubdirectoryEntry sub
= (SubdirectoryEntry
) this.Header
.Directory
.Lookup (TagId
.ExifIfdPointer
);
1974 DirectoryEntry e
= sub
.Directory
[0].Lookup (TagId
.DateTimeOriginal
);
1977 return DirectoryEntry
.DateTimeFromString (e
.StringValue
);