#------------------------------------------------------------------------------ # File: PhaseOne.pm # # Description: Phase One maker notes tags # # Revisions: 2013-02-17 - P. Harvey Created # # References: 1) http://www.cybercom.net/~dcoffin/dcraw/ #------------------------------------------------------------------------------ package Image::ExifTool::PhaseOne; use strict; use vars qw($VERSION); use Image::ExifTool qw(:DataAccess :Utils); use Image::ExifTool::Exif; $VERSION = '1.07'; sub WritePhaseOne($$$); sub ProcessPhaseOne($$$); # default formats based on PhaseOne format size my @formatName = ( undef, 'string', 'int16s', undef, 'int32s' ); # Phase One maker notes (ref PH) %Image::ExifTool::PhaseOne::Main = ( PROCESS_PROC => \&ProcessPhaseOne, WRITE_PROC => \&WritePhaseOne, CHECK_PROC => \&Image::ExifTool::Exif::CheckExif, WRITABLE => '1', FORMAT => 'int32s', GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' }, VARS => { ENTRY_SIZE => 16 }, # (entries contain a format field) NOTES => 'These tags are extracted from the maker notes of Phase One images.', 0x0100 => { #1 Name => 'CameraOrientation', ValueConv => '$val & 0x03', # ignore other bits for now PrintConv => { 0 => 'Horizontal (normal)', 1 => 'Rotate 90 CW', 2 => 'Rotate 270 CW', 3 => 'Rotate 180', }, }, # 0x0101 - int32u: 96,160,192,256,544 (same as 0x0213) 0x0102 => { Name => 'SerialNumber', Format => 'string' }, # 0x0103 - int32u: 19,20,59769034 # 0x0104 - int32u: 50,200 0x0105 => 'ISO', 0x0106 => { Name => 'ColorMatrix1', Format => 'float', Count => 9, PrintConv => q{ my @a = map { sprintf('%.3f', $_) } split ' ', $val; return "@a"; }, PrintConvInv => '$val', }, 0x0107 => { Name => 'WB_RGBLevels', Format => 'float', Count => 3 }, 0x0108 => 'SensorWidth', 0x0109 => 'SensorHeight', 0x010a => 'SensorLeftMargin', #1 0x010b => 'SensorTopMargin', #1 0x010c => 'ImageWidth', 0x010d => 'ImageHeight', 0x010e => { #1 Name => 'RawFormat', # 1 = raw bit mask 0x5555 (>1 mask 0x1354) # >2 = compressed # 5 = non-linear PrintConv => { #PH 1 => 'RAW 1', #? (encrypted) 2 => 'RAW 2', #? (encrypted) 3 => 'IIQ L', # (now "L14", ref IB) # 4? 5 => 'IIQ S', 6 => 'IIQ Sv2', # (now "S14" for "IIQ 14 Smart" and "IIQ 14 Sensor+", ref IB) 8 => 'IIQ L16', #IB ("IIQ 16 Extended" and "IIQ 16 Large") }, }, 0x010f => { Name => 'RawData', Format => 'undef', # (actually 2-byte integers, but don't convert) Binary => 1, PutFirst => 1, Writable => 0, Drop => 1, # don't copy to other file types }, 0x0110 => { #1 Name => 'SensorCalibration', SubDirectory => { TagTable => 'Image::ExifTool::PhaseOne::SensorCalibration' }, }, 0x0112 => { Name => 'DateTimeOriginal', Description => 'Date/Time Original', Format => 'int32u', Writable => 0, # (don't write because this is an encryption key for RawFormat 1 and 2) Priority => 0, Shift => 'Time', Groups => { 2 => 'Time' }, Notes => 'may be used as a key to encrypt the raw data', #1 ValueConv => 'ConvertUnixTime($val)', ValueConvInv => 'GetUnixTime($val)', PrintConv => '$self->ConvertDateTime($val)', PrintConvInv => '$self->InverseDateTime($val)', }, 0x0113 => 'ImageNumber', # (NC) 0x0203 => { Name => 'Software', Format => 'string' }, 0x0204 => { Name => 'System', Format => 'string' }, # 0x020b - int32u: 0,1 # 0x020c - int32u: 1,2 # 0x020e - int32u: 1,3 0x0210 => { # (NC) (used in linearization formula - ref 1) Name => 'SensorTemperature', Format => 'float', PrintConv => 'sprintf("%.2f C",$val)', PrintConvInv => '$val=~s/ ?C//; $val', }, 0x0211 => { # (NC) Name => 'SensorTemperature2', Format => 'float', PrintConv => 'sprintf("%.2f C",$val)', PrintConvInv => '$val=~s/ ?C//; $val', }, 0x0212 => { Name => 'UnknownDate', Format => 'int32u', Groups => { 2 => 'Time' }, # (this time is within about 10 minutes before or after 0x0112) Unknown => 1, Shift => 'Time', ValueConv => 'ConvertUnixTime($val)', ValueConvInv => 'GetUnixTime($val)', PrintConv => '$self->ConvertDateTime($val)', PrintConvInv => '$self->InverseDateTime($val)', }, # 0x0213 - int32u: 96,160,192,256,544 (same as 0x0101) # 0x0215 - int32u: 4,5 # 0x021a - used by dcraw 0x021c => { Name => 'StripOffsets', Binary => 1, Writable => 0 }, 0x021d => 'BlackLevel', #1 # 0x021e - int32u: 1 # 0x0220 - int32u: 32 # 0x0221 - float: 0-271 0x0222 => 'SplitColumn', #1 0x0223 => { Name => 'BlackLevelData', Format => 'int16u', Count => -1, Binary => 1 }, #1 # 0x0224 - int32u: 1688,2748,3372 0x0225 => { Name => 'PhaseOne_0x0225', Format => 'int16s', Count => -1, Flags => ['Unknown','Hidden'], PrintConv => 'length($val) > 60 ? substr($val,0,55) . "[...]" : $val', }, 0x0226 => { Name => 'ColorMatrix2', Format => 'float', Count => 9, PrintConv => q{ my @a = map { sprintf('%.3f', $_) } split ' ', $val; return "@a"; }, PrintConvInv => '$val', }, # 0x0227 - int32u: 0,1 # 0x0228 - int32u: 1,2 # 0x0229 - int32s: -2,0 0x0267 => { #PH Name => 'AFAdjustment', Format => 'float', }, 0x022b => { #PH Name => 'PhaseOne_0x022b', Format => 'float', Flags => ['Unknown','Hidden'], }, # 0x0242 - int32u: 55 # 0x0244 - int32u: 102 # 0x0245 - float: 1.2 0x0258 => { #PH Name => 'PhaseOne_0x0258', Format => 'int16s', Flags => ['Unknown','Hidden'], PrintConv => 'length($val) > 60 ? substr($val,0,55) . "[...]" : $val', }, 0x025a => { #PH Name => 'PhaseOne_0x025a', Format => 'int16s', Flags => ['Unknown','Hidden'], PrintConv => 'length($val) > 60 ? substr($val,0,55) . "[...]" : $val', }, # 0x0300 - int32u: 100,101,102 0x0301 => { Name => 'FirmwareVersions', Format => 'string' }, # 0x0304 - int32u: 8,3073,3076 0x0400 => { Name => 'ShutterSpeedValue', Format => 'float', ValueConv => 'abs($val)<100 ? 2**(-$val) : 0', ValueConvInv => '$val>0 ? -log($val)/log(2) : -100', PrintConv => 'Image::ExifTool::Exif::PrintExposureTime($val)', PrintConvInv => 'Image::ExifTool::Exif::ConvertFraction($val)', }, 0x0401 => { Name => 'ApertureValue', Format => 'float', ValueConv => '2 ** ($val / 2)', ValueConvInv => '$val>0 ? 2*log($val)/log(2) : 0', PrintConv => 'sprintf("%.1f",$val)', PrintConvInv => '$val', }, 0x0402 => { Name => 'ExposureCompensation', Format => 'float', PrintConv => 'sprintf("%.3f",$val)', PrintConvInv => '$val', }, 0x0403 => { Name => 'FocalLength', Format => 'float', PrintConv => 'sprintf("%.1f mm",$val)', PrintConvInv => '$val=~s/\s*mm$//;$val', }, # 0x0404 - int32u: 0,3 # 0x0405 - int32u? (big numbers) # 0x0406 - int32u: 1 # 0x0407 - float: -0.333 (exposure compensation again?) # 0x0408-0x0409 - int32u: 1 0x0410 => { Name => 'CameraModel', Format => 'string' }, # 0x0411 - int32u: 33556736 0x0412 => { Name => 'LensModel', Format => 'string' }, 0x0414 => { Name => 'MaxApertureValue', Format => 'float', ValueConv => '2 ** ($val / 2)', ValueConvInv => '$val>0 ? 2*log($val)/log(2) : 0', PrintConv => 'sprintf("%.1f",$val)', PrintConvInv => '$val', }, 0x0415 => { Name => 'MinApertureValue', Format => 'float', ValueConv => '2 ** ($val / 2)', ValueConvInv => '$val>0 ? 2*log($val)/log(2) : 0', PrintConv => 'sprintf("%.1f",$val)', PrintConvInv => '$val', }, # 0x0416 - float: (min focal length? ref LR, Credo50) (but looks more like an int32u date for the 645DF - PH) # 0x0417 - float: 80 (max focal length? ref LR) 0x0455 => { #PH Name => 'Viewfinder', Format => 'string', }, ); # Phase One metadata (ref 1) %Image::ExifTool::PhaseOne::SensorCalibration = ( PROCESS_PROC => \&ProcessPhaseOne, WRITE_PROC => \&WritePhaseOne, CHECK_PROC => \&Image::ExifTool::Exif::CheckExif, GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' }, TAG_PREFIX => 'SensorCalibration', WRITE_GROUP => 'PhaseOne', VARS => { ENTRY_SIZE => 12 }, # (entries do not contain a format field) 0x0400 => { Name => 'SensorDefects', # list of defects. each defect is 4 x int16u values: # 0=column, 1=row, 2=type (129=bad pixel, 131=bad column), 3=? # (but it isn't really worth the time decoding this -- it can be a few hundred kB) Format => 'undef', Binary => 1, }, 0x0401 => { Name => 'AllColorFlatField1', Format => 'undef', Flags => ['Unknown','Binary'], }, 0x0404 => { #PH Name => 'SensorCalibration_0x0404', Format => 'string', Flags => ['Unknown','Hidden'], }, 0x0405 => { #PH Name => 'SensorCalibration_0x0405', Format => 'string', Flags => ['Unknown','Hidden'], }, 0x0406 => { #PH Name => 'SensorCalibration_0x0406', Format => 'string', Flags => ['Unknown','Hidden'], }, 0x0407 => { #PH Name => 'SerialNumber', Format => 'string', Writable => 1, }, 0x0408 => { #PH Name => 'SensorCalibration_0x0408', Format => 'float', Flags => ['Unknown','Hidden'], }, 0x040b => { Name => 'RedBlueFlatField', Format => 'undef', Flags => ['Unknown','Binary'], }, 0x040f => { #PH Name => 'SensorCalibration_0x040f', Format => 'undef', Flags => ['Unknown','Hidden'], }, 0x0410 => { Name => 'AllColorFlatField2', Format => 'undef', Flags => ['Unknown','Binary'], }, # 0x0412 - used by dcraw 0x0413 => { #PH Name => 'SensorCalibration_0x0413', Format => 'double', Flags => ['Unknown','Hidden'], }, 0x0414 => { #PH Name => 'SensorCalibration_0x0414', Format => 'undef', Flags => ['Unknown','Hidden'], ValueConv => q{ my $order = GetByteOrder(); if (length $val >= 8 and SetByteOrder(substr($val,0,2))) { $val = ReadValue(\$val, 2, 'int16u', 1, length($val)-2) . ' ' . ReadValue(\$val, 4, 'float', undef, length($val)-4); SetByteOrder($order); } return $val; }, }, 0x0416 => { Name => 'AllColorFlatField3', Format => 'undef', Flags => ['Unknown','Binary'], }, 0x0418 => { #PH Name => 'SensorCalibration_0x0418', Format => 'undef', Flags => ['Unknown','Hidden'], }, 0x0419 => { Name => 'LinearizationCoefficients1', Format => 'float', PrintConv => 'my @a=split " ",$val;join " ", map { sprintf("%.5g",$_) } @a', }, 0x041a => { Name => 'LinearizationCoefficients2', Format => 'float', PrintConv => 'my @a=split " ",$val;join " ", map { sprintf("%.5g",$_) } @a', }, 0x041c => { #PH Name => 'SensorCalibration_0x041c', Format => 'float', Flags => ['Unknown','Hidden'], }, 0x041e => { #PH Name => 'SensorCalibration_0x041e', Format => 'undef', Flags => ['Unknown','Hidden'], ValueConv => q{ my $order = GetByteOrder(); if (length $val >= 8 and SetByteOrder(substr($val,0,2))) { $val = ReadValue(\$val, 2, 'int16u', 1, length($val)-2) . ' ' . ReadValue(\$val, 4, 'float', undef, length($val)-4); SetByteOrder($order); } return $val; }, }, ); #------------------------------------------------------------------------------ # Do HTML dump of an IFD entry # Inputs: 0) ExifTool ref, 1) tag table ref, 3) tag ID, 4) tag value, # 5) IFD entry offset, 6) IFD entry size, 7) parameter hash sub HtmlDump($$$$$$%) { my ($et, $tagTablePtr, $tagID, $value, $entry, $entryLen, %parms) = @_; my ($dirName, $index, $formatStr, $dataPos, $base, $size, $valuePtr) = @parms{qw(DirName Index Format DataPos Base Size Start)}; my $tagInfo = $et->GetTagInfo($tagTablePtr, $tagID); my ($tagName, $colName, $subdir); my $count = $parms{Count} || $size; $base = 0 unless defined $base; if ($tagInfo) { $tagName = $$tagInfo{Name}; $subdir = $$tagInfo{SubDirectory}; if ($$tagInfo{Format}) { $formatStr = $$tagInfo{Format}; $count = $size / Image::ExifTool::FormatSize($formatStr); } } else { $tagName = sprintf("Tag 0x%.4x", $tagID); } my $dname = sprintf("${dirName}-%.2d", $index); # build our tool tip my $fstr = "$formatStr\[$count]"; my $tip = sprintf("Tag ID: 0x%.4x\n", $tagID) . "Format: $fstr\nSize: $size bytes\n"; if ($size > 4) { $tip .= sprintf("Value offset: 0x%.4x\n", $valuePtr - $base); $tip .= sprintf("Actual offset: 0x%.4x\n", $valuePtr + $dataPos); $tip .= sprintf("Offset base: 0x%.4x\n", $dataPos + $base); $colName = "$tagName"; } else { $colName = $tagName; } unless (ref $value) { my $tval = length($value) > 32 ? substr($value,0,28) . '[...]' : $value; $tval =~ tr/\x00-\x1f\x7f-\xff/./; $tip .= "Value: $tval"; } $et->HDump($entry+$dataPos, $entryLen, "$dname $colName", $tip, 1); if ($size > 4) { my $dumpPos = $valuePtr + $dataPos; # add value data block $et->HDump($dumpPos,$size,"$tagName value",'SAME', $subdir ? 0x04 : 0); } } #------------------------------------------------------------------------------ # Write PhaseOne maker notes (both types of PhaseOne IFD) # Inputs: 0) ExifTool object ref, 1) source dirInfo ref, 2) tag table ref # Returns: data block or undef on error sub WritePhaseOne($$$) { my ($et, $dirInfo, $tagTablePtr) = @_; $et or return 1; # allow dummy access to autoload this package # nothing to do if we aren't changing any PhaseOne tags my $newTags = $et->GetNewTagInfoHash($tagTablePtr); return undef unless %$newTags or $$et{DropTags} or $$et{EDIT_DIRS}{PhaseOne}; my $dataPt = $$dirInfo{DataPt}; my $dataPos = $$dirInfo{DataPos} || 0; my $dirStart = $$dirInfo{DirStart} || 0; my $dirLen = $$dirInfo{DirLen} || $$dirInfo{DataLen} - $dirStart; my $dirName = $$dirInfo{DirName}; my $verbose = $et->Options('Verbose'); return undef if $dirLen < 12; unless ($$tagTablePtr{VARS} and $$tagTablePtr{VARS}{ENTRY_SIZE}) { $et->WarnOnce("No ENTRY_SIZE for $$tagTablePtr{TABLE_NAME}"); return undef; } my $entrySize = $$tagTablePtr{VARS}{ENTRY_SIZE}; my $ifdType = $$tagTablePtr{TAG_PREFIX} || 'PhaseOne'; my $hdr = substr($$dataPt, $dirStart, 12); if ($entrySize == 16) { return undef unless $hdr =~ /^(IIII.waR|MMMMRaw.)/s; } elsif ($hdr !~ /^(IIII\x01\0\0\0|MMMM\0\0\0\x01)/s) { $et->Warn("Unrecognized $ifdType directory version"); return undef; } SetByteOrder(substr($hdr, 0, 2)); # get offset to start of PhaseOne directory my $ifdStart = Get32u(\$hdr, 8); return undef if $ifdStart + 8 > $dirLen; # initialize output directory buffer with (fixed) number of entries plus 4-byte padding my $dirBuff = substr($$dataPt, $dirStart + $ifdStart, 8); # get number of entries in PhaseOne directory my $numEntries = Get32u(\$dirBuff, 0); my $ifdEnd = $ifdStart + 8 + $entrySize * $numEntries; return undef if $numEntries < 2 or $numEntries > 300 or $ifdEnd > $dirLen; my $hdrBuff = $hdr; my $valBuff = ''; # buffer for value data my $fixup = new Image::ExifTool::Fixup; my $index; for ($index=0; $index<$numEntries; ++$index) { my $entry = $dirStart + $ifdStart + 8 + $entrySize * $index; my $tagID = Get32u($dataPt, $entry); my $size = Get32u($dataPt, $entry+$entrySize-8); my ($formatSize, $formatStr); if ($entrySize == 16) { $formatSize = Get32u($dataPt, $entry+4); $formatStr = $formatName[$formatSize]; unless ($formatStr) { $et->Warn("Possibly invalid $ifdType IFD entry $index",1); delete $$newTags{$tagID}; # make sure we don't try to change this one } } else { # (no format code for SensorCalibration IFD entries) $formatSize = 1; $formatStr = 'undef'; } my $valuePtr = $entry + $entrySize - 4; if ($size > 4) { if ($size > 0x7fffffff) { $et->Error("Invalid size for $ifdType IFD entry $index",1); return undef; } $valuePtr = Get32u($dataPt, $valuePtr); if ($valuePtr + $size > $dirLen) { $et->Error(sprintf("Invalid offset 0x%.4x for $ifdType IFD entry $index",$valuePtr),1); return undef; } $valuePtr += $dirStart; } my $value = substr($$dataPt, $valuePtr, $size); my $tagInfo = $$newTags{$tagID} || $$tagTablePtr{$tagID}; $tagInfo = $et->GetTagInfo($tagTablePtr, $tagID) if $tagInfo and ref($tagInfo) ne 'HASH'; if ($$newTags{$tagID}) { $formatStr = $$tagInfo{Format} if $$tagInfo{Format}; my $count = int($size / Image::ExifTool::FormatSize($formatStr)); my $val = ReadValue(\$value, 0, $formatStr, $count, $size); my $nvHash = $et->GetNewValueHash($tagInfo); if ($et->IsOverwriting($nvHash, $val)) { my $newVal = $et->GetNewValue($nvHash); # allow count to change for string and undef types only undef $count if $formatStr eq 'string' or $formatStr eq 'undef'; my $newValue = WriteValue($newVal, $formatStr, $count); if (defined $newValue) { $value = $newValue; $size = length $newValue; $et->VerboseValue("- $dirName:$$tagInfo{Name}", $val); $et->VerboseValue("+ $dirName:$$tagInfo{Name}", $newVal); ++$$et{CHANGED}; } } } elsif ($tagInfo and $$tagInfo{SubDirectory}) { my $subTable = GetTagTable($$tagInfo{SubDirectory}{TagTable}); my %subdirInfo = ( DirName => $$tagInfo{Name}, DataPt => \$value, DataLen => length $value, ); my $newValue = $et->WriteDirectory(\%subdirInfo, $subTable); if (defined $newValue and length($newValue)) { $value = $newValue; $size = length $newValue; } } elsif ($$et{DropTags} and (($tagInfo and $$tagInfo{Drop}) or $size > 8192)) { # decrease the number of entries in the directory Set32u(Get32u(\$dirBuff, 0) - 1, \$dirBuff, 0); next; # drop this tag } # add the tagID, possibly format size, and size to this directory entry $dirBuff .= substr($$dataPt, $entry, $entrySize - 8) . Set32u($size); # pad value to an even 4-byte boundary just in case $value .= ("\0" x (4 - ($size & 0x03))) if $size & 0x03 or not $size; if ($size <= 4) { # store value in place of the IFD value pointer (already padded to 4 bytes) $dirBuff .= $value; } elsif ($tagInfo and $$tagInfo{PutFirst}) { # store value immediately after header $dirBuff .= Set32u(length $hdrBuff); $hdrBuff .= $value; } else { # store value at end of value buffer $fixup->AddFixup(length $dirBuff); $dirBuff .= Set32u(length $valBuff); $valBuff .= $value; } } # apply necessary fixup to offsets in PhaseOne directory $$fixup{Shift} = length $hdrBuff; $fixup->ApplyFixup(\$dirBuff); # set pointer to PhaseOneIFD in header Set32u(length($hdrBuff) + length($valBuff), \$hdrBuff, 8); return $hdrBuff . $valBuff . $dirBuff; } #------------------------------------------------------------------------------ # Read Phase One maker notes # Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref # Returns: 1 on success # Notes: This routine processes both the main PhaseOne IFD type (with 16 bytes # per entry), and the SensorCalibration IFD type (12 bytes per entry) sub ProcessPhaseOne($$$) { my ($et, $dirInfo, $tagTablePtr) = @_; my $dataPt = $$dirInfo{DataPt}; my $dataPos = ($$dirInfo{DataPos} || 0) + ($$dirInfo{Base} || 0); my $dirStart = $$dirInfo{DirStart} || 0; my $dirLen = $$dirInfo{DirLen} || $$dirInfo{DataLen} - $dirStart; my $binary = $et->Options('Binary'); my $verbose = $et->Options('Verbose'); my $htmlDump = $$et{HTML_DUMP}; return 0 if $dirLen < 12; unless ($$tagTablePtr{VARS} and $$tagTablePtr{VARS}{ENTRY_SIZE}) { $et->WarnOnce("No ENTRY_SIZE for $$tagTablePtr{TABLE_NAME}"); return undef; } my $entrySize = $$tagTablePtr{VARS}{ENTRY_SIZE}; my $ifdType = $$tagTablePtr{TAG_PREFIX} || 'PhaseOne'; my $hdr = substr($$dataPt, $dirStart, 12); if ($entrySize == 16) { return 0 unless $hdr =~ /^(IIII.waR|MMMMRaw.)/s; } elsif ($hdr !~ /^(IIII\x01\0\0\0|MMMM\0\0\0\x01)/s) { $et->Warn("Unrecognized $ifdType directory version"); return 0; } SetByteOrder(substr($hdr, 0, 2)); # get offset to start of PhaseOne directory my $ifdStart = Get32u(\$hdr, 8); return 0 if $ifdStart + 8 > $dirLen; # get number of entries in PhaseOne directory my $numEntries = Get32u($dataPt, $dirStart + $ifdStart); my $ifdEnd = $ifdStart + 8 + $entrySize * $numEntries; return 0 if $numEntries < 2 or $numEntries > 300 or $ifdEnd > $dirLen; $et->VerboseDir($ifdType, $numEntries); if ($htmlDump) { $et->HDump($dirStart + $dataPos, 8, "$ifdType header"); $et->HDump($dirStart + $dataPos + 8, 4, "$ifdType IFD offset"); $et->HDump($dirStart + $dataPos + $ifdStart, 4, "$ifdType entries", "Entry count: $numEntries"); $et->HDump($dirStart + $dataPos + $ifdStart + 4, 4, '[unused]'); } my $index; for ($index=0; $index<$numEntries; ++$index) { my $entry = $dirStart + $ifdStart + 8 + $entrySize * $index; my $tagID = Get32u($dataPt, $entry); my $size = Get32u($dataPt, $entry+$entrySize-8); my $valuePtr = $entry + $entrySize - 4; my ($formatSize, $formatStr, $value); if ($entrySize == 16) { # (format code only for the 16-byte IFD entry) $formatSize = Get32u($dataPt, $entry+4); $formatStr = $formatName[$formatSize]; unless ($formatStr) { $et->WarnOnce("Unrecognized $ifdType format size $formatSize",1); $formatSize = 1; $formatStr = 'undef'; } } elsif ($size %4) { $formatSize = 1; $formatStr = 'undef'; } else { $formatSize = 4; $formatStr = 'int32s'; } if ($size > 4) { if ($size > 0x7fffffff) { $et->Warn("Invalid size for $ifdType IFD entry $index"); return 0; } $valuePtr = Get32u($dataPt, $valuePtr); if ($valuePtr + $size > $dirLen) { $et->Warn(sprintf("Invalid offset 0x%.4x for $ifdType IFD entry $index",$valuePtr)); return 0; } $valuePtr += $dirStart; } my $tagInfo = $et->GetTagInfo($tagTablePtr, $tagID); if ($tagInfo) { $formatStr = $$tagInfo{Format} if $$tagInfo{Format}; } else { next unless $verbose or $htmlDump; } my $count = int($size / Image::ExifTool::FormatSize($formatStr)); if ($count > 100000 and not $binary) { $value = \ "Binary data $size bytes"; } else { $value = ReadValue($dataPt,$valuePtr,$formatStr,$count,$size); # try to distinguish between the various format types if ($formatStr eq 'int32s') { my ($val) = split ' ', $value; if (defined $val) { # get floating point exponent (has bias of 127) my $exp = ($val & 0x7f800000) >> 23; if ($exp > 120 and $exp < 140) { $formatStr = 'float'; $value = ReadValue($dataPt,$valuePtr,$formatStr,$count,$size); } } } } my %parms = ( DirName => $ifdType, Index => $index, DataPt => $dataPt, DataPos => $dataPos, Size => $size, Start => $valuePtr, Format => $formatStr, Count => $count ); $htmlDump and HtmlDump($et, $tagTablePtr, $tagID, $value, $entry, $entrySize, %parms, Base => $dirStart); $et->HandleTag($tagTablePtr, $tagID, $value, %parms); } return 1; } 1; # end __END__ =head1 NAME Image::ExifTool::PhaseOne - Phase One maker notes tags =head1 SYNOPSIS This module is loaded automatically by Image::ExifTool when required. =head1 DESCRIPTION This module contains definitions required by Image::ExifTool to decode Phase One maker notes. =head1 AUTHOR Copyright 2003-2023, Phil Harvey (philharvey66 at gmail.com) This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =head1 REFERENCES =over 4 =item L =back =head1 SEE ALSO L, L =cut