summaryrefslogtreecommitdiff
path: root/lib/Image/ExifTool
diff options
context:
space:
mode:
authorexiftool <exiftool@users.sourceforge.net>2021-04-22 14:56:56 -0400
committerexiftool <exiftool@users.sourceforge.net>2021-04-22 14:56:56 -0400
commitceff3cbc4564e93518f3d2a2e00d8ae203ff54af (patch)
tree016a2d6f76fe9f9af7bfb4528d40f5c202363668 /lib/Image/ExifTool
parentcf0f4e7dcd024ca99615bfd1102a841a25dde031 (diff)
Update to 12.25
Diffstat (limited to 'lib/Image/ExifTool')
-rw-r--r--lib/Image/ExifTool/Canon.pm5
-rw-r--r--lib/Image/ExifTool/Exif.pm23
-rw-r--r--lib/Image/ExifTool/Jpeg2000.pm24
-rw-r--r--lib/Image/ExifTool/MRC.pm341
-rw-r--r--lib/Image/ExifTool/Microsoft.pm4
-rw-r--r--lib/Image/ExifTool/Nikon.pm5
-rw-r--r--lib/Image/ExifTool/NikonSettings.pm11
-rw-r--r--lib/Image/ExifTool/QuickTime.pm109
-rw-r--r--lib/Image/ExifTool/QuickTimeStream.pl82
-rw-r--r--lib/Image/ExifTool/RIFF.pm94
-rw-r--r--lib/Image/ExifTool/Sony.pm5
-rw-r--r--lib/Image/ExifTool/TagLookup.pm34
-rw-r--r--lib/Image/ExifTool/TagNames.pod197
-rw-r--r--lib/Image/ExifTool/WriteQuickTime.pl49
-rw-r--r--lib/Image/ExifTool/WriteXMP.pl8
-rw-r--r--lib/Image/ExifTool/XMP.pm23
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)) {