exiftool-vendored.pl 12.55.0 → 12.60.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/bin/Changes +97 -4
  2. package/bin/MANIFEST +9 -0
  3. package/bin/META.json +1 -1
  4. package/bin/META.yml +1 -1
  5. package/bin/README +19 -19
  6. package/bin/arg_files/xmp2exif.args +4 -1
  7. package/bin/config_files/example.config +1 -0
  8. package/bin/config_files/rotate_regions.config +1 -1
  9. package/bin/exiftool +197 -122
  10. package/bin/fmt_files/kml.fmt +3 -0
  11. package/bin/fmt_files/kml_track.fmt +3 -0
  12. package/bin/lib/Image/ExifTool/AIFF.pm +2 -2
  13. package/bin/lib/Image/ExifTool/APE.pm +2 -2
  14. package/bin/lib/Image/ExifTool/BuildTagLookup.pm +25 -19
  15. package/bin/lib/Image/ExifTool/Canon.pm +33 -7
  16. package/bin/lib/Image/ExifTool/CanonRaw.pm +5 -1
  17. package/bin/lib/Image/ExifTool/DJI.pm +28 -2
  18. package/bin/lib/Image/ExifTool/Exif.pm +107 -20
  19. package/bin/lib/Image/ExifTool/FlashPix.pm +92 -9
  20. package/bin/lib/Image/ExifTool/FujiFilm.pm +9 -4
  21. package/bin/lib/Image/ExifTool/GPS.pm +7 -2
  22. package/bin/lib/Image/ExifTool/Geotag.pm +30 -7
  23. package/bin/lib/Image/ExifTool/InfiRay.pm +227 -0
  24. package/bin/lib/Image/ExifTool/JPEG.pm +53 -7
  25. package/bin/lib/Image/ExifTool/Jpeg2000.pm +5 -5
  26. package/bin/lib/Image/ExifTool/LIF.pm +10 -2
  27. package/bin/lib/Image/ExifTool/LNK.pm +5 -4
  28. package/bin/lib/Image/ExifTool/MIE.pm +3 -3
  29. package/bin/lib/Image/ExifTool/MPEG.pm +2 -2
  30. package/bin/lib/Image/ExifTool/MakerNotes.pm +3 -2
  31. package/bin/lib/Image/ExifTool/Minolta.pm +6 -7
  32. package/bin/lib/Image/ExifTool/MinoltaRaw.pm +2 -1
  33. package/bin/lib/Image/ExifTool/Nikon.pm +1199 -1325
  34. package/bin/lib/Image/ExifTool/NikonCustom.pm +2 -2
  35. package/bin/lib/Image/ExifTool/NikonSettings.pm +1 -1
  36. package/bin/lib/Image/ExifTool/Olympus.pm +88 -6
  37. package/bin/lib/Image/ExifTool/OpenEXR.pm +32 -15
  38. package/bin/lib/Image/ExifTool/PNG.pm +89 -3
  39. package/bin/lib/Image/ExifTool/PanasonicRaw.pm +27 -1
  40. package/bin/lib/Image/ExifTool/Pentax.pm +8 -5
  41. package/bin/lib/Image/ExifTool/PhaseOne.pm +14 -1
  42. package/bin/lib/Image/ExifTool/Photoshop.pm +38 -7
  43. package/bin/lib/Image/ExifTool/QuickTime.pm +44 -13
  44. package/bin/lib/Image/ExifTool/QuickTimeStream.pl +63 -20
  45. package/bin/lib/Image/ExifTool/README +19 -2
  46. package/bin/lib/Image/ExifTool/RIFF.pm +34 -13
  47. package/bin/lib/Image/ExifTool/Rawzor.pm +2 -2
  48. package/bin/lib/Image/ExifTool/Real.pm +2 -2
  49. package/bin/lib/Image/ExifTool/Ricoh.pm +2 -1
  50. package/bin/lib/Image/ExifTool/Sigma.pm +5 -4
  51. package/bin/lib/Image/ExifTool/SigmaRaw.pm +9 -3
  52. package/bin/lib/Image/ExifTool/Sony.pm +28 -1
  53. package/bin/lib/Image/ExifTool/TagLookup.pm +4691 -4624
  54. package/bin/lib/Image/ExifTool/TagNames.pod +511 -117
  55. package/bin/lib/Image/ExifTool/VCard.pm +19 -5
  56. package/bin/lib/Image/ExifTool/Validate.pm +5 -5
  57. package/bin/lib/Image/ExifTool/WriteExif.pl +42 -0
  58. package/bin/lib/Image/ExifTool/WriteXMP.pl +1 -1
  59. package/bin/lib/Image/ExifTool/Writer.pl +150 -36
  60. package/bin/lib/Image/ExifTool/XMP.pm +19 -4
  61. package/bin/lib/Image/ExifTool/XMP2.pl +2 -1
  62. package/bin/lib/Image/ExifTool.pm +248 -54
  63. package/bin/lib/Image/ExifTool.pod +94 -58
  64. package/bin/perl-Image-ExifTool.spec +18 -18
  65. package/bin/pp_build_exe.args +7 -6
  66. package/package.json +3 -3
@@ -17,7 +17,7 @@ use strict;
17
17
  use vars qw($VERSION);
18
18
  use Image::ExifTool qw(:DataAccess :Utils);
19
19
 
20
- $VERSION = '1.06';
20
+ $VERSION = '1.07';
21
21
 
22
22
  my %unescapeVCard = ( '\\'=>'\\', ','=>',', 'n'=>"\n", 'N'=>"\n" );
23
23
 
@@ -190,6 +190,15 @@ my %timeInfo = (
190
190
  'X-wr-alarmuid' => 'AlarmUID',
191
191
  );
192
192
 
193
+ %Image::ExifTool::VCard::VNote = (
194
+ GROUPS => { 1 => 'VNote', 2 => 'Document' },
195
+ NOTES => 'Tags extracted from V-Note VNT files.',
196
+ Version => { },
197
+ Body => { },
198
+ Dcreated => { Name => 'CreateDate', Groups => { 2 => 'Time' }, %timeInfo },
199
+ 'Last-modified' => { Name => 'ModifyDate', Groups => { 2 => 'Time' }, %timeInfo },
200
+ );
201
+
193
202
  #------------------------------------------------------------------------------
194
203
  # Get vCard tag, creating if necessary
195
204
  # Inputs: 0) ExifTool ref, 1) tag table ref, 2) tag ID, 3) tag Name,
@@ -254,9 +263,14 @@ sub ProcessVCard($$)
254
263
  my $raf = $$dirInfo{RAF};
255
264
  my ($buff, $val, $ok, $component, %compNum, @count);
256
265
 
257
- return 0 unless $raf->Read($buff, 24) and $raf->Seek(0,0) and $buff=~/^BEGIN:(VCARD|VCALENDAR)\r\n/i;
258
- my ($type, $lbl, $tbl, $ext) = uc($1) eq 'VCARD' ? qw(VCard vCard Main VCF) : qw(ICS iCalendar VCalendar ICS);
259
- $et->SetFileType($type, undef, $ext);
266
+ return 0 unless $raf->Read($buff, 24) and $raf->Seek(0,0) and $buff=~/^BEGIN:(VCARD|VCALENDAR|VNOTE)\r\n/i;
267
+ my %info = (
268
+ VCARD => [ qw(VCard vCard Main VCF) ],
269
+ VCALENDAR => [ qw(ICS iCalendar VCalendar ICS) ],
270
+ VNOTE => [ qw(VNote vNote VNote VNT text/v-note) ],
271
+ );
272
+ my ($type, $lbl, $tbl, $ext, $mime) = @{$info{uc($1)}};
273
+ $et->SetFileType($type, $mime, $ext);
260
274
  return 1 if $$et{OPTIONS}{FastScan} and $$et{OPTIONS}{FastScan} == 3;
261
275
  local $/ = "\r\n";
262
276
  my $tagTablePtr = GetTagTable("Image::ExifTool::VCard::$tbl");
@@ -274,7 +288,7 @@ sub ProcessVCard($$)
274
288
  }
275
289
  if ($val =~ /^(BEGIN|END):(V?)(\w+)$/i) {
276
290
  my ($begin, $v, $what) = ((lc($1) eq 'begin' ? 1 : 0), $2, ucfirst lc $3);
277
- if ($what eq 'Card' or $what eq 'Calendar') {
291
+ if ($what eq 'Card' or $what eq 'Calendar' or $what eq 'Note') {
278
292
  if ($begin) {
279
293
  @count = ( { } ); # reset group counters
280
294
  } else {
@@ -17,7 +17,7 @@ package Image::ExifTool::Validate;
17
17
  use strict;
18
18
  use vars qw($VERSION %exifSpec);
19
19
 
20
- $VERSION = '1.19';
20
+ $VERSION = '1.20';
21
21
 
22
22
  use Image::ExifTool qw(:Utils);
23
23
  use Image::ExifTool::Exif;
@@ -437,8 +437,8 @@ sub ValidateExif($$$$$$$$)
437
437
  $et->Warn(sprintf('Wrong IFD for 0x%.4x %s (should be %s not %s)', $tag, $$ti{Name}, $wgp, $ifd));
438
438
  }
439
439
  }
440
- } elsif (not $otherSpec{$$et{VALUE}{FileType}} or
441
- (not $otherSpec{$$et{VALUE}{FileType}}{$tag} and not $otherSpec{$$et{VALUE}{FileType}}{All}))
440
+ } elsif (not $otherSpec{$$et{FileType}} or
441
+ (not $otherSpec{$$et{FileType}}{$tag} and not $otherSpec{$$et{FileType}}{All}))
442
442
  {
443
443
  if ($tagTablePtr eq \%Image::ExifTool::Exif::Main or $$tagInfo{Unknown}) {
444
444
  $et->Warn(sprintf('Non-standard %s tag 0x%.4x %s', $ifd, $tag, $$ti{Name}), 1);
@@ -459,8 +459,8 @@ sub ValidateExif($$$$$$$$)
459
459
  $et->Warn(sprintf('Non-standard count (%d) for %s 0x%.4x %s', $count, $ifd, $tag, $$ti{Name}));
460
460
  }
461
461
  }
462
- } elsif (not $otherSpec{$$et{VALUE}{FileType}} or
463
- (not $otherSpec{$$et{VALUE}{FileType}}{$tag} and not $otherSpec{$$et{VALUE}{FileType}}{All}))
462
+ } elsif (not $otherSpec{$$et{FileType}} or
463
+ (not $otherSpec{$$et{FileType}}{$tag} and not $otherSpec{$$et{FileType}}{All}))
464
464
  {
465
465
  $et->Warn(sprintf('Unknown %s tag 0x%.4x', $ifd, $tag), 1);
466
466
  }
@@ -419,6 +419,48 @@ sub ValidateImageData($$$;$)
419
419
  }
420
420
  }
421
421
 
422
+ #------------------------------------------------------------------------------
423
+ # Add specified image data to ImageDataMD5 hash
424
+ # Inputs: 0) ExifTool ref, 1) dirInfo ref, 2) lookup for [tagInfo,value] based on tagID
425
+ sub AddImageDataMD5($$$)
426
+ {
427
+ my ($et, $dirInfo, $offsetInfo) = @_;
428
+ my ($tagID, $offset, $buff);
429
+
430
+ my $verbose = $et->Options('Verbose');
431
+ my $md5 = $$et{ImageDataMD5};
432
+ my $raf = $$dirInfo{RAF};
433
+
434
+ foreach $tagID (sort keys %$offsetInfo) {
435
+ next unless ref $$offsetInfo{$tagID} eq 'ARRAY'; # ignore scalar tag values used for Validate
436
+ my $tagInfo = $$offsetInfo{$tagID}[0];
437
+ next unless $$tagInfo{IsImageData}; # only consider image data
438
+ my $sizeID = $$tagInfo{OffsetPair};
439
+ my @sizes;
440
+ if ($$tagInfo{NotRealPair}) {
441
+ @sizes = 999999999; # (Panasonic hack: raw data runs to end of file)
442
+ } elsif ($sizeID and $$offsetInfo{$sizeID}) {
443
+ @sizes = split ' ', $$offsetInfo{$sizeID}[1];
444
+ } else {
445
+ next;
446
+ }
447
+ my @offsets = split ' ', $$offsetInfo{$tagID}[1];
448
+ $sizes[0] = 999999999 if $$tagInfo{NotRealPair};
449
+ my $total = 0;
450
+ foreach $offset (@offsets) {
451
+ my $size = shift @sizes;
452
+ next unless $offset =~ /^\d+$/ and $size and $size =~ /^\d+$/ and $size;
453
+ next unless $raf->Seek($offset, 0); # (offset is absolute)
454
+ $total += $et->ImageDataMD5($raf, $size);
455
+ }
456
+ if ($verbose) {
457
+ my $name = "$$dirInfo{DirName}:$$tagInfo{Name}";
458
+ $name =~ s/Offsets?|Start$//;
459
+ $et->VPrint(0, "$$et{INDENT}(ImageDataMD5: $total bytes of $name data)\n");
460
+ }
461
+ }
462
+ }
463
+
422
464
  #------------------------------------------------------------------------------
423
465
  # Handle error while writing EXIF
424
466
  # Inputs: 0) ExifTool ref, 1) error string, 2) tag table ref
@@ -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
@@ -1419,9 +1420,12 @@ sub SetNewValuesFromFile($$;@)
1419
1420
  $tag =~ s/(.+?)\s*(>|<) ?//;
1420
1421
  $$opts{EXPR} = 1; # flag this expression
1421
1422
  } else {
1423
+ # (not sure why this is here because sign should be before '<')
1424
+ # (--> allows "<+" or "<-", which is an undocumented feature)
1422
1425
  $opt = $1 if $tag =~ s/^([-+])\s*//;
1423
1426
  }
1424
1427
  }
1428
+ $$opts{Replace} = 0 if $dstTag =~ s/^\+//;
1425
1429
  # validate tag name(s)
1426
1430
  unless ($$opts{EXPR} or ValidTagName($tag)) {
1427
1431
  $self->Warn("Invalid tag name '${tag}'. Use '=' not '<' to assign a tag value");
@@ -1438,6 +1442,8 @@ sub SetNewValuesFromFile($$;@)
1438
1442
  $$opts{Type} = 'ValueConv' if $dstTag =~ s/#$//;
1439
1443
  # replace tag name of 'all' with '*'
1440
1444
  $dstTag = '*' if $dstTag eq 'all';
1445
+ } else {
1446
+ $$opts{Replace} = 0 if $tag =~ s/^\+//;
1441
1447
  }
1442
1448
  unless ($$opts{EXPR}) {
1443
1449
  $isExclude = ($tag =~ s/^-//);
@@ -1447,7 +1453,17 @@ sub SetNewValuesFromFile($$;@)
1447
1453
  # save family/groups in list (ignoring 'all' and '*')
1448
1454
  next unless length($_) and /^(\d+)?(.*)/;
1449
1455
  my ($f, $g) = ($1, $2);
1450
- $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
+ }
1451
1467
  push @fg, [ $f, $g ] unless $g eq '*' or $g eq 'all';
1452
1468
  }
1453
1469
  }
@@ -1484,26 +1500,44 @@ sub SetNewValuesFromFile($$;@)
1484
1500
  # save in reverse order so we don't set tags before an exclude
1485
1501
  unshift @setList, [ \@fg, $tag, $dst, $opts ];
1486
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
+ }
1487
1514
  # 2) initialize lists of matching tags for each setTag
1488
1515
  foreach $set (@setList) {
1489
1516
  $$set[2] and $setMatches{$set} = [ ];
1490
1517
  }
1491
1518
  # 3) loop through all tags in source image and save tags matching each setTag
1492
- my %rtnInfo;
1519
+ my (%rtnInfo, $isAlt);
1493
1520
  foreach $tag (@tags) {
1494
1521
  # don't try to set errors or warnings
1495
1522
  if ($tag =~ /^(Error|Warning)( |$)/) {
1496
- $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
+ }
1497
1528
  next;
1498
1529
  }
1499
1530
  # only set specified tags
1500
1531
  my $lcTag = lc(GetTagName($tag));
1501
1532
  my (@grp, %grp);
1502
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;
1503
1537
  # check first for matching tag
1504
1538
  unless ($$set[1] eq $lcTag or $$set[1] eq '*') {
1505
1539
  # handle wildcards
1506
- next unless $$set[3]{WILD} and $lcTag =~ /^$$set[1]$/;
1540
+ next unless $$opts{WILD} and $lcTag =~ /^$$set[1]$/;
1507
1541
  }
1508
1542
  # then check for matching group
1509
1543
  if (@{$$set[0]}) {
@@ -1835,6 +1869,27 @@ sub RestoreNewValues($)
1835
1869
  $$self{DEL_GROUP} = \%delGrp;
1836
1870
  }
1837
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
+
1838
1893
  #------------------------------------------------------------------------------
1839
1894
  # Set filesystem time from from FileModifyDate or FileCreateDate tag
1840
1895
  # Inputs: 0) ExifTool object reference, 1) file name or file ref
@@ -2722,13 +2777,16 @@ sub GetAllGroups($;$)
2722
2777
  $family == 4 and return('Copy#');
2723
2778
  $family == 5 and return('[too many possibilities to list]');
2724
2779
  $family == 6 and return(@Image::ExifTool::Exif::formatName[1..$#Image::ExifTool::Exif::formatName]);
2780
+ $family == 8 and return('File#');
2725
2781
 
2726
2782
  LoadAllTables(); # first load all our tables
2727
2783
 
2728
2784
  my @tableNames = keys %allTables;
2729
2785
 
2730
- # loop through all tag tables and get all group names
2731
2786
  my %allGroups;
2787
+ # add family 1 groups not in tables
2788
+ $family == 1 and map { $allGroups{$_} = 1 } qw(Garmin);
2789
+ # loop through all tag tables and get all group names
2732
2790
  while (@tableNames) {
2733
2791
  my $table = GetTagTable(pop @tableNames);
2734
2792
  my ($grps, $grp, $tag, $tagInfo);
@@ -3159,42 +3217,46 @@ sub InsertTagValues($$$;$$$)
3159
3217
  $tag = $docGrp . ':' . $tag;
3160
3218
  $lcTag = lc $tag;
3161
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
+ }
3162
3224
  if ($lcTag eq 'all') {
3163
3225
  $val = 1; # always some tag available
3164
- } elsif (defined $$self{OPTIONS}{UserParam}{$lcTag}) {
3165
- $val = $$self{OPTIONS}{UserParam}{$lcTag};
3226
+ } elsif (defined $$et{OPTIONS}{UserParam}{$lcTag}) {
3227
+ $val = $$et{OPTIONS}{UserParam}{$lcTag};
3166
3228
  } elsif ($tag =~ /(.*):(.+)/) {
3167
3229
  my $group;
3168
3230
  ($group, $tag) = ($1, $2);
3169
3231
  if (lc $tag eq 'all') {
3170
3232
  # see if any tag from the specified group exists
3171
- my $match = $self->GroupMatches($group, $foundTags);
3233
+ my $match = $et->GroupMatches($group, $foundTags);
3172
3234
  $val = $match ? 1 : 0;
3173
3235
  } else {
3174
3236
  # find the specified tag
3175
3237
  my @matches = grep /^$tag(\s|$)/i, @$foundTags;
3176
- @matches = $self->GroupMatches($group, \@matches);
3238
+ @matches = $et->GroupMatches($group, \@matches);
3177
3239
  foreach $tg (@matches) {
3178
3240
  if (defined $val and $tg =~ / \((\d+)\)$/) {
3179
3241
  # take the most recently extracted tag
3180
3242
  my $tagNum = $1;
3181
3243
  next if $tag !~ / \((\d+)\)$/ or $1 > $tagNum;
3182
3244
  }
3183
- $val = $self->GetValue($tg, $type);
3245
+ $val = $et->GetValue($tg, $type);
3184
3246
  $tag = $tg;
3185
3247
  last unless $tag =~ / /; # all done if we got our best match
3186
3248
  }
3187
3249
  }
3188
3250
  } elsif ($tag eq 'self') {
3189
- $val = $self; # ("$self{var}" or "$self->{var}" in string)
3251
+ $val = $et; # ("$self{var}" or "$file1:self{var}" in string)
3190
3252
  } else {
3191
3253
  # get the tag value
3192
- $val = $self->GetValue($tag, $type);
3254
+ $val = $et->GetValue($tag, $type);
3193
3255
  unless (defined $val) {
3194
3256
  # check for tag name with different case
3195
3257
  ($tg) = grep /^$tag$/i, @$foundTags;
3196
3258
  if (defined $tg) {
3197
- $val = $self->GetValue($tg, $type);
3259
+ $val = $et->GetValue($tg, $type);
3198
3260
  $tag = $tg;
3199
3261
  }
3200
3262
  }
@@ -3262,15 +3324,19 @@ sub InsertTagValues($$$;$$$)
3262
3324
  undef $advFmtSelf;
3263
3325
  $didExpr = 1; # set flag indicating an expression was evaluated
3264
3326
  }
3265
- unless (defined $val or ref $opt) {
3327
+ unless (defined $val) {
3266
3328
  $val = $$self{OPTIONS}{MissingTagValue};
3267
3329
  unless (defined $val) {
3268
3330
  my $g3 = ($docGrp and $var !~ /\b(main|doc\d+):/i) ? $docGrp . ':' : '';
3269
3331
  my $msg = $didExpr ? "Advanced formatting expression returned undef for '$g3${var}'" :
3270
3332
  "Tag '$g3${var}' not defined";
3271
- no strict 'refs';
3272
- $opt and ($opt eq 'Silent' or &$opt($self, $msg, 2)) and return $$self{FMT_EXPR} = undef;
3273
- $val = '';
3333
+ if (ref $opt) {
3334
+ $self->Warn($msg,2) or $val = '';
3335
+ } elsif ($opt) {
3336
+ no strict 'refs';
3337
+ ($opt eq 'Silent' or &$opt($self, $msg, 2)) and return $$self{FMT_EXPR} = undef;
3338
+ $val = '';
3339
+ }
3274
3340
  }
3275
3341
  }
3276
3342
  if (ref $opt eq 'HASH') {
@@ -4202,7 +4268,7 @@ sub WriteDirectory($$$;$)
4202
4268
  return '' unless $dataPt or $$dirInfo{RAF}; # nothing to do if block never existed
4203
4269
  # don't allow MakerNotes to be removed from RAW files
4204
4270
  if ($blockName eq 'MakerNotes' and $rawType{$$self{FileType}}) {
4205
- $self->Warn("Can't delete MakerNotes from $$self{VALUE}{FileType}",1);
4271
+ $self->Warn("Can't delete MakerNotes from $$self{FileType}",1);
4206
4272
  return undef;
4207
4273
  }
4208
4274
  $verb = 'Deleting';
@@ -4267,7 +4333,7 @@ sub WriteDirectory($$$;$)
4267
4333
  if ($out) {
4268
4334
  print $out " Deleting $name\n" if defined $newData and not length $newData;
4269
4335
  if ($$self{CHANGED} == $oldChanged and $$self{OPTIONS}{Verbose} > 2) {
4270
- print $out "$$self{INDENT} [nothing changed in $dirName]\n";
4336
+ print $out "$$self{INDENT} [nothing changed in $name]\n";
4271
4337
  }
4272
4338
  }
4273
4339
  return $newData;
@@ -5432,7 +5498,6 @@ sub WriteJPEG($$)
5432
5498
  my $verbose = $$self{OPTIONS}{Verbose};
5433
5499
  my $out = $$self{OPTIONS}{TextOut};
5434
5500
  my $rtnVal = 0;
5435
- my %dumpParms = ( Out => $out );
5436
5501
  my ($writeBuffer, $oldOutfile); # used to buffer writing until PreviewImage position is known
5437
5502
 
5438
5503
  # check to be sure this is a valid JPG or EXV file
@@ -5447,7 +5512,6 @@ sub WriteJPEG($$)
5447
5512
  Write($outfile,"\xff\x01") or $err = 1;
5448
5513
  $isEXV = 1;
5449
5514
  }
5450
- $dumpParms{MaxLen} = 128 unless $verbose > 3;
5451
5515
 
5452
5516
  delete $$self{PREVIEW_INFO}; # reset preview information
5453
5517
  delete $$self{DEL_PREVIEW}; # reset flag to delete preview
@@ -5506,7 +5570,7 @@ sub WriteJPEG($$)
5506
5570
  $s =~ /^JFXX\0\x10/ and $dirName = 'JFXX';
5507
5571
  $s =~ /^(II|MM).{4}HEAPJPGM/s and $dirName = 'CIFF';
5508
5572
  } elsif ($marker == 0xe1) {
5509
- if ($s =~ /^(.{0,4})$exifAPP1hdr(.{1,4})/is) {
5573
+ if ($s =~ /^(.{0,4})Exif\0.(.{1,4})/is) {
5510
5574
  $dirName = 'IFD0';
5511
5575
  my ($junk, $bytes) = ($1, $2);
5512
5576
  # support multi-segment EXIF
@@ -6017,12 +6081,7 @@ sub WriteJPEG($$)
6017
6081
  #
6018
6082
  my $segDataPt = \$segData;
6019
6083
  $length = length($segData);
6020
- if ($verbose) {
6021
- print $out "JPEG $markerName ($length bytes):\n";
6022
- if ($verbose > 2 and $markerName =~ /^APP/) {
6023
- HexDump($segDataPt, undef, %dumpParms);
6024
- }
6025
- }
6084
+ print $out "JPEG $markerName ($length bytes)\n" if $verbose;
6026
6085
  # group delete of APP segments
6027
6086
  if ($$delGroup{$dirName}) {
6028
6087
  $verbose and print $out " Deleting $dirName segment\n";
@@ -6077,7 +6136,7 @@ sub WriteJPEG($$)
6077
6136
  }
6078
6137
  } elsif ($marker == 0xe1) { # APP1 (EXIF, XMP)
6079
6138
  # check for EXIF data
6080
- if ($$segDataPt =~ /^(.{0,4})$exifAPP1hdr/is) {
6139
+ if ($$segDataPt =~ /^(.{0,4})Exif\0./is) {
6081
6140
  my $hdrLen = length $exifAPP1hdr;
6082
6141
  if (length $1) {
6083
6142
  $hdrLen += length $1;
@@ -6812,6 +6871,36 @@ sub SetFileTime($$;$$$$)
6812
6871
  return 1; # (nothing to do)
6813
6872
  }
6814
6873
 
6874
+ #------------------------------------------------------------------------------
6875
+ # Add data to MD5 checksum
6876
+ # Inputs: 0) ExifTool ref, 1) RAF ref, 2) data size (or undef to read to end of file),
6877
+ # 3) data name (or undef for no warnings or messages), 4) flag for no verbose message
6878
+ # Returns: number of bytes read and MD5'd
6879
+ sub ImageDataMD5($$$;$$)
6880
+ {
6881
+ my ($self, $raf, $size, $type, $noMsg) = @_;
6882
+ my $md5 = $$self{ImageDataMD5} or return;
6883
+ my ($bytesRead, $n) = (0, 65536);
6884
+ my $buff;
6885
+ for (;;) {
6886
+ if (defined $size) {
6887
+ last unless $size;
6888
+ $n = $size > 65536 ? 65536 : $size;
6889
+ $size -= $n;
6890
+ }
6891
+ unless ($raf->Read($buff, $n)) {
6892
+ $self->Warn("Error reading $type data") if $type and defined $size;
6893
+ last;
6894
+ }
6895
+ $md5->add($buff);
6896
+ $bytesRead += length $buff;
6897
+ }
6898
+ if ($$self{OPTIONS}{Verbose} and $bytesRead and $type and not $noMsg) {
6899
+ $self->VPrint(0, "$$self{INDENT}(ImageDataMD5: $bytesRead bytes of $type data)\n");
6900
+ }
6901
+ return $bytesRead;
6902
+ }
6903
+
6815
6904
  #------------------------------------------------------------------------------
6816
6905
  # Copy data block from RAF to output file in max 64kB chunks
6817
6906
  # Inputs: 0) RAF ref, 1) outfile ref, 2) block size
@@ -6870,6 +6959,7 @@ sub WriteBinaryData($$$)
6870
6959
 
6871
6960
  # get default format ('int8u' unless specified)
6872
6961
  my $dataPt = $$dirInfo{DataPt} or return undef;
6962
+ my $dataLen = length $$dataPt;
6873
6963
  my $defaultFormat = $$tagTablePtr{FORMAT} || 'int8u';
6874
6964
  my $increment = FormatSize($defaultFormat);
6875
6965
  unless ($increment) {
@@ -6886,7 +6976,8 @@ sub WriteBinaryData($$$)
6886
6976
  delete $$dirInfo{VarFormatData};
6887
6977
  }
6888
6978
  my $dirStart = $$dirInfo{DirStart} || 0;
6889
- my $dirLen = $$dirInfo{DirLen} || length($$dataPt) - $dirStart;
6979
+ my $dirLen = $$dirInfo{DirLen};
6980
+ $dirLen = $dataLen - $dirStart if not defined $dirLen or $dirLen > $dataLen - $dirStart;
6890
6981
  my $newData = substr($$dataPt, $dirStart, $dirLen) or return undef;
6891
6982
  my $dirName = $$dirInfo{DirName};
6892
6983
  my $varSize = 0;
@@ -7022,11 +7113,34 @@ sub WriteBinaryData($$$)
7022
7113
  $tagInfo = $self->GetTagInfo($tagTablePtr, $tagID, \$v);
7023
7114
  next unless $tagInfo;
7024
7115
  }
7025
- next unless $$tagInfo{SubDirectory}; # (just to be safe)
7026
- my %subdirInfo = ( DataPt => \$newData, DirStart => $entry );
7027
- my $subTablePtr = GetTagTable($$tagInfo{SubDirectory}{TagTable});
7028
- my $dat = $self->WriteDirectory(\%subdirInfo, $subTablePtr);
7029
- substr($newData, $entry) = $dat if defined $dat and length $dat;
7116
+ my $subdir = $$tagInfo{SubDirectory} or next;
7117
+ my $start = $$subdir{Start};
7118
+ my $len;
7119
+ if (not $start) {
7120
+ $start = $entry;
7121
+ $len = $dirLen - $start;
7122
+ } elsif ($start =~ /\$/) {
7123
+ my $count = 1;
7124
+ my $format = $$tagInfo{Format} || $defaultFormat;
7125
+ $format =~ /(.*)\[(.*)\]/ and ($format, $count) = ($1, $2);
7126
+ my $val = ReadValue($dataPt, $entry, $format, $count, $dirLen - $entry);
7127
+ # ignore directories with a zero offset (ie. missing Nikon ShotInfo entries)
7128
+ next unless $val;
7129
+ my $dirStart = 0;
7130
+ #### eval Start ($val, $dirStart)
7131
+ $start = eval($start);
7132
+ next if $start < $dirStart or $start > $dataLen;
7133
+ $len = $$subdir{DirLen};
7134
+ $len = $dataLen - $start unless $len and $len <= $dataLen - $start;
7135
+ }
7136
+ my %subdirInfo = (
7137
+ DataPt => \$newData,
7138
+ DirStart => $start,
7139
+ DirLen => $len,
7140
+ TagInfo => $tagInfo,
7141
+ );
7142
+ my $dat = $self->WriteDirectory(\%subdirInfo, GetTagTable($$subdir{TagTable}));
7143
+ substr($newData, $start, $len) = $dat if defined $dat and length $dat;
7030
7144
  }
7031
7145
  }
7032
7146
  return $newData;
@@ -50,7 +50,7 @@ use Image::ExifTool::Exif;
50
50
  use Image::ExifTool::GPS;
51
51
  require Exporter;
52
52
 
53
- $VERSION = '3.56';
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' },
@@ -3097,7 +3109,7 @@ sub ScanForXMP($$)
3097
3109
  undef $buff;
3098
3110
  }
3099
3111
  }
3100
- unless ($$et{VALUE}{FileType}) {
3112
+ unless ($$et{FileType}) {
3101
3113
  $$et{FILE_TYPE} = $$et{FILE_EXT};
3102
3114
  $et->SetFileType('<unknown file containing XMP>', undef, '');
3103
3115
  }
@@ -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)