5
// Larry Ewing <lewing@novell.com>
8
// Copyright (C) 2004 - 2006 Novell, Inc (http://www.novell.com)
10
// Permission is hereby granted, free of charge, to any person obtaining
11
// a copy of this software and associated documentation files (the
12
// "Software"), to deal in the Software without restriction, including
13
// without limitation the rights to use, copy, modify, merge, publish,
14
// distribute, sublicense, and/or sell copies of the Software, and to
15
// permit persons to whom the Software is furnished to do so, subject to
16
// the following conditions:
18
// The above copyright notice and this permission notice shall be
19
// included in all copies or substantial portions of the Software.
21
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
25
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
27
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33
namespace Beagle.Util.Tiff {
35
// This is primarily to preserve the names from the specification
36
// because they differ from the tiff standard names
37
public enum NiffId : ushort {
39
PelPathLength = 0x0100,
40
LineProgressionLength = 257,
41
BitsPerSample = 0x0101,
42
PhotometricInterpretation = 0x0106,
44
SamplesPerPixel = 0x0115,
45
DataByteCounts = 0x0117,
46
PelPathResolution = 0x011a,
47
LineProgressionResolution = 0x011b,
48
ResolutionUnit = 0x0128,
49
ColumnsPerPelPath = 322,
50
RowsPerLineProgression = 323,
52
NavyCompression = 33466,
56
public enum TagId : ushort {
57
InteroperabilityIndex = 0x0001,
58
InteroperabilityVersion = 0x0002,
60
NewSubfileType = 0x00fe,
65
BitsPerSample = 0x0102,
67
PhotometricInterpretation = 0x0106,
69
DocumentName = 0x010d,
70
ImageDescription = 0x010e,
73
StripOffsets = 0x0111,
75
SamplesPerPixel = 0x0115,
76
RowsPerStrip = 0x0116,
77
StripByteCounts = 0x0117,
80
PlanarConfiguration = 0x011c,
85
ResolutionUnit = 0x0128,
86
TransferFunction = 0x012d,
91
PrimaryChromaticities = 0x013f,
93
HalftoneHints = 0x0141,
98
TileByteCounts = 0x0145,
100
SubIFDs = 0x014a, // TIFF-EP
104
NumberOfInks = 0x014e,
107
TargetPrinter = 0x0151,
108
ExtraSamples = 0x0152,
109
SampleFormat = 0x0153,
110
SMinSampleValue = 0x0154,
111
SMaxSampleValue = 0x0155,
113
TransferRange = 0x0156,
115
ClipPath = 0x0157, // TIFF PageMaker Technote #2.
117
JPEGTables = 0x015b, // TIFF-EP
120
JPEGInterchangeFormat = 0x0201,
121
JPEGInterchangeFormatLength = 0x0202,
122
JPEGRestartInterval = 0x0203,
123
JPEGLosslessPredictors = 0x0205,
124
JPEGPointTransforms = 0x0206,
125
JPEGQTables = 0x0207,
126
JPEGDCTables = 0x0208,
127
JPEGACTables = 0x0209,
129
YCbCrCoefficients = 0x0211,
130
YCbCrSubSampling = 0x0212,
131
YCbCrPositioning = 0x0213,
133
ReferenceBlackWhite = 0x0214,
134
RelatedImageFileFormat = 0x1000,
135
RelatedImageWidth = 0x1001,
136
RelatedImageLength = 0x1002,
137
CFARepeatPatternDim = 0x828d,
139
BatteryLevel = 0x828f,
141
ExposureTime = 0x829a,
144
// These are from the NIFF spec and only really valid when the header begins with IIN1
145
// see the NiffTag enum for the specifcation specific names
147
NavyCompression = 0x82ba,
153
PhotoshopPrivate = 0x8649,
155
ExifIfdPointer = 0x8769,
156
InterColorProfile = 0x8773,
157
ExposureProgram = 0x8822,
158
SpectralSensitivity = 0x8824,
159
GPSInfoIfdPointer = 0x8825,
160
ISOSpeedRatings = 0x8827,
162
ExifVersion = 0x9000,
163
DateTimeOriginal = 0x9003,
164
DateTimeDigitized = 0x9004,
165
ComponentsConfiguration = 0x9101,
166
CompressedBitsPerPixel = 0x9102,
167
ShutterSpeedValue = 0x9201,
168
ApertureValue = 0x9202,
169
BrightnessValue = 0x9203,
170
ExposureBiasValue = 0x9204,
171
MaxApertureValue = 0x9205,
172
SubjectDistance = 0x9206,
173
MeteringMode = 0x9207,
174
LightSource = 0x9208,
176
FocalLength = 0x920a,
178
FlashEnergy_TIFFEP = 0x920b,// TIFF-EP
179
SpacialFrequencyResponse = 0x920c,// TIFF-EP
180
Noise = 0x920d,// TIFF-EP
181
FocalPlaneXResolution_TIFFEP = 0x920e,// TIFF-EP
182
FocalPlaneYResolution_TIFFEP = 0x920f,// TIFF-EP
183
FocalPlaneResolutionUnit_TIFFEP = 0x9210,// TIFF-EP
184
ImageName = 0x9211,// TIFF-EP
185
SecurityClassification = 0x9212,// TIFF-EP
187
ImageHistory = 0x9213, // TIFF-EP null separated list
189
SubjectArea = 0x9214,
191
ExposureIndex_TIFFEP = 0x9215, // TIFF-EP
192
TIFFEPStandardID = 0x9216, // TIFF-EP
193
SensingMethod_TIFFEP = 0x9217, // TIFF-EP
196
UserComment = 0x9286,
198
SubSecTimeOriginal = 0x9291,
199
SubSecTimeDigitized = 0x9292,
200
FlashPixVersion = 0xa000,
202
PixelXDimension = 0xa002,
203
PixelYDimension = 0xa003,
204
RelatedSoundFile = 0xa004,
205
InteroperabilityIfdPointer = 0xa005,
206
FlashEnergy = 0xa20b,
207
SpatialFrequencyResponse = 0xa20c,
208
FocalPlaneXResolution = 0xa20e,
209
FocalPlaneYResolution = 0xa20f,
210
FocalPlaneResolutionUnit = 0xa210,
211
SubjectLocation = 0xa214,
212
ExposureIndex = 0xa215,
213
SensingMethod = 0xa217,
216
ExifCFAPattern = 0xa302,
217
CustomRendered = 0xa401,
218
ExposureMode = 0xa402,
219
WhiteBalance = 0xa403,
220
DigitalZoomRatio = 0xa404,
221
FocalLengthIn35mmFilm = 0xa405,
222
SceneCaptureType = 0xa406,
223
GainControl = 0xa407,
227
DeviceSettingDescription = 0xa40b,
228
SubjectDistanceRange = 0xa40c,
229
ImageUniqueId = 0xa420,
231
// The Following IDs are not described the EXIF spec
234
// The XMP spec declares that XMP data should live 0x2bc when
235
// embedded in tiff images.
239
DNGVersion = 0xc612, // Ifd0
240
DNGBackwardVersion = 0xc613, // Ifd0
241
UniqueCameraModel = 0xc614, // Ifd0
242
LocalizedCameraModel = 0xc615, // Ifd0
243
CFAPlaneColor = 0xc616, // RawIfd
244
CFALayout = 0xc617, // RawIfd
245
LinearizationTable = 0xc618, // RawIfd
246
BlackLevelRepeatDim = 0xc619, // RawIfd
247
BlackLevel = 0xc61a, // RawIfd
248
BlackLevelDeltaH = 0xc61b, // RawIfd
249
BlackLevelDeltaV = 0xc61c, // RawIfd
250
WhiteLevel = 0xc61d, // RawIfd
251
DefaultScale = 0xc61e, // RawIfd
252
DefaultCropOrigin = 0xc61f, // RawIfd
253
DefaultCropSize = 0xc620, // RawIfd
254
ColorMatrix1 = 0xc621, // Ifd0
255
ColorMatrix2 = 0xc622, // Ifd0
256
CameraCalibration1 = 0xc623, // Ifd0
257
CameraCalibration2 = 0xc624, // Ifd0
258
ReductionMatrix1 = 0xc625, // Ifd0
259
ReductionMatrix2 = 0xc626, // Ifd0
260
AnalogBalance = 0xc627, // Ifd0
261
AsShotNetural = 0xc628, // Ifd0
262
AsShotWhiteXY = 0xc629, // Ifd0
263
BaselineExposure = 0xc62a, // Ifd0
264
BaselineNoise = 0xc62b, // Ifd0
265
BaselineSharpness = 0xc62c, // Ifd0
266
BayerGreeSpit = 0xc62d, // Ifd0
267
LinearResponseLimit = 0xc62e, // Ifd0
268
CameraSerialNumber = 0xc62f, // Ifd0
269
LensInfo = 0xc630, // Ifd0
270
ChromaBlurRadius = 0xc631, // RawIfd
271
AntiAliasStrength = 0xc632, // RawIfd
272
DNGPrivateData = 0xc634, // Ifd0
274
MakerNoteSafety = 0xc635, // Ifd0
276
// The Spec says BestQualityScale is 0xc635 but it appears to be wrong
277
//BestQualityScale = 0xc635, // RawIfd
278
BestQualityScale = 0xc63c, // RawIfd this looks like the correct value
280
CalibrationIlluminant1 = 0xc65a, // Ifd0
281
CalibrationIlluminant2 = 0xc65b, // Ifd0
283
// Print Image Matching data
284
PimIfdPointer = 0xc4a5
287
public struct SRational {
288
public int Numerator;
289
public int Denominator;
291
public SRational (byte [] raw_data, int offset, bool little)
293
Numerator = BitConverter.ToInt32 (raw_data, offset, little);
294
Denominator = BitConverter.ToInt32 (raw_data, offset, little);
297
public SRational (int numerator, int denominator)
299
Numerator = numerator;
300
Denominator = denominator;
303
public static SRational BitwiseCopy (Rational rational)
307
result.Numerator = unchecked ((int) rational.Numerator);
308
result.Denominator = unchecked ((int) rational.Denominator);
312
public override string ToString ()
316
else if (Numerator % Denominator == 0)
317
return String.Format ("{0}", Numerator / Denominator);
318
else if (Denominator % Numerator == 0)
319
return String.Format ("1/{0}", Denominator / Numerator);
321
return String.Format ("{0}/{1}", Numerator, Denominator);
324
public double Value {
326
return Numerator / (double)Denominator;
331
public struct Rational {
332
public uint Numerator;
333
public uint Denominator;
335
public Rational (uint numerator, uint denominator)
337
Numerator = numerator;
338
Denominator = denominator;
341
public Rational (string value)
343
string [] vals = value.Split ('/');
344
if (vals.Length == 2) {
345
this.Numerator = UInt32.Parse (vals [0]);
346
this.Denominator = UInt32.Parse (vals [1]);
348
} if (vals.Length == 1) {
349
double tmp = Double.Parse (value);
350
this.Numerator = (uint) (tmp * 100000);
351
this.Denominator = 100000;
353
throw new System.Exception ("unable to parse rational value");
356
public override string ToString ()
360
else if (Numerator % Denominator == 0)
361
return String.Format ("{0}", Numerator / Denominator);
362
else if (Denominator % Numerator == 0)
363
return String.Format ("1/{0}", Denominator / Numerator);
365
return String.Format ("{0}/{1}", Numerator, Denominator);
368
public double Value {
370
return Numerator / (double)Denominator;
379
public UserComment (string value)
385
public UserComment (byte [] raw_data, bool little)
387
if (raw_data.Length == 8 || raw_data.Length == 0) {
391
} else if (raw_data.Length < 8) {
392
throw new Exception ("Invalid UserComment value, no charset found");
395
string charset = System.Text.Encoding.ASCII.GetString (raw_data, 0, 8);
396
System.Text.Encoding enc;
400
enc = System.Text.Encoding.ASCII;
404
enc = new System.Text.UnicodeEncoding (! little, true);
406
case "JIS\0\0\0\0\0":
407
// FIXME this requires mono locale extras
409
enc = System.Text.Encoding.GetEncoding ("euc-jp");
411
System.Console.WriteLine ("missing jis0208 encoding");
412
enc = System.Text.Encoding.Default;
415
case "\0\0\0\0\0\0\0\0":
416
// FIXME the spec says to use the local encoding in this case, we could probably
417
// do something smarter, but whatever.
418
enc = System.Text.Encoding.Default;
422
throw new System.Exception (System.String.Format ("Invalid charset name: {0}", charset));
426
Value = enc.GetString (raw_data, 8, raw_data.Length - 8);
429
public byte [] GetBytes (bool is_little)
432
string description = Value;
433
System.Text.Encoding enc;
436
for (int i = 0; i < description.Length; i++) {
437
if (description [i] > 127) {
444
heading = "ASCII\0\0\0";
445
enc = new System.Text.ASCIIEncoding ();
447
heading = "Unicode\0";
448
enc = new System.Text.UnicodeEncoding (! is_little, true);
451
int len = enc.GetByteCount (description);
452
byte [] data = new byte [len + heading.Length];
453
System.Text.Encoding.ASCII.GetBytes (heading, 0, heading.Length, data, 0);
454
enc.GetBytes (Value, 0, Value.Length, data, heading.Length);
456
UserComment c = new UserComment (data, is_little);
457
//System.Console.WriteLine ("old = \"{0}\" new = \"{1}\" heading = \"{2}\"", c.Value, description, heading);
461
public override string ToString ()
463
return String.Format ("({0},charset={1})", Value, Charset);
467
public struct CFAPattern {
469
public ushort Columns;
470
public byte [] Values;
472
public CFAPattern (byte [] raw_data, bool little)
474
Columns = BitConverter.ToUInt16 (raw_data, 0, little);
475
Rows = BitConverter.ToUInt16 (raw_data, 2, little);
477
Values = new byte [Rows * Columns];
478
System.Array.Copy (raw_data, 4, Values, 0, Values.Length);
482
* Note the Exif spec defines a CFA pattern tag that includes the row and column counts as shorts
483
* inside the first four bytes of the entry. The Tiff-EP standard define the CFARepeatPattern tag
484
* that contains the row and column counts presumably since the Exif version wouldn't allow you to
485
* alter the endian of the file without knowing the tag layout.
488
public CFAPattern (ushort rows, ushort cols, byte [] raw_data, bool little)
492
Values = new byte [rows * cols];
493
System.Array.Copy (raw_data, 0, Values, 0, Values.Length);
497
public struct OECFTable {
499
public ushort Columns;
500
public string [] Names;
501
public SRational [] Values;
503
public OECFTable (byte [] raw_data, bool little)
505
Columns = BitConverter.ToUInt16 (raw_data, 0, little);
506
Rows = BitConverter.ToUInt16 (raw_data, 2, little);
507
Names = new string [Columns];
508
Values = new SRational [Columns * Rows];
512
int type_size = DirectoryEntry.GetTypeSize (EntryType.SRational);
514
for (i = 0; i < Names.Length; i++)
515
Names [i] = ReadString (raw_data, ref pos);
517
for (i = 0; i < Values.Length; i++)
518
Values [i] = new SRational (raw_data, pos + i * type_size, little);
522
public string ReadString (byte [] data, ref int pos)
525
for (; pos < data.Length; pos++) {
529
return System.Text.Encoding.ASCII.GetString (data, start, pos - start);
533
public enum ExtraSamples {
539
public enum FileSource {
543
public enum PhotometricInterpretation : ushort {
548
TransparencyMask = 4,
549
Separated = 5, // CMYK
554
LogL = 32844, // Log Luminance
556
ColorFilterArray = 32803, // ColorFilterArray... the good stuff
557
LinearRaw = 34892 // DBG LinearRaw
560
public enum PlanarConfiguration {
565
public enum Compression {
572
JPEGStream = 7, // TIFF-EP stores full jpeg stream
577
NikonCompression = 34713,
578
Deflate_experimental = 0x80b2
581
public enum JPEGProc {
582
BaselineSequencial = 1,
583
LosslessHuffman = 14,
586
public enum SubfileType {
588
ReducedResolution = 2,
592
public enum ExposureProgram {
595
NormalProgram = 2, // Normal Program
596
AperturePriority = 3, // Aperture priority
597
ShutterPriorty = 4, // Shutter priority
598
CreativeProgram = 5, // Creative program
599
ActionProgram = 6, // Action program
600
PortraitMode = 7, // Portrait mode
601
LandscapeMode = 8 // Landscape mode
604
public enum ExposureMode {
610
public enum CustomRendered : ushort {
616
public enum SceneType {
617
DirectlyPhotographed = 1
620
public enum MeteringMode {
623
CenterWeightedAverage = 2,
630
public enum SceneCaptureType : ushort {
637
public enum GainControl : ushort {
645
public enum Contrast : ushort {
651
public enum Saturation : ushort {
657
public enum WhiteBalance : ushort {
662
public enum Sharpness : ushort {
668
public enum LightSource {
677
DaylightFluorescent = 12,
678
DaylightWhiteFluorescent = 13,
679
CoolWhiteFluorescent = 14,
680
WhiteFluorescent = 15,
688
ISOStudioTungsten = 24,
692
public enum ColorSpace {
693
StandardRGB = 1, // sRGB
695
Uncalibrated = 0xffff
698
public enum ComponentsConfiguration {
707
public enum ResolutionUnit : ushort {
713
public enum SensingMethod : short {
715
OneChipColorAreaSensor = 2,
716
TwoChipColorAreaSensor = 3,
717
ThreeChipColorAreaSensor = 4,
718
ColorSequentialAreaSensor = 5,
720
ColorSequentialLinearSensor = 8
724
public enum NewSubfileType : uint {
725
ReducedResolution = 1,
726
PageOfMultipage= 1 << 1,
727
TransparencyMask = 1 << 2
730
public enum EntryType {
743
Ifd // TIFF-EP - TIFF PageMaker TechnicalNote 2
748
public EntryType Type;
751
public string Description;
754
public class CanonTag : Tag {
755
// http://www.gvsoft.homedns.org/exif/makernote-canon.html
757
public enum CanonId {
759
CameraSettings1 = 0x0001,
761
CameraSettings2 = 0x0004,
763
FirmwareVersion = 0x0007,
764
ImageNumber = 0x0008,
767
CameraSerialNumber = 0x000c,
769
CustomFunctions = 0x000f
772
public CanonTag (CanonId id, EntryType type, int count, string name, string description)
774
this.Id = (ushort)id;
778
this.Description = description;
781
public static System.Collections.Hashtable Tags;
785
new CanonTag (CanonId.Unknown1, EntryType.Short, 6, null, null),
786
new CanonTag (CanonId.CameraSettings1, EntryType.Short, -1, "Camera Settings 1", "First Canon MakerNote settings section"),
787
new CanonTag (CanonId.Unknown2, EntryType.Short, 4, null, null),
788
new CanonTag (CanonId.CameraSettings2, EntryType.Short, -1, "Camera Settings 2", "Second Canon MakerNote settings section"),
789
new CanonTag (CanonId.ImageType, EntryType.Ascii, 32, "Image Type", null), // FIXME description
790
new CanonTag (CanonId.FirmwareVersion, EntryType.Ascii, 24, "Firmware Version", "Version of the firmware installed on the camera"),
791
new CanonTag (CanonId.ImageNumber, EntryType.Long, 1, "Image Number", null), // FIXME description
792
new CanonTag (CanonId.OwnerName, EntryType.Long, 32, "Owner Name", "Name of the Camera Owner"), // FIXME description
793
new CanonTag (CanonId.Unknown4, EntryType.Short, -1, null, null),
794
new CanonTag (CanonId.CameraSerialNumber, EntryType.Short, 1, "Serial Number", null), //FIXME description
795
new CanonTag (CanonId.Unknown4, EntryType.Short, -1, null, null),
796
new CanonTag (CanonId.CustomFunctions, EntryType.Short, -1, "Custom Functions", "Camera Custom Functions")
799
foreach (CanonTag tag in tags)
810
public class Converter {
811
public static uint ReadUInt (System.IO.Stream stream, Endian endian)
813
byte [] tmp = new byte [4];
815
if (stream.Read (tmp, 0, tmp.Length) < 4)
816
throw new System.Exception ("Short Read");
818
return BitConverter.ToUInt32 (tmp, 0, endian == Endian.Little);
821
public static ushort ReadUShort (System.IO.Stream stream, Endian endian)
823
byte [] tmp = new byte [2];
825
if (stream.Read (tmp, 0, tmp.Length) < 2)
826
throw new System.Exception ("Short Read");
828
return BitConverter.ToUInt16 (tmp, 0, endian == Endian.Little);
832
public class Header : SemWeb.StatementSource {
833
public Endian endian;
835
private uint directory_offset;
836
public ImageDirectory Directory;
838
// false seems a safe default
839
public bool Distinct {
840
get { return false; }
843
public Header (System.IO.Stream stream)
845
//using (new Timer ("new Tiff.Header")) {
846
byte [] data = new byte [8];
847
stream.Read (data, 0, data.Length);
848
if (data [0] == 'M' && data [1] == 'M')
850
else if (data [0] == 'I' && data [1] == 'I')
851
endian = Endian.Little;
853
ushort marker = BitConverter.ToUInt16 (data, 2, endian == Endian.Little);
856
//System.Console.WriteLine ("Found Standard Tiff Marker {0}", marker);
859
//System.Console.WriteLine ("Found Olympus Tiff Marker {0}", marker.ToString ("x"));
862
//System.Console.WriteLine ("Found Navy Interchange File Format Tiff Marker {0}", marker.ToString ("x"));
865
//System.Console.WriteLine ("Found Unknown Tiff Marker {0}", marker.ToString ("x"));
869
//System.Console.WriteLine ("Converting Something");
870
directory_offset = BitConverter.ToUInt32 (data, 4, endian == Endian.Little);
872
if (directory_offset < 8)
873
throw new System.Exception ("Invalid IFD0 Offset [" + directory_offset.ToString () + "]");
876
//System.Console.WriteLine ("Reading First IFD");
878
Directory = new ImageDirectory (stream, directory_offset, endian);
883
public void Select (SemWeb.StatementSink sink)
885
//using (new Timer ("Tiff.Header.Select")) {
886
SelectDirectory (Directory, sink);
890
public void SelectDirectory (ImageDirectory dir, StatementSink sink)
892
foreach (DirectoryEntry e in dir.Entries) {
894
//System.Console.WriteLine ("{0}", e.Id);
898
System.IO.Stream iptcstream = new System.IO.MemoryStream (e.RawData);
899
Iptc.IptcFile iptc = new Iptc.IptcFile (iptcstream);
902
case TagId.PhotoshopPrivate:
903
System.IO.Stream bimstream = new System.IO.MemoryStream (e.RawData);
904
Bim.BimFile bim = new Bim.BimFile (bimstream);
908
System.IO.Stream xmpstream = new System.IO.MemoryStream (e.RawData);
909
Xmp.XmpFile xmp = new Xmp.XmpFile (xmpstream);
912
case TagId.ImageDescription:
913
MetadataStore.AddLiteral (sink, "dc:description", "rdf:Alt",
914
new Literal (e.ValueAsString [0], "x-default", null));
916
case TagId.UserComment:
917
MetadataStore.AddLiteral (sink, "exif:UserComment", "rdf:Alt",
918
new Literal (e.ValueAsString [0], "x-default", null));
920
case TagId.Copyright:
921
MetadataStore.AddLiteral (sink, "dc:rights", "rdf:Alt",
922
new Literal (e.ValueAsString [0], "x-default", null));
925
MetadataStore.Add (sink, "dc:creator", "rdf:Seq", e.ValueAsString);
927
case TagId.ExifIfdPointer:
929
ImageDirectory sub = ((SubdirectoryEntry)e).Directory [0];
930
SelectDirectory (sub, sink);
931
} catch (System.Exception exc) {
932
System.Console.WriteLine (exc);
936
MetadataStore.AddLiteral (sink, "xmp:CreatorTool", e.ValueAsString [0]);
941
MetadataStore.AddLiteral (sink, "xmp:ModifyDate",
942
e.ValueAsDate.ToString ("yyyy-MM-ddThh:mm:ss"));
943
} catch (System.Exception ex) {
944
System.Console.WriteLine (String.Format ("error parsing {0}\n{1}", e.ValueAsString[0], ex));
948
case TagId.DateTimeOriginal:
949
case TagId.DateTimeDigitized:
950
// FIXME subsectime needs to be included in these values
951
// FIXME shouldn't DateTimeOriginal be xmp:CreateDate? the spec says no but wtf?
953
MetadataStore.AddLiteral (sink, "exif:" + e.Id.ToString (),
954
e.ValueAsDate.ToString ("yyyy-MM-ddThh:mm:ss"));
955
} catch (System.Exception ex) {
956
System.Console.WriteLine (String.Format ("error parsing {0}\n{1}", e.ValueAsString[0], ex));
959
//case TagId.SpatialFrequencyResponse
960
case TagId.ExifCFAPattern:
961
CFAPattern pattern = new CFAPattern (e.RawData, e.IsLittle);
962
Entity empty = new BNode ();
963
Statement top = new Statement (MetadataStore.FSpotXMPBase,
964
(Entity)MetadataStore.Namespaces.Resolve ("exif:" + e.Id.ToString ()),
967
Statement cols = new Statement (empty,
968
(Entity) MetadataStore.Namespaces.Resolve ("exif:Columns"),
969
new Literal (pattern.Columns.ToString (), null, null));
971
Statement rows = new Statement (empty,
972
(Entity) MetadataStore.Namespaces.Resolve ("exif:Rows"),
973
new Literal (pattern.Rows.ToString (), null, null));
975
string [] vals = e.ArrayToString (pattern.Values);
976
MetadataStore.Add (sink, empty, "exif:Values", "rdf:Seq", vals);
979
case TagId.ExifVersion:
980
case TagId.FlashPixVersion:
981
case TagId.ColorSpace:
982
case TagId.CompressedBitsPerPixel:
983
case TagId.PixelYDimension:
984
case TagId.PixelXDimension:
985
case TagId.RelatedSoundFile:
986
case TagId.ExposureTime:
988
case TagId.ExposureProgram:
989
case TagId.SpectralSensitivity:
990
case TagId.ShutterSpeedValue:
991
case TagId.ApertureValue:
992
case TagId.BrightnessValue:
993
case TagId.ExposureBiasValue:
994
case TagId.MaxApertureValue:
995
case TagId.SubjectDistance:
996
case TagId.MeteringMode:
997
case TagId.LightSource:
998
case TagId.FocalLength:
999
case TagId.FlashEnergy:
1000
case TagId.FocalPlaneXResolution:
1001
case TagId.FocalPlaneYResolution:
1002
case TagId.FocalPlaneResolutionUnit:
1003
case TagId.ExposureIndex:
1004
case TagId.SensingMethod:
1005
case TagId.FileSource:
1006
case TagId.SceneType:
1007
case TagId.CustomRendered:
1008
case TagId.ExposureMode:
1009
case TagId.WhiteBalance:
1010
case TagId.DigitalZoomRatio:
1011
case TagId.FocalLengthIn35mmFilm:
1012
case TagId.SceneCaptureType:
1013
case TagId.GainControl:
1014
case TagId.Contrast:
1015
case TagId.Saturation:
1016
case TagId.Sharpness:
1017
MetadataStore.AddLiteral (sink, "exif:" + e.Id.ToString (), e.ValueAsString [0]);
1019
case TagId.ComponentsConfiguration:
1020
case TagId.ISOSpeedRatings:
1021
case TagId.SubjectArea:
1022
case TagId.SubjectLocation:
1023
MetadataStore.Add (sink, "exif:" + e.Id.ToString (), "rdf:Seq", e.ValueAsString);
1025
case TagId.TransferFunction:
1026
case TagId.YCbCrSubSampling:
1027
case TagId.WhitePoint:
1028
case TagId.PrimaryChromaticities:
1029
case TagId.YCbCrCoefficients:
1030
case TagId.ReferenceBlackWhite:
1031
case TagId.BitsPerSample:
1032
MetadataStore.Add (sink, "tiff:" + e.Id.ToString (), "rdf:Seq", e.ValueAsString);
1034
case TagId.Orientation:
1035
case TagId.Compression:
1036
case TagId.PhotometricInterpretation:
1037
case TagId.SamplesPerPixel:
1038
case TagId.PlanarConfiguration:
1039
case TagId.YCbCrPositioning:
1040
case TagId.ResolutionUnit:
1041
case TagId.ImageWidth:
1042
case TagId.ImageLength:
1045
MetadataStore.AddLiteral (sink, "tiff:" + e.Id.ToString (), e.ValueAsString [0]);
1051
public void Dump (string name)
1053
ImageDirectory ifd = Directory;
1054
for (int i = 0; ifd != null; i++) {
1055
ifd.Dump (System.String.Format ("IFD[{0}]:", i));
1056
ifd = ifd.NextDirectory;
1061
public class ImageDirectory {
1062
protected Endian endian;
1063
protected ushort num_entries;
1064
protected System.Collections.ArrayList entries;
1065
protected uint orig_position;
1067
protected uint next_directory_offset;
1068
ImageDirectory next_directory;
1070
protected bool has_header;
1071
protected bool has_footer;
1073
public ImageDirectory (System.IO.Stream stream, uint start_position, Endian endian)
1075
this.endian = endian;
1076
orig_position = start_position;
1080
protected void Load (System.IO.Stream stream)
1082
ReadHeader (stream);
1083
ReadEntries (stream);
1084
ReadFooter (stream);
1086
LoadEntries (stream);
1087
LoadNextDirectory (stream);
1090
public virtual bool ReadHeader (System.IO.Stream stream)
1092
stream.Seek ((long)orig_position, System.IO.SeekOrigin.Begin);
1096
protected virtual void ReadEntries (System.IO.Stream stream)
1098
num_entries = Converter.ReadUShort (stream, endian);
1100
System.Console.WriteLine ("reading {0} entries", num_entries);
1102
entries = new System.Collections.ArrayList (num_entries);
1103
int entry_length = num_entries * 12;
1104
byte [] content = new byte [entry_length];
1106
if (stream.Read (content, 0, content.Length) < content.Length)
1107
throw new System.Exception ("Short Read");
1109
for (int pos = 0; pos < entry_length; pos += 12) {
1110
DirectoryEntry entry = EntryFactory.CreateEntry (this, content, pos, this.endian);
1111
entries.Add (entry);
1113
System.Console.WriteLine ("Added Entry {0} {1} - {2} * {3}", entry.Id.ToString (), entry.Id.ToString ("x"), entry.Type, entry.Count);
1115
if (entry.Id == TagId.NewSubfileType) {
1121
protected virtual void ReadFooter (System.IO.Stream stream)
1123
next_directory_offset = Converter.ReadUInt (stream, this.endian);
1126
protected void LoadEntries (System.IO.Stream stream)
1128
foreach (DirectoryEntry entry in entries) {
1129
entry.LoadExternal (stream);
1133
protected void LoadNextDirectory (System.IO.Stream stream)
1136
System.Console.WriteLine ("start_position = {1} next_directory_offset = {0}", next_directory_offset, orig_position);
1138
next_directory = null;
1140
if (next_directory_offset != 0 && next_directory_offset != orig_position)
1141
next_directory = new ImageDirectory (stream, next_directory_offset, this.endian);
1143
} catch (System.Exception) {
1144
//System.Console.WriteLine ("Error loading directory {0}", e.ToString ());
1145
next_directory = null;
1146
next_directory_offset = 0;
1150
public ImageDirectory NextDirectory {
1152
return next_directory;
1156
public System.Collections.ArrayList Entries {
1162
public DirectoryEntry Lookup (TagId id)
1164
foreach (DirectoryEntry entry in entries)
1172
public DirectoryEntry Lookup (uint id)
1174
foreach (DirectoryEntry entry in entries)
1175
if ((uint)entry.Id == id)
1181
#if false // FIXME: Do we need Cms ?
1183
public Cms.Profile GetProfile ()
1185
Cms.ColorCIExyY whitepoint = new Cms.ColorCIExyY (0, 0, 0);
1186
Cms.ColorCIExyYTriple primaries = new Cms.ColorCIExyYTriple (whitepoint, whitepoint, whitepoint);
1187
Cms.GammaTable [] transfer = null;
1188
int bits_per_sample = 8;
1191
foreach (DirectoryEntry e in entries) {
1193
case TagId.InterColorProfile:
1195
return new Cms.Profile (e.RawData);
1196
} catch (System.Exception ex) {
1197
System.Console.WriteLine (ex);
1200
case TagId.ColorSpace:
1201
switch ((ColorSpace)e.ValueAsLong [0]) {
1202
case ColorSpace.StandardRGB:
1203
return Cms.Profile.CreateStandardRgb ();
1204
case ColorSpace.AdobeRGB:
1205
return Cms.Profile.CreateAlternateRgb ();
1206
case ColorSpace.Uncalibrated:
1207
//System.Console.WriteLine ("Uncalibrated colorspace");
1212
case TagId.WhitePoint:
1213
Rational [] white = e.RationalValue;
1214
whitepoint.x = white [0].Value;
1215
whitepoint.y = white [1].Value;
1218
case TagId.PrimaryChromaticities:
1219
Rational [] colors = e.RationalValue;
1220
primaries.Red.x = colors [0].Value;
1221
primaries.Red.y = colors [1].Value;
1222
primaries.Red.Y = 1.0;
1224
primaries.Green.x = colors [2].Value;
1225
primaries.Green.y = colors [3].Value;
1226
primaries.Green.Y = 1.0;
1228
primaries.Blue.x = colors [4].Value;
1229
primaries.Blue.y = colors [5].Value;
1230
primaries.Blue.Y = 1.0;
1232
case TagId.TransferFunction:
1233
ushort [] trns = e.ShortValue;
1234
ushort gamma_count = (ushort) (1 << bits_per_sample);
1235
Cms.GammaTable [] tables = new Cms.GammaTable [3];
1236
//System.Console.WriteLine ("Parsing transfer function: count = {0}", trns.Length);
1238
// FIXME we should use the TransferRange here
1239
// FIXME we should use bits per sample here
1240
for (int c = 0; c < 3; c++) {
1241
tables [c] = new Cms.GammaTable (trns, c * gamma_count, gamma_count);
1246
case TagId.ExifIfdPointer:
1247
SubdirectoryEntry exif = (SubdirectoryEntry) e;
1248
DirectoryEntry ee = exif.Directory [0].Lookup ((int)TagId.Gamma);
1253
Rational rgamma = ee.RationalValue [0];
1254
gamma = rgamma.Value;
1259
if (transfer == null) {
1260
Cms.GammaTable basic = new Cms.GammaTable (1 << bits_per_sample, gamma);
1261
transfer = new Cms.GammaTable [] { basic, basic, basic };
1264
// if we didn't get a white point or primaries, give up
1265
if (whitepoint.Y != 1.0 || primaries.Red.Y != 1.0)
1268
return new Cms.Profile (whitepoint, primaries, transfer);
1272
public void Dump (string name)
1274
System.Console.WriteLine ("Starting {0}", name);
1275
foreach (DirectoryEntry e in this.Entries)
1277
System.Console.WriteLine ("Ending {0}", name);
1280
public string Dump2 ()
1282
System.Text.StringBuilder builder = new System.Text.StringBuilder ();
1283
builder.Append ("Dummping IFD");
1284
foreach (DirectoryEntry entry in entries) {
1285
builder.Append (entry.ToString ()+ "\n");
1287
if (entry is SubdirectoryEntry)
1288
builder.Append ("Found SUBDIRECTORYENTRY\n");
1291
if (next_directory != null) {
1292
builder.Append ("Dummping Next IFD");
1293
builder.Append (next_directory.Dump2 ());
1296
return builder.ToString ();
1300
public class EntryFactory {
1301
//public delegate DirectoryEntry ConstructorFunc (byte [], Endian endian);
1302
//public static System.Collections.Hashtable ctors = new System.Collections.Hashtable ();
1304
public static DirectoryEntry CreateEntry (ImageDirectory parent, byte [] input, int start, Endian header_endian)
1309
DirectoryEntry.ParseHeader (input, start, out tagid, out type, header_endian);
1310
//ConstructorFunc ctor = ctors[tagid];
1311
//if (ctor == null) {
1312
// return ctor (input, header_endian);
1316
case TagId.ExifIfdPointer:
1317
case TagId.GPSInfoIfdPointer:
1318
case TagId.InteroperabilityIfdPointer:
1320
return new SubdirectoryEntry (input, start, header_endian);
1321
//case TagId.MakerNote:
1322
//return new MakerNoteEntry (input, start, header_endian);
1323
//case TagId.PimIfdPointer:
1325
//case TagId.MakerNote:
1326
//return new MakerNoteEntry (input, start, header_endian);
1331
//System.Console.WriteLine ("Trying to load {0} {1}", tagid, tagid.ToString ("x"));
1332
return new SubdirectoryEntry (input, start, header_endian);
1333
case EntryType.Byte:
1334
return new ByteEntry (input, start, header_endian);
1335
case EntryType.Long:
1336
return new LongEntry (input, start, header_endian);
1337
case EntryType.Short:
1338
return new ShortEntry (input, start, header_endian);
1341
return new DirectoryEntry (input, start, header_endian);
1345
public class MakerNoteEntry : SubdirectoryEntry {
1346
public MakerNoteEntry (byte [] data, int offset, Endian endian) : base (data, offset, endian)
1351
public override uint GetEntryCount ()
1356
public override void LoadExternal (System.IO.Stream stream)
1362
public class SubdirectoryEntry : DirectoryEntry {
1363
public uint directory_offset;
1364
public ImageDirectory [] Directory;
1366
public SubdirectoryEntry (byte [] data, int offset, Endian endian) : base (data, offset, endian)
1368
if (this.GetEntryCount () > 1) {
1369
//System.Console.WriteLine ("Count is greater than 1 ({1}) on Subdirectory {0} interesting", tagid, count);
1373
public virtual uint GetEntryCount ()
1378
public override void LoadExternal (System.IO.Stream stream)
1380
uint entry_count = GetEntryCount ();
1381
Directory = new ImageDirectory [entry_count];
1383
base.LoadExternal (stream);
1385
for (int i = 0; i < entry_count; i++) {
1387
directory_offset = BitConverter.ToUInt32 (raw_data, i * 4, endian == Endian.Little);
1388
Directory [i] = new ImageDirectory (stream, directory_offset, endian);
1389
} catch (System.Exception e) {
1390
System.Console.WriteLine ("Error loading Subdirectory {0} at {2} of {3}bytes:\n{1}",
1391
this.Id, e, directory_offset, stream.Length);
1397
public override void Dump (string name)
1399
for (int i = 0; i < GetEntryCount (); i++) {
1400
string subdirname = System.String.Format ("{0}{1}[{2}]({3})]", name, tagid, i, directory_offset);
1403
if (Directory [i] != null)
1404
Directory [i].Dump (subdirname);
1405
} catch (System.Exception e) {
1406
System.Console.WriteLine (e);
1412
public class ShortEntry : DirectoryEntry {
1413
public ShortEntry (byte [] data, int offset, Endian endian) : base (data, offset, endian)
1418
public class LongEntry : DirectoryEntry {
1419
public LongEntry (byte [] data, int offset, Endian endian) : base (data, offset, endian)
1421
if (type != EntryType.Long)
1422
throw new System.Exception (System.String.Format ("Invalid Settings At Birth {0}", tagid));
1426
public class ByteEntry : DirectoryEntry {
1427
public ByteEntry (byte [] data, int offset, Endian endian) : base (data, offset, endian)
1429
if (type != EntryType.Byte)
1430
throw new System.Exception ("Invalid Settings At Birth");
1435
public class ImageLoader {
1439
PhotometricInterpretation interpretation;
1440
Compression compression;
1442
uint [] strip_byte_counts;
1443
uint rows_per_strip;
1446
public ImageLoader (ImageDirectory directory)
1448
width = directory.Lookup (TagId.ImageWidth).ValueAsLong [0];
1449
length = directory.Lookup (TagId.ImageLength).ValueAsLong [0];
1450
bps = directory.Lookup (TagId.BitsPerSample).ValueAsLong;
1452
compression = (Compression) directory.Lookup (TagId.Compression).ValueAsLong [0];
1453
interpretation = (PhotometricInterpretation) directory.Lookup (TagId.PhotometricInterpretation).ValueAsLong [0];
1455
offsets = directory.Lookup (TagId.StripOffsets).ValueAsLong;
1456
strip_byte_counts = directory.Lookup (TagId.StripByteCounts).ValueAsLong;
1457
rows_per_strip = directory.Lookup (TagId.RowsPerStrip).ValueAsLong [0];
1459
if (interpretation !=
1463
public Gdk.Pixbuf LoadPixbuf (System.IO.Stream stream)
1465
Gdk.Pixbuf dest = new Gdk.Pixbuf (Gdk.Colorspace.Rgb, false, width, height);
1466
strip = new byte [strip_byte_counts];
1468
for (int i = 0; i < offsets.Length; i++) {
1469
strip = new byte [strip_byte_counts [i]];
1470
stream.Read (strip, 0, strip.Length);
1471
switch (compression) {
1472
case Compression.Notice
1480
public class DirectoryEntry {
1481
protected TagId tagid;
1482
protected EntryType type;
1483
protected uint count;
1484
protected uint offset_origin;
1485
protected uint data_offset;
1487
protected byte [] raw_data;
1488
protected Endian endian;
1496
public EntryType Type {
1508
public void SetOrigin (uint pos)
1510
offset_origin = pos;
1513
public uint Position {
1515
return offset_origin + data_offset;
1519
public virtual int GetTypeSize ()
1521
return GetTypeSize (type);
1524
public static int GetTypeSize (EntryType type)
1527
case EntryType.Byte:
1528
case EntryType.SByte:
1529
case EntryType.Undefined:
1530
case EntryType.Ascii:
1532
case EntryType.Short:
1533
case EntryType.SShort:
1535
case EntryType.Long:
1536
case EntryType.SLong:
1537
case EntryType.Float:
1539
case EntryType.Double:
1540
case EntryType.Rational:
1541
case EntryType.SRational:
1548
public bool IsLittle {
1550
return (endian == Endian.Little);
1554
public static int ParseHeader (byte [] data, int start, out TagId tagid, out EntryType type, Endian endian)
1556
tagid = (TagId) BitConverter.ToUInt16 (data, start, endian == Endian.Little);
1557
type = (EntryType) BitConverter.ToUInt16 (data, start + 2, endian == Endian.Little);
1561
public DirectoryEntry (byte [] data, int start, Endian endian)
1563
this.endian = endian;
1565
start += ParseHeader (data, start, out this.tagid, out this.type, endian);
1566
ParseStream (data, start);
1569
public virtual void LoadExternal (System.IO.Stream stream)
1571
if (data_offset != 0) {
1572
stream.Seek ((long)Position, System.IO.SeekOrigin.Begin);
1573
byte [] data = new byte [count * GetTypeSize ()];
1574
if (stream.Read (data, 0, data.Length) < data.Length)
1575
throw new System.Exception ("Short Read");
1580
switch ((int)this.Id) {
1581
case (int)TagId.NewSubfileType:
1582
//System.Console.WriteLine ("XXXXXXXXXXXXXXXXXXXXX new NewSubFileType {0}", (NewSubfileType) this.ValueAsLong [0]);
1584
case (int)TagId.SubfileType:
1585
//System.Console.WriteLine ("XXXXXXXXXXXXXXXXXXXXX new SubFileType {0}", (SubfileType) this.ValueAsLong [0]);
1587
case (int)TagId.Compression:
1588
//System.Console.WriteLine ("XXXXXXXXXXXXXXXXXXXXX new Compression {0}", (Compression) this.ValueAsLong [0]);
1591
case (int)TagId.JPEGProc:
1592
//System.Console.WriteLine ("XXXXXXXXXXXXXXXXXXXXX new JPEGProc {0}", (JPEGProc) this.ValueAsLong [0]);
1595
case (int)TagId.PhotometricInterpretation:
1596
//System.Console.WriteLine ("XXXXXXXXXXXXXXXXXXXXX new PhotometricInterpretation {0}", (PhotometricInterpretation) this.ValueAsLong [0]);
1598
case (int)TagId.ImageWidth:
1599
case (int)TagId.ImageLength:
1600
//System.Console.WriteLine ("XXXXXXXXXXXXXXXXXXXXX new {1} {0}", this.ValueAsLong [0], this.Id);
1605
//System.Console.WriteLine ("XXXXXXXXXXXXXXXXXXXXX {0}({1}) - {2} {3}", this.Id, this.Id.ToString ("x"), this.type, raw_data.Length);
1606
//System.Console.WriteLine ("XXXX ", System.Text.Encoding.ASCII.GetString (raw_data));
1607
switch (this.type) {
1608
case EntryType.Long:
1609
//foreach (uint val in ((LongEntry)this).Value)
1610
//System.Console.Write (" {0}", val);
1612
case EntryType.Short:
1613
//foreach (ushort val in ((ShortEntry)this).ShortValue)
1614
//System.Console.Write (" {0}", val);
1616
case EntryType.Byte:
1617
//foreach (byte val in this.RawData)
1618
//System.Console.Write (" {0}", val);
1621
//System.Console.WriteLine ("");
1627
public virtual void Dump (string name)
1629
switch (this.Type) {
1630
case EntryType.Short:
1631
case EntryType.Long:
1632
uint [] vals = this.ValueAsLong;
1633
//System.Console.Write ("{3}{1}({2}) [{0}] (", vals.Length, this.Id, this.Type, name);
1634
//for (int i = 0; i < System.Math.Min (15, vals.Length); i++) {
1635
// System.Console.Write (" {0}", vals [i]);
1637
//System.Console.WriteLine (")");
1639
case EntryType.Ascii:
1640
//System.Console.WriteLine ("{3}{1}({2}) (\"{0}\")", this.StringValue, this.Id, this.Type, name);
1643
//System.Console.WriteLine ("{3}{1}({2}) [{0}]", this.Count, this.Id, this.Type, name);
1648
protected void ParseStream (byte [] data, int start)
1652
count = BitConverter.ToUInt32 (data, i, endian == Endian.Little);
1654
int size = (int)count * GetTypeSize ();
1656
data_offset = BitConverter.ToUInt32 (data, i, endian == Endian.Little);
1659
raw_data = new byte [size];
1660
System.Array.Copy (data, i, raw_data, 0, size);
1664
public void SetData (string value)
1666
int len = System.Text.Encoding.UTF8.GetByteCount (value);
1667
byte [] tmp = new byte [len + 1];
1668
System.Text.Encoding.UTF8.GetBytes (value, 0, value.Length, tmp, 0);
1670
//System.Console.WriteLine ("SetData: value = {0} len = {1}", value, len);
1674
public static System.DateTime DateTimeFromString (string dt)
1676
// Exif DateTime strings are formatted as
1677
// "YYYY:MM:DD HH:MM:SS"
1679
string delimiters = " :";
1680
string[] dt_data = dt.Split ( delimiters.ToCharArray(), 6 );
1681
System.DateTime result;
1682
result = new System.DateTime (System.Int32.Parse(dt_data[0]),
1683
System.Int32.Parse(dt_data[1]),
1684
System.Int32.Parse(dt_data[2]),
1685
System.Int32.Parse(dt_data[3]),
1686
System.Int32.Parse(dt_data[4]),
1687
System.Int32.Parse(dt_data[5]));
1692
public void SetData (byte [] data)
1695
count = (uint)raw_data.Length / (uint)GetTypeSize ();
1699
public object GetValue () {
1701
case EntryType.Short:
1703
case EntryType.Long:
1705
case EntryType.Rational:
1706
return RationalValue;
1707
case EntryType.SRational:
1708
return SRationalValue;
1709
case EntryType.Ascii:
1710
return StringValue.Split ('\0');
1713
//System.Console.WriteLine ("{1}({2}) [{0}]", this.Count, this.Id, this.Type);
1721
public byte [] Value {
1727
public byte [] RawData {
1733
public string [] ValueAsString {
1735
switch (this.Type) {
1736
case EntryType.Short:
1737
case EntryType.Long:
1738
return ArrayToString (this.ValueAsLong);
1739
case EntryType.Rational:
1740
return ArrayToString (this.RationalValue);
1741
case EntryType.SRational:
1742
return ArrayToString (this.SRationalValue);
1743
case EntryType.Undefined:
1745
case TagId.UserComment:
1746
return new string [] { UserCommentValue };
1747
case TagId.FlashPixVersion:
1748
case TagId.ExifVersion:
1749
return new string [] { StringValue };
1750
case TagId.FileSource:
1751
case TagId.SceneType:
1752
return ArrayToString (this.RawData);
1753
case TagId.ComponentsConfiguration:
1754
return ArrayToString (ValueAsLong);
1756
//System.Console.WriteLine ("Cannot convert type \"{0}\" to string", Id);
1760
case EntryType.Ascii:
1761
return StringValue.Split ('\0');
1767
public string [] ArrayToString (System.Array array)
1769
string [] vals = new string [array.Length];
1770
for (int i = 0; i < array.Length; i++)
1771
vals [i] = array.GetValue (i).ToString ();
1776
public uint [] ValueAsLong {
1778
uint [] data = new uint [this.Count];
1779
for (int i = 0; i < this.Count; i++) {
1780
switch (this.Type) {
1781
case EntryType.Long:
1782
data [i] = BitConverter.ToUInt32 (raw_data, i * GetTypeSize (), endian == Endian.Little);
1784
case EntryType.Short:
1785
data [i] = BitConverter.ToUInt16 (raw_data, i * GetTypeSize (), endian == Endian.Little);
1787
case EntryType.Undefined:
1788
case EntryType.Byte:
1789
data [i] = raw_data [i];
1792
throw new System.Exception ("Invalid conversion");
1799
// The following methods are usded to convert the data
1800
// to the various type regardless of the entry
1801
// type, they are used internally in processing the data
1802
// Use at your own risk.
1804
public string StringValue {
1806
return System.Text.Encoding.ASCII.GetString (raw_data);
1810
public System.DateTime ValueAsDate {
1812
return DirectoryEntry.DateTimeFromString (StringValue);
1816
public string UserCommentValue {
1818
UserComment comment = new UserComment (raw_data, IsLittle);
1819
return comment.Value;
1823
public SRational [] SRationalValue {
1825
Rational [] vals = RationalValue;
1826
SRational [] data = new SRational [vals.Length];
1828
for (int i = 0; i < vals.Length; i++)
1829
data [i] = SRational.BitwiseCopy (vals [i]);
1835
public Rational [] RationalValue {
1837
uint [] vals = LongValue;
1838
Rational [] data = new Rational [vals.Length / 2];
1840
for (int i = 0; i < vals.Length; i += 2)
1841
data [i/2] = new Rational (vals [i], vals [i + 1]);
1848
public uint [] LongValue {
1850
uint [] data = new uint [raw_data.Length / 4];
1851
for (int i = 0; i < raw_data.Length; i+= 4)
1852
data [i/4] = BitConverter.ToUInt32 (raw_data, i, endian == Endian.Little);
1858
public ushort [] ShortValue {
1860
ushort [] data = new ushort [raw_data.Length];
1861
for (int i = 0; i < raw_data.Length; i+= 2) {
1862
data [i] = BitConverter.ToUInt16 (raw_data, i, endian == Endian.Little);
1871
public class TiffFile : ImageFile, SemWeb.StatementSource {
1872
public Header Header;
1874
// false seems a safe default
1875
public bool Distinct {
1876
get { return false; }
1879
public TiffFile (string path) : base (path)
1882
using (System.IO.Stream input = Open ()) {
1883
this.Header = new Header (input);
1887
Header.Dump (this.ToString () + ":");
1889
} catch (System.Exception e) {
1890
System.Console.WriteLine (e.ToString ());
1894
public TiffFile (Uri uri) : base (uri)
1897
using (System.IO.Stream input = Open ()) {
1898
this.Header = new Header (input);
1902
Header.Dump (this.ToString () + ":");
1904
} catch (System.Exception e) {
1905
System.Console.WriteLine (e.ToString ());
1909
public virtual void Select (SemWeb.StatementSink sink)
1911
Header.SelectDirectory (Header.Directory, sink);
1914
public override System.DateTime Date {
1916
SubdirectoryEntry sub = (SubdirectoryEntry) this.Header.Directory.Lookup (TagId.ExifIfdPointer);
1920
e = sub.Directory [0].Lookup (TagId.DateTimeOriginal);
1923
return DirectoryEntry.DateTimeFromString (e.StringValue).ToUniversalTime ();
1926
e = this.Header.Directory.Lookup (TagId.DateTime);
1929
return DirectoryEntry.DateTimeFromString (e.StringValue).ToUniversalTime ();
1935
public override System.IO.Stream PixbufStream ()
1940
public override PixbufOrientation GetOrientation ()
1942
ShortEntry e = (ShortEntry)(this.Header.Directory.Lookup (TagId.Orientation));
1944
return (PixbufOrientation)(e.ShortValue[0]);
1946
return PixbufOrientation.TopLeft;
1949
public System.IO.Stream LookupJpegSubstream (ImageDirectory directory)
1951
uint offset = directory.Lookup (TagId.JPEGInterchangeFormat).ValueAsLong [0];
1953
System.IO.Stream file = Open ();
1954
file.Position = offset;
1958
public Gdk.Pixbuf LoadJpegInterchangeFormat (ImageDirectory directory)
1960
uint offset = directory.Lookup (TagId.JPEGInterchangeFormat).ValueAsLong [0];
1961
uint length = directory.Lookup (TagId.JPEGInterchangeFormatLength).ValueAsLong [0];
1963
using (System.IO.Stream file = Open ()) {
1964
file.Position = offset;
1966
byte [] data = new byte [32768];
1969
Gdk.PixbufLoader loader = new Gdk.PixbufLoader ();
1971
while (length > 0) {
1972
read = file.Read (data, 0, (int)System.Math.Min ((int)data.Length, length));
1976
loader.Write (data, (ulong)read);
1977
length -= (uint) read;
1979
Gdk.Pixbuf result = loader.Pixbuf;
1986
public class DngFile : TiffFile {
1987
public DngFile (string path) : base (path)
1991
public DngFile (System.Uri uri) : base (uri)
1995
public override System.IO.Stream PixbufStream ()
1998
SubdirectoryEntry sub = (SubdirectoryEntry) Header.Directory.Lookup (TagId.SubIFDs);
1999
ImageDirectory directory = sub.Directory [sub.Directory.Length - 1];
2001
uint offset = directory.Lookup (TagId.StripOffsets).ValueAsLong [0];
2002
System.IO.Stream file = Open ();
2003
file.Position = offset;
2006
return DCRawFile.RawPixbufStream (uri);
2010
public override void Select (SemWeb.StatementSink sink)
2013
/* this is just a sanity pass, if the first ifd is not a subfile use the normal
2016
DirectoryEntry e = Header.Directory.Lookup (TagId.NewSubfileType);
2023
* Even though Ifd0 doesn't have the full resolution image
2024
* it would have the XMP data so we look for it
2026
e = Header.Directory.Lookup (TagId.XMP);
2028
System.IO.Stream xmpstream = new System.IO.MemoryStream (e.RawData);
2029
Xmp.XmpFile xmp = new Xmp.XmpFile (xmpstream);
2034
* Ifd0 will also have the exif directory
2036
ImageDirectory dir = Header.Directory;
2037
SubdirectoryEntry sub = (SubdirectoryEntry) dir.Lookup (TagId.ExifIfdPointer);
2039
Header.SelectDirectory (sub.Directory [0], sink);
2042
* now we lookup subifd0 (we should probably scan the newsubfile types here)
2043
* and load the metadata we are interested in from it.
2045
sub = (SubdirectoryEntry) Header.Directory.Lookup (TagId.SubIFDs);
2049
uint dirtype = e.ValueAsLong [0];
2051
Header.SelectDirectory (dir, sink);
2058
dir = sub.Directory [i];
2059
e = dir.Lookup (TagId.NewSubfileType);
2061
} while (i < sub.Directory.Length);
2067
public class NefFile : TiffFile, IThumbnailContainer {
2068
public NefFile (string path) : base (path)
2072
public NefFile (Uri uri) : base (uri)
2076
public override void Select (SemWeb.StatementSink sink)
2078
DirectoryEntry e = Header.Directory.Lookup (TagId.NewSubfileType);
2085
ImageDirectory dir = Header.Directory;
2086
SubdirectoryEntry sub = (SubdirectoryEntry) dir.Lookup (TagId.ExifIfdPointer);
2089
Header.SelectDirectory (sub.Directory [0], sink);
2091
sub = (SubdirectoryEntry) Header.Directory.Lookup (TagId.SubIFDs);
2095
uint dirtype = e.ValueAsLong [0];
2097
Header.SelectDirectory (dir, sink);
2104
dir = sub.Directory [i];
2105
e = dir.Lookup (TagId.NewSubfileType);
2107
} while (i < sub.Directory.Length);
2110
public Gdk.Pixbuf GetEmbeddedThumbnail ()
2112
using (System.IO.Stream stream = Open ()) {
2113
return TransformAndDispose (new Gdk.Pixbuf (stream));
2117
public override System.IO.Stream PixbufStream ()
2120
SubdirectoryEntry sub = (SubdirectoryEntry) Header.Directory.Lookup (TagId.SubIFDs);
2121
ImageDirectory jpeg_directory = sub.Directory [0];
2122
return LookupJpegSubstream (jpeg_directory);
2123
} catch (System.Exception) {
2124
return DCRawFile.RawPixbufStream (uri);
2130
public class Cr2File : TiffFile, IThumbnailContainer {
2131
public Cr2File (string path) : base (path)
2135
public Cr2File (Uri uri) : base (uri)
2140
public override PixbufOrientation GetOrientation ()
2142
return PixbufOrientation.TopLeft;
2146
public Gdk.Pixbuf GetEmbeddedThumbnail ()
2148
ImageDirectory directory;
2149
directory = Header.Directory.NextDirectory;
2150
return TransformAndDispose (LoadJpegInterchangeFormat (directory));
2154
public override System.IO.Stream PixbufStream ()
2156
uint offset = Header.Directory.Lookup (TagId.StripOffsets).ValueAsLong [0];
2157
System.IO.Stream file = Open ();
2158
file.Position = offset;