Image-Info-1.27.tar.gz
[Image-Info.git] / lib / Image / TIFF.pm
blobcbd8318b4aec2a2f296f5f3ee90aacd8449fca22
1 package Image::TIFF;
3 # Copyright 1999-2001, Gisle Aas.
4 # Copyright 2006, 2007 Tels
6 # This library is free software; you can redistribute it and/or
7 # modify it under the same terms as Perl v5.8.8 itself.
9 use strict;
10 use vars qw($VERSION);
12 $VERSION = '1.06';
14 my @types = (
15 [ "BYTE", "C1", 1],
16 [ "ASCII", "A1", 1],
17 [ "SHORT", "n1", 2],
18 [ "LONG", "N1", 4],
19 [ "RATIONAL", "N2", 8],
20 [ "SBYTE", "c1", 1],
21 [ "UNDEFINED", "a1", 1],
22 [ "BINARY", "a1", 1], # treat binary data as UNDEFINED
23 [ "SSHORT", "n1", 2],
24 [ "SLONG", "N1", 4],
25 [ "SRATIONAL", "N2", 8],
26 [ "FLOAT", "f1", 4], # XXX 4-byte IEEE format
27 [ "DOUBLE", "d1", 8], # XXX 8-byte IEEE format
28 # XXX TODO:
29 # [ "IFD", "??", ?], # See ExifTool
32 my %nikon1_tags = (
33 0x0003 => "Quality",
34 0x0004 => "ColorMode",
35 0x0005 => "ImageAdjustment",
36 0x0006 => "CCDSensitivity",
37 0x0007 => "Whitebalance",
38 0x0008 => "Focus",
39 0x000A => "DigitalZoom",
40 0x000B => "Converter",
43 my %nikon2_tags = (
44 0x0001 => "NikonVersion",
45 0x0002 => "ISOSetting",
46 0x0003 => "ColorMode",
47 0x0004 => "Quality",
48 0x0005 => "Whitebalance",
49 0x0006 => "ImageSharpening",
50 0x0007 => "FocusMode",
51 0x0008 => "FlashSetting",
52 0x0009 => "FlashMetering",
53 0x000B => "WBAdjustment",
54 0x000F => "ISOSelection",
55 0x0080 => "ImageAdjustment",
56 0x0082 => "AuxiliaryLens",
57 0x0084 => "Lens",
58 0x0085 => "ManualFocusDistance",
59 0x0086 => "DigitalZoom",
60 0x0088 => { __TAG__ => "AFFocusPosition",
61 pack("xCxx",0) => "Center",
62 pack("xCxx",1) => "Top",
63 pack("xCxx",2) => "Bottom",
64 pack("xCxx",3) => "Left",
65 pack("xCxx",4) => "Right",
67 0x008d => "ColorMode",
68 0x0090 => "FlashType",
69 0x0095 => "NoiseReduction",
70 0x0010 => "DataDump",
73 my %olympus_tags = (
74 0x0200 => "SpecialMode",
75 0x0201 => { __TAG__ => "JpegQual", 0 => "SQ", 1 => "HQ", 2 => "SHQ" },
76 0x0202 => { __TAG__ => "Macro", 0 => "Normal", 1 => "Macro" },
77 0x0204 => "DigiZoom",
78 0x0207 => "SoftwareRelease",
79 0x0208 => "PictInfo",
80 0x0209 => "CameraID",
81 0x0f00 => "DataDump",
84 my %fujifilm_tags = (
85 0x0000 => "Version",
86 0x1000 => "Quality",
87 0x1001 => { __TAG__ => "Sharpness",
88 1 => "Very Soft",
89 2 => "Soft",
90 3 => "Normal",
91 4 => "Hard",
92 5 => "Very Hard",
94 0x1002 => { __TAG__ => "WhiteBalance",
95 0 => "Auto",
96 256 => "Daylight",
97 512 => "Cloudy",
98 768 => "DaylightColor-fluorescence",
99 769 => "DaywhiteColor-fluorescence",
100 770 => "White-fluorescence",
101 1024 => "Incandenscense",
102 3840 => "Custom white balance",
104 0x1003 => { __TAG__ => "Color", 0 => "Normal", 256 => "High", 512 => "Low" },
105 0x1004 => { __TAG__ => "Tone" , 0 => "Normal", 256 => "High", 512 => "Low" },
106 0x1010 => { __TAG__ => "FlashMode", 0 => "Auto", 1 => "On", 2 => "Off", 3 => "Red-eye reduction" },
107 0x1011 => "FlashStrength",
108 0x1020 => { __TAG__ => "Macro", 0 => "Off", 1 => "On"},
109 0x1021 => { __TAG__ => "FocusMode", 0 => "Auto", 1 => "Manual" },
110 0x1030 => { __TAG__ => "SlowSync", 0 => "Off", 1 => "On"},
111 0x1031 => { __TAG__ => "PictureMode",
112 0 => "Auto",
113 1 => "Portrait",
114 2 => "Landscape",
115 4 => "Sports",
116 5 => "Night",
117 6 => "Program AE",
118 256 => "Aperture priority",
119 512 => "Shutter priority",
120 768 => "Manual",
122 0x1100 => { __TAG__ => "AutoBracketing", 0 => "Off", 1 => "On"},
123 0x1300 => { __TAG__ => "BlurWarning", 0 => "No", 1 => "Yes"},
124 0x1301 => { __TAG__ => "FocusWarning", 0 => "No", 1 => "Yes"},
125 0x1302 => { __TAG__ => "AEWarning", 0 => "No", 1 => "Yes"},
128 my %casio_tags = (
129 0x0001 => { __TAG__ => "RecordingMode",
130 1 => "SingleShutter",
131 2 => "Panorama",
132 3 => "Night scene",
133 4 => "Portrait",
134 5 => "Landscape",
136 0x0002 => { __TAG__ => "Quality", 1 => "Economy", 2 => "Normal", 3 => "Fine" },
137 0x0003 => { __TAG__ => "FocusingMode",
138 2 => "Macro",
139 3 => "Auto",
140 4 => "Manual",
141 5 => "Infinity",
143 0x0004 => { __TAG__ => "FlashMode", 1 => "Auto", 2 => "On", 3 => "Off", 4 => "Red-eye reduction" },
144 0x0005 => { __TAG__ => "FlashIntensity", 11 => "Weak", 13 => "Normal", 15 => "Strong" },
145 0x0006 => "ObjectDistance",
146 0x0007 => { __TAG__ => "WhiteBalance",
147 1 => "Auto",
148 2 => "Tungsten",
149 3 => "Daylight",
150 4 => "Fluorescent",
151 5 => "Shade",
152 129 => "Manual",
154 0x000a => { __TAG__ => "DigitalZoom", 65536 => "Off", 65537 => "2X" },
155 0x000b => { __TAG__ => "Sharpness", 0 => "Normal", 1 => "Soft", 2 => "Hard" },
156 0x000c => { __TAG__ => "Contrast" , 0 => "Normal", 1 => "Low", 2 => "High" },
157 0x000d => { __TAG__ => "Saturation", 0 => "Normal", 1 => "Low", 2 => "High" },
158 0x0014 => { __TAG__ => "CCDSensitivity",
159 64 => "Normal",
160 125 => "+1.0",
161 250 => "+2.0",
162 244 => "+3.0",
163 80 => "Normal",
164 100 => "High",
168 my %canon_0x0001_tags = (
169 0 => { __TAG__ => "MacroMode", 1 => "Macro", 2 => "Normal" },
170 1 => "SelfTimer",
171 2 => { __TAG__ => "Quality", 2 => "Normal", 3 => "Fine", 5 => "SuperFine" },
172 3 => 'Tag-0x0001-03',
173 4 => { __TAG__ => 'FlashMode',
174 0 => 'Flash Not Fired',
175 1 => 'Auto',
176 2 => 'On',
177 3 => 'Red-Eye Reduction',
178 4 => 'Slow Synchro',
179 5 => 'Auto + Red-Eye Reduction',
180 6 => 'On + Red-Eye Reduction',
181 16 => 'External Flash'
183 5 => { __TAG__ => 'ContinuousDriveMode', 0 => 'Single Or Timer', 1 => 'Continuous' },
184 6 => 'Tag-0x0001-06',
185 7 => { __TAG__ => 'FocusMode',
186 0 => 'One-Shot',
187 1 => 'AI Servo',
188 2 => 'AI Focus',
189 3 => 'MF',
190 4 => 'Single',
191 5 => 'Continuous',
192 6 => 'MF'
194 8 => 'Tag-0x0001-08',
195 9 => 'Tag-0x0001-09',
196 10 => { __TAG__ => 'ImageSize', 0 => 'Large', 1 => 'Medium', 2 => 'Small' },
197 11 => { __TAG__ => 'EasyShootingMode',
198 0 => 'Full Auto',
199 1 => 'Manual',
200 2 => 'Landscape',
201 3 => 'Fast Shutter',
202 4 => 'Slow Shutter',
203 5 => 'Night',
204 6 => 'B&W',
205 7 => 'Sepia',
206 8 => 'Portrait',
207 9 => 'Sports',
208 10 => 'Macro/Close-Up',
209 11 => 'Pan Focus'
211 12 => { __TAG__ => 'DigitalZoom', 0 => 'None', 1 => '2x', 2 => '4x' },
212 13 => { __TAG__ => 'Contrast', 0xFFFF => 'Low', 0 => 'Normal', 1 => 'High' },
213 14 => { __TAG__ => 'Saturation', 0xFFFF => 'Low', 0 => 'Normal', 1 => 'High' },
214 15 => { __TAG__ => 'Sharpness', 0xFFFF => 'Low', 0 => 'Normal', 1 => 'High' },
215 16 => { __TAG__ => 'ISO',
216 0 => 'See ISOSpeedRatings Tag',
217 15 => 'Auto',
218 16 => '50',
219 17 => '100',
220 18 => '200',
221 19 => '400'
223 17 => { __TAG__ => 'MeteringMode', 3 => 'Evaluative', 4 => 'Partial', 5 => 'Center-Weighted' },
224 18 => { __TAG__ => 'FocusType',
225 0 => 'Manual',
226 1 => 'Auto',
227 3 => 'Close-Up (Macro)',
228 8 => 'Locked (Pan Mode)'
230 19 => { __TAG__ => 'AFPointSelected',
231 0x3000 => 'None { __TAG__ => MF)',
232 0x3001 => 'Auto-Selected',
233 0x3002 => 'Right',
234 0x3003 => 'Center',
235 0x3004 => 'Left'
237 20 => { __TAG__ => 'ExposureMode',
238 0 => 'Easy Shooting',
239 1 => 'Program',
240 2 => 'Tv-priority',
241 3 => 'Av-priority',
242 4 => 'Manual',
243 5 => 'A-DEP'
245 21 => 'Tag-0x0001-21',
246 22 => 'Tag-0x0001-22',
247 23 => 'LongFocalLengthOfLensInFocalUnits',
248 24 => 'ShortFocalLengthOfLensInFocalUnits',
249 25 => 'FocalUnitsPerMM',
250 26 => 'Tag-0x0001-26',
251 27 => 'Tag-0x0001-27',
252 28 => { __TAG__ => 'FlashActivity', 0 => 'Did Not Fire', 1 => 'Fired' },
253 29 => { __TAG__ => 'FlashDetails',
254 14 => 'External E-TTL',
255 13 => 'Internal Flash',
256 11 => 'FP Sync Used',
257 7 => '2nd ("Rear")-Curtain Sync Used',
258 4 => 'FP Sync Enabled'
260 30 => 'Tag-0x0001-30',
261 31 => 'Tag-0x0001-31',
262 32 => { __TAG__ => 'FocusMode', 0 => 'Single', 1 => 'Continuous' },
265 my %canon_0x0004_tags = (
266 7 => { __TAG__ => 'WhiteBalance',
267 0 => 'Auto',
268 1 => 'Sunny',
269 2 => 'Cloudy',
270 3 => 'Tungsten',
271 4 => 'Fluorescent',
272 5 => 'Flash',
273 6 => 'Custom'
275 9 => 'SequenceNumber',
276 14 => 'AFPointUsed',
277 15 => { __TAG__ => 'FlashBias',
278 0xFFC0 => '-2 EV',
279 0xFFCC => '-1.67 EV',
280 0xFFD0 => '-1.50 EV',
281 0xFFD4 => '-1.33 EV',
282 0xFFE0 => '-1 EV',
283 0xFFEC => '-0.67 EV',
284 0xFFF0 => '-0.50 EV',
285 0xFFF4 => '-0.33 EV',
286 0x0000 => '0 EV',
287 0x000C => '0.33 EV',
288 0x0010 => '0.50 EV',
289 0x0014 => '0.67 EV',
290 0x0020 => '1 EV',
291 0x002C => '1.33 EV',
292 0x0030 => '1.50 EV',
293 0x0034 => '1.67 EV',
294 0x0040 => '2 EV',
296 19 => 'SubjectDistance'
300 my %canon_tags = (
301 0x0001 => { __TAG__ => "Custom_0x0001", __ARRAYOFFSET__ => \%canon_0x0001_tags },
302 0x0004 => { __TAG__ => "Custom_0x0004", __ARRAYOFFSET__ => \%canon_0x0004_tags },
303 0x0006 => "ImageType",
304 0x0007 => "FirmwareVersion",
305 0x0008 => "ImageNumber",
306 0x0009 => "OwnerName",
307 0x000c => "SerialNumber",
310 # see http://www.compton.nu/panasonic.html
312 my %panasonic_tags = (
313 0x0001 => { __TAG__ => "ImageQuality",
314 2 => 'High',
315 3 => 'Normal',
316 6 => 'Very High', #3 (Leica)
317 7 => 'Raw', #3 (Leica)
319 0x0002 => "FirmwareVersion",
320 0x0003 => { __TAG__ => "WhiteBalance",
321 1 => 'Auto',
322 2 => 'Daylight',
323 3 => 'Cloudy',
324 4 => 'Halogen',
325 5 => 'Manual',
326 8 => 'Flash',
327 10 => 'Black & White', #3 (Leica)
329 0x0007 => { __TAG__ => "FocusMode",
330 1 => 'Auto',
331 2 => 'Manual',
332 5 => 'Auto, Continuous',
333 4 => 'Auto, Focus button',
335 0x000f => { __TAG__ => "SpotMode",
336 # XXX TODO: does not decode properly
337 "0,1" => 'On',
338 "0,16" => 'Off',
340 0x001a => { __TAG__ => "ImageStabilizer",
341 2 => 'On, Mode 1',
342 3 => 'Off',
343 4 => 'On, Mode 2',
345 0x001c => { __TAG__ => "MacroMode",
346 1 => 'On',
347 2 => 'Off',
349 0x001f => { __TAG__ => "ShootingMode",
350 1 => 'Normal',
351 2 => 'Portrait',
352 3 => 'Scenery',
353 4 => 'Sports',
354 5 => 'Night Portrait',
355 6 => 'Program',
356 7 => 'Aperture Priority',
357 8 => 'Shutter Priority',
358 9 => 'Macro',
359 11 => 'Manual',
360 13 => 'Panning',
361 18 => 'Fireworks',
362 19 => 'Party',
363 20 => 'Snow',
364 21 => 'Night Scenery',
366 0x0020 => { __TAG__ => "Audio",
367 1 => 'Yes',
368 2 => 'No',
370 0x0021 => "DataDump",
371 0x0022 => "Panasonic 0x0022",
372 0x0023 => "WhiteBalanceBias",
373 0x0024 => "FlashBias",
374 0x0025 => "SerialNumber",
375 0x0026 => "Panasonic 0x0026",
376 0x0027 => "Panasonic 0x0027",
377 0x0028 => { __TAG__ => "ColorEffect",
378 1 => 'Off',
379 2 => 'Warm',
380 3 => 'Cool',
381 4 => 'Black & White',
382 5 => 'Sepia',
384 0x0029 => "Panasonic 0x0029",
385 0x002a => { __TAG__ => "BurstMode",
386 0 => 'Off',
387 1 => 'Low/High Quality',
388 2 => 'Infinite',
390 0x002b => "ImageSequenceNumber",
391 0x002c => { __TAG__ => "Contrast",
392 0 => 'Normal',
393 1 => 'Low',
394 2 => 'High',
395 0x100 => 'Low', # Leica
396 0x110 => 'Normal', # Leica
397 0x120 => 'High', # Leica
399 0x002d => { __TAG__ => "NoiseReduction",
400 0 => 'Standard',
401 1 => 'Low',
402 2 => 'High',
404 0x002e => { __TAG__ => "SelfTimer",
405 1 => 'Off',
406 2 => '10s',
407 3 => '2s',
409 0x002f => "Panasonic 0x002f",
410 0x0030 => "Panasonic 0x0030",
411 0x0031 => "Panasonic 0x0031",
412 0x0032 => "Panasonic 0x0032",
415 # format:
416 # "Make Model" => [ Offset, 'Tag_prefix', ptr to tags ]
417 # Offset is either 0, or a positive number of Bytes.
418 # Offset -1 or -2 means a kludge for Fuji or Nikon
420 my %makernotes = (
421 "NIKON CORPORATION NIKON D1" => [0, 'NikonD1', \%nikon2_tags],
422 "NIKON CORPORATION NIKON D70" => [-2, 'NikonD1', \%nikon2_tags],
423 "NIKON CORPORATION NIKON D100" => [-2, 'NikonD1', \%nikon2_tags],
425 # For the following manufacturers we simple discard the model and always
426 # decode the MakerNote in the same manner. This makes it work with all
427 # models, even yet unreleased ones. (That's better than the very limited
428 # list of models we previously had)
430 "CANON" => [0, 'Canon', \%canon_tags],
431 'PANASONIC' => [12, 'Panasonic', \%panasonic_tags],
432 "FUJIFILM" => [-1, 'FinePix', \%fujifilm_tags],
433 "CASIO" => [0, 'Casio', \%casio_tags],
434 "OLYMPUS" => [8, 'Olympus', \%olympus_tags],
437 BEGIN
439 # add some Nikon cameras
440 for my $model (qw/E700 E800 E900 E900S E910 E950/)
442 $makernotes{'NIKON ' . $model} = [8, 'CoolPix', \%nikon1_tags];
444 for my $model (qw/E880 E990 E995/)
446 $makernotes{'NIKON ' . $model} = [0, 'CoolPix', \%nikon2_tags];
450 my %exif_intr_tags = (
451 0x1 => "InteroperabilityIndex",
452 0x2 => "InteroperabilityVersion",
453 0x1000 => "RelatedImageFileFormat",
454 0x1001 => "RelatedImageWidth",
455 0x1002 => "RelatedImageLength",
458 # Tag decode helpers
459 sub components_configuration_decoder;
460 sub file_source_decoder;
461 sub scene_type_decoder;
463 my %exif_tags = (
464 0x828D => "CFARepeatPatternDim",
465 0x828E => "CFAPattern",
466 0x828F => "BatteryLevel",
467 0x8298 => "Copyright",
468 0x829A => "ExposureTime",
469 0x829D => "FNumber",
470 0x83BB => "IPTC/NAA",
471 0x8769 => "ExifOffset",
472 0x8773 => "InterColorProfile",
473 0x8822 => { __TAG__ => "ExposureProgram",
474 0 => "unknown",
475 1 => "Manual",
476 2 => "Program",
477 3 => "Aperture priority",
478 4 => "Shutter priority",
479 5 => "Program creative",
480 6 => "Program action",
481 7 => "Portrait",
482 8 => "Landscape",
483 # 9 .. 255 reserved
485 0x8824 => "SpectralSensitivity",
486 0x8827 => "ISOSpeedRatings",
487 0x8828 => "OECF",
488 0x9000 => "ExifVersion",
489 0x9003 => "DateTimeOriginal",
490 0x9004 => "DateTimeDigitized",
491 0x9101 => { __TAG__ => "ComponentsConfiguration",
492 DECODER => \&components_configuration_decoder,
494 0x9102 => "CompressedBitsPerPixel",
495 0x9201 => "ShutterSpeedValue",
496 0x9202 => "ApertureValue",
497 0x9203 => "BrightnessValue",
498 0x9204 => "ExposureBiasValue",
499 0x9205 => "MaxApertureValue",
500 0x9206 => "SubjectDistance",
501 0x9207 => { __TAG__ => "MeteringMode",
502 0 => "unknown",
503 1 => "Average",
504 2 => "CenterWeightedAverage",
505 3 => "Spot",
506 4 => "MultiSpot",
507 5 => "Pattern",
508 6 => "Partial",
509 # 7 .. 254 reserved in EXIF 1.2
510 255 => "other",
512 0x9208 => { __TAG__ => "LightSource",
513 0 => "unknown",
514 1 => "Daylight",
515 2 => "Fluorescent",
516 3 => "Tungsten",
517 4 => "Flash",
518 # 5 .. 8 reserveed in EXIF 2.2
519 9 => "Fine weather",
520 10 => "Cloudy weather",
521 11 => "Shade",
522 12 => "Daylight fluorescent (D 5700-7100K)",
523 13 => "Day white fluorescent (N 4600-5400K)",
524 14 => "Cool white fluorescent (W 3900-4500K)",
525 15 => "White fluorescent (WW 3200-3700K)",
526 17 => "Standard light A",
527 18 => "Standard light B",
528 19 => "Standard light C",
529 20 => "D55",
530 21 => "D65",
531 22 => "D75",
532 23 => "D50",
533 24 => "ISO studio tungesten",
534 # 25 .. 254 reserved in EXIF 2.2
535 255 => "other light source",
537 0x9209 => { __TAG__ => "Flash",
538 0x0000 => "Flash did not fire",
539 0x0001 => "Flash fired",
540 0x0005 => "Strobe return light not detected",
541 0x0007 => "Strobe return light detected",
542 0x0009 => "Flash fired, compulsory flash mode",
543 0x000D => "Flash fired, compulsory flash mode, return light not detected",
544 0x000F => "Flash fired, compulsory flash mode, return light detected",
545 0x0010 => "Flash did not fire, compulsory flash mode",
546 0x0018 => "Flash did not fire, auto mode",
547 0x0019 => "Flash fired, auto mode",
548 0x001D => "Flash fired, auto mode, return light not detected",
549 0x001F => "Flash fired, auto mode, return light detected",
550 0x0020 => "No flash function",
551 0x0041 => "Flash fired, red-eye reduction mode",
552 0x0045 => "Flash fired, red-eye reduction mode, return light not detected",
553 0x0047 => "Flash fired, red-eye reduction mode, return light detected",
554 0x0049 => "Flash fired, compulsory flash mode, red-eye reduction mode",
555 0x004D => "Flash fired, compulsory flash mode, red-eye reduction mode, return light not detected",
556 0x004F => "Flash fired, compulsory flash mode, red-eye reduction mode, return light detected",
557 0x0059 => "Flash fired, auto mode, red-eye reduction mode",
558 0x005D => "Flash fired, auto mode, return light not detected, red-eye reduction mode",
559 0x005F => "Flash fired, auto mode, return light detected, red-eye reduction mode"
561 0x920A => "FocalLength",
562 0x9214 => "SubjectArea",
563 0x927C => "MakerNote",
564 0x9286 => "UserComment",
565 0x9290 => "SubSecTime",
566 0x9291 => "SubSecTimeOriginal",
567 0x9292 => "SubSecTimeDigitized",
568 0xA000 => "FlashPixVersion",
569 0xA001 => "ColorSpace",
570 0xA002 => "ExifImageWidth",
571 0xA003 => "ExifImageLength",
572 0xA004 => "RelatedAudioFile",
573 0xA005 => {__TAG__ => "InteroperabilityOffset",
574 __SUBIFD__ => \%exif_intr_tags,
576 0xA20B => "FlashEnergy", # 0x920B in TIFF/EP
577 0xA20C => "SpatialFrequencyResponse", # 0x920C - -
578 0xA20E => "FocalPlaneXResolution", # 0x920E - -
579 0xA20F => "FocalPlaneYResolution", # 0x920F - -
580 0xA210 => { __TAG__ => "FocalPlaneResolutionUnit", # 0x9210 - -
581 1 => "pixels",
582 2 => "dpi",
583 3 => "dpcm",
585 0xA214 => "SubjectLocation", # 0x9214 - -
586 0xA215 => "ExposureIndex", # 0x9215 - -
587 0xA217 => {__TAG__ => "SensingMethod",
588 1 => "Not defined",
589 2 => "One-chip color area sensor",
590 3 => "Two-chip color area sensor",
591 4 => "Three-chip color area sensor",
592 5 => "Color sequential area sensor",
593 7 => "Trilinear sensor",
594 8 => "Color sequential linear sensor"
596 0xA300 => {__TAG__ => "FileSource",
597 DECODER => \&file_source_decoder,
599 0xA301 => {__TAG__ => "SceneType",
600 DECODER => \&scene_type_decoder,
602 0xA302 => "CFAPattern",
603 0xA401 => {__TAG__ => "CustomRendered",
604 0 => "Normal process",
605 1 => "Custom process"
607 0xA402 => {__TAG__ => "ExposureMode",
608 0 => "Auto exposure",
609 1 => "Manual exposure",
610 2 => "Auto bracket"
612 0xA403 => {__TAG__ => "WhiteBalance",
613 0 => "Auto white balance",
614 1 => "Manual white balance"
616 0xA404 => "DigitalZoomRatio",
617 0xA405 => "FocalLengthIn35mmFilm",
618 0xA406 => {__TAG__ => "SceneCaptureType",
619 0 => "Standard",
620 1 => "Landscape",
621 2 => "Portrait",
622 3 => "Night Scene"
624 0xA407 => {__TAG__ => "GainControl",
625 0 => "None",
626 1 => "Low gain up",
627 2 => "High gain up",
628 3 => "Low gain down",
629 4 => "High gain down"
631 0xA408 => {__TAG__ => "Contrast",
632 0 => "Normal",
633 1 => "Soft",
634 2 => "Hard"
636 0xA409 => {__TAG__ => "Saturation",
637 0 => "Normal",
638 1 => "Low saturation",
639 2 => "High saturation"
641 0xA40A => {__TAG__ => "Sharpness",
642 0 => "Normal",
643 1 => "Soft",
644 2 => "Hard"
646 0xA40B => "DeviceSettingDescription",
647 0xA40C => {__TAG__ => "SubjectDistanceRange",
648 0 => "Unknown",
649 1 => "Macro",
650 2 => "Close view",
651 3 => "Distant view"
653 0xA420 => "ImageUniqueID",
656 my %gps_tags = (
657 0x0000 => 'GPSVersionID',
658 0x0001 => 'GPSLatitudeRef',
659 0x0002 => 'GPSLatitude',
660 0x0003 => 'GPSLongitudeRef',
661 0x0004 => 'GPSLongitude',
662 0x0005 => 'GPSAltitudeRef',
663 0x0006 => 'GPSAltitude',
664 0x0007 => 'GPSTimeStamp',
665 0x0008 => 'GPSSatellites',
666 0x0009 => 'GPSStatus',
667 0x000A => 'GPSMeasureMode',
668 0x000B => 'GPSDOP',
669 0x000C => 'GPSSpeedRef',
670 0x000D => 'GPSSpeed',
671 0x000E => 'GPSTrackRef',
672 0x000F => 'GPSTrack',
673 0x0010 => 'GPSImgDirectionRef',
674 0x0011 => 'GPSImgDirection',
675 0x0012 => 'GPSMapDatum',
676 0x0013 => 'GPSDestLatitudeRef',
677 0x0014 => 'GPSDestLatitude',
678 0x0015 => 'GPSDestLongitudeRef',
679 0x0016 => 'GPSDestLongitude',
680 0x0017 => 'GPSDestBearingRef',
681 0x0018 => 'GPSDestBearing',
682 0x0019 => 'GPSDestDistanceRef',
683 0x001A => 'GPSDestDistance',
684 0x001B => 'GPSProcessingMethod',
685 0x001C => 'GPSAreaInformation',
686 0x001D => 'GPSDateStamp',
687 0x001E => 'GPSDifferential',
690 my %tiff_tags = (
691 254 => { __TAG__ => "NewSubfileType",
692 1 => "ReducedResolution",
693 2 => "SinglePage",
694 4 => "TransparencyMask",
696 255 => { __TAG__ => "SubfileType",
697 1 => "FullResolution",
698 2 => "ReducedResolution",
699 3 => "SinglePage",
701 256 => "width",
702 257 => "height",
703 258 => "BitsPerSample",
704 259 => { __TAG__ => "Compression",
705 1 => "PackBytes",
706 2 => "CCITT Group3",
707 3 => "CCITT T4",
708 4 => "CCITT T6",
709 5 => "LZW",
710 6 => "JPEG",
711 7 => "JPEG DCT",
712 8 => "Deflate (Adobe)",
713 32766 => "NeXT 2-bit RLE",
714 32771 => "#1 w/ word alignment",
715 32773 => "PackBits (Macintosh RLE)",
716 32809 => "ThunderScan RLE",
717 32895 => "IT8 CT w/padding",
718 32896 => "IT8 Linework RLE",
719 32897 => "IT8 Monochrome picture",
720 32898 => "IT8 Binary line art",
721 32908 => "Pixar companded 10bit LZW",
722 32909 => "Pixar companded 11bit ZIP",
723 32946 => "Deflate",
725 262 => { __TAG__ => "PhotometricInterpretation",
726 0 => "WhiteIsZero",
727 1 => "BlackIsZero",
728 2 => "RGB",
729 3 => "RGB Palette",
730 4 => "Transparency Mask",
731 5 => "CMYK",
732 6 => "YCbCr",
733 8 => "CIELab",
735 263 => { __TAG__ => "Threshholding",
736 1 => "NoDithering",
737 2 => "OrderedDither",
738 3 => "Randomized",
740 266 => { __TAG__ => "FillOrder",
741 1 => "LowInHigh",
742 2 => "HighInLow",
744 269 => "DocumentName",
745 270 => "ImageDescription",
746 271 => "Make",
747 272 => "Model",
748 273 => "StripOffsets",
749 274 => { __TAG__ => "Orientation",
750 1 => "top_left",
751 2 => "top_right",
752 3 => "bot_right",
753 4 => "bot_left",
754 5 => "left_top",
755 6 => "right_top",
756 7 => "right_bot",
757 8 => "left_bot",
759 277 => "SamplesPerPixel",
760 278 => "RowsPerStrip",
761 279 => "StripByteCounts",
762 282 => "XResolution",
763 283 => "YResolution",
764 284 => {__TAG__ => "PlanarConfiguration",
765 1 => "Chunky", 2 => "Planar",
767 296 => {__TAG__ => "ResolutionUnit",
768 1 => "pixels", 2 => "dpi", 3 => "dpcm",
770 297 => "PageNumber",
771 301 => "TransferFunction",
772 305 => "Software",
773 306 => "DateTime",
774 315 => "Artist",
775 316 => "Host",
776 318 => "WhitePoint",
777 319 => "PrimaryChromaticities",
778 320 => "ColorMap",
779 513 => "JPEGInterchangeFormat",
780 514 => "JPEGInterchangeFormatLength",
781 529 => "YCbCrCoefficients",
782 530 => "YCbCrSubSampling",
783 531 => "YCbCrPositioning",
784 532 => "ReferenceBlackWhite",
785 33432 => "Copyright",
786 34665 => { __TAG__ => "ExifOffset",
787 __SUBIFD__ => \%exif_tags,
789 34853 => { __TAG__ => "GPSInfo",
790 __SUBIFD__ => \%gps_tags,
795 sub new
797 my $class = shift;
798 my $source = shift;
800 if (!ref($source)) {
801 local(*F);
802 open(F, $source) || return;
803 binmode(F);
804 $source = \*F;
807 if (ref($source) ne "SCALAR") {
808 # XXX should really only read the file on demand
809 local($/); # slurp mode
810 my $data = <$source>;
811 $source = \$data;
814 my $self = bless { source => $source }, $class;
816 for ($$source) {
817 my $byte_order = substr($_, 0, 2);
818 $self->{little_endian} = ($byte_order eq "II");
819 $self->{version} = $self->unpack("n", substr($_, 2, 2));
821 my $ifd = $self->unpack("N", substr($_, 4, 4));
822 while ($ifd) {
823 push(@{$self->{ifd}}, $ifd);
824 my($num_fields) = $self->unpack("x$ifd n", $_);
825 my $next_ifd = $self->unpack("N", substr($_, $ifd + 2 + $num_fields*12, 4));
826 # guard against (bug #26127)
827 $next_ifd = 0 if $next_ifd > length($_);
828 # guard against looping ifd (bug #26130)
829 if ($next_ifd <= $ifd) {
830 # bad TIFF header - would cause a loop or strange results
831 last;
833 $ifd = $next_ifd;
837 $self;
840 sub unpack
842 my $self = shift;
843 my $template = shift;
844 if ($self->{little_endian}) {
845 $template =~ tr/nN/vV/;
847 #print "UNPACK $template\n";
848 CORE::unpack($template, $_[0]);
851 sub num_ifds
853 my $self = shift;
854 scalar @{$self->{ifd}};
857 sub ifd
859 my $self = shift;
860 my $num = shift || 0;
861 my @ifd;
863 $self->add_fields($self->{ifd}[$num], \@ifd);
866 sub tagname
868 $tiff_tags{$_[1]} || sprintf "Tag-0x%04x",$_[1];
871 sub exif_tagname
873 $tiff_tags{$_[1]} || $exif_tags{$_[1]} || sprintf "Tag-0x%04x",$_[1];
876 sub add_fields
878 my($self, $offset, $ifds, $tags, $voff_plus) = @_;
879 return unless $offset;
880 # guard against finding the same offset twice (bug #29088)
881 return if $self->{seen_offset}{$offset}++;
882 $tags ||= \%tiff_tags;
884 for (${$self->{source}}) { # alias as $_
885 last if $offset > length($_) - 2; # bad offset
886 my $entries = $self->unpack("x$offset n", $_);
887 my $max_entries = int((length($_) - $offset - 2) / 12);
888 # print "ENTRIES $entries $max_entries\n";
889 if ($entries > $max_entries) {
890 # Hmmm, something smells bad here... parsing garbage
891 $entries = $max_entries;
892 last;
894 FIELD:
895 for my $i (0 .. $entries-1) {
896 my $entry_offset = 2 + $offset + $i*12;
897 my($tag, $type, $count, $voff) =
898 $self->unpack("nnNN", substr($_, $entry_offset, 12));
899 #print STDERR "TAG $tag $type $count $voff\n";
901 if ($type == 0 || $type > @types) {
902 # unknown type code might indicate that we are parsing garbage
903 print STDERR "# Ignoring unknown type code $type\n";
904 next;
907 # extract type information
908 my($tmpl, $vlen);
909 ($type, $tmpl, $vlen) = @{$types[$type-1]};
911 die "Undefined type while parsing" unless $type;
913 if ($count * $vlen <= 4) {
914 $voff = $entry_offset + 8;
916 elsif ($voff + $count * $vlen > length($_)) {
917 # offset points outside of string, corrupt entry ignore
918 print STDERR "# ignoring offset pointer outside of string\n";
919 next;
921 else {
922 $voff += $voff_plus || 0;
925 $tmpl =~ s/(\d+)$/$count*$1/e;
927 my @v = $self->unpack("x$voff $tmpl", $_);
929 if ($type =~ /^S(SHORT|LONG|RATIONAL)/) {
930 my $max = 2 ** ($1 eq "SHORT" ? 15 : 31);
931 $v[0] -= ($max * 2) if $v[0] >= $max;
934 my $val = (@v > 1) ? \@v : $v[0];
935 bless $val, "Image::TIFF::Rational" if $type =~ /^S?RATIONAL$/;
937 if ($type eq 'ASCII' || $type eq 'UNDEFINED')
939 if ($val =~ /\0[^\0]+\z/)
941 # cut out the first "ASCII\0" and take the remaining text
942 # (fix bug #29243 parsing of UserComment)
943 # XXX TODO: this needs to handle UNICODE, too:
944 $val =~ /\0([^\0]+)/;
945 $val = '' . ($1 || '');
947 else
949 # avoid things like "Foo\x00\x00\x00" by removing trailing nulls
950 # this needs to handle UNICODE, too:
951 $val =~ /^([^\0]*)/;
952 $val = '' . ($1 || '');
956 $tag = $tags->{$tag} || $self->tagname($tag);
958 if ($tag eq 'MakerNote')
960 my $maker = uc($self->{Make} || '');
961 $maker =~ /^([A-Z]+)/; $maker = $1 || ''; # "OLYMPUS ..." > "OLYMPUS"
963 # if 'Panasonic' doesn't exist, try 'Panasonic DMC-FZ5'
964 $maker = $self->{Make}.' '.$self->{Model}
965 unless exists $makernotes{$maker};
967 if (exists $makernotes{$maker}) {
968 my ($ifd_off, $tag_prefix, $sub) = @{$makernotes{$maker}};
970 #print STDERR "# Decoding Makernotes from $maker\n";
972 $self->{tag_prefix} = $tag_prefix;
973 if ($ifd_off == -1) {
974 # fuji kludge - http://www.butaman.ne.jp/~tsuruzoh/Computer/Digicams/exif-e.html#APP4
975 my $save_endian = $self->{little_endian};
976 $self->{little_endian} = 1;
977 $ifd_off = $self->unpack("N", substr($val, 8, 4));
978 $self->add_fields($voff+$ifd_off, $ifds, $sub, $voff);
979 $self->{little_endian} = $save_endian;
980 } elsif ($ifd_off == -2) {
981 # Nikon D70/D100 kludge -- word "Nikon" and 5
982 # bytes of data is tacked to the front of MakerNote;
983 # all EXIF offsets are relative to MakerNote section
984 my ($nikon_voff);
985 $nikon_voff = 0;
986 if (substr($val, 0, 5) eq 'Nikon') {
987 $nikon_voff = $voff+10;
989 #print "IFD_OFF $ifd_off NIKON_VOFF $nikon_voff\n";
990 $self->add_fields($voff+18, $ifds, $sub, $nikon_voff);
991 } else {
992 $self->add_fields($voff+$ifd_off, $ifds, $sub)
994 delete $self->{tag_prefix};
995 next FIELD;
999 if (ref($tag)) {
1000 die "Assert" unless ref($tag) eq "HASH";
1001 if (my $sub = $tag->{__SUBIFD__}) {
1002 next if $val < 0 || $val > length($_);
1003 #print "SUBIFD $tag->{__TAG__} $val ", length($_), "\n";
1004 $self->add_fields($val, $ifds, $sub);
1005 next FIELD;
1007 if (my $sub = $tag->{__ARRAYOFFSET__}) {
1008 my $prefix = $tag = $self->{tag_prefix} . '-' if $self->{tag_prefix};
1009 for (my $i=0; $i < @$val; $i++) {
1010 if ( exists($sub->{$i}) )
1011 { if ( ref($sub->{$i}) eq "HASH" && exists($sub->{$i}->{__TAG__}) )
1012 { if ( exists($sub->{$i}->{$val->[$i]}) )
1014 push @$ifds, [ $prefix . $sub->{$i}->{__TAG__}, $type, $count,
1015 $sub->{$i}->{$val->[$i]} ];
1017 else
1019 push @$ifds, [ $prefix . $sub->{$i}->{__TAG__}, $type, $count,
1020 "Unknown (" . $val->[$i] . ")" ];
1023 else
1024 { push @$ifds, [ $prefix . $sub->{$i}, $type, $count, $val->[$i] ]; }
1027 next FIELD;
1029 #hack for UNDEFINED values, they all have different
1030 #meanings depending on tag
1031 $val = &{$tag->{DECODER}}($self,$val) if defined($tag->{DECODER});
1032 $val = $tag->{$val} if exists $tag->{$val};
1033 $tag = $tag->{__TAG__};
1036 $tag = $self->{tag_prefix} . '-' . $tag if $self->{tag_prefix};
1038 #if ( $val =~ m/ARRAY/ ) { $val = join(', ',@$val); }
1039 push @$ifds, [ $tag, $type, $count, $val ];
1041 $self->{$tag} = $val if ($tag eq 'Make' or $tag eq 'Model');
1045 $ifds;
1048 sub components_configuration_decoder
1050 my $self = shift;
1051 my $val = shift;
1052 my $rv = "";
1053 my %v = (
1054 0 => undef,
1055 1 => 'Y',
1056 2 => 'Cb',
1057 3 => 'Cr',
1058 4 => 'R',
1059 5 => 'G',
1060 6 => 'B',
1062 return join ( '', map { $v{$_} if defined($v{$_}) } $self->unpack('c4',$val) );
1065 sub file_source_decoder
1067 my $self = shift;
1068 my $val = shift;
1069 my %v = (
1070 3 => "(DSC) Digital Still Camera",
1072 $val = $self->unpack('c',$val);
1073 return $v{$val} if $v{$val};
1074 "Other";
1077 sub scene_type_decoder
1079 my $self = shift;
1080 my $val = shift;
1081 my %v = (
1082 1 => "Directly Photographed Image",
1084 $val = $self->unpack('c',$val);
1085 return $v{$val} if $v{$val};
1086 "Other";
1089 package Image::TIFF::Rational;
1091 use overload '""' => \&as_string,
1092 '0+' => \&as_float,
1093 fallback => 1;
1095 sub new {
1096 my($class, $a, $b) = @_;
1097 bless [$a, $b], $class;
1100 sub as_string {
1101 my $self = shift;
1102 #warn "@$self";
1103 "$self->[0]/$self->[1]";
1106 sub as_float {
1107 my $self = shift;
1109 # We check here because some stupid cameras (Samsung Digimax 200)
1110 # use rationals with 0 denominator (found in thumbnail resolution spec).
1111 if ($self->[1]) {
1112 return $self->[0] / $self->[1];
1114 else {
1115 return $self->[0];