diff options
author | exiftool <exiftool@users.sourceforge.net> | 2021-04-22 14:56:56 -0400 |
---|---|---|
committer | exiftool <exiftool@users.sourceforge.net> | 2021-04-22 14:56:56 -0400 |
commit | ceff3cbc4564e93518f3d2a2e00d8ae203ff54af (patch) | |
tree | 016a2d6f76fe9f9af7bfb4528d40f5c202363668 /lib/Image/ExifTool | |
parent | cf0f4e7dcd024ca99615bfd1102a841a25dde031 (diff) |
Update to 12.25
Diffstat (limited to 'lib/Image/ExifTool')
-rw-r--r-- | lib/Image/ExifTool/Canon.pm | 5 | ||||
-rw-r--r-- | lib/Image/ExifTool/Exif.pm | 23 | ||||
-rw-r--r-- | lib/Image/ExifTool/Jpeg2000.pm | 24 | ||||
-rw-r--r-- | lib/Image/ExifTool/MRC.pm | 341 | ||||
-rw-r--r-- | lib/Image/ExifTool/Microsoft.pm | 4 | ||||
-rw-r--r-- | lib/Image/ExifTool/Nikon.pm | 5 | ||||
-rw-r--r-- | lib/Image/ExifTool/NikonSettings.pm | 11 | ||||
-rw-r--r-- | lib/Image/ExifTool/QuickTime.pm | 109 | ||||
-rw-r--r-- | lib/Image/ExifTool/QuickTimeStream.pl | 82 | ||||
-rw-r--r-- | lib/Image/ExifTool/RIFF.pm | 94 | ||||
-rw-r--r-- | lib/Image/ExifTool/Sony.pm | 5 | ||||
-rw-r--r-- | lib/Image/ExifTool/TagLookup.pm | 34 | ||||
-rw-r--r-- | lib/Image/ExifTool/TagNames.pod | 197 | ||||
-rw-r--r-- | lib/Image/ExifTool/WriteQuickTime.pl | 49 | ||||
-rw-r--r-- | lib/Image/ExifTool/WriteXMP.pl | 8 | ||||
-rw-r--r-- | lib/Image/ExifTool/XMP.pm | 23 |
16 files changed, 867 insertions, 147 deletions
diff --git a/lib/Image/ExifTool/Canon.pm b/lib/Image/ExifTool/Canon.pm index 09e8461c..a9147d09 100644 --- a/lib/Image/ExifTool/Canon.pm +++ b/lib/Image/ExifTool/Canon.pm @@ -88,7 +88,7 @@ sub ProcessCTMD($$$); sub ProcessExifInfo($$$); sub SwapWords($); -$VERSION = '4.46'; +$VERSION = '4.47'; # Note: Removed 'USM' from 'L' lenses since it is redundant - PH # (or is it? Ref 32 shows 5 non-USM L-type lenses) @@ -473,7 +473,8 @@ $VERSION = '4.46'; 253 => 'Canon EF 70-200mm f/2.8L IS II USM + 2x', #PH (NC) 253.1 => 'Canon EF 70-200mm f/2.8L IS III USM + 2x', #PH (NC) # 253.2 => 'Tamron SP 70-200mm f/2.8 Di VC USD G2 (A025) + 2x', #forum9367 - 254 => 'Canon EF 100mm f/2.8L Macro IS USM', #42 + 254 => 'Canon EF 100mm f/2.8L Macro IS USM or Tamron Lens', #42 + 254.1 => 'Tamron SP 90mm f/2.8 Di VC USD 1:1 Macro (F017)', #PH 255 => 'Sigma 24-105mm f/4 DG OS HSM | A or Other Lens', #50 255.1 => 'Sigma 180mm f/2.8 EX DG OS HSM APO Macro', #50 255.2 => 'Tamron SP 70-200mm f/2.8 Di VC USD', #exiv issue 1202 (A009) diff --git a/lib/Image/ExifTool/Exif.pm b/lib/Image/ExifTool/Exif.pm index e6257525..230b05f4 100644 --- a/lib/Image/ExifTool/Exif.pm +++ b/lib/Image/ExifTool/Exif.pm @@ -56,7 +56,7 @@ use vars qw($VERSION $AUTOLOAD @formatSize @formatName %formatNumber %intFormat use Image::ExifTool qw(:DataAccess :Utils); use Image::ExifTool::MakerNotes; -$VERSION = '4.33'; +$VERSION = '4.34'; sub ProcessExif($$$); sub WriteExif($$$); @@ -321,6 +321,7 @@ my %utf8StringConv = ( my %longBin = ( ValueConv => 'length($val) > 64 ? \$val : $val', ValueConvInv => '$val', + LongBinary => 1, # flag to avoid decoding values of a large array ); # PrintConv for SampleFormat (0x153) @@ -3618,11 +3619,11 @@ my %opcodeInfo = ( }, 0xc6fc => { Name => 'ProfileToneCurve', + %longBin, Writable => 'float', WriteGroup => 'IFD0', Count => -1, Protected => 1, - Binary => 1, }, 0xc6fd => { Name => 'ProfileEmbedPolicy', @@ -3747,11 +3748,11 @@ my %opcodeInfo = ( }, 0xc726 => { Name => 'ProfileLookTableData', + %longBin, Writable => 'float', WriteGroup => 'IFD0', Count => -1, Protected => 1, - Binary => 1, }, 0xc740 => { Name => 'OpcodeList1', %opcodeInfo }, # DNG 1.3 0xc741 => { Name => 'OpcodeList2', %opcodeInfo }, # DNG 1.3 @@ -6172,15 +6173,23 @@ sub ProcessExif($$$) # (avoids long delays when processing some corrupted files) if ($count > 100000 and $formatStr !~ /^(undef|string|binary)$/) { my $tagName = $tagInfo ? $$tagInfo{Name} : sprintf('tag 0x%.4x', $tagID); + # (count of 196608 is typical for ColorMap) if ($tagName ne 'TransferFunction' or $count != 196608) { my $minor = $count > 2000000 ? 0 : 2; next if $et->Warn("Ignoring $dirName $tagName with excessive count", $minor); } } - # convert according to specified format - $val = ReadValue($valueDataPt,$valuePtr,$formatStr,$count,$readSize,\$rational); - # re-code if necessary - $val = $et->Decode($val, $strEnc) if $strEnc and $formatStr eq 'string' and defined $val; + if ($count > 500 and $formatStr !~ /^(undef|string|binary)$/ and + (not $tagInfo or $$tagInfo{LongBinary}) and not $$et{OPTIONS}{IgnoreMinorErrors}) + { + $et->WarnOnce('Not decoding some large array(s). Ignore minor errors to decode', 2); + $val = "(large array of $count $formatStr values)"; + } else { + # convert according to specified format + $val = ReadValue($valueDataPt,$valuePtr,$formatStr,$count,$readSize,\$rational); + # re-code if necessary + $val = $et->Decode($val, $strEnc) if $strEnc and $formatStr eq 'string' and defined $val; + } } if ($verbose) { diff --git a/lib/Image/ExifTool/Jpeg2000.pm b/lib/Image/ExifTool/Jpeg2000.pm index ba141244..1438a5d6 100644 --- a/lib/Image/ExifTool/Jpeg2000.pm +++ b/lib/Image/ExifTool/Jpeg2000.pm @@ -16,7 +16,7 @@ use strict; use vars qw($VERSION); use Image::ExifTool qw(:DataAccess :Utils); -$VERSION = '1.28'; +$VERSION = '1.29'; sub ProcessJpeg2000Box($$$); sub ProcessJUMD($$$); @@ -389,7 +389,7 @@ my %jumbfTypes = ( # # stuff seen in JPEG XL images: # - # jbrd - JXL Brotli Compressed Data? + # jbrd - JPEG Bitstream Reconstruction Data (allows lossless conversion back to original JPG) jxlc => { Name => 'JXLCodestream', Format => 'undef', @@ -1034,16 +1034,18 @@ sub GetBits($$) { my ($a, $n) = @_; my $v = 0; + my $bit = 1; my $i; while ($n--) { for ($i=0; $i<@$a; ++$i) { - my $set = $$a[$i] & 0x80000000; - $$a[$i] <<= 1; + # consume bits LSB first + my $set = $$a[$i] & 1; + $$a[$i] >>= 1; if ($i) { - $$a[$i-1] |= 1 if $set; + $$a[$i-1] |= 0x80 if $set; } else { - $v <<= 1; - $v |= 1 if $set; + $v |= $bit if $set; + $bit <<= 1; } } } @@ -1062,11 +1064,7 @@ sub ProcessJXLCodestream($$) my $tmp = $$dataPt . ("\0" x 14); $dataPt = \$tmp; } - # Note: I have a test ISO BMFF JXL image with EXIF that shows y=130, x=200 - # but the codestream decodes as y=128, x=254, so I'm not sure this is correct... - # 200x130 image should be (binary) 0 00 010000001 000 00 011000111 - # JXL codestream is 0 00 010000000 010 (01000111010000001) - my @a = unpack 'x2N3', $$dataPt; + my @a = unpack 'x2C12', $$dataPt; my ($x, $y); my $small = GetBits(\@a, 1); if ($small) { @@ -1183,7 +1181,6 @@ sub ProcessJXL($$) # add metadata to empty ISO BMFF container $$dirInfo{RAF} = new File::RandomAccess(\$buff); } else { - $et->Warn('JPEG XL codestream support is currently experimental',1); $et->SetFileType('JXL Codestream','image/jxl', 'jxl'); return ProcessJXLCodestream($et, \$hdr); } @@ -1191,7 +1188,6 @@ sub ProcessJXL($$) return 0; } $raf->Seek(0,0) or $et->Error('Seek error'), return 0; - $et->Warn('JPEG XL support is currently experimental',1); my $success = ProcessJP2($et, $dirInfo); diff --git a/lib/Image/ExifTool/MRC.pm b/lib/Image/ExifTool/MRC.pm new file mode 100644 index 00000000..7e184e90 --- /dev/null +++ b/lib/Image/ExifTool/MRC.pm @@ -0,0 +1,341 @@ +#------------------------------------------------------------------------------ +# File: MRC.pm +# +# Description: Read MRC (Medical Research Council) image files +# +# Revisions: 2021-04-21 - P. Harvey Created +# +# References: 1) https://www.ccpem.ac.uk/mrc_format/mrc2014.php +# 2) http://legacy.ccp4.ac.uk/html/library.html +# 3) https://github.com/ccpem/mrcfile/blob/master/mrcfile/dtypes.py +# +# Notes: The header is basically identical to the older CCP4 file format +#------------------------------------------------------------------------------ + +package Image::ExifTool::MRC; + +use strict; +use vars qw($VERSION); +use Image::ExifTool qw(:DataAccess :Utils); + +$VERSION = '1.00'; + +my %bool = ( + Format => 'int8u', + PrintConv => { 0 => 'No', 1 => 'Yes' } +); + +%Image::ExifTool::MRC::Main = ( + PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData, + GROUPS => { 0 => 'File', 1 => 'File', 2 => 'Image' }, + VARS => { NO_LOOKUP => 1 }, # omit tags from lookup + FORMAT => 'int32u', + NOTES => q{ + Tags extracted from Medical Research Council (MRC) format imaging files. + See L<https://www.ccpem.ac.uk/mrc_format/mrc2014.php> for the specification. + }, + 0 => 'ImageWidth', + 1 => 'ImageHeight', + 2 => { + Name => 'ImageDepth', + Notes => q{ + number of sections. Use ExtractEmbedded option to extract metadata for all + sections + }, + RawConv => '$$self{ImageDepth} = $val', + }, + 3 => { + Name => 'ImageMode', + PrintConv => { + 0 => '8-bit signed integer', + 1 => '16-bit signed integer', + 2 => '32-bit signed real', + 3 => 'complex 16-bit integer', + 4 => 'complex 32-bit real', + 6 => '16-bit unsigned integer', + }, + }, + 4 => { Name => 'StartPoint', Format => 'int32u[3]' }, + 7 => { Name => 'GridSize', Format => 'int32u[3]' }, + 10 => { Name => 'CellWidth', Format => 'float', Notes => 'cell size in angstroms' }, + 11 => { Name => 'CellHeight',Format => 'float' }, + 12 => { Name => 'CellDepth', Format => 'float' }, + 13 => { Name => 'CellAlpha', Format => 'float' }, + 14 => { Name => 'CellBeta', Format => 'float' }, + 15 => { Name => 'CellGamma', Format => 'float' }, + 16 => { Name => 'ImageWidthAxis', PrintConv => { 1 => 'X', 2 => 'Y', 3 => 'Z' } }, + 17 => { Name => 'ImageHeightAxis', PrintConv => { 1 => 'X', 2 => 'Y', 3 => 'Z' } }, + 18 => { Name => 'ImageDepthAxis', PrintConv => { 1 => 'X', 2 => 'Y', 3 => 'Z' } }, + 19 => { Name => 'DensityMin', Format => 'float' }, + 20 => { Name => 'DensityMax', Format => 'float' }, + 21 => { Name => 'DensityMean',Format => 'float' }, + 22 => 'SpaceGroupNumber', + 23 => { Name => 'ExtendedHeaderSize', RawConv => '$$self{ExtendedHeaderSize} = $val' }, + 26 => { Name => 'ExtendedHeaderType', Format => 'string[4]', RawConv => '$$self{ExtendedHeaderType} = $val' }, + 27 => 'MRCVersion', + 49 => { Name => 'Origin', Format => 'float[3]' }, + 53 => { Name => 'MachineStamp', Format => 'int8u[4]', PrintConv => 'sprintf("0x%.2x 0x%.2x 0x%.2x 0x%.2x",split " ", $val)' }, + 54 => { Name => 'RMSDeviation', Format => 'float' }, + 55 => { Name => 'NumberOfLabels', RawConv => '$$self{NLab} = $val' }, + 56 => { Name => 'Label0', Format => 'string[80]', Condition => '$$self{NLab} > 0' }, + 76 => { Name => 'Label1', Format => 'string[80]', Condition => '$$self{NLab} > 1' }, + 96 => { Name => 'Label2', Format => 'string[80]', Condition => '$$self{NLab} > 2' }, + 116 => { Name => 'Label3', Format => 'string[80]', Condition => '$$self{NLab} > 3' }, + 136 => { Name => 'Label4', Format => 'string[80]', Condition => '$$self{NLab} > 4' }, + 156 => { Name => 'Label5', Format => 'string[80]', Condition => '$$self{NLab} > 5' }, + 176 => { Name => 'Label6', Format => 'string[80]', Condition => '$$self{NLab} > 6' }, + 196 => { Name => 'Label7', Format => 'string[80]', Condition => '$$self{NLab} > 7' }, + 216 => { Name => 'Label8', Format => 'string[80]', Condition => '$$self{NLab} > 8' }, + 236 => { Name => 'Label9', Format => 'string[80]', Condition => '$$self{NLab} > 9' }, +); + +%Image::ExifTool::MRC::FEI12 = ( + PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData, + GROUPS => { 0 => 'File', 1 => 'File', 2 => 'Image' }, + VARS => { NO_LOOKUP => 1 }, # omit tags from lookup (way too many!) + NOTES => 'Tags extracted from FEI1 and FEI2 extended headers.', + 0 => { Name => 'MetadataSize', Format => 'int32u', RawConv => '$$self{MetadataSize} = $val' }, + 4 => { Name => 'MetadataVersion', Format => 'int32u' }, + 8 => { + Name => 'Bitmask1', + Format => 'int32u', + RawConv => '$$self{BitM} = $val', + PrintConv => 'sprintf("0x%.8x", $val)', + }, + 12 => { + Name => 'TimeStamp', + Format => 'double', + Condition => '$$self{BitM} & 0x01', + Groups => { 2 => 'Time'}, + # shift from days since Dec 30, 1899 to Unix epoch of Jan 1, 1970 + # (my sample looks like local time, although it should be UTC) + ValueConv => 'ConvertUnixTime(($val-25569)*24*3600)', + PrintConv => '$self->ConvertDateTime($val)', + }, + 20 => { Name => 'MicroscopeType', Format => 'string[16]', Condition => '$$self{BitM} & 0x02' }, + 36 => { Name => 'MicroscopeID', Format => 'string[16]', Condition => '$$self{BitM} & 0x04' }, + 52 => { Name => 'Application', Format => 'string[16]', Condition => '$$self{BitM} & 0x08' }, + 68 => { Name => 'AppVersion', Format => 'string[16]', Condition => '$$self{BitM} & 0x10' }, + 84 => { Name => 'HighTension', Format => 'double', Condition => '$$self{BitM} & 0x20', Notes => 'volts' }, + 92 => { Name => 'Dose', Format => 'double', Condition => '$$self{BitM} & 0x40', Notes => 'electrons/m2' }, + 100 => { Name => 'AlphaTilt', Format => 'double', Condition => '$$self{BitM} & 0x80' }, + 108 => { Name => 'BetaTilt', Format => 'double', Condition => '$$self{BitM} & 0x100' }, + 116 => { Name => 'XStage', Format => 'double', Condition => '$$self{BitM} & 0x200' }, + 124 => { Name => 'YStage', Format => 'double', Condition => '$$self{BitM} & 0x400' }, + 132 => { Name => 'ZStage', Format => 'double', Condition => '$$self{BitM} & 0x800' }, + 140 => { Name => 'TiltAxisAngle', Format => 'double', Condition => '$$self{BitM} & 0x1000' }, + 148 => { Name => 'DualAxisRot', Format => 'double', Condition => '$$self{BitM} & 0x2000' }, + 156 => { Name => 'PixelSizeX', Format => 'double', Condition => '$$self{BitM} & 0x4000' }, + 164 => { Name => 'PixelSizeY', Format => 'double', Condition => '$$self{BitM} & 0x8000' }, + 220 => { Name => 'Defocus', Format => 'double', Condition => '$$self{BitM} & 0x400000' }, + 228 => { Name => 'STEMDefocus', Format => 'double', Condition => '$$self{BitM} & 0x800000' }, + 236 => { Name => 'AppliedDefocus', Format => 'double', Condition => '$$self{BitM} & 0x1000000' }, + 244 => { Name => 'InstrumentMode', Format => 'int32u', Condition => '$$self{BitM} & 0x2000000', PrintConv => { 1 => 'TEM', 2 => 'STEM' } }, + 248 => { Name => 'ProjectionMode', Format => 'int32u', Condition => '$$self{BitM} & 0x4000000', PrintConv => { 1 => 'Diffraction', 2 => 'Imaging' } }, + 252 => { Name => 'ObjectiveLens', Format => 'string[16]', Condition => '$$self{BitM} & 0x8000000' }, + 268 => { Name => 'HighMagnificationMode', Format => 'string[16]', Condition => '$$self{BitM} & 0x10000000' }, + 284 => { Name => 'ProbeMode', Format => 'int32u', Condition => '$$self{BitM} & 0x20000000', PrintConv => { 1 => 'Nano', 2 => 'Micro' } }, + 288 => { Name => 'EFTEMOn', %bool, Condition => '$$self{BitM} & 0x40000000' }, + 289 => { Name => 'Magnification', Format => 'double', Condition => '$$self{BitM} & 0x80000000' }, + 297 => { + Name => 'Bitmask2', + Format => 'int32u', + RawConv => '$$self{BitM} = $val', + PrintConv => 'sprintf("0x%.8x", $val)', + }, + 301 => { Name => 'CameraLength', Format => 'double', Condition => '$$self{BitM} & 0x01' }, + 309 => { Name => 'SpotIndex', Format => 'int32u', Condition => '$$self{BitM} & 0x02' }, + 313 => { Name => 'IlluminationArea',Format => 'double', Condition => '$$self{BitM} & 0x04' }, + 321 => { Name => 'Intensity', Format => 'double', Condition => '$$self{BitM} & 0x08' }, + 329 => { Name => 'ConvergenceAngle',Format => 'double', Condition => '$$self{BitM} & 0x10' }, + 337 => { Name => 'IlluminationMode',Format => 'string[16]', Condition => '$$self{BitM} & 0x20' }, + 353 => { Name => 'WideConvergenceAngleRange', %bool, Condition => '$$self{BitM} & 0x40' }, + 354 => { Name => 'SlitInserted', %bool, Condition => '$$self{BitM} & 0x80' }, + 355 => { Name => 'SlitWidth', Format => 'double', Condition => '$$self{BitM} & 0x100' }, + 363 => { Name => 'AccelVoltOffset', Format => 'double', Condition => '$$self{BitM} & 0x200' }, + 371 => { Name => 'DriftTubeVolt', Format => 'double', Condition => '$$self{BitM} & 0x400' }, + 379 => { Name => 'EnergyShift', Format => 'double', Condition => '$$self{BitM} & 0x800' }, + 387 => { Name => 'ShiftOffsetX', Format => 'double', Condition => '$$self{BitM} & 0x1000' }, + 395 => { Name => 'ShiftOffsetY', Format => 'double', Condition => '$$self{BitM} & 0x2000' }, + 403 => { Name => 'ShiftX', Format => 'double', Condition => '$$self{BitM} & 0x4000' }, + 411 => { Name => 'ShiftY', Format => 'double', Condition => '$$self{BitM} & 0x8000' }, + 419 => { Name => 'IntegrationTime', Format => 'double', Condition => '$$self{BitM} & 0x10000' }, + 427 => { Name => 'BinningWidth', Format => 'int32u', Condition => '$$self{BitM} & 0x20000' }, + 431 => { Name => 'BinningHeight', Format => 'int32u', Condition => '$$self{BitM} & 0x40000' }, + 435 => { Name => 'CameraName', Format => 'string[16]', Condition => '$$self{BitM} & 0x80000' }, + 451 => { Name => 'ReadoutAreaLeft', Format => 'int32u', Condition => '$$self{BitM} & 0x100000' }, + 455 => { Name => 'ReadoutAreaTop', Format => 'int32u', Condition => '$$self{BitM} & 0x200000' }, + 459 => { Name => 'ReadoutAreaRight',Format => 'int32u', Condition => '$$self{BitM} & 0x400000' }, + 463 => { Name => 'ReadoutAreaBottom',Format=> 'int32u', Condition => '$$self{BitM} & 0x800000' }, + 467 => { Name => 'CetaNoiseReduct', %bool, Condition => '$$self{BitM} & 0x1000000' }, + 468 => { Name => 'CetaFramesSummed',Format => 'int32u', Condition => '$$self{BitM} & 0x2000000' }, + 472 => { Name => 'DirectDetElectronCounting', %bool, Condition => '$$self{BitM} & 0x4000000' }, + 473 => { Name => 'DirectDetAlignFrames', %bool, Condition => '$$self{BitM} & 0x8000000' }, + 490 => { + Name => 'Bitmask3', + Format => 'int32u', + RawConv => '$$self{BitM} = $val', + PrintConv => 'sprintf("0x%.8x", $val)', + }, + 518 => { Name => 'PhasePlate', %bool, Condition => '$$self{BitM} & 0x40' }, + 519 => { Name => 'STEMDetectorName',Format => 'string[16]', Condition => '$$self{BitM} & 0x80' }, + 535 => { Name => 'Gain', Format => 'double', Condition => '$$self{BitM} & 0x100' }, + 543 => { Name => 'Offset', Format => 'double', Condition => '$$self{BitM} & 0x200' }, + 571 => { Name => 'DwellTime', Format => 'double', Condition => '$$self{BitM} & 0x8000' }, + 579 => { Name => 'FrameTime', Format => 'double', Condition => '$$self{BitM} & 0x10000' }, + 587 => { Name => 'ScanSizeLeft', Format => 'int32u', Condition => '$$self{BitM} & 0x20000' }, + 591 => { Name => 'ScanSizeTop', Format => 'int32u', Condition => '$$self{BitM} & 0x40000' }, + 595 => { Name => 'ScanSizeRight', Format => 'int32u', Condition => '$$self{BitM} & 0x80000' }, + 599 => { Name => 'ScanSizeBottom', Format => 'int32u', Condition => '$$self{BitM} & 0x100000' }, + 603 => { Name => 'FullScanFOV_X', Format => 'double', Condition => '$$self{BitM} & 0x200000' }, + 611 => { Name => 'FullScanFOV_Y', Format => 'double', Condition => '$$self{BitM} & 0x400000' }, + 619 => { Name => 'Element', Format => 'string[16]', Condition => '$$self{BitM} & 0x800000' }, + 635 => { Name => 'EnergyIntervalLower', Format => 'double', Condition => '$$self{BitM} & 0x1000000' }, + 643 => { Name => 'EnergyIntervalHigher',Format => 'double', Condition => '$$self{BitM} & 0x2000000' }, + 651 => { Name => 'Method', Format=> 'int32u', Condition => '$$self{BitM} & 0x4000000' }, + 655 => { Name => 'IsDoseFraction', %bool, Condition => '$$self{BitM} & 0x8000000' }, + 656 => { Name => 'FractionNumber', Format => 'int32u', Condition => '$$self{BitM} & 0x10000000' }, + 660 => { Name => 'StartFrame', Format => 'int32u', Condition => '$$self{BitM} & 0x20000000' }, + 664 => { Name => 'EndFrame', Format => 'int32u', Condition => '$$self{BitM} & 0x40000000' }, + 668 => { Name =>'InputStackFilename',Format=> 'string[80]', Condition => '$$self{BitM} & 0x80000000' }, + 748 => { + Name => 'Bitmask4', + Format => 'int32u', + RawConv => '$$self{BitM} = $val', + PrintConv => 'sprintf("0x%.8x", $val)', + }, + 752 => { Name => 'AlphaTiltMin', Format => 'double', Condition => '$$self{BitM} & 0x01' }, + 760 => { Name => 'AlphaTiltMax', Format => 'double', Condition => '$$self{BitM} & 0x02' }, +# +# FEI2 header starts here +# + 768 => { Name => 'ScanRotation', Format => 'double', Condition => '$$self{BitM} & 0x04' }, + 776 => { Name => 'DiffractionPatternRotation',Format=>'double', Condition => '$$self{BitM} & 0x08' }, + 784 => { Name => 'ImageRotation', Format => 'double', Condition => '$$self{BitM} & 0x10' }, + 792 => { Name => 'ScanModeEnumeration',Format => 'int32u', Condition => '$$self{BitM} & 0x20', PrintConv => { 0 => 'Other', 1 => 'Raster', 2 => 'Serpentine' } }, + 796 => { + Name => 'AcquisitionTimeStamp', + Format => 'int64u', + Condition => '$$self{BitM} & 0x40', + Groups => { 2 => 'Time' }, + # microseconds since 1970 UTC + ValueConv => 'ConvertUnixTime($val / 1e6, 1, 6)', + PrintConv => '$self->ConvertDateTime($val)', + }, + 804 => { Name => 'DetectorCommercialName', Format => 'string[16]', Condition => '$$self{BitM} & 0x80' }, + 820 => { Name => 'StartTiltAngle', Format => 'double', Condition => '$$self{BitM} & 0x100' }, + 828 => { Name => 'EndTiltAngle', Format => 'double', Condition => '$$self{BitM} & 0x200' }, + 836 => { Name => 'TiltPerImage', Format => 'double', Condition => '$$self{BitM} & 0x400' }, + 844 => { Name => 'TitlSpeed', Format => 'double', Condition => '$$self{BitM} & 0x800' }, + 852 => { Name => 'BeamCenterX', Format => 'int32u', Condition => '$$self{BitM} & 0x1000' }, + 856 => { Name => 'BeamCenterY', Format => 'int32u', Condition => '$$self{BitM} & 0x2000' }, + 860 => { + Name => 'CFEGFlashTimeStamp', + Format => 'int64u', + Condition => '$$self{BitM} & 0x4000', + Groups => { 2 => 'Time' }, + ValueConv => 'ConvertUnixTime($val / 1e6, 1, 6)', + PrintConv => '$self->ConvertDateTime($val)', + }, + 868 => { Name => 'PhasePlatePosition',Format => 'int32u', Condition => '$$self{BitM} & 0x8000' }, + 872 => { Name => 'ObjectiveAperture', Format=>'string[16]',Condition => '$$self{BitM} & 0x10000' }, +); + +#------------------------------------------------------------------------------ +# Extract metadata from a MRC image +# Inputs: 0) ExifTool object reference, 1) dirInfo reference +# Returns: 1 on success, 0 if this wasn't a valid MRC file +sub ProcessMRC($$) +{ + my ($et, $dirInfo) = @_; + my $raf = $$dirInfo{RAF}; + my ($buff, $tagTablePtr, $i); + + # verify this is a valid MRC file + return 0 unless $raf->Read($buff, 1024) == 1024; + # validate axes, "MAP" file type and machine stamp + return 0 unless $buff =~ /^.{64}[\x01\x02\x03]\0\0\0[\x01\x02\x03]\0\0\0[\x01\x02\x03]\0\0\0.{132}MAP[\0 ](\x44\x44|\x44\x41|\x11\x11)\0\0/s; + + $et->SetFileType(); + SetByteOrder('II'); + my %dirInfo = ( + DataPt => \$buff, + DirStart => 0, + DirLen => length($buff), + ); + $tagTablePtr = GetTagTable('Image::ExifTool::MRC::Main'); + $et->ProcessDirectory(\%dirInfo, $tagTablePtr); + + # (I don't have any samples with extended headers for testing, so these are not yet decoded) + if ($$et{ExtendedHeaderSize} and $$et{ExtendedHeaderType} =~ /^FEI[12]/) { + unless ($raf->Read($buff,4)==4 and $raf->Seek(-4,1)) { # read metadata size + $et->Warn('Error reading extended header'); + return 1; + } + my $size = Get32u(\$buff, 0); + if ($size * $$et{ImageDepth} > $$et{ExtendedHeaderSize}) { + $et->Warn('Corrupted extended header'); + return 1; + } + $dirInfo{DirLen} = $size; + $tagTablePtr = GetTagTable('Image::ExifTool::MRC::FEI12'); + for ($i=0; ;) { + $dirInfo{DataPos} = $raf->Tell(); + $raf->Read($buff, $size) == $size or $et->Warn("Error reading extended header $i"), last; + $et->ProcessDirectory(\%dirInfo, $tagTablePtr); + last if ++$i >= $$et{ImageDepth}; + unless ($$et{OPTIONS}{ExtractEmbedded}) { + $et->Warn('Use the ExtractEmbedded option to read metadata for all frames',1); + last; + } + $$et{DOC_NUM} = ++$$et{DOC_COUNT}; + } + delete $$et{DOC_NUM}; + } + + return 1; +} + +1; # end + +__END__ + +=head1 NAME + +Image::ExifTool::MRC - Read MRC meta information + +=head1 SYNOPSIS + +This module is used by Image::ExifTool + +=head1 DESCRIPTION + +This module contains definitions required by Image::ExifTool to read +metadata from Medical Research Council (MRC) images. + +=head1 AUTHOR + +Copyright 2003-2021, 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<https://www.ccpem.ac.uk/mrc_format/mrc2014.php> + +=item L<http://legacy.ccp4.ac.uk/html/library.html> + +=item L<https://github.com/ccpem/mrcfile/blob/master/mrcfile/dtypes.py> + +=back + +=head1 SEE ALSO + +L<Image::ExifTool::TagNames/MRC Tags>, +L<Image::ExifTool(3pm)|Image::ExifTool> + +=cut + diff --git a/lib/Image/ExifTool/Microsoft.pm b/lib/Image/ExifTool/Microsoft.pm index 5de05129..abd43d87 100644 --- a/lib/Image/ExifTool/Microsoft.pm +++ b/lib/Image/ExifTool/Microsoft.pm @@ -17,7 +17,7 @@ use vars qw($VERSION); use Image::ExifTool qw(:DataAccess :Utils); use Image::ExifTool::XMP; -$VERSION = '1.22'; +$VERSION = '1.23'; sub ProcessXtra($$$); sub WriteXtra($$$); @@ -989,11 +989,11 @@ sub WriteXtra($$$) last; # (it was a cheap goto) } if ($done{$tag}) { + $changed = 1; # write changed values my $buff = WriteXtraValue($et, $$newTags{$tag}, \@newVals); if (length $buff) { $newData .= Set32u(8+length($tag)+length($buff)) . Set32u(length($tag)) . $tag . $buff; - $changed = 1; } } else { # nothing changed; just copy over diff --git a/lib/Image/ExifTool/Nikon.pm b/lib/Image/ExifTool/Nikon.pm index f4b3aa7a..834d4890 100644 --- a/lib/Image/ExifTool/Nikon.pm +++ b/lib/Image/ExifTool/Nikon.pm @@ -62,7 +62,7 @@ use Image::ExifTool qw(:DataAccess :Utils); use Image::ExifTool::Exif; use Image::ExifTool::GPS; -$VERSION = '3.94'; +$VERSION = '3.95'; sub LensIDConv($$$); sub ProcessNikonAVI($$$); @@ -563,7 +563,7 @@ sub GetAFPointGrid($$;$); 'F0 3F 2D 8A 2C 40 DF 0E' => 'Tamron AF 18-270mm f/3.5-6.3 Di II VC PZD (B008)', 'E0 40 2D 98 2C 41 DF 4E' => 'Tamron 18-400mm f/3.5-6.3 Di II VC HLD (B028)', # (removed AF designation, ref 37) '07 40 2F 44 2C 34 03 02' => 'Tamron AF 19-35mm f/3.5-4.5 (A10)', - '07 40 30 45 2D 35 03 02' => 'Tamron AF 19-35mm f/3.5-4.5 (A10)', + '07 40 30 45 2D 35 03 02.1' => 'Tamron AF 19-35mm f/3.5-4.5 (A10)', '00 49 30 48 22 2B 00 02' => 'Tamron SP AF 20-40mm f/2.7-3.5 (166D)', '0E 4A 31 48 23 2D 0E 02' => 'Tamron SP AF 20-40mm f/2.7-3.5 (166D)', 'FE 48 37 5C 24 24 DF 0E' => 'Tamron SP 24-70mm f/2.8 Di VC USD (A007)', #24 @@ -656,6 +656,7 @@ sub GetAFPointGrid($$;$); '00 54 48 48 18 18 00 00' => 'Voigtlander Ultron 40mm F2 SLII Aspherical', '00 54 55 55 0C 0C 00 00' => 'Voigtlander Nokton 58mm F1.4 SLII', '00 40 64 64 2C 2C 00 00' => 'Voigtlander APO-Lanthar 90mm F3.5 SLII Close Focus', + '07 40 30 45 2D 35 03 02.2' => 'Voigtlander Ultragon 19-35mm F3.5-4.5 VMV', #NJ # '00 40 2D 2D 2C 2C 00 00' => 'Carl Zeiss Distagon T* 3.5/18 ZF.2', '00 48 27 27 24 24 00 00' => 'Carl Zeiss Distagon T* 2.8/15 ZF.2', #MykytaKozlov diff --git a/lib/Image/ExifTool/NikonSettings.pm b/lib/Image/ExifTool/NikonSettings.pm index 69f6fde3..f2194e80 100644 --- a/lib/Image/ExifTool/NikonSettings.pm +++ b/lib/Image/ExifTool/NikonSettings.pm @@ -17,7 +17,7 @@ use strict; use vars qw($VERSION); use Image::ExifTool qw(:DataAccess :Utils); -$VERSION = '1.02'; +$VERSION = '1.03'; sub ProcessNikonSettings($$$); @@ -1919,6 +1919,15 @@ my %infoD6 = ( 10 => 'Auto (Animals)', }, }, + 0x170 => { Name => 'PreferSubSelectorCenter', PrintConv => \%offOn }, # CSf13 (D6 firmware v1.2.0) + 0x174 => { # CSa17-d (D6 firmware v1.2.0) + Name => 'FocusPointSelectionSpeed', + PrintConv => { + 1 => 'Normal', + 2 => 'High', + 3 => 'Very High', + }, + }, ); #------------------------------------------------------------------------------ diff --git a/lib/Image/ExifTool/QuickTime.pm b/lib/Image/ExifTool/QuickTime.pm index 89464d70..f09418ee 100644 --- a/lib/Image/ExifTool/QuickTime.pm +++ b/lib/Image/ExifTool/QuickTime.pm @@ -47,7 +47,7 @@ use Image::ExifTool qw(:DataAccess :Utils); use Image::ExifTool::Exif; use Image::ExifTool::GPS; -$VERSION = '2.62'; +$VERSION = '2.63'; sub ProcessMOV($$;$); sub ProcessKeys($$$); @@ -1581,36 +1581,29 @@ my %eeBox2 = ( coll => { Name => 'CollectionName', %langText3gp }, #17 rtng => { Name => 'Rating', + Writable => 'undef', + Avoid => 1, # (4-byte flags, 4-char entity, 4-char criteria, 2-byte lang, string) - RawConv => q{ - return '<err>' unless length $val >= 14; - my $str = 'Entity=' . substr($val,4,4) . ' Criteria=' . substr($val,8,4); - $str =~ tr/\0-\x1f\x7f-\xff//d; # remove unprintable characters - my $lang = Image::ExifTool::QuickTime::UnpackLang(Get16u(\$val, 12)); - $lang = $lang ? "($lang) " : ''; - $val = substr($val, 14); - $val = $self->Decode($val, 'UCS2') if $val =~ /^\xfe\xff/; - return $lang . $str . ' ' . $val; - }, + IText => 14, # (14 bytes before string) + Notes => 'string in the form "Entity=XXXX Criteria=XXXX XXXXX", used in 3gp videos', + ValueConv => '$val=~s/^(.{4})(.{4})/Entity=$1 Criteria=$2 /i; $val', + ValueConvInv => '$val=~s/Entity=(.{4}) Criteria=(.{4}) ?/$1$2/i; $val', }, clsf => { Name => 'Classification', + Writable => 'undef', + Avoid => 1, # (4-byte flags, 4-char entity, 2-byte index, 2-byte lang, string) - RawConv => q{ - return '<err>' unless length $val >= 12; - my $str = 'Entity=' . substr($val,4,4) . ' Index=' . Get16u(\$val,8); - $str =~ tr/\0-\x1f\x7f-\xff//d; # remove unprintable characters - my $lang = Image::ExifTool::QuickTime::UnpackLang(Get16u(\$val, 10)); - $lang = $lang ? "($lang) " : ''; - $val = substr($val, 12); - $val = $self->Decode($val, 'UCS2') if $val =~ /^\xfe\xff/; - return $lang . $str . ' ' . $val; - }, + IText => 12, + Notes => 'string in the form "Entity=XXXX Index=### XXXXX", used in 3gp videos', + ValueConv => '$val=~s/^(.{4})(.{2})/"Entity=$1 Index=".unpack("n",$2)." "/ie; $val', + ValueConvInv => '$val=~s/Entity=(.{4}) Index=(\d+) ?/$1.pack("n",$2)/ie; $val', }, kywd => { Name => 'Keywords', # (4 byte flags, 2-byte lang, 1-byte count, count x pascal strings, ref 17) - # (but I have also seen a simple string written by iPhone) + # (but I have also seen a simple string written by iPhone, so don't make writable yet) + Notes => "not writable because Apple doesn't follow the 3gp specification", RawConv => q{ my $sep = $self->Options('ListSep'); return join($sep, split /\0+/, $val) unless $val =~ /^\0/; # (iPhone) @@ -1635,20 +1628,24 @@ my %eeBox2 = ( loci => { Name => 'LocationInformation', Groups => { 2 => 'Location' }, + Writable => 'undef', + IText => 6, + Avoid => 1, + NoDecode => 1, # (we'll decode the data ourself) + Notes => q{ + string in the form "XXXXX Role=XXX Lat=XXX Lon=XXX Alt=XXX Body=XXX + Notes=XXX", used in 3gp videos + }, # (4-byte flags, 2-byte lang, location string, 1-byte role, 4-byte fixed longitude, # 4-byte fixed latitude, 4-byte fixed altitude, body string, notes string) RawConv => q{ - return '<err>' unless length $val >= 6; - my $lang = Image::ExifTool::QuickTime::UnpackLang(Get16u(\$val, 4)); - $lang = $lang ? "($lang) " : ''; - $val = substr($val, 6); my $str; if ($val =~ /^\xfe\xff/) { $val =~ s/^(\xfe\xff(.{2})*?)\0\0//s or return '<err>'; $str = $self->Decode($1, 'UCS2'); } else { $val =~ s/^(.*?)\0//s or return '<err>'; - $str = $1; + $str = $self->Decode($1, 'UTF8'); } $str = '(none)' unless length $str; return '<err>' if length $val < 13; @@ -1663,27 +1660,52 @@ my %eeBox2 = ( if ($val =~ s/^(\xfe\xff(.{2})*?)\0\0//s) { $str .= ' Body=' . $self->Decode($1, 'UCS2'); } elsif ($val =~ s/^(.*?)\0//s) { - $str .= " Body=$1"; + $str .= ' Body=' . $self->Decode($1, 'UTF8'); } if ($val =~ s/^(\xfe\xff(.{2})*?)\0\0//s) { $str .= ' Notes=' . $self->Decode($1, 'UCS2'); } elsif ($val =~ s/^(.*?)\0//s) { - $str .= " Notes=$1"; + $str .= ' Notes=' . $self->Decode($1, 'UTF8'); } - return $lang . $str; + return $str; + }, + RawConvInv => q{ + my ($role, $lat, $lon, $alt, $body, $note); + $lat = $1 if $val =~ s/ Lat=([-+]?[.\d]+)//i; + $lon = $1 if $val =~ s/ Lon=([-+]?[.\d]+)//i; + $alt = $1 if $val =~ s/ Alt=([-+]?[.\d]+)//i; + $note = $val =~ s/ Notes=(.*)//i ? $1 : ''; + $body = $val =~ s/ Body=(.*)//i ? $1 : ''; + $role = $val =~ s/ Role=(.*)//i ? $1 : ''; + $val = '' if $val eq '(none)'; + $role = {shooting=>0,real=>1,fictional=>2}->{lc $role} || 0; + return $self->Encode($val, 'UTF8') . "\0" . Set8u($role) . + SetFixed32s(defined $lon ? $lon : 999) . + SetFixed32s(defined $lat ? $lat : 999) . + SetFixed32s(defined $alt ? $alt : 0) . + $self->Encode($body) . "\0" . + $self->Encode($note) . "\0"; }, }, yrrc => { Name => 'Year', + Writable => 'undef', Groups => { 2 => 'Time' }, - RawConv => 'length($val) >= 6 ? Get16u(\$val,4) : "<err>"', + Avoid => 1, + Notes => 'used in 3gp videos', + ValueConv => 'length($val) >= 6 ? unpack("x4n",$val) : "<err>"', + ValueConvInv => 'pack("Nn",0,$val)', }, urat => { #17 Name => 'UserRating', - RawConv => q{ + Writable => 'undef', + Notes => 'used in 3gp videos', + Avoid => 1, + ValueConv => q{ return '<err>' unless length $val >= 8; - return Get8u(\$val, 7); + unpack('x7C', $val); }, + ValueConvInv => 'pack("N2",0,$val)', }, # tsel - TrackSelection (ref 17) # Apple tags (ref 16[dead] -- see ref 25 instead) @@ -9394,9 +9416,9 @@ ItemID: foreach $id (keys %$items) { } for (;;) { my ($len, $lang); - if ($$tagInfo{IText} and $$tagInfo{IText} == 6) { - last if $pos + 6 > $size; - $pos += 4; + if ($$tagInfo{IText} and $$tagInfo{IText} >= 6) { + last if $pos + $$tagInfo{IText} > $size; + $pos += $$tagInfo{IText} - 2; $lang = unpack("x${pos}n", $val); $pos += 2; $len = $size - $pos; @@ -9416,7 +9438,7 @@ ItemID: foreach $id (keys %$items) { # ignore any empty entries (or null padding) after the first next if not $len and $pos; my $str = substr($val, $pos, $len); - my $langInfo; + my ($langInfo, $enc); if (($lang < 0x400 or $lang == 0x7fff) and $str !~ /^\xfe\xff/) { # this is a Macintosh language code # a language code of 0 is Macintosh english, so treat as default @@ -9433,15 +9455,22 @@ ItemID: foreach $id (keys %$items) { } # the spec says only "Macintosh text encoding", but # allow this to be configured by the user - $str = $et->Decode($str, $charsetQuickTime); + $enc = $charsetQuickTime; } else { # convert language code to ASCII (ignore read-only bit) $lang = UnpackLang($lang); # may be either UTF-8 or UTF-16BE - my $enc = $str=~s/^\xfe\xff// ? 'UTF16' : 'UTF8'; + $enc = $str=~s/^\xfe\xff// ? 'UTF16' : 'UTF8'; + } + unless ($$tagInfo{NoDecode}) { $str = $et->Decode($str, $enc); + $str =~ s/\0+$//; # remove any trailing nulls (eg. 3gp tags) + } + if ($$tagInfo{IText} and $$tagInfo{IText} > 6) { + my $n = $$tagInfo{IText} - 6; + # add back extra bytes (eg. 'rtng' box) + $str = substr($val, $pos-$n-2, $n) . $str; } - $str =~ s/\0+$//; # remove any trailing nulls (eg. 3gp tags) $langInfo = GetLangInfoQT($et, $tagInfo, $lang) if $lang; $et->FoundTag($langInfo || $tagInfo, $str); $pos += $len; diff --git a/lib/Image/ExifTool/QuickTimeStream.pl b/lib/Image/ExifTool/QuickTimeStream.pl index 417c6c3d..0d3ebb8a 100644 --- a/lib/Image/ExifTool/QuickTimeStream.pl +++ b/lib/Image/ExifTool/QuickTimeStream.pl @@ -2274,37 +2274,59 @@ sub ProcessNMEA($$$) { my ($et, $dirInfo, $tagTbl) = @_; my $dataPt = $$dirInfo{DataPt}; - my $rtnVal; - # parse only RMC sentence (with leading timestamp) for now - while ($$dataPt =~ /(?:\[(\d+)\])?\$[A-Z]{2}RMC,(\d{2})(\d{2})(\d+(\.\d*)?),A?,(\d+\.\d+),([NS]),(\d+\.\d+),([EW]),(\d*\.?\d*),(\d*\.?\d*),(\d{2})(\d{2})(\d+)/g) { - $rtnVal = 1; - my $tc = $1; # milliseconds since 1970 (local time) - my ($lat,$latRef,$lon,$lonRef) = ($6,$7,$8,$9); - my $yr = $14 + ($14 >= 70 ? 1900 : 2000); - my ($mon,$day,$hr,$min,$sec) = ($13,$12,$2,$3,$4); - my ($spd, $trk); - $spd = $10 * $knotsToKph if length $10; - $trk = $11 if length $11; - # lat/long are in DDDMM.MMMM format - my $deg = int($lat / 100); - $lat = $deg + ($lat - $deg * 100) / 60; - $deg = int($lon / 100); - $lon = $deg + ($lon - $deg * 100) / 60; - $sec = '0' . $sec unless $sec =~ /^\d{2}/; # pad integer part of seconds to 2 digits - my $time = sprintf('%.4d:%.2d:%.2d %.2d:%.2d:%sZ',$yr,$mon,$day,$hr,$min,$sec); - my $sampleTime; - $sampleTime = ($tc - $$et{StartTime}) / 1000 if $tc and $$et{StartTime}; - FoundSomething($et, $tagTbl, $sampleTime); - $et->HandleTag($tagTbl, GPSDateTime => $time); - $et->HandleTag($tagTbl, GPSLatitude => $lat * ($latRef eq 'S' ? -1 : 1)); - $et->HandleTag($tagTbl, GPSLongitude => $lon * ($lonRef eq 'W' ? -1 : 1)); - if (defined $spd) { - $et->HandleTag($tagTbl, GPSSpeed => $spd); - $et->HandleTag($tagTbl, GPSSpeedRef => 'K'); + my ($rtnVal, %fix); + # parse only RMC and GGA sentence [with leading timecode] for now + for (;;) { + my ($tc, $type, $tim); + if ($$dataPt =~ /(?:\[(\d+)\])?\$[A-Z]{2}(RMC|GGA),(\d{2}\d{2}\d+(\.\d*)?),/g) { + ($tc, $type, $tim) = ($1, $2, $3); } - if (defined $trk) { - $et->HandleTag($tagTbl, GPSTrack => $trk); - $et->HandleTag($tagTbl, GPSTrackRef => 'T'); + # write out last fix now if complete + # (use the GPS timestamps because they may be different for the same timecode) + if ($fix{tim} and (not $tim or $fix{tim} != $tim)) { + if ($fix{dat} and defined $fix{lat} and defined $fix{lon}) { + my $sampleTime; + $sampleTime = ($fix{tc} - $$et{StartTime}) / 1000 if $fix{tc} and $$et{StartTime}; + FoundSomething($et, $tagTbl, $sampleTime); + $et->HandleTag($tagTbl, GPSDateTime => $fix{dat}); + $et->HandleTag($tagTbl, GPSLatitude => $fix{lat}); + $et->HandleTag($tagTbl, GPSLongitude => $fix{lon}); + if (defined $fix{spd}) { + $et->HandleTag($tagTbl, GPSSpeed => $fix{spd} * $knotsToKph); + $et->HandleTag($tagTbl, GPSSpeedRef => 'K'); + } + if (defined $fix{trk}) { + $et->HandleTag($tagTbl, GPSTrack => $fix{trk}); + $et->HandleTag($tagTbl, GPSTrackRef => 'T'); + } + $et->HandleTag($tagTbl, GPSAltitude => $fix{alt}) if defined $fix{alt}; + $et->HandleTag($tagTbl, GPSSatellites => $fix{nsats}+0) if defined $fix{nsats}; + $et->HandleTag($tagTbl, GPSDOP => $fix{hdop}) if defined $fix{hdop}; + } + undef %fix; + } + $fix{tim} = $tim or last; + my $pos = pos($$dataPt); + pos($$dataPt) = $pos - length($tim) - 1; # rewind to re-parse time + # (parsing of NMEA strings copied from Geotag.pm) + if ($type eq 'RMC' and + $$dataPt =~ /\G(\d{2})(\d{2})(\d+(\.\d*)?),A?,(\d*?)(\d{1,2}\.\d+),([NS]),(\d*?)(\d{1,2}\.\d+),([EW]),(\d*\.?\d*),(\d*\.?\d*),(\d{2})(\d{2})(\d+)/g) + { + my $year = $15 + ($15 >= 70 ? 1900 : 2000); + $fix{tc} = $tc; # use timecode of RMC sentence + $fix{dat} = sprintf('%.4d:%.2d:%.2d %.2d:%.2d:%sZ',$year,$14,$13,$1,$2,$3); + $fix{lat} = (($5 || 0) + $6/60) * ($7 eq 'N' ? 1 : -1); + $fix{lon} = (($8 || 0) + $9/60) * ($10 eq 'E' ? 1 : -1); + $fix{spd} = $11 if length $11; + $fix{trk} = $12 if length $12; + } elsif ($type eq 'GGA' and + $$dataPt =~ /\G(\d{2})(\d{2})(\d+(\.\d*)?),(\d*?)(\d{1,2}\.\d+),([NS]),(\d*?)(\d{1,2}\.\d+),([EW]),[1-6]?,(\d+)?,(\.\d+|\d+\.?\d*)?,(-?\d+\.?\d*)?,M?/g) + { + $fix{lat} = (($5 || 0) + $6/60) * ($7 eq 'N' ? 1 : -1); + $fix{lon} = (($8 || 0) + $9/60) * ($10 eq 'E' ? 1 : -1); + @fix{qw(nsats hdop alt)} = ($11,$12,$13); + } else { + pos($$dataPt) = $pos; # continue searching from our last match } } delete $$et{DOC_NUM}; diff --git a/lib/Image/ExifTool/RIFF.pm b/lib/Image/ExifTool/RIFF.pm index 8b0169bd..3f040f45 100644 --- a/lib/Image/ExifTool/RIFF.pm +++ b/lib/Image/ExifTool/RIFF.pm @@ -21,6 +21,7 @@ # 13) http://tech.ebu.ch/docs/tech/tech3285.pdf # 14) https://developers.google.com/speed/webp/docs/riff_container # 15) https://tech.ebu.ch/docs/tech/tech3306-2009.pdf +# 16) https://sites.google.com/site/musicgapi/technical-documents/wav-file-format #------------------------------------------------------------------------------ package Image::ExifTool::RIFF; @@ -29,7 +30,7 @@ use strict; use vars qw($VERSION); use Image::ExifTool qw(:DataAccess :Utils); -$VERSION = '1.57'; +$VERSION = '1.58'; sub ConvertTimecode($); sub ProcessSGLT($$$); @@ -354,9 +355,35 @@ my %code2charset = ( SubDirectory => { TagTable => 'Image::ExifTool::RIFF::DS64' }, }, list => 'ListType', #15 - labl => { #15 - Name => 'Label', - SubDirectory => { TagTable => 'Image::ExifTool::RIFF::Label' }, + labl => { #16 (in 'adtl' chunk) + Name => 'CuePointLabel', + Priority => 0, # (so they are stored in sequence) + ValueConv => 'my $str=substr($val,4); $str=~s/\0+$//; unpack("V",$val) . " " . $str', + }, + note => { #16 (in 'adtl' chunk) + Name => 'CuePointNote', + Priority => 0, # (so they are stored in sequence) + ValueConv => 'my $str=substr($val,4); $str=~s/\0+$//; unpack("V",$val) . " " . $str', + }, + ltxt => { #16 (in 'adtl' chunk) + Name => 'LabeledText', + Notes => 'CuePointID Length Purpose Country Language Dialect Codepage Text', + Priority => 0, # (so they are stored in sequence) + ValueConv => q{ + my @a = unpack('VVa4vvvv', $val); + $a[2] = "'$a[2]'"; + my $txt = substr($val, 18); + $txt =~ s/\0+$//; # remove null terminator + return join(' ', @a, $txt); + }, + }, + smpl => { #16 + Name => 'Sampler', + SubDirectory => { TagTable => 'Image::ExifTool::RIFF::Sampler' }, + }, + inst => { #16 + Name => 'Instrument', + SubDirectory => { TagTable => 'Image::ExifTool::RIFF::Instrument' }, }, LIST_INFO => { Name => 'Info', @@ -396,6 +423,10 @@ my %code2charset = ( ProcessProc => \&Image::ExifTool::RIFF::ProcessChunks, }, }, + LIST_adtl => { #PH (ref 16, forum12387) + Name => 'AssociatedDataList', + SubDirectory => { TagTable => 'Image::ExifTool::RIFF::Main' }, + }, # seen LIST_JUNK JUNK => [ { @@ -466,10 +497,15 @@ my %code2charset = ( Name => 'NumberOfSamples', RawConv => 'Get32u(\$val, 0)', }, - 'cue ' => { + 'cue '=> { Name => 'CuePoints', Binary => 1, + Notes => q{ + config_files/cutepointlist.config from full distribution will decode this + and generate a list of cue points with labels + }, }, + plst => { Name => 'Playlist', Binary => 1 }, #16 afsp => { }, IDIT => { Name => 'DateTimeOriginal', @@ -704,16 +740,52 @@ my %code2charset = ( # very unlikely, support for these is not yet implemented) ); -# cue point labels (ref 15) -%Image::ExifTool::RIFF::Label = ( +# Sampler chunk (ref 16) +%Image::ExifTool::RIFF::Sampler = ( PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData, GROUPS => { 2 => 'Audio' }, FORMAT => 'int32u', - 0 => 'LabelID', - 1 => { - Name => 'LabelText', - Format => 'string[$size-4]', + 0 => 'Manufacturer', + 1 => 'Product', + 2 => 'SamplePeriod', + 3 => 'MIDIUnityNote', + 4 => 'MIDIPitchFraction', + 5 => { + Name => 'SMPTEFormat', + PrintConv => { + 0 => 'none', + 24 => '24 fps', + 25 => '25 fps', + 29 => '29 fps', + 30 => '30 fps', + }, }, + 6 => { + Name => 'SMPTEOffset', + Notes => 'HH:MM:SS:FF', + ValueConv => q{ + my $str = sprintf('%.8x', $val); + $str =~ s/(..)(..)(..)(..)/$1:$2:$3:$4/; + return $str; + }, + }, + 7 => 'NumSampleLoops', + 8 => 'SamplerDataLen', + 9 => { Name => 'SamplerData', Format => 'undef[$size-40]', Binary => 1 }, +); + +# Instrument chunk (ref 16) +%Image::ExifTool::RIFF::Instrument = ( + PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData, + GROUPS => { 2 => 'Audio' }, + FORMAT => 'int8s', + 0 => 'UnshiftedNote', + 1 => 'FineTune', + 2 => 'Gain', + 3 => 'LowNote', + 4 => 'HighNote', + 5 => 'LowVelocity', + 6 => 'HighVelocity', ); # Sub chunks of INFO LIST chunk diff --git a/lib/Image/ExifTool/Sony.pm b/lib/Image/ExifTool/Sony.pm index 0962b88c..9c2baf79 100644 --- a/lib/Image/ExifTool/Sony.pm +++ b/lib/Image/ExifTool/Sony.pm @@ -34,7 +34,7 @@ use Image::ExifTool qw(:DataAccess :Utils); use Image::ExifTool::Exif; use Image::ExifTool::Minolta; -$VERSION = '3.40'; +$VERSION = '3.41'; sub ProcessSRF($$$); sub ProcessSR2($$$); @@ -148,6 +148,7 @@ sub PrintInvLensSpec($;$$); 32859 => 'Sony FE 20mm F1.8 G', #IB/JR 32860 => 'Sony FE 12-24mm F2.8 GM', #JR/IB 32862 => 'Sony FE 50mm F1.2 GM', #IB/JR + 32863 => 'Sony FE 14mm F1.8 GM', #IB 32864 => 'Sony FE 28-60mm F4-5.6', #JR 32865 => 'Sony FE 35mm F1.4 GM', #IB/JR 32866 => 'Sony FE 24mm F2.8 G', #IB @@ -1552,6 +1553,7 @@ my %hidUnk = ( Hidden => 1, Unknown => 1 ); Name => 'FocusFrameSize', Format => 'int16u', Count => '3', + Notes => 'width and height of FocusFrame, centered on FocusLocation', PrintConv => q{ my @a = split ' ', $val; return $a[2] ? sprintf('%3dx%3d', $a[0], $a[1]) : 'n/a'; @@ -6127,6 +6129,7 @@ my %pictureProfile2010 = ( 33 => 'Gamma HLG2 (PP10)', #14 34 => 'Gamma HLG3', #IB 37 => 'FL', + 38 => 'VV2', 39 => 'IN', 40 => 'SH', }, diff --git a/lib/Image/ExifTool/TagLookup.pm b/lib/Image/ExifTool/TagLookup.pm index 767d349d..d8c400cb 100644 --- a/lib/Image/ExifTool/TagLookup.pm +++ b/lib/Image/ExifTool/TagLookup.pm @@ -1456,6 +1456,7 @@ my %tagLookup = ( 'clarity' => { 225 => 0x35, 226 => 0x3d, 411 => 0x2036, 468 => 'Clarity', 470 => 'Clarity' }, 'clarity2012' => { 468 => 'Clarity2012', 470 => 'Clarity2012' }, 'claritycontrol' => { 346 => 0x96 }, + 'classification' => { 371 => 'clsf' }, 'classifystate' => { 129 => 0xe1 }, 'clearretouch' => { 311 => 0x7c }, 'clearretouchvalue' => { 311 => 0xa3 }, @@ -3639,6 +3640,7 @@ my %tagLookup = ( 'locationcreatedsublocation' => { 480 => [\'LocationCreated','LocationCreatedSublocation'] }, 'locationcreatedworldregion' => { 480 => [\'LocationCreated','LocationCreatedWorldRegion'] }, 'locationdate' => { 365 => 'location.date' }, + 'locationinformation' => { 371 => 'loci' }, 'locationinfoversion' => { 221 => 0x0 }, 'locationname' => { 365 => 'location.name', 385 => 0x31 }, 'locationnote' => { 365 => 'location.note' }, @@ -4878,7 +4880,7 @@ my %tagLookup = ( 'rads' => { 371 => 'rads' }, 'rangefinder' => { 276 => '4.1', 277 => '5.1', 278 => '5.1' }, 'rasterizedcaption' => { 129 => 0x7d }, - 'rating' => { 117 => 0x4746, 125 => 0x1431, 357 => 0xdf, 363 => 'rtng', 411 => 0x2002, 463 => 'rating', 472 => 'rating', 480 => 'Rating', 484 => 'rating', 492 => 'Rating' }, + 'rating' => { 117 => 0x4746, 125 => 0x1431, 357 => 0xdf, 363 => 'rtng', 371 => 'rtng', 411 => 0x2002, 463 => 'rating', 472 => 'rating', 480 => 'Rating', 484 => 'rating', 492 => 'Rating' }, 'ratingpercent' => { 117 => 0x4749, 175 => 'Rating', 363 => 'rate', 492 => 'RatingPercent' }, 'ratingregion' => { 480 => [\'Rating','RatingRatingRegion'] }, 'ratingregioncity' => { 480 => [\'Rating','RatingRatingRegionCity'] }, @@ -6003,7 +6005,7 @@ my %tagLookup = ( 'userfields' => { 461 => 'UserFields' }, 'userlabel' => { 372 => 0x2b, 373 => 0x5a, 374 => 0x68 }, 'userprofile' => { 306 => 0x302, 310 => 0x34c, 313 => 0x3038 }, - 'userrating' => { 365 => 'rating.user' }, + 'userrating' => { 365 => 'rating.user', 371 => 'urat' }, 'usmlenselectronicmf' => { 2 => 0x7, 81 => 0x7, 82 => 0x501 }, 'uspsnumber' => { 484 => 'uspsNumber' }, 'utmeasting' => { 165 => 'Easting' }, @@ -6405,7 +6407,7 @@ my %tagLookup = ( 'ycbcrcoefficients' => { 117 => 0x211, 490 => 'YCbCrCoefficients' }, 'ycbcrpositioning' => { 117 => 0x213, 490 => 'YCbCrPositioning' }, 'ycbcrsubsampling' => { 117 => 0x212, 490 => 'YCbCrSubSampling' }, - 'year' => { 363 => 'yrrc', 365 => 'year' }, + 'year' => { 363 => 'yrrc', 365 => 'year', 371 => 'yrrc' }, 'yearcreated' => { 137 => 0x10, 146 => 0xc }, 'yellowhsl' => { 101 => 0x20912 }, 'yield' => { 486 => 'yield' }, @@ -6621,6 +6623,7 @@ my %tagExists = ( 'aspectratioy' => 1, 'assistantsname' => 1, 'assistantsphone' => 1, + 'associateddatalist' => 1, 'associatedimagefile' => 1, 'association' => 1, 'assumeddisplaysize' => 1, @@ -7163,7 +7166,6 @@ my %tagExists = ( 'cip3side' => 1, 'circleofconfusion' => 1, 'class' => 1, - 'classification' => 1, 'cleanaperture' => 1, 'cleanaperturedimensions' => 1, 'cleanapertureheight' => 1, @@ -7448,6 +7450,8 @@ my %tagExists = ( 'ctmd' => 1, 'cubemapproj' => 1, 'cuepoint' => 1, + 'cuepointlabel' => 1, + 'cuepointnote' => 1, 'cuepoints' => 1, 'cuesheet' => 1, 'currentbitrate' => 1, @@ -8048,6 +8052,7 @@ my %tagExists = ( 'filterserialnumber' => 1, 'finalflushsequence' => 1, 'finalframeblocks' => 1, + 'finetune' => 1, 'finishedfileprocessingrequest' => 1, 'finishipaversion' => 1, 'finishipfversion' => 1, @@ -8132,6 +8137,7 @@ my %tagExists = ( 'focuspeakinglevel' => 1, 'focuspointbrightness' => 1, 'focuspointpersistence' => 1, + 'focuspointselectionspeed' => 1, 'focuspos' => 1, 'focussettings' => 1, 'folder' => 1, @@ -8391,6 +8397,8 @@ my %tagExists = ( 'highisomode' => 1, 'highlightdata' => 1, 'highlightendpoints' => 1, + 'highnote' => 1, + 'highvelocity' => 1, 'hintformat' => 1, 'hintheader' => 1, 'hintinfo' => 1, @@ -8772,8 +8780,7 @@ my %tagExists = ( 'label1' => 1, 'label2' => 1, 'label3' => 1, - 'labelid' => 1, - 'labeltext' => 1, + 'labeledtext' => 1, 'lamebitrate' => 1, 'lameheader' => 1, 'lamelowpassfilter' => 1, @@ -8948,7 +8955,6 @@ my %tagExists = ( 'localeindicator' => 1, 'localpositionned' => 1, 'locationinfo' => 1, - 'locationinformation' => 1, 'lockedpropertylist' => 1, 'locks' => 1, 'loglintable' => 1, @@ -8973,6 +8979,8 @@ my %tagExists = ( 'lookuptable' => 1, 'lotus' => 1, 'lowlightaf' => 1, + 'lownote' => 1, + 'lowvelocity' => 1, 'lr' => 1, 'lslv' => 1, 'lucasjunk' => 1, @@ -9266,7 +9274,9 @@ my %tagExists = ( 'middlename' => 1, 'midicontrol' => 1, 'midicontrolversion' => 1, + 'midipitchfraction' => 1, 'midisong' => 1, + 'midiunitynote' => 1, 'mie' => 1, 'mileage' => 1, 'mimeencoding' => 1, @@ -9470,6 +9480,7 @@ my %tagExists = ( 'numproperties' => 1, 'numrules' => 1, 'numsampleframes' => 1, + 'numsampleloops' => 1, 'numslices' => 1, 'numstreams' => 1, 'numtemporallayers' => 1, @@ -9811,6 +9822,7 @@ my %tagExists = ( 'playbackflickup' => 1, 'playbackflickuprating' => 1, 'playcounter' => 1, + 'playlist' => 1, 'playlistdelay' => 1, 'playlistindex' => 1, 'plus' => 1, @@ -9841,6 +9853,7 @@ my %tagExists = ( 'preferredrate' => 1, 'preferredsubfamily' => 1, 'preferredvolume' => 1, + 'prefersubselectorcenter' => 1, 'preroll' => 1, 'presentationformat' => 1, 'presentationtarget' => 1, @@ -10280,7 +10293,11 @@ my %tagExists = ( 'sampleformat' => 1, 'samplegroupdescription' => 1, 'samplepaddingbits' => 1, + 'sampleperiod' => 1, + 'sampler' => 1, 'samplerate2' => 1, + 'samplerdata' => 1, + 'samplerdatalen' => 1, 'samplesize' => 1, 'samplesizes' => 1, 'sampletable' => 1, @@ -10514,6 +10531,8 @@ my %tagExists = ( 'slideshow' => 1, 'smaxsamplevalue' => 1, 'sminsamplevalue' => 1, + 'smpteformat' => 1, + 'smpteoffset' => 1, 'snapshotid' => 1, 'snapshotname' => 1, 'soctemperature' => 1, @@ -10993,6 +11012,7 @@ my %tagExists = ( 'unknowntemperature1' => 1, 'unknowntemperature2' => 1, 'unsharpdata' => 1, + 'unshiftednote' => 1, 'untitled0' => 1, 'untitled1' => 1, 'untitled2' => 1, diff --git a/lib/Image/ExifTool/TagNames.pod b/lib/Image/ExifTool/TagNames.pod index f1e52e1c..a012f776 100644 --- a/lib/Image/ExifTool/TagNames.pod +++ b/lib/Image/ExifTool/TagNames.pod @@ -12,7 +12,7 @@ meta information extracted from or written to a file. =head1 TAG TABLES The tables listed below give the names of all tags recognized by ExifTool. -They contain a total of 24118 tags, with 15614 unique tag names. +They contain a total of 24273 tags, with 15738 unique tag names. B<Tag ID>, B<Index#> or B<Sequence> is given in the first column of each table. A B<Tag ID> is the computer-readable equivalent of a tag name, and @@ -6101,6 +6101,8 @@ Unknown only to reduce the volume of the normal output. 0x016b LimitAF-AreaModeSelWideLPeople? no 0x016c LimitAF-AreaModeSelWideLAnimals? no 0x016e AFAreaMode no + 0x0170 PreferSubSelectorCenter no + 0x0174 FocusPointSelectionSpeed no =head2 Canon Tags @@ -24426,6 +24428,155 @@ files. 48 PrimaryFileGUID no 64 FileGUID no +=head2 MRC Tags + +Tags extracted from Medical Research Council (MRC) format imaging files. +See L<https://www.ccpem.ac.uk/mrc_format/mrc2014.php> for the specification. + + Index4 Tag Name Writable + ------ -------- -------- + 0 ImageWidth no + 1 ImageHeight no + 2 ImageDepth no + 3 ImageMode no + 4 StartPoint no + 7 GridSize no + 10 CellWidth no + 11 CellHeight no + 12 CellDepth no + 13 CellAlpha no + 14 CellBeta no + 15 CellGamma no + 16 ImageWidthAxis no + 17 ImageHeightAxis no + 18 ImageDepthAxis no + 19 DensityMin no + 20 DensityMax no + 21 DensityMean no + 22 SpaceGroupNumber no + 23 ExtendedHeaderSize no + 26 ExtendedHeaderType no + 27 MRCVersion no + 49 Origin no + 53 MachineStamp no + 54 RMSDeviation no + 55 NumberOfLabels no + 56 Label0 no + 76 Label1 no + 96 Label2 no + 116 Label3 no + 136 Label4 no + 156 Label5 no + 176 Label6 no + 196 Label7 no + 216 Label8 no + 236 Label9 no + +=head3 MRC FEI12 Tags + +Tags extracted from FEI1 and FEI2 extended headers. + + Index1 Tag Name Writable + ------ -------- -------- + 0 MetadataSize no + 4 MetadataVersion no + 8 Bitmask1 no + 12 TimeStamp no + 20 MicroscopeType no + 36 MicroscopeID no + 52 Application no + 68 AppVersion no + 84 HighTension no + 92 Dose no + 100 AlphaTilt no + 108 BetaTilt no + 116 XStage no + 124 YStage no + 132 ZStage no + 140 TiltAxisAngle no + 148 DualAxisRot no + 156 PixelSizeX no + 164 PixelSizeY no + 220 Defocus no + 228 STEMDefocus no + 236 AppliedDefocus no + 244 InstrumentMode no + 248 ProjectionMode no + 252 ObjectiveLens no + 268 HighMagnificationMode no + 284 ProbeMode no + 288 EFTEMOn no + 289 Magnification no + 297 Bitmask2 no + 301 CameraLength no + 309 SpotIndex no + 313 IlluminationArea no + 321 Intensity no + 329 ConvergenceAngle no + 337 IlluminationMode no + 353 WideConvergenceAngleRange no + 354 SlitInserted no + 355 SlitWidth no + 363 AccelVoltOffset no + 371 DriftTubeVolt no + 379 EnergyShift no + 387 ShiftOffsetX no + 395 ShiftOffsetY no + 403 ShiftX no + 411 ShiftY no + 419 IntegrationTime no + 427 BinningWidth no + 431 BinningHeight no + 435 CameraName no + 451 ReadoutAreaLeft no + 455 ReadoutAreaTop no + 459 ReadoutAreaRight no + 463 ReadoutAreaBottom no + 467 CetaNoiseReduct no + 468 CetaFramesSummed no + 472 DirectDetElectronCounting no + 473 DirectDetAlignFrames no + 490 Bitmask3 no + 518 PhasePlate no + 519 STEMDetectorName no + 535 Gain no + 543 Offset no + 571 DwellTime no + 579 FrameTime no + 587 ScanSizeLeft no + 591 ScanSizeTop no + 595 ScanSizeRight no + 599 ScanSizeBottom no + 603 FullScanFOV_X no + 611 FullScanFOV_Y no + 619 Element no + 635 EnergyIntervalLower no + 643 EnergyIntervalHigher no + 651 Method no + 655 IsDoseFraction no + 656 FractionNumber no + 660 StartFrame no + 664 EndFrame no + 668 InputStackFilename no + 748 Bitmask4 no + 752 AlphaTiltMin no + 760 AlphaTiltMax no + 768 ScanRotation no + 776 DiffractionPatternRotation no + 784 ImageRotation no + 792 ScanModeEnumeration no + 796 AcquisitionTimeStamp no + 804 DetectorCommercialName no + 820 StartTiltAngle no + 828 EndTiltAngle no + 836 TiltPerImage no + 844 TitlSpeed no + 852 BeamCenterX no + 856 BeamCenterY no + 860 CFEGFlashTimeStamp no + 868 PhasePlatePosition no + 872 ObjectiveAperture no + =head2 MIFF Tags The MIFF (Magick Image File Format) format allows aribrary tag names to be @@ -27030,7 +27181,7 @@ the config file. 'chpl' ChapterList no 'clfn' ClipFileName string 'clid' ClipID string - 'clsf' Classification no + 'clsf' Classification undef/ 'cmid' CameraID string 'cmnm' Model string/ 'coll' CollectionName string/ @@ -27048,7 +27199,7 @@ the config file. 'infu' InfoURL string 'kgtt' TrackType string 'kywd' Keywords no - 'loci' LocationInformation no + 'loci' LocationInformation undef/ 'lrcu' LyricsURI string 'lvlm' LevelMeter? rational64s 'manu' Make no @@ -27063,7 +27214,7 @@ the config file. 'rads' Rads? rational64s 'reel' ReelName string 'roll' Roll rational64s/ - 'rtng' Rating no + 'rtng' Rating undef/ 'scen' Scene string 'scrn' OlympusPreview Olympus scrn 'shot' ShotName string @@ -27076,12 +27227,12 @@ the config file. ThumbnailPNG string UnknownThumbnail string 'titl' Title string/ - 'urat' UserRating no + 'urat' UserRating undef/ 'uuid' GarminSoftware string UUID-Unknown? no 'vndr' Vendor string 'vrot' AccelerometerData no - 'yrrc' Year no + 'yrrc' Year undef/ "\xa9ART" Artist string "\xa9TIM" StartTimecode string "\xa9TSC" StartTimeScale string @@ -29889,6 +30040,7 @@ sub-documents, but the Duration is calculated for the full video. 'LIST_INF0' Info RIFF Info 'LIST_INFO' Info RIFF Info 'LIST_Tdat' Tdat RIFF Tdat + 'LIST_adtl' AssociatedDataList RIFF 'LIST_exif' Exif RIFF Exif 'LIST_hdrl' Hdrl RIFF Hdrl 'LIST_hydt' PentaxData Pentax AVI @@ -29911,9 +30063,14 @@ sub-documents, but the Duration is calculated for the full video. 'gps0' GPSTrack QuickTime Stream 'gsen' GSensor QuickTime Stream 'iXML' IXML XMP XML - 'labl' Label RIFF Label + 'inst' Instrument RIFF Instrument + 'labl' CuePointLabel no 'list' ListType no + 'ltxt' LabeledText no + 'note' CuePointNote no 'olym' Olym Olympus WAV + 'plst' Playlist no + 'smpl' Sampler RIFF Sampler 'tx_USER' UserText RIFF UserText 'tx_Unknown' Text no @@ -30210,12 +30367,32 @@ L<https://tech.ebu.ch/docs/tech/tech3306-2009.pdf> for the specification. 1 DataSize64 no 2 NumberOfSamples64 no -=head3 RIFF Label Tags +=head3 RIFF Instrument Tags + + Index1 Tag Name Writable + ------ -------- -------- + 0 UnshiftedNote no + 1 FineTune no + 2 Gain no + 3 LowNote no + 4 HighNote no + 5 LowVelocity no + 6 HighVelocity no + +=head3 RIFF Sampler Tags Index4 Tag Name Writable ------ -------- -------- - 0 LabelID no - 1 LabelText no + 0 Manufacturer no + 1 Product no + 2 SamplePeriod no + 3 MIDIUnityNote no + 4 MIDIPitchFraction no + 5 SMPTEFormat no + 6 SMPTEOffset no + 7 NumSampleLoops no + 8 SamplerDataLen no + 9 SamplerData no =head3 RIFF UserText Tags diff --git a/lib/Image/ExifTool/WriteQuickTime.pl b/lib/Image/ExifTool/WriteQuickTime.pl index 87c575ef..a325552e 100644 --- a/lib/Image/ExifTool/WriteQuickTime.pl +++ b/lib/Image/ExifTool/WriteQuickTime.pl @@ -1232,10 +1232,14 @@ sub WriteQuickTime($$$) } elsif ($format) { $val = ReadValue(\$buff, 0, $format, undef, $size); } elsif (($tag =~ /^\xa9/ or $$tagInfo{IText}) and $size >= ($$tagInfo{IText} || 4)) { - if ($$tagInfo{IText} and $$tagInfo{IText} == 6) { - $lang = unpack('x4n', $buff); - $len = $size - 6; - $val = substr($buff, 6, $len); + my $hdr; + if ($$tagInfo{IText} and $$tagInfo{IText} >= 6) { + my $iText = $$tagInfo{IText}; + my $pos = $iText - 2; + $lang = unpack("x${pos}n", $buff); + $hdr = substr($buff,4,$iText-6); + $len = $size - $iText; + $val = substr($buff, $iText, $len); } else { ($len, $lang) = unpack('nn', $buff); $len -= 4 if 4 + $len > $size; # (see QuickTime.pm for explanation) @@ -1243,14 +1247,18 @@ sub WriteQuickTime($$$) $val = substr($buff, 4, $len); } $lang or $lang = $undLang; # treat both 0 and 'und' as 'und' + my $enc; if ($lang < 0x400 and $val !~ /^\xfe\xff/) { $charsetQuickTime = $et->Options('CharsetQuickTime'); - $val = $et->Decode($val, $charsetQuickTime); + $enc = $charsetQuickTime; } else { - my $enc = $val=~s/^\xfe\xff// ? 'UTF16' : 'UTF8'; + $enc = $val=~s/^\xfe\xff// ? 'UTF16' : 'UTF8'; + } + unless ($$tagInfo{NoDecode}) { $val = $et->Decode($val, $enc); + $val =~ s/\0+$//; # remove trailing nulls if they exist } - $val =~ s/\0+$//; # remove trailing nulls if they exist + $val = $hdr . $val if defined $hdr; my $langCode = UnpackLang($lang, 1); $langInfo = GetLangInfo($tagInfo, $langCode); $nvHash = $et->GetNewValueHash($langInfo); @@ -1267,6 +1275,9 @@ sub WriteQuickTime($$$) } } else { $val = $buff; + if ($tag =~ /^\xa9/ or $$tagInfo{IText}) { + $et->Warn("Corrupted $$tagInfo{Name} value"); + } } if ($nvHash and defined $val) { if ($et->IsOverwriting($nvHash, $val)) { @@ -1279,12 +1290,23 @@ sub WriteQuickTime($$$) $et->VerboseValue("+ $grp:$$langInfo{Name}", $newData); # add back necessary header and encode as necessary if (defined $lang) { - $newData = $et->Encode($newData, $lang < 0x400 ? $charsetQuickTime : 'UTF8'); + my $iText = $$tagInfo{IText} || 0; + my $hdr; + if ($iText > 6) { + $newData .= ' 'x($iText-6) if length($newData) < $iText-6; + $hdr = substr($newData, 0, $iText-6); + $newData = substr($newData, $iText-6); + } + unless ($$tagInfo{NoDecode}) { + $newData = $et->Encode($newData, $lang < 0x400 ? $charsetQuickTime : 'UTF8'); + } my $wLang = $lang eq $undLang ? 0 : $lang; - if ($$tagInfo{IText} and $$tagInfo{IText} == 6) { + if ($iText < 6) { + $newData = pack('nn', length($newData), $wLang) . $newData; + } elsif ($iText == 6) { $newData = pack('Nn', 0, $wLang) . $newData . "\0"; } else { - $newData = pack('nn', length($newData), $wLang) . $newData; + $newData = "\0\0\0\0" . $hdr . pack('n', $wLang) . $newData . "\0"; } } elsif (not $format or $format =~ /^string/ and not $$tagInfo{Binary} and not $$tagInfo{ValueConv}) @@ -1443,9 +1465,12 @@ sub WriteQuickTime($$$) my $grp = $et->GetGroup($tagInfo,1); $et->Warn("Can't use country code for $grp:$$tagInfo{Name}"); next; - } elsif ($$tagInfo{IText} and $$tagInfo{IText} == 6) { + } elsif ($$tagInfo{IText} and $$tagInfo{IText} >= 6) { # add 6-byte langText header and trailing null - $newVal = pack('Nn',0,$lang) . $newVal . "\0"; + # (with extra junk before language code if IText > 6) + my $n = $$tagInfo{IText} - 6; + $newVal .= ' ' x $n if length($newVal) < $n; + $newVal = "\0\0\0\0" . substr($newVal,0,$n) . pack('n',0,$lang) . substr($newVal,$n) . "\0"; } else { # add IText header $newVal = pack('nn',length($newVal),$lang) . $newVal; diff --git a/lib/Image/ExifTool/WriteXMP.pl b/lib/Image/ExifTool/WriteXMP.pl index 2fee0b33..23694612 100644 --- a/lib/Image/ExifTool/WriteXMP.pl +++ b/lib/Image/ExifTool/WriteXMP.pl @@ -506,7 +506,7 @@ sub ConformPathToNamespace($$) my $prop; foreach $prop (@propList) { my ($ns, $tag) = $prop =~ /(.+?):(.*)/; - next if $$nsUsed{$ns}; + next if not defined $ns or $$nsUsed{$ns}; my $uri = $nsURI{$ns}; unless ($uri) { warn "No URI for namespace prefix $ns!\n"; @@ -1417,7 +1417,11 @@ sub WriteXMP($$;$) my $uri = $nsUsed{$1}; unless ($uri) { $uri = $nsURI{$1}; # we must have added a namespace - $uri or $xmpErr = "Undefined XMP namespace: $1", next; + unless ($uri) { + # (namespace may be empty if trying to write empty XMP structure, forum12384) + $xmpErr = "Undefined XMP namespace: $1" if length $uri; + next; + } } $nsNew{$1} = $uri; # need a new description if any new namespaces diff --git a/lib/Image/ExifTool/XMP.pm b/lib/Image/ExifTool/XMP.pm index 2fcced23..970151ec 100644 --- a/lib/Image/ExifTool/XMP.pm +++ b/lib/Image/ExifTool/XMP.pm @@ -50,7 +50,7 @@ use Image::ExifTool::Exif; use Image::ExifTool::GPS; require Exporter; -$VERSION = '3.40'; +$VERSION = '3.41'; @ISA = qw(Exporter); @EXPORT_OK = qw(EscapeXML UnescapeXML); @@ -71,6 +71,13 @@ sub ConvertRational($); sub ConvertRationalList($); sub WriteGSpherical($$$); +# standard path locations for XMP in major file types +my %stdPath = ( + JPEG => 'JPEG-APP1-XMP', + TIFF => 'TIFF-IFD0-XMP', + PSD => 'PSD-XMP', +); + # lookup for translating to ExifTool namespaces (and family 1 group names) %stdXlatNS = ( # shorten ugly namespace prefixes @@ -3844,6 +3851,7 @@ sub ProcessXMP($$;$) my ($buff, $fmt, $hasXMP, $isXML, $isRDF, $isSVG); my $rtnVal = 0; my $bom = 0; + my $path = $et->MetadataPath(); # namespaces and prefixes currently in effect while parsing the file, # and lookup to translate brain-dead-Microsoft-Photo-software prefixes @@ -3861,11 +3869,7 @@ sub ProcessXMP($$;$) (($$dirInfo{DirName} || '') eq 'XMP' or $$et{FILE_TYPE} eq 'XMP')) { $$et{XmpValidate} = { } if $$et{OPTIONS}{Validate}; - my $path = $et->MetadataPath(); - my $nonStd; - if ($$et{FILE_TYPE} =~ /^(JPEG|TIFF|PSD)$/ and $path !~ /^(JPEG-APP1-XMP|TIFF-IFD0-XMP|PSD-XMP)$/) { - $nonStd = 1; - } + my $nonStd = ($stdPath{$$et{FILE_TYPE}} and $path ne $stdPath{$$et{FILE_TYPE}}); if ($nonStd and $Image::ExifTool::MWG::strict) { $et->Warn("Ignored non-standard XMP at $path"); return 1; @@ -4125,6 +4129,13 @@ sub ProcessXMP($$;$) } defined $fmt or $et->Warn('XMP character encoding error'); } + # warn if standard XMP is missing xpacket wrapper + if ($$et{XMP_NO_XPACKET} and $$et{OPTIONS}{Validate} and + $stdPath{$$et{FILE_TYPE}} and $path eq $stdPath{$$et{FILE_TYPE}} and + not $$dirInfo{IsExtended} and not $$et{DOC_NUM}) + { + $et->Warn('XMP is missing xpacket wrapper', 1); + } if ($fmt) { # trim if necessary to avoid converting non-UTF data if ($dirStart or $dirEnd != length($$dataPt)) { |