exiftool_vendored 12.58.0 → 12.59.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -12,7 +12,7 @@ meta information extracted from or written to a file.
12
12
  =head1 TAG TABLES
13
13
 
14
14
  The tables listed below give the names of all tags recognized by ExifTool.
15
- They contain a total of 26381 tags, with 16854 unique tag names.
15
+ They contain a total of 26390 tags, with 16859 unique tag names.
16
16
 
17
17
  B<Tag ID>, B<Index#> or B<Sequence> is given in the first column of each
18
18
  table. A B<Tag ID> is the computer-readable equivalent of a tag name, and
@@ -1287,6 +1287,7 @@ L<http://www.adobe.com/devnet/xmp/> for the official XMP specification.
1287
1287
  digiKam XMP digiKam
1288
1288
  drone-dji DJI XMP
1289
1289
  dwc DarwinCore
1290
+ et XMP ExifTool
1290
1291
  exif XMP exif
1291
1292
  exifEX XMP exifEX
1292
1293
  expressionmedia XMP ExpressionMedia
@@ -4034,6 +4035,14 @@ These tags belong to the ExifTool XMP-digiKam family 1 group.
4034
4035
  PickLabel string
4035
4036
  TagsList string+
4036
4037
 
4038
+ =head3 XMP ExifTool Tags
4039
+
4040
+ These tags belong to the ExifTool XMP-et family 1 group.
4041
+
4042
+ Tag Name Writable
4043
+ -------- --------
4044
+ OriginalImageMD5 string
4045
+
4037
4046
  =head3 XMP exif Tags
4038
4047
 
4039
4048
  EXIF namespace for EXIF tags. See
@@ -5307,7 +5316,8 @@ These tags belong to the ExifTool XMP-pmi family 1 group.
5307
5316
  =head3 XMP prism Tags
5308
5317
 
5309
5318
  Publishing Requirements for Industry Standard Metadata 3.0 namespace
5310
- tags. (see L<http://www.prismstandard.org/>)
5319
+ tags. (see
5320
+ L<https://www.w3.org/Submission/2020/SUBM-prism-20200910/prism-basic.html/>)
5311
5321
 
5312
5322
  These tags belong to the ExifTool XMP-prism family 1 group.
5313
5323
 
@@ -17329,6 +17339,8 @@ any information found here will be extracted, even if the tag is not listed.
17329
17339
  0x0304 FocusStepNear int16u
17330
17340
  0x0305 FocusDistance rational64u
17331
17341
  0x0308 AFPoint int16u
17342
+ 0x031b AFPointDetails no
17343
+ AFPointDetails int16u
17332
17344
  0x0328 AFInfo Olympus AFInfo
17333
17345
  0x1201 ExternalFlash int16u[2]
17334
17346
  0x1203 ExternalFlashGuideNumber? rational64s
@@ -21862,7 +21874,10 @@ These tags are found in IFD0 of Panasonic/Leica RAW, RW2 and RWL images.
21862
21874
  0x011c Gamma int16u
21863
21875
  0x0120 CameraIFD PanasonicRaw CameraIFD
21864
21876
  0x0121 Multishot int32u
21877
+ 0x0127 JpgFromRaw2 no
21878
+ 0x013b Artist string
21865
21879
  0x02bc ApplicationNotes XMP
21880
+ 0x8298 Copyright string
21866
21881
  0x83bb IPTC-NAA IPTC
21867
21882
  0x8769 ExifOffset EXIF
21868
21883
  0x8825 GPSInfo GPS
@@ -22143,30 +22158,32 @@ Note that Microsoft is not consistent with the time zone used for some
22143
22158
  date/time tags, and it may be either UTC or local time depending on the
22144
22159
  software used to create the file.
22145
22160
 
22146
- Tag ID Tag Name Writable
22147
- ------ -------- --------
22148
- "\x01CompObj" CompObj FlashPix CompObj
22149
- "\x05Audio Info" AudioInfo FlashPix AudioInfo
22150
- "\x05Data Object" DataObject FlashPix DataObject
22161
+ Tag ID Tag Name Writable
22162
+ ------ -------- --------
22163
+ "\x01CompObj" CompObj FlashPix CompObj
22164
+ "\x05Audio Info" AudioInfo FlashPix AudioInfo
22165
+ "\x05Data Object" DataObject FlashPix DataObject
22151
22166
  "\x05DocumentSummaryInformation" DocumentInfo FlashPix DocumentInfo
22152
- "\x05Extension List" Extensions FlashPix Extensions
22153
- "\x05Global Info" GlobalInfo FlashPix GlobalInfo
22154
- "\x05Image Contents" Image FlashPix Image
22155
- "\x05Image Info" ImageInfo FlashPix ImageInfo
22156
- "\x05Operation" Operation FlashPix Operation
22157
- "\x05Screen Nail" ScreenNail no
22158
- "\x05SummaryInformation" SummaryInfo FlashPix SummaryInfo
22159
- "\x05Transform" Transform FlashPix Transform
22160
- 'Audio Stream' AudioStream no
22161
- 'BasicFileInfo' BasicFileInfo no
22162
- 'Contents' Contents XMP
22163
- 'Current User' CurrentUser no
22164
- 'ICC Profile 0001' ICC_Profile ICC_Profile
22165
- 'IeImg' EmbeddedImage no
22166
- 'Preview' PreviewImage no
22167
- 'Property' PreviewInfo FlashPix PreviewInfo
22168
- 'Subimage 0000 Header' SubimageHdr FlashPix SubimageHdr
22169
- 'WordDocument' WordDocument FlashPix WordDocument
22167
+ "\x05Extension List" Extensions FlashPix Extensions
22168
+ "\x05Global Info" GlobalInfo FlashPix GlobalInfo
22169
+ "\x05Image Contents" Image FlashPix Image
22170
+ "\x05Image Info" ImageInfo FlashPix ImageInfo
22171
+ "\x05Operation" Operation FlashPix Operation
22172
+ "\x05Screen Nail" ScreenNail no
22173
+ "\x05SummaryInformation" SummaryInfo FlashPix SummaryInfo
22174
+ "\x05Transform" Transform FlashPix Transform
22175
+ 'Audio Stream' AudioStream no
22176
+ 'BasicFileInfo' BasicFileInfo no
22177
+ 'Contents' Contents XMP
22178
+ 'Current User' CurrentUser no
22179
+ 'ICC Profile 0001' ICC_Profile ICC_Profile
22180
+ 'IeImg' EmbeddedImage no
22181
+ 'IeImg_class' EmbeddedImageClass no
22182
+ 'IeImg_rect' EmbeddedImageRectangle no
22183
+ 'Preview' PreviewImage no
22184
+ 'Property' PreviewInfo FlashPix PreviewInfo
22185
+ 'Subimage 0000 Header' SubimageHdr FlashPix SubimageHdr
22186
+ 'WordDocument' WordDocument FlashPix WordDocument
22170
22187
 
22171
22188
  =head3 FlashPix CompObj Tags
22172
22189
 
@@ -435,11 +435,18 @@ sub AddImageDataMD5($$$)
435
435
  foreach $tagID (sort keys %$offsetInfo) {
436
436
  next unless ref $$offsetInfo{$tagID} eq 'ARRAY'; # ignore scalar tag values used for Validate
437
437
  my $tagInfo = $$offsetInfo{$tagID}[0];
438
- next unless $$tagInfo{IsImageData} and $$tagInfo{OffsetPair}; # only consider image data
438
+ next unless $$tagInfo{IsImageData}; # only consider image data
439
439
  my $sizeID = $$tagInfo{OffsetPair};
440
- next unless $sizeID and $$offsetInfo{$sizeID};
440
+ my @sizes;
441
+ if ($$tagInfo{NotRealPair}) {
442
+ @sizes = 999999999; # (Panasonic hack: raw data runs to end of file)
443
+ } elsif ($sizeID and $$offsetInfo{$sizeID}) {
444
+ @sizes = split ' ', $$offsetInfo{$sizeID}[1];
445
+ } else {
446
+ next;
447
+ }
441
448
  my @offsets = split ' ', $$offsetInfo{$tagID}[1];
442
- my @sizes = split ' ', $$offsetInfo{$sizeID}[1];
449
+ $sizes[0] = 999999999 if $$tagInfo{NotRealPair};
443
450
  my $total = 0;
444
451
  foreach $offset (@offsets) {
445
452
  my $size = shift @sizes;
@@ -455,7 +462,7 @@ sub AddImageDataMD5($$$)
455
462
  }
456
463
  if ($verbose) {
457
464
  my $name = "$$dirInfo{DirName}:$$tagInfo{Name}";
458
- $name =~ s/Offsets|Start$//;
465
+ $name =~ s/Offsets?|Start$//;
459
466
  $et->VPrint(0, "$$et{INDENT}(ImageDataMD5: $total bytes of $name data)\n");
460
467
  }
461
468
  }
@@ -1490,7 +1490,7 @@ sub WriteXMP($$;$)
1490
1490
  my @ns = sort keys %nsCur;
1491
1491
  $long[-2] .= "$nl$sp<$prop rdf:about='${about}'";
1492
1492
  # generate et:toolkit attribute if this is an exiftool RDF/XML output file
1493
- if (@ns and $nsCur{$ns[0]} =~ m{^http://ns.exiftool.(?:ca|org)/}) {
1493
+ if ($$et{XMP_NO_XMPMETA} and @ns and $nsCur{$ns[0]} =~ m{^http://ns.exiftool.(?:ca|org)/}) {
1494
1494
  $long[-2] .= "\n$sp${sp}xmlns:et='http://ns.exiftool.org/1.0/'" .
1495
1495
  " et:toolkit='Image::ExifTool $Image::ExifTool::VERSION'";
1496
1496
  }
@@ -1321,6 +1321,7 @@ sub SetNewValuesFromFile($$;@)
1321
1321
  XMPAutoConv => $$options{XMPAutoConv},
1322
1322
  );
1323
1323
  $$srcExifTool{GLOBAL_TIME_OFFSET} = $$self{GLOBAL_TIME_OFFSET};
1324
+ $$srcExifTool{ALT_EXIFTOOL} = $$self{ALT_EXIFTOOL};
1324
1325
  foreach $tag (@setTags) {
1325
1326
  next if ref $tag;
1326
1327
  if ($tag =~ /^-(.*)/) {
@@ -1387,7 +1388,7 @@ sub SetNewValuesFromFile($$;@)
1387
1388
  # transfer specified tags in the proper order
1388
1389
  #
1389
1390
  # 1) loop through input list of tags to set, and build @setList
1390
- my (@setList, $set, %setMatches, $t);
1391
+ my (@setList, $set, %setMatches, $t, %altFiles);
1391
1392
  foreach $t (@setTags) {
1392
1393
  if (ref $t eq 'HASH') {
1393
1394
  # update current options
@@ -1424,6 +1425,7 @@ sub SetNewValuesFromFile($$;@)
1424
1425
  $opt = $1 if $tag =~ s/^([-+])\s*//;
1425
1426
  }
1426
1427
  }
1428
+ $$opts{Replace} = 0 if $dstTag =~ s/^\+//;
1427
1429
  # validate tag name(s)
1428
1430
  unless ($$opts{EXPR} or ValidTagName($tag)) {
1429
1431
  $self->Warn("Invalid tag name '${tag}'. Use '=' not '<' to assign a tag value");
@@ -1440,6 +1442,8 @@ sub SetNewValuesFromFile($$;@)
1440
1442
  $$opts{Type} = 'ValueConv' if $dstTag =~ s/#$//;
1441
1443
  # replace tag name of 'all' with '*'
1442
1444
  $dstTag = '*' if $dstTag eq 'all';
1445
+ } else {
1446
+ $$opts{Replace} = 0 if $tag =~ s/^\+//;
1443
1447
  }
1444
1448
  unless ($$opts{EXPR}) {
1445
1449
  $isExclude = ($tag =~ s/^-//);
@@ -1449,7 +1453,17 @@ sub SetNewValuesFromFile($$;@)
1449
1453
  # save family/groups in list (ignoring 'all' and '*')
1450
1454
  next unless length($_) and /^(\d+)?(.*)/;
1451
1455
  my ($f, $g) = ($1, $2);
1452
- $f = 7 if $g =~ s/^ID-//i;
1456
+ $f = 7 if (not $f or $f eq '7') and $g =~ s/^ID-//i;
1457
+ if ($g =~ /^file\d+$/i and (not $f or $f eq '8')) {
1458
+ $f = 8;
1459
+ my $g8 = ucfirst $g;
1460
+ if ($$srcExifTool{ALT_EXIFTOOL}{$g8}) {
1461
+ $$opts{GROUP8} = $g8;
1462
+ $altFiles{$g8} or $altFiles{$g8} = [ ];
1463
+ # save list of requested tags for this alternate ExifTool object
1464
+ push @{$altFiles{$g8}}, "$grp:$tag";
1465
+ }
1466
+ }
1453
1467
  push @fg, [ $f, $g ] unless $g eq '*' or $g eq 'all';
1454
1468
  }
1455
1469
  }
@@ -1486,26 +1500,44 @@ sub SetNewValuesFromFile($$;@)
1486
1500
  # save in reverse order so we don't set tags before an exclude
1487
1501
  unshift @setList, [ \@fg, $tag, $dst, $opts ];
1488
1502
  }
1503
+ # 1b) copy requested tags for each alternate ExifTool object
1504
+ my $g8;
1505
+ foreach $g8 (sort keys %altFiles) {
1506
+ # request specific alternate tags to load them from the alternate ExifTool object
1507
+ my $altInfo = $srcExifTool->GetInfo($altFiles{$g8});
1508
+ # add to tags list after dummy entry to signify start of tags for this alt file
1509
+ if (%$altInfo) {
1510
+ push @tags, 'Warning DUMMY', reverse sort keys %$altInfo;
1511
+ $$info{$_} = $$altInfo{$_} foreach keys %$altInfo;
1512
+ }
1513
+ }
1489
1514
  # 2) initialize lists of matching tags for each setTag
1490
1515
  foreach $set (@setList) {
1491
1516
  $$set[2] and $setMatches{$set} = [ ];
1492
1517
  }
1493
1518
  # 3) loop through all tags in source image and save tags matching each setTag
1494
- my %rtnInfo;
1519
+ my (%rtnInfo, $isAlt);
1495
1520
  foreach $tag (@tags) {
1496
1521
  # don't try to set errors or warnings
1497
1522
  if ($tag =~ /^(Error|Warning)( |$)/) {
1498
- $rtnInfo{$tag} = $$info{$tag};
1523
+ if ($tag eq 'Warning DUMMY') {
1524
+ $isAlt = 1; # start of the alt tags
1525
+ } else {
1526
+ $rtnInfo{$tag} = $$info{$tag};
1527
+ }
1499
1528
  next;
1500
1529
  }
1501
1530
  # only set specified tags
1502
1531
  my $lcTag = lc(GetTagName($tag));
1503
1532
  my (@grp, %grp);
1504
1533
  SET: foreach $set (@setList) {
1534
+ my $opts = $$set[3];
1535
+ next if $$opts{EXPR}; # (expressions handled in step 4)
1536
+ next if $$opts{GROUP8} xor $isAlt;
1505
1537
  # check first for matching tag
1506
1538
  unless ($$set[1] eq $lcTag or $$set[1] eq '*') {
1507
1539
  # handle wildcards
1508
- next unless $$set[3]{WILD} and $lcTag =~ /^$$set[1]$/;
1540
+ next unless $$opts{WILD} and $lcTag =~ /^$$set[1]$/;
1509
1541
  }
1510
1542
  # then check for matching group
1511
1543
  if (@{$$set[0]}) {
@@ -1837,6 +1869,27 @@ sub RestoreNewValues($)
1837
1869
  $$self{DEL_GROUP} = \%delGrp;
1838
1870
  }
1839
1871
 
1872
+ #------------------------------------------------------------------------------
1873
+ # Set alternate file for extracting information
1874
+ # Inputs: 0) ExifTool ref, 1) family 8 group name (of the form "File#" where # is any number)
1875
+ # 2) alternate file name, or undef to reset
1876
+ # Returns: 1 on success, or 0 on invalid group name
1877
+ sub SetAlternateFile($$$)
1878
+ {
1879
+ my ($self, $g8, $file) = @_;
1880
+ $g8 = ucfirst lc $g8;
1881
+ return 0 unless $g8 =~ /^File\d+$/;
1882
+ # keep the same file if already initialized (possibly has metadata extracted)
1883
+ if (not defined $file) {
1884
+ delete $$self{ALT_EXIFTOOL}{$g8};
1885
+ } elsif (not ($$self{ALT_EXIFTOOL}{$g8} and $$self{ALT_EXIFTOOL}{$g8}{ALT_FILE} eq $file)) {
1886
+ my $altExifTool = Image::ExifTool->new;
1887
+ $$altExifTool{ALT_FILE} = $file;
1888
+ $$self{ALT_EXIFTOOL}{$g8} = $altExifTool;
1889
+ }
1890
+ return 1;
1891
+ }
1892
+
1840
1893
  #------------------------------------------------------------------------------
1841
1894
  # Set filesystem time from from FileModifyDate or FileCreateDate tag
1842
1895
  # Inputs: 0) ExifTool object reference, 1) file name or file ref
@@ -2724,6 +2777,7 @@ sub GetAllGroups($;$)
2724
2777
  $family == 4 and return('Copy#');
2725
2778
  $family == 5 and return('[too many possibilities to list]');
2726
2779
  $family == 6 and return(@Image::ExifTool::Exif::formatName[1..$#Image::ExifTool::Exif::formatName]);
2780
+ $family == 8 and return('File#');
2727
2781
 
2728
2782
  LoadAllTables(); # first load all our tables
2729
2783
 
@@ -3163,42 +3217,46 @@ sub InsertTagValues($$$;$$$)
3163
3217
  $tag = $docGrp . ':' . $tag;
3164
3218
  $lcTag = lc $tag;
3165
3219
  }
3220
+ my $et = $self;
3221
+ if ($tag =~ s/(\bfile\d+)://i) {
3222
+ $et = $$self{ALT_EXIFTOOL}{ucfirst lc $1} or $et=$self, $tag = 'no_alt_file';
3223
+ }
3166
3224
  if ($lcTag eq 'all') {
3167
3225
  $val = 1; # always some tag available
3168
- } elsif (defined $$self{OPTIONS}{UserParam}{$lcTag}) {
3169
- $val = $$self{OPTIONS}{UserParam}{$lcTag};
3226
+ } elsif (defined $$et{OPTIONS}{UserParam}{$lcTag}) {
3227
+ $val = $$et{OPTIONS}{UserParam}{$lcTag};
3170
3228
  } elsif ($tag =~ /(.*):(.+)/) {
3171
3229
  my $group;
3172
3230
  ($group, $tag) = ($1, $2);
3173
3231
  if (lc $tag eq 'all') {
3174
3232
  # see if any tag from the specified group exists
3175
- my $match = $self->GroupMatches($group, $foundTags);
3233
+ my $match = $et->GroupMatches($group, $foundTags);
3176
3234
  $val = $match ? 1 : 0;
3177
3235
  } else {
3178
3236
  # find the specified tag
3179
3237
  my @matches = grep /^$tag(\s|$)/i, @$foundTags;
3180
- @matches = $self->GroupMatches($group, \@matches);
3238
+ @matches = $et->GroupMatches($group, \@matches);
3181
3239
  foreach $tg (@matches) {
3182
3240
  if (defined $val and $tg =~ / \((\d+)\)$/) {
3183
3241
  # take the most recently extracted tag
3184
3242
  my $tagNum = $1;
3185
3243
  next if $tag !~ / \((\d+)\)$/ or $1 > $tagNum;
3186
3244
  }
3187
- $val = $self->GetValue($tg, $type);
3245
+ $val = $et->GetValue($tg, $type);
3188
3246
  $tag = $tg;
3189
3247
  last unless $tag =~ / /; # all done if we got our best match
3190
3248
  }
3191
3249
  }
3192
3250
  } elsif ($tag eq 'self') {
3193
- $val = $self; # ("$self{var}" or "$self->{var}" in string)
3251
+ $val = $et; # ("$self{var}" or "$file1:self{var}" in string)
3194
3252
  } else {
3195
3253
  # get the tag value
3196
- $val = $self->GetValue($tag, $type);
3254
+ $val = $et->GetValue($tag, $type);
3197
3255
  unless (defined $val) {
3198
3256
  # check for tag name with different case
3199
3257
  ($tg) = grep /^$tag$/i, @$foundTags;
3200
3258
  if (defined $tg) {
3201
- $val = $self->GetValue($tg, $type);
3259
+ $val = $et->GetValue($tg, $type);
3202
3260
  $tag = $tg;
3203
3261
  }
3204
3262
  }
@@ -50,7 +50,7 @@ use Image::ExifTool::Exif;
50
50
  use Image::ExifTool::GPS;
51
51
  require Exporter;
52
52
 
53
- $VERSION = '3.57';
53
+ $VERSION = '3.58';
54
54
  @ISA = qw(Exporter);
55
55
  @EXPORT_OK = qw(EscapeXML UnescapeXML);
56
56
 
@@ -753,6 +753,10 @@ my %sRangeMask = (
753
753
  Name => 'album',
754
754
  SubDirectory => { TagTable => 'Image::ExifTool::XMP::Album' },
755
755
  },
756
+ et => {
757
+ Name => 'et',
758
+ SubDirectory => { TagTable => 'Image::ExifTool::XMP::ExifTool' },
759
+ },
756
760
  prism => {
757
761
  Name => 'prism',
758
762
  SubDirectory => { TagTable => 'Image::ExifTool::XMP::prism' },
@@ -2550,6 +2554,14 @@ my %sPantryItem = (
2550
2554
  Notes => { },
2551
2555
  );
2552
2556
 
2557
+ # ExifTool namespace properties (et)
2558
+ %Image::ExifTool::XMP::ExifTool = (
2559
+ %xmpTableDefaults,
2560
+ GROUPS => { 1 => 'XMP-et', 2 => 'Image' },
2561
+ NAMESPACE => 'et',
2562
+ OriginalImageMD5 => { Notes => 'used to store ExifTool ImageDataMD5 digest' },
2563
+ );
2564
+
2553
2565
  # table to add tags in other namespaces
2554
2566
  %Image::ExifTool::XMP::other = (
2555
2567
  GROUPS => { 2 => 'Unknown' },
@@ -3539,6 +3551,7 @@ NoLoop:
3539
3551
  DirLen => length $$dataPt,
3540
3552
  IgnoreProp => $$subdir{IgnoreProp}, # (allow XML to ignore specified properties)
3541
3553
  IsExtended => 1, # (hack to avoid Duplicate warning for embedded XMP)
3554
+ NoStruct => 1, # (don't try to build structures since this isn't true XMP)
3542
3555
  );
3543
3556
  my $oldOrder = GetByteOrder();
3544
3557
  SetByteOrder($$subdir{ByteOrder}) if $$subdir{ByteOrder};
@@ -4375,8 +4388,10 @@ sub ProcessXMP($$;$)
4375
4388
 
4376
4389
  # restore structures if necessary
4377
4390
  if ($$et{IsStruct}) {
4378
- require 'Image/ExifTool/XMPStruct.pl';
4379
- RestoreStruct($et, $keepFlat);
4391
+ unless ($$dirInfo{NoStruct}) {
4392
+ require 'Image/ExifTool/XMPStruct.pl';
4393
+ RestoreStruct($et, $keepFlat);
4394
+ }
4380
4395
  delete $$et{IsStruct};
4381
4396
  }
4382
4397
  # reset NO_LIST flag (must do this _after_ RestoreStruct() above)
@@ -843,7 +843,8 @@ my %prismPublicationDate = (
843
843
  AVOID => 1,
844
844
  NOTES => q{
845
845
  Publishing Requirements for Industry Standard Metadata 3.0 namespace
846
- tags. (see L<http://www.prismstandard.org/>)
846
+ tags. (see
847
+ L<https://www.w3.org/Submission/2020/SUBM-prism-20200910/prism-basic.html/>)
847
848
  },
848
849
  academicField => { }, # (3.0)
849
850
  aggregateIssueNumber => { Writable => 'integer' }, # (3.0)
@@ -29,7 +29,7 @@ use vars qw($VERSION $RELEASE @ISA @EXPORT_OK %EXPORT_TAGS $AUTOLOAD @fileTypes
29
29
  %jpegMarker %specialTags %fileTypeLookup $testLen $exeDir
30
30
  %static_vars);
31
31
 
32
- $VERSION = '12.58';
32
+ $VERSION = '12.59';
33
33
  $RELEASE = '';
34
34
  @ISA = qw(Exporter);
35
35
  %EXPORT_TAGS = (
@@ -75,6 +75,7 @@ sub GetAllGroups($;$);
75
75
  sub GetNewGroups($);
76
76
  sub GetDeleteGroups();
77
77
  sub AddUserDefinedTags($%);
78
+ sub SetAlternateFile($$$);
78
79
  # non-public routines below
79
80
  sub InsertTagValues($$$;$$$);
80
81
  sub IsWritable($);
@@ -1824,8 +1825,11 @@ my %systemTagsNotes = (
1824
1825
  ImageDataMD5 => {
1825
1826
  Notes => q{
1826
1827
  MD5 of image data. Generated only if specifically requested for JPEG and
1827
- TIFF-based images, except Panasonic raw for now. Includes image data,
1828
- OtherImage and JpgFromRaw in the MD5, but not ThumbnailImage or PreviewImage
1828
+ TIFF-based images, CR3, MRW and PNG images, MOV/MP4 videos, and RIFF-based
1829
+ files. The MD5 includes the main image data, plus JpgFromRaw/OtherImage for
1830
+ some formats, but does not include ThumbnailImage or PreviewImage. Includes
1831
+ video and audio data for MOV/MP4. The L<XMP-et:OriginalImageMD5
1832
+ tag|XMP.html#ExifTool> provides a place to store these values in the file.
1829
1833
  },
1830
1834
  },
1831
1835
  );
@@ -2052,6 +2056,7 @@ sub new
2052
2056
  $$self{FILE_SEQUENCE} = 0; # sequence number for files when reading
2053
2057
  $$self{FILES_WRITTEN} = 0; # count of files successfully written
2054
2058
  $$self{INDENT2} = ''; # indentation of verbose messages from SetNewValue
2059
+ $$self{ALT_EXIFTOOL} = { }; # alternate exiftool objects
2055
2060
 
2056
2061
  # initialize our new groups for writing
2057
2062
  $self->SetNewGroups(@defaultWriteGroups);
@@ -2496,6 +2501,23 @@ sub ExtractInfo($;@)
2496
2501
  }
2497
2502
  }
2498
2503
  ++$$self{FILE_SEQUENCE}; # count files read
2504
+ # extract information from alternate files if necessary
2505
+ my ($g8, $altExifTool);
2506
+ foreach $g8 (keys %{$$self{ALT_EXIFTOOL}}) {
2507
+ $altExifTool = $$self{ALT_EXIFTOOL}{$g8};
2508
+ next if $$altExifTool{DID_EXTRACT}; # avoid extracting twice
2509
+ $$altExifTool{OPTIONS} = $$self{OPTIONS};
2510
+ $$altExifTool{GLOBAL_TIME_OFFSET} = $$self{GLOBAL_TIME_OFFSET};
2511
+ $$altExifTool{REQ_TAG_LOOKUP} = $$self{REQ_TAG_LOOKUP};
2512
+ $altExifTool->ExtractInfo($$altExifTool{ALT_FILE});
2513
+ # set family 8 group name for all tags
2514
+ foreach (keys %{$$altExifTool{VALUE}}) {
2515
+ my $ex = $$altExifTool{TAG_EXTRA}{$_};
2516
+ $ex or $ex = $$altExifTool{TAG_EXTRA}{$_} = { };
2517
+ $$ex{G8} = $g8;
2518
+ }
2519
+ $$altExifTool{DID_EXTRACT} = 1;
2520
+ }
2499
2521
  }
2500
2522
 
2501
2523
  my $filename = $$self{FILENAME}; # image file name ('' if already open)
@@ -3520,6 +3542,10 @@ sub GetGroup($$;$)
3520
3542
  $groups[6] = $$ex{G6};
3521
3543
  }
3522
3544
  }
3545
+ if ($$ex{G8}) {
3546
+ $groups[7] = '';
3547
+ $groups[8] = $$ex{G8};
3548
+ }
3523
3549
  # generate tag ID group names unless obviously not needed
3524
3550
  unless ($noID) {
3525
3551
  my $id = $$tagInfo{KeysID} || $$tagInfo{TagID};
@@ -4153,7 +4179,11 @@ sub SplitFileName($)
4153
4179
  } else {
4154
4180
  ($name = $file) =~ tr/\\/\//;
4155
4181
  # remove path
4156
- $dir = length($1) ? $1 : '/' if $name =~ s/(.*)\///;
4182
+ if ($name =~ s/(.*)\///) {
4183
+ $dir = length($1) ? $1 : '/';
4184
+ } else {
4185
+ $dir = '.';
4186
+ }
4157
4187
  }
4158
4188
  return ($dir, $name);
4159
4189
  }
@@ -4594,11 +4624,18 @@ sub SetFoundTags($)
4594
4624
  my $tagHash = $$self{VALUE};
4595
4625
  my $reqTag;
4596
4626
  foreach $reqTag (@$reqTags) {
4597
- my (@matches, $group, $allGrp, $allTag, $byValue);
4627
+ my (@matches, $group, $allGrp, $allTag, $byValue, $g8, $altOrder);
4628
+ my $et = $self;
4598
4629
  if ($reqTag =~ /^(.*):(.+)/) {
4599
4630
  ($group, $tag) = ($1, $2);
4600
4631
  if ($group =~ /^(\*|all)$/i) {
4601
4632
  $allGrp = 1;
4633
+ } elsif ($reqTag =~ /\bfile(\d+):/i) {
4634
+ $g8 = "File$1";
4635
+ $altOrder = ($1 + 1) * 100000;
4636
+ $et = $$self{ALT_EXIFTOOL}{$g8} || $self;
4637
+ $fileOrder = $$et{FILE_ORDER};
4638
+ $tagHash = $$et{VALUE};
4602
4639
  } elsif ($group !~ /^[-\w:]*$/) {
4603
4640
  $self->Warn("Invalid group name '${group}'");
4604
4641
  $group = 'invalid';
@@ -4640,7 +4677,7 @@ sub SetFoundTags($)
4640
4677
  }
4641
4678
  if (defined $group and not $allGrp) {
4642
4679
  # keep only specified group
4643
- @matches = $self->GroupMatches($group, \@matches);
4680
+ @matches = $et->GroupMatches($group, \@matches);
4644
4681
  next unless @matches or not $allTag;
4645
4682
  }
4646
4683
  if (@matches > 1) {
@@ -4649,9 +4686,9 @@ sub SetFoundTags($)
4649
4686
  # return only the highest priority tag unless duplicates wanted
4650
4687
  unless ($doDups or $allTag or $allGrp) {
4651
4688
  $tag = shift @matches;
4652
- my $oldPriority = $$self{PRIORITY}{$tag} || 1;
4689
+ my $oldPriority = $$et{PRIORITY}{$tag} || 1;
4653
4690
  foreach (@matches) {
4654
- my $priority = $$self{PRIORITY}{$_};
4691
+ my $priority = $$et{PRIORITY}{$_};
4655
4692
  $priority = 1 unless defined $priority;
4656
4693
  next unless $priority >= $oldPriority;
4657
4694
  $tag = $_;
@@ -4665,6 +4702,22 @@ sub SetFoundTags($)
4665
4702
  # bogus file order entry to avoid warning if sorting in file order
4666
4703
  $$self{FILE_ORDER}{$matches[0]} = 9999;
4667
4704
  }
4705
+ # copy over necessary information for tags from alternate files
4706
+ if ($g8) {
4707
+ my $tag;
4708
+ foreach $tag (@matches) {
4709
+ my $vtag = $tag;
4710
+ $vtag =~ s/( |$)/ #[$g8]/;
4711
+ $$self{VALUE}{$vtag} = $$et{VALUE}{$tag};
4712
+ $$self{TAG_INFO}{$vtag} = $$et{TAG_INFO}{$tag};
4713
+ $$self{TAG_EXTRA}{$vtag} = $$et{TAG_EXTRA}{$tag} || { };
4714
+ $$self{FILE_ORDER}{$vtag} = ($$et{FILE_ORDER}{$tag} || 0) + $altOrder;
4715
+ $tag = $vtag;
4716
+ }
4717
+ # restore variables to original values for main file
4718
+ $fileOrder = $$self{FILE_ORDER};
4719
+ $tagHash = $$self{VALUE};
4720
+ }
4668
4721
  # save indices of tags extracted by value
4669
4722
  push @byValue, scalar(@$rtnTags) .. (scalar(@$rtnTags)+scalar(@matches)-1) if $byValue;
4670
4723
  # save indices of wildcard tags
@@ -6347,7 +6400,6 @@ sub ProcessJPEG($$)
6347
6400
  {
6348
6401
  local $_;
6349
6402
  my ($self, $dirInfo) = @_;
6350
- my ($ch, $s, $length);
6351
6403
  my $options = $$self{OPTIONS};
6352
6404
  my $verbose = $$options{Verbose};
6353
6405
  my $out = $$options{TextOut};
@@ -6356,12 +6408,16 @@ sub ProcessJPEG($$)
6356
6408
  my $req = $$self{REQ_TAG_LOOKUP};
6357
6409
  my $htmlDump = $$self{HTML_DUMP};
6358
6410
  my %dumpParms = ( Out => $out );
6411
+ my ($ch, $s, $length, $md5, $md5size);
6359
6412
  my ($success, $wantTrailer, $trailInfo, $foundSOS, %jumbfChunk);
6360
6413
  my (@iccChunk, $iccChunkCount, $iccChunksTotal, @flirChunk, $flirCount, $flirTotal);
6361
- my ($preview, $scalado, @dqt, $subSampling, $dumpEnd, %extendedXMP, $md5);
6414
+ my ($preview, $scalado, @dqt, $subSampling, $dumpEnd, %extendedXMP);
6362
6415
 
6363
6416
  # get pointer to MD5 object if it exists and we are the top-level JPEG
6364
- $md5 = $$self{ImageDataMD5} if $$self{FILE_TYPE} eq 'JPEG' and not $$self{DOC_NUM};
6417
+ if ($$self{FILE_TYPE} eq 'JPEG' and not $$self{DOC_NUM}) {
6418
+ $md5 = $$self{ImageDataMD5};
6419
+ $md5size = 0;
6420
+ }
6365
6421
 
6366
6422
  # check to be sure this is a valid JPG (or J2C, or EXV) file
6367
6423
  return 0 unless $raf->Read($s, 2) == 2 and $s =~ /^\xff[\xd8\x4f\x01]/;
@@ -6441,8 +6497,19 @@ sub ProcessJPEG($$)
6441
6497
  $nextSegPos = $raf->Tell();
6442
6498
  $len -= 4; # subtract size of length word
6443
6499
  last unless $raf->Seek($len, 1);
6444
- } elsif ($md5 and defined $marker and ($marker == 0x00 or $marker == 0xda)) {
6445
- $md5->add($buff); # (note: this includes the terminating 0xff's)
6500
+ } elsif ($md5 and defined $marker and ($marker == 0x00 or $marker == 0xda or
6501
+ ($marker >= 0xd0 and $marker <= 0xd7)))
6502
+ {
6503
+ # calculate MD5 for image data (includes leading ff d9 but not trailing ff da)
6504
+ $md5->add("\xff" . chr($marker));
6505
+ my $n = $skipped - (length($buff) - 1); # number of extra 0xff's
6506
+ if (not $n) {
6507
+ $buff = substr($buff, 0, -1); # remove trailing 0xff
6508
+ } elsif ($n > 1) {
6509
+ $buff .= "\xff" x ($n - 1); # add back extra 0xff's
6510
+ }
6511
+ $md5->add($buff);
6512
+ $md5size += $skipped + 2;
6446
6513
  }
6447
6514
  # read second segment too if this was the first
6448
6515
  next unless defined $marker;
@@ -7457,6 +7524,8 @@ sub ProcessJPEG($$)
7457
7524
  delete $extendedXMP{$guid};
7458
7525
  }
7459
7526
  }
7527
+ # print verbose MD5 message if necessary
7528
+ print $out "$$self{INDENT}(ImageDataMD5: $md5size bytes of JPEG image data)\n" if $md5size and $verbose;
7460
7529
  # calculate JPEGDigest if requested
7461
7530
  if (@dqt) {
7462
7531
  require Image::ExifTool::JPEGDigest;