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.
10 use vars
qw($VERSION);
19 [ "RATIONAL", "N2", 8],
21 [ "UNDEFINED", "a1", 1],
22 [ "BINARY", "a1", 1], # treat binary data as UNDEFINED
25 [ "SRATIONAL", "N2", 8],
26 [ "FLOAT", "f1", 4], # XXX 4-byte IEEE format
27 [ "DOUBLE", "d1", 8], # XXX 8-byte IEEE format
29 # [ "IFD", "??", ?], # See ExifTool
34 0x0004 => "ColorMode",
35 0x0005 => "ImageAdjustment",
36 0x0006 => "CCDSensitivity",
37 0x0007 => "Whitebalance",
39 0x000A => "DigitalZoom",
40 0x000B => "Converter",
44 0x0001 => "NikonVersion",
45 0x0002 => "ISOSetting",
46 0x0003 => "ColorMode",
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",
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",
74 0x0200 => "SpecialMode",
75 0x0201 => { __TAG__ => "JpegQual", 0 => "SQ", 1 => "HQ", 2 => "SHQ" },
76 0x0202 => { __TAG__ => "Macro", 0 => "Normal", 1 => "Macro" },
78 0x0207 => "SoftwareRelease",
87 0x1001 => { __TAG__ => "Sharpness",
94 0x1002 => { __TAG__ => "WhiteBalance",
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",
118 256 => "Aperture priority",
119 512 => "Shutter priority",
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"},
129 0x0001 => { __TAG__ => "RecordingMode",
130 1 => "SingleShutter",
136 0x0002 => { __TAG__ => "Quality", 1 => "Economy", 2 => "Normal", 3 => "Fine" },
137 0x0003 => { __TAG__ => "FocusingMode",
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",
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",
168 my %canon_0x0001_tags = (
169 0 => { __TAG__ => "MacroMode", 1 => "Macro", 2 => "Normal" },
171 2 => { __TAG__ => "Quality", 2 => "Normal", 3 => "Fine", 5 => "SuperFine" },
172 3 => 'Tag-0x0001-03',
173 4 => { __TAG__ => 'FlashMode',
174 0 => 'Flash Not Fired',
177 3 => 'Red-Eye Reduction',
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',
194 8 => 'Tag-0x0001-08',
195 9 => 'Tag-0x0001-09',
196 10 => { __TAG__ => 'ImageSize', 0 => 'Large', 1 => 'Medium', 2 => 'Small' },
197 11 => { __TAG__ => 'EasyShootingMode',
208 10 => 'Macro/Close-Up',
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',
223 17 => { __TAG__ => 'MeteringMode', 3 => 'Evaluative', 4 => 'Partial', 5 => 'Center-Weighted' },
224 18 => { __TAG__ => 'FocusType',
227 3 => 'Close-Up (Macro)',
228 8 => 'Locked (Pan Mode)'
230 19 => { __TAG__ => 'AFPointSelected',
231 0x3000 => 'None { __TAG__ => MF)',
232 0x3001 => 'Auto-Selected',
237 20 => { __TAG__ => 'ExposureMode',
238 0 => 'Easy Shooting',
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',
275 9 => 'SequenceNumber',
277 15 => { __TAG__ => 'FlashBias',
279 0xFFCC => '-1.67 EV',
280 0xFFD0 => '-1.50 EV',
281 0xFFD4 => '-1.33 EV',
283 0xFFEC => '-0.67 EV',
284 0xFFF0 => '-0.50 EV',
285 0xFFF4 => '-0.33 EV',
296 19 => 'SubjectDistance'
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",
316 6 => 'Very High', #3 (Leica)
317 7 => 'Raw', #3 (Leica)
319 0x0002 => "FirmwareVersion",
320 0x0003 => { __TAG__ => "WhiteBalance",
327 10 => 'Black & White', #3 (Leica)
329 0x0007 => { __TAG__ => "FocusMode",
332 5 => 'Auto, Continuous',
333 4 => 'Auto, Focus button',
335 0x000f => { __TAG__ => "SpotMode",
336 # XXX TODO: does not decode properly
340 0x001a => { __TAG__ => "ImageStabilizer",
345 0x001c => { __TAG__ => "MacroMode",
349 0x001f => { __TAG__ => "ShootingMode",
354 5 => 'Night Portrait',
356 7 => 'Aperture Priority',
357 8 => 'Shutter Priority',
364 21 => 'Night Scenery',
366 0x0020 => { __TAG__ => "Audio",
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",
381 4 => 'Black & White',
384 0x0029 => "Panasonic 0x0029",
385 0x002a => { __TAG__ => "BurstMode",
387 1 => 'Low/High Quality',
390 0x002b => "ImageSequenceNumber",
391 0x002c => { __TAG__ => "Contrast",
395 0x100 => 'Low', # Leica
396 0x110 => 'Normal', # Leica
397 0x120 => 'High', # Leica
399 0x002d => { __TAG__ => "NoiseReduction",
404 0x002e => { __TAG__ => "SelfTimer",
409 0x002f => "Panasonic 0x002f",
410 0x0030 => "Panasonic 0x0030",
411 0x0031 => "Panasonic 0x0031",
412 0x0032 => "Panasonic 0x0032",
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
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],
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",
459 sub components_configuration_decoder;
460 sub file_source_decoder;
461 sub scene_type_decoder;
464 0x828D => "CFARepeatPatternDim",
465 0x828E => "CFAPattern",
466 0x828F => "BatteryLevel",
467 0x8298 => "Copyright",
468 0x829A => "ExposureTime",
470 0x83BB => "IPTC/NAA",
471 0x8769 => "ExifOffset",
472 0x8773 => "InterColorProfile",
473 0x8822 => { __TAG__ => "ExposureProgram",
477 3 => "Aperture priority",
478 4 => "Shutter priority",
479 5 => "Program creative",
480 6 => "Program action",
485 0x8824 => "SpectralSensitivity",
486 0x8827 => "ISOSpeedRatings",
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",
504 2 => "CenterWeightedAverage",
509 # 7 .. 254 reserved in EXIF 1.2
512 0x9208 => { __TAG__ => "LightSource",
518 # 5 .. 8 reserveed in EXIF 2.2
520 10 => "Cloudy weather",
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",
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 - -
585 0xA214 => "SubjectLocation", # 0x9214 - -
586 0xA215 => "ExposureIndex", # 0x9215 - -
587 0xA217 => {__TAG__ => "SensingMethod",
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",
612 0xA403 => {__TAG__ => "WhiteBalance",
613 0 => "Auto white balance",
614 1 => "Manual white balance"
616 0xA404 => "DigitalZoomRatio",
617 0xA405 => "FocalLengthIn35mmFilm",
618 0xA406 => {__TAG__ => "SceneCaptureType",
624 0xA407 => {__TAG__ => "GainControl",
628 3 => "Low gain down",
629 4 => "High gain down"
631 0xA408 => {__TAG__ => "Contrast",
636 0xA409 => {__TAG__ => "Saturation",
638 1 => "Low saturation",
639 2 => "High saturation"
641 0xA40A => {__TAG__ => "Sharpness",
646 0xA40B => "DeviceSettingDescription",
647 0xA40C => {__TAG__ => "SubjectDistanceRange",
653 0xA420 => "ImageUniqueID",
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',
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',
691 254 => { __TAG__ => "NewSubfileType",
692 1 => "ReducedResolution",
694 4 => "TransparencyMask",
696 255 => { __TAG__ => "SubfileType",
697 1 => "FullResolution",
698 2 => "ReducedResolution",
703 258 => "BitsPerSample",
704 259 => { __TAG__ => "Compression",
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",
725 262 => { __TAG__ => "PhotometricInterpretation",
730 4 => "Transparency Mask",
735 263 => { __TAG__ => "Threshholding",
737 2 => "OrderedDither",
740 266 => { __TAG__ => "FillOrder",
744 269 => "DocumentName",
745 270 => "ImageDescription",
748 273 => "StripOffsets",
749 274 => { __TAG__ => "Orientation",
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",
771 301 => "TransferFunction",
777 319 => "PrimaryChromaticities",
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,
802 open(F, $source) || return;
807 if (ref($source) ne "SCALAR") {
808 # XXX should really only read the file on demand
809 local($/); # slurp mode
810 my $data = <$source>;
814 my $self = bless { source => $source }, $class;
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));
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
843 my $template = shift;
844 if ($self->{little_endian}) {
845 $template =~ tr/nN/vV/;
847 #print "UNPACK $template\n";
848 CORE::unpack($template, $_[0]);
854 scalar @{$self->{ifd}};
860 my $num = shift || 0;
863 $self->add_fields($self->{ifd}[$num], \@ifd);
868 $tiff_tags{$_[1]} || sprintf "Tag-0x%04x",$_[1];
873 $tiff_tags{$_[1]} || $exif_tags{$_[1]} || sprintf "Tag-0x%04x",$_[1];
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;
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";
907 # extract type information
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";
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 || '');
949 # avoid things like "Foo\x00\x00\x00" by removing trailing nulls
950 # this needs to handle UNICODE, too:
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
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);
992 $self->add_fields($voff+$ifd_off, $ifds, $sub)
994 delete $self->{tag_prefix};
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);
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]} ];
1019 push @$ifds, [ $prefix . $sub->{$i}->{__TAG__}, $type, $count,
1020 "Unknown (" . $val->[$i] . ")" ];
1024 { push @$ifds, [ $prefix . $sub->{$i}, $type, $count, $val->[$i] ]; }
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');
1048 sub components_configuration_decoder
1062 return join ( '', map { $v{$_} if defined($v{$_}) } $self->unpack('c4',$val) );
1065 sub file_source_decoder
1070 3 => "(DSC) Digital Still Camera",
1072 $val = $self->unpack('c',$val);
1073 return $v{$val} if $v{$val};
1077 sub scene_type_decoder
1082 1 => "Directly Photographed Image",
1084 $val = $self->unpack('c',$val);
1085 return $v{$val} if $v{$val};
1089 package Image::TIFF::Rational;
1091 use overload '""' => \&as_string,
1096 my($class, $a, $b) = @_;
1097 bless [$a, $b], $class;
1103 "$self->[0]/$self->[1]";
1109 # We check here because some stupid cameras (Samsung Digimax 200)
1110 # use rationals with 0 denominator (found in thumbnail resolution spec).
1112 return $self->[0] / $self->[1];