123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707 |
- #------------------------------------------------------------------------------
- # 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.03';
- 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',
- # 4?
- 5 => 'IIQ S',
- 6 => 'IIQ Sv2',
- },
- },
- 0x010f => {
- Name => 'RawData',
- Format => 'undef', # (actually 2-byte integers, but don't convert)
- Binary => 1,
- PutFirst => 1,
- Writable => 0,
- },
- 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
- 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 LibRaw, Credo50) (but looks more like an int32u date for the 645DF - PH)
- # 0x0417 - float: 80 (max focal length? ref LibRaw)
- );
- # 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',
- 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'],
- },
- 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, 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 = "<span class=F>$tagName</span>";
- } 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;
- 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} || $et->GetTagInfo($tagTablePtr, $tagID);
- 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;
- }
- }
- # 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;
- 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-2016, Phil Harvey (phil at owl.phy.queensu.ca)
- 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<http://www.cybercom.net/~dcoffin/dcraw/>
- =back
- =head1 SEE ALSO
- L<Image::ExifTool::TagNames/PhaseOne Tags>,
- L<Image::ExifTool(3pm)|Image::ExifTool>
- =cut
|