exiftool-vendored.exe 12.56.0 → 12.62.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 (60) hide show
  1. package/bin/exiftool_files/Changes +115 -5
  2. package/bin/exiftool_files/LICENSE +674 -0
  3. package/bin/exiftool_files/README +45 -44
  4. package/bin/exiftool_files/config_files/example.config +1 -0
  5. package/bin/exiftool_files/config_files/rotate_regions.config +1 -1
  6. package/bin/exiftool_files/exiftool.pl +262 -160
  7. package/bin/exiftool_files/lib/Image/ExifTool/AIFF.pm +2 -2
  8. package/bin/exiftool_files/lib/Image/ExifTool/APE.pm +2 -2
  9. package/bin/exiftool_files/lib/Image/ExifTool/BMP.pm +0 -1
  10. package/bin/exiftool_files/lib/Image/ExifTool/BuildTagLookup.pm +23 -19
  11. package/bin/exiftool_files/lib/Image/ExifTool/Canon.pm +26 -6
  12. package/bin/exiftool_files/lib/Image/ExifTool/CanonRaw.pm +5 -1
  13. package/bin/exiftool_files/lib/Image/ExifTool/DJI.pm +28 -2
  14. package/bin/exiftool_files/lib/Image/ExifTool/Exif.pm +77 -19
  15. package/bin/exiftool_files/lib/Image/ExifTool/FlashPix.pm +33 -10
  16. package/bin/exiftool_files/lib/Image/ExifTool/FujiFilm.pm +7 -3
  17. package/bin/exiftool_files/lib/Image/ExifTool/GPS.pm +7 -2
  18. package/bin/exiftool_files/lib/Image/ExifTool/Geotag.pm +30 -7
  19. package/bin/exiftool_files/lib/Image/ExifTool/JPEG.pm +14 -2
  20. package/bin/exiftool_files/lib/Image/ExifTool/Jpeg2000.pm +36 -11
  21. package/bin/exiftool_files/lib/Image/ExifTool/LIF.pm +10 -2
  22. package/bin/exiftool_files/lib/Image/ExifTool/LNK.pm +5 -4
  23. package/bin/exiftool_files/lib/Image/ExifTool/MIE.pm +3 -3
  24. package/bin/exiftool_files/lib/Image/ExifTool/MPEG.pm +2 -2
  25. package/bin/exiftool_files/lib/Image/ExifTool/MakerNotes.pm +3 -2
  26. package/bin/exiftool_files/lib/Image/ExifTool/Minolta.pm +6 -7
  27. package/bin/exiftool_files/lib/Image/ExifTool/MinoltaRaw.pm +2 -1
  28. package/bin/exiftool_files/lib/Image/ExifTool/Nikon.pm +1005 -909
  29. package/bin/exiftool_files/lib/Image/ExifTool/NikonCustom.pm +2 -2
  30. package/bin/exiftool_files/lib/Image/ExifTool/NikonSettings.pm +1 -1
  31. package/bin/exiftool_files/lib/Image/ExifTool/Olympus.pm +88 -6
  32. package/bin/exiftool_files/lib/Image/ExifTool/PDF.pm +17 -8
  33. package/bin/exiftool_files/lib/Image/ExifTool/PNG.pm +10 -2
  34. package/bin/exiftool_files/lib/Image/ExifTool/PanasonicRaw.pm +27 -1
  35. package/bin/exiftool_files/lib/Image/ExifTool/Pentax.pm +8 -5
  36. package/bin/exiftool_files/lib/Image/ExifTool/PhaseOne.pm +14 -1
  37. package/bin/exiftool_files/lib/Image/ExifTool/Photoshop.pm +38 -7
  38. package/bin/exiftool_files/lib/Image/ExifTool/QuickTime.pm +48 -14
  39. package/bin/exiftool_files/lib/Image/ExifTool/QuickTimeStream.pl +91 -27
  40. package/bin/exiftool_files/lib/Image/ExifTool/README +19 -2
  41. package/bin/exiftool_files/lib/Image/ExifTool/RIFF.pm +34 -13
  42. package/bin/exiftool_files/lib/Image/ExifTool/Rawzor.pm +2 -2
  43. package/bin/exiftool_files/lib/Image/ExifTool/Ricoh.pm +2 -1
  44. package/bin/exiftool_files/lib/Image/ExifTool/Sigma.pm +5 -4
  45. package/bin/exiftool_files/lib/Image/ExifTool/SigmaRaw.pm +9 -3
  46. package/bin/exiftool_files/lib/Image/ExifTool/Sony.pm +39 -10
  47. package/bin/exiftool_files/lib/Image/ExifTool/TagLookup.pm +4687 -4628
  48. package/bin/exiftool_files/lib/Image/ExifTool/TagNames.pod +338 -117
  49. package/bin/exiftool_files/lib/Image/ExifTool/Validate.pm +5 -5
  50. package/bin/exiftool_files/lib/Image/ExifTool/WPG.pm +296 -0
  51. package/bin/exiftool_files/lib/Image/ExifTool/WriteExif.pl +42 -0
  52. package/bin/exiftool_files/lib/Image/ExifTool/WritePDF.pl +7 -8
  53. package/bin/exiftool_files/lib/Image/ExifTool/WriteXMP.pl +1 -1
  54. package/bin/exiftool_files/lib/Image/ExifTool/Writer.pl +162 -40
  55. package/bin/exiftool_files/lib/Image/ExifTool/XMP.pm +35 -8
  56. package/bin/exiftool_files/lib/Image/ExifTool/XMP2.pl +2 -1
  57. package/bin/exiftool_files/lib/Image/ExifTool/ZIP.pm +159 -41
  58. package/bin/exiftool_files/lib/Image/ExifTool.pm +286 -65
  59. package/bin/exiftool_files/lib/Image/ExifTool.pod +95 -51
  60. package/package.json +2 -2
@@ -1302,6 +1302,7 @@ sub SetNewValuesFromFile($$;@)
1302
1302
  MDItemTags => $$options{MDItemTags},
1303
1303
  MissingTagValue => $$options{MissingTagValue},
1304
1304
  NoPDFList => $$options{NoPDFList},
1305
+ NoWarning => $$options{NoWarning},
1305
1306
  Password => $$options{Password},
1306
1307
  PrintConv => $$options{PrintConv},
1307
1308
  QuickTimeUTC => $$options{QuickTimeUTC},
@@ -1321,6 +1322,7 @@ sub SetNewValuesFromFile($$;@)
1321
1322
  XMPAutoConv => $$options{XMPAutoConv},
1322
1323
  );
1323
1324
  $$srcExifTool{GLOBAL_TIME_OFFSET} = $$self{GLOBAL_TIME_OFFSET};
1325
+ $$srcExifTool{ALT_EXIFTOOL} = $$self{ALT_EXIFTOOL};
1324
1326
  foreach $tag (@setTags) {
1325
1327
  next if ref $tag;
1326
1328
  if ($tag =~ /^-(.*)/) {
@@ -1387,7 +1389,7 @@ sub SetNewValuesFromFile($$;@)
1387
1389
  # transfer specified tags in the proper order
1388
1390
  #
1389
1391
  # 1) loop through input list of tags to set, and build @setList
1390
- my (@setList, $set, %setMatches, $t);
1392
+ my (@setList, $set, %setMatches, $t, %altFiles);
1391
1393
  foreach $t (@setTags) {
1392
1394
  if (ref $t eq 'HASH') {
1393
1395
  # update current options
@@ -1419,9 +1421,12 @@ sub SetNewValuesFromFile($$;@)
1419
1421
  $tag =~ s/(.+?)\s*(>|<) ?//;
1420
1422
  $$opts{EXPR} = 1; # flag this expression
1421
1423
  } else {
1424
+ # (not sure why this is here because sign should be before '<')
1425
+ # (--> allows "<+" or "<-", which is an undocumented feature)
1422
1426
  $opt = $1 if $tag =~ s/^([-+])\s*//;
1423
1427
  }
1424
1428
  }
1429
+ $$opts{Replace} = 0 if $dstTag =~ s/^\+//;
1425
1430
  # validate tag name(s)
1426
1431
  unless ($$opts{EXPR} or ValidTagName($tag)) {
1427
1432
  $self->Warn("Invalid tag name '${tag}'. Use '=' not '<' to assign a tag value");
@@ -1438,6 +1443,8 @@ sub SetNewValuesFromFile($$;@)
1438
1443
  $$opts{Type} = 'ValueConv' if $dstTag =~ s/#$//;
1439
1444
  # replace tag name of 'all' with '*'
1440
1445
  $dstTag = '*' if $dstTag eq 'all';
1446
+ } else {
1447
+ $$opts{Replace} = 0 if $tag =~ s/^\+//;
1441
1448
  }
1442
1449
  unless ($$opts{EXPR}) {
1443
1450
  $isExclude = ($tag =~ s/^-//);
@@ -1447,7 +1454,17 @@ sub SetNewValuesFromFile($$;@)
1447
1454
  # save family/groups in list (ignoring 'all' and '*')
1448
1455
  next unless length($_) and /^(\d+)?(.*)/;
1449
1456
  my ($f, $g) = ($1, $2);
1450
- $f = 7 if $g =~ s/^ID-//i;
1457
+ $f = 7 if (not $f or $f eq '7') and $g =~ s/^ID-//i;
1458
+ if ($g =~ /^file\d+$/i and (not $f or $f eq '8')) {
1459
+ $f = 8;
1460
+ my $g8 = ucfirst $g;
1461
+ if ($$srcExifTool{ALT_EXIFTOOL}{$g8}) {
1462
+ $$opts{GROUP8} = $g8;
1463
+ $altFiles{$g8} or $altFiles{$g8} = [ ];
1464
+ # save list of requested tags for this alternate ExifTool object
1465
+ push @{$altFiles{$g8}}, "$grp:$tag";
1466
+ }
1467
+ }
1451
1468
  push @fg, [ $f, $g ] unless $g eq '*' or $g eq 'all';
1452
1469
  }
1453
1470
  }
@@ -1484,26 +1501,44 @@ sub SetNewValuesFromFile($$;@)
1484
1501
  # save in reverse order so we don't set tags before an exclude
1485
1502
  unshift @setList, [ \@fg, $tag, $dst, $opts ];
1486
1503
  }
1504
+ # 1b) copy requested tags for each alternate ExifTool object
1505
+ my $g8;
1506
+ foreach $g8 (sort keys %altFiles) {
1507
+ # request specific alternate tags to load them from the alternate ExifTool object
1508
+ my $altInfo = $srcExifTool->GetInfo($altFiles{$g8});
1509
+ # add to tags list after dummy entry to signify start of tags for this alt file
1510
+ if (%$altInfo) {
1511
+ push @tags, 'Warning DUMMY', reverse sort keys %$altInfo;
1512
+ $$info{$_} = $$altInfo{$_} foreach keys %$altInfo;
1513
+ }
1514
+ }
1487
1515
  # 2) initialize lists of matching tags for each setTag
1488
1516
  foreach $set (@setList) {
1489
1517
  $$set[2] and $setMatches{$set} = [ ];
1490
1518
  }
1491
1519
  # 3) loop through all tags in source image and save tags matching each setTag
1492
- my %rtnInfo;
1520
+ my (%rtnInfo, $isAlt);
1493
1521
  foreach $tag (@tags) {
1494
1522
  # don't try to set errors or warnings
1495
1523
  if ($tag =~ /^(Error|Warning)( |$)/) {
1496
- $rtnInfo{$tag} = $$info{$tag};
1524
+ if ($tag eq 'Warning DUMMY') {
1525
+ $isAlt = 1; # start of the alt tags
1526
+ } else {
1527
+ $rtnInfo{$tag} = $$info{$tag};
1528
+ }
1497
1529
  next;
1498
1530
  }
1499
1531
  # only set specified tags
1500
1532
  my $lcTag = lc(GetTagName($tag));
1501
1533
  my (@grp, %grp);
1502
1534
  SET: foreach $set (@setList) {
1535
+ my $opts = $$set[3];
1536
+ next if $$opts{EXPR}; # (expressions handled in step 4)
1537
+ next if $$opts{GROUP8} xor $isAlt;
1503
1538
  # check first for matching tag
1504
1539
  unless ($$set[1] eq $lcTag or $$set[1] eq '*') {
1505
1540
  # handle wildcards
1506
- next unless $$set[3]{WILD} and $lcTag =~ /^$$set[1]$/;
1541
+ next unless $$opts{WILD} and $lcTag =~ /^$$set[1]$/;
1507
1542
  }
1508
1543
  # then check for matching group
1509
1544
  if (@{$$set[0]}) {
@@ -1535,10 +1570,17 @@ SET: foreach $set (@setList) {
1535
1570
  # handle expressions
1536
1571
  if ($$opts{EXPR}) {
1537
1572
  my $val = $srcExifTool->InsertTagValues(\@tags, $$set[1], 'Error');
1538
- if ($$srcExifTool{VALUE}{Error}) {
1539
- # pass on any error as a warning
1540
- $tag = NextFreeTagKey(\%rtnInfo, 'Warning');
1541
- $rtnInfo{$tag} = $$srcExifTool{VALUE}{Error};
1573
+ my $err = $$srcExifTool{VALUE}{Error};
1574
+ if ($err) {
1575
+ # pass on any error as a warning unless it is suppressed
1576
+ my $noWarn = $$srcExifTool{OPTIONS}{NoWarning};
1577
+ unless ($noWarn and (eval { $err =~ /$noWarn/ } or
1578
+ # (also apply expression to warning without "[minor] " prefix)
1579
+ ($err =~ s/^\[minor\] //i and eval { $err =~ /$noWarn/ })))
1580
+ {
1581
+ $tag = NextFreeTagKey(\%rtnInfo, 'Warning');
1582
+ $rtnInfo{$tag} = $$srcExifTool{VALUE}{Error};
1583
+ }
1542
1584
  delete $$srcExifTool{VALUE}{Error};
1543
1585
  next unless defined $val;
1544
1586
  }
@@ -1835,6 +1877,27 @@ sub RestoreNewValues($)
1835
1877
  $$self{DEL_GROUP} = \%delGrp;
1836
1878
  }
1837
1879
 
1880
+ #------------------------------------------------------------------------------
1881
+ # Set alternate file for extracting information
1882
+ # Inputs: 0) ExifTool ref, 1) family 8 group name (of the form "File#" where # is any number)
1883
+ # 2) alternate file name, or undef to reset
1884
+ # Returns: 1 on success, or 0 on invalid group name
1885
+ sub SetAlternateFile($$$)
1886
+ {
1887
+ my ($self, $g8, $file) = @_;
1888
+ $g8 = ucfirst lc $g8;
1889
+ return 0 unless $g8 =~ /^File\d+$/;
1890
+ # keep the same file if already initialized (possibly has metadata extracted)
1891
+ if (not defined $file) {
1892
+ delete $$self{ALT_EXIFTOOL}{$g8};
1893
+ } elsif (not ($$self{ALT_EXIFTOOL}{$g8} and $$self{ALT_EXIFTOOL}{$g8}{ALT_FILE} eq $file)) {
1894
+ my $altExifTool = Image::ExifTool->new;
1895
+ $$altExifTool{ALT_FILE} = $file;
1896
+ $$self{ALT_EXIFTOOL}{$g8} = $altExifTool;
1897
+ }
1898
+ return 1;
1899
+ }
1900
+
1838
1901
  #------------------------------------------------------------------------------
1839
1902
  # Set filesystem time from from FileModifyDate or FileCreateDate tag
1840
1903
  # Inputs: 0) ExifTool object reference, 1) file name or file ref
@@ -2722,13 +2785,16 @@ sub GetAllGroups($;$)
2722
2785
  $family == 4 and return('Copy#');
2723
2786
  $family == 5 and return('[too many possibilities to list]');
2724
2787
  $family == 6 and return(@Image::ExifTool::Exif::formatName[1..$#Image::ExifTool::Exif::formatName]);
2788
+ $family == 8 and return('File#');
2725
2789
 
2726
2790
  LoadAllTables(); # first load all our tables
2727
2791
 
2728
2792
  my @tableNames = keys %allTables;
2729
2793
 
2730
- # loop through all tag tables and get all group names
2731
2794
  my %allGroups;
2795
+ # add family 1 groups not in tables
2796
+ $family == 1 and map { $allGroups{$_} = 1 } qw(Garmin);
2797
+ # loop through all tag tables and get all group names
2732
2798
  while (@tableNames) {
2733
2799
  my $table = GetTagTable(pop @tableNames);
2734
2800
  my ($grps, $grp, $tag, $tagInfo);
@@ -3159,42 +3225,46 @@ sub InsertTagValues($$$;$$$)
3159
3225
  $tag = $docGrp . ':' . $tag;
3160
3226
  $lcTag = lc $tag;
3161
3227
  }
3228
+ my $et = $self;
3229
+ if ($tag =~ s/(\bfile\d+)://i) {
3230
+ $et = $$self{ALT_EXIFTOOL}{ucfirst lc $1} or $et=$self, $tag = 'no_alt_file';
3231
+ }
3162
3232
  if ($lcTag eq 'all') {
3163
3233
  $val = 1; # always some tag available
3164
- } elsif (defined $$self{OPTIONS}{UserParam}{$lcTag}) {
3165
- $val = $$self{OPTIONS}{UserParam}{$lcTag};
3234
+ } elsif (defined $$et{OPTIONS}{UserParam}{$lcTag}) {
3235
+ $val = $$et{OPTIONS}{UserParam}{$lcTag};
3166
3236
  } elsif ($tag =~ /(.*):(.+)/) {
3167
3237
  my $group;
3168
3238
  ($group, $tag) = ($1, $2);
3169
3239
  if (lc $tag eq 'all') {
3170
3240
  # see if any tag from the specified group exists
3171
- my $match = $self->GroupMatches($group, $foundTags);
3241
+ my $match = $et->GroupMatches($group, $foundTags);
3172
3242
  $val = $match ? 1 : 0;
3173
3243
  } else {
3174
3244
  # find the specified tag
3175
3245
  my @matches = grep /^$tag(\s|$)/i, @$foundTags;
3176
- @matches = $self->GroupMatches($group, \@matches);
3246
+ @matches = $et->GroupMatches($group, \@matches);
3177
3247
  foreach $tg (@matches) {
3178
3248
  if (defined $val and $tg =~ / \((\d+)\)$/) {
3179
3249
  # take the most recently extracted tag
3180
3250
  my $tagNum = $1;
3181
3251
  next if $tag !~ / \((\d+)\)$/ or $1 > $tagNum;
3182
3252
  }
3183
- $val = $self->GetValue($tg, $type);
3253
+ $val = $et->GetValue($tg, $type);
3184
3254
  $tag = $tg;
3185
3255
  last unless $tag =~ / /; # all done if we got our best match
3186
3256
  }
3187
3257
  }
3188
3258
  } elsif ($tag eq 'self') {
3189
- $val = $self; # ("$self{var}" or "$self->{var}" in string)
3259
+ $val = $et; # ("$self{var}" or "$file1:self{var}" in string)
3190
3260
  } else {
3191
3261
  # get the tag value
3192
- $val = $self->GetValue($tag, $type);
3262
+ $val = $et->GetValue($tag, $type);
3193
3263
  unless (defined $val) {
3194
3264
  # check for tag name with different case
3195
3265
  ($tg) = grep /^$tag$/i, @$foundTags;
3196
3266
  if (defined $tg) {
3197
- $val = $self->GetValue($tg, $type);
3267
+ $val = $et->GetValue($tg, $type);
3198
3268
  $tag = $tg;
3199
3269
  }
3200
3270
  }
@@ -3262,15 +3332,19 @@ sub InsertTagValues($$$;$$$)
3262
3332
  undef $advFmtSelf;
3263
3333
  $didExpr = 1; # set flag indicating an expression was evaluated
3264
3334
  }
3265
- unless (defined $val or ref $opt) {
3335
+ unless (defined $val) {
3266
3336
  $val = $$self{OPTIONS}{MissingTagValue};
3267
3337
  unless (defined $val) {
3268
3338
  my $g3 = ($docGrp and $var !~ /\b(main|doc\d+):/i) ? $docGrp . ':' : '';
3269
3339
  my $msg = $didExpr ? "Advanced formatting expression returned undef for '$g3${var}'" :
3270
3340
  "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 = '';
3341
+ if (ref $opt) {
3342
+ $self->Warn($msg,2) or $val = '';
3343
+ } elsif ($opt) {
3344
+ no strict 'refs';
3345
+ ($opt eq 'Silent' or &$opt($self, $msg, 2)) and return $$self{FMT_EXPR} = undef;
3346
+ $val = '';
3347
+ }
3274
3348
  }
3275
3349
  }
3276
3350
  if (ref $opt eq 'HASH') {
@@ -4202,7 +4276,7 @@ sub WriteDirectory($$$;$)
4202
4276
  return '' unless $dataPt or $$dirInfo{RAF}; # nothing to do if block never existed
4203
4277
  # don't allow MakerNotes to be removed from RAW files
4204
4278
  if ($blockName eq 'MakerNotes' and $rawType{$$self{FileType}}) {
4205
- $self->Warn("Can't delete MakerNotes from $$self{VALUE}{FileType}",1);
4279
+ $self->Warn("Can't delete MakerNotes from $$self{FileType}",1);
4206
4280
  return undef;
4207
4281
  }
4208
4282
  $verb = 'Deleting';
@@ -4267,7 +4341,7 @@ sub WriteDirectory($$$;$)
4267
4341
  if ($out) {
4268
4342
  print $out " Deleting $name\n" if defined $newData and not length $newData;
4269
4343
  if ($$self{CHANGED} == $oldChanged and $$self{OPTIONS}{Verbose} > 2) {
4270
- print $out "$$self{INDENT} [nothing changed in $dirName]\n";
4344
+ print $out "$$self{INDENT} [nothing changed in $name]\n";
4271
4345
  }
4272
4346
  }
4273
4347
  return $newData;
@@ -5432,7 +5506,6 @@ sub WriteJPEG($$)
5432
5506
  my $verbose = $$self{OPTIONS}{Verbose};
5433
5507
  my $out = $$self{OPTIONS}{TextOut};
5434
5508
  my $rtnVal = 0;
5435
- my %dumpParms = ( Out => $out );
5436
5509
  my ($writeBuffer, $oldOutfile); # used to buffer writing until PreviewImage position is known
5437
5510
 
5438
5511
  # check to be sure this is a valid JPG or EXV file
@@ -5447,7 +5520,6 @@ sub WriteJPEG($$)
5447
5520
  Write($outfile,"\xff\x01") or $err = 1;
5448
5521
  $isEXV = 1;
5449
5522
  }
5450
- $dumpParms{MaxLen} = 128 unless $verbose > 3;
5451
5523
 
5452
5524
  delete $$self{PREVIEW_INFO}; # reset preview information
5453
5525
  delete $$self{DEL_PREVIEW}; # reset flag to delete preview
@@ -5506,7 +5578,7 @@ sub WriteJPEG($$)
5506
5578
  $s =~ /^JFXX\0\x10/ and $dirName = 'JFXX';
5507
5579
  $s =~ /^(II|MM).{4}HEAPJPGM/s and $dirName = 'CIFF';
5508
5580
  } elsif ($marker == 0xe1) {
5509
- if ($s =~ /^(.{0,4})$exifAPP1hdr(.{1,4})/is) {
5581
+ if ($s =~ /^(.{0,4})Exif\0.(.{1,4})/is) {
5510
5582
  $dirName = 'IFD0';
5511
5583
  my ($junk, $bytes) = ($1, $2);
5512
5584
  # support multi-segment EXIF
@@ -6017,12 +6089,7 @@ sub WriteJPEG($$)
6017
6089
  #
6018
6090
  my $segDataPt = \$segData;
6019
6091
  $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
- }
6092
+ print $out "JPEG $markerName ($length bytes)\n" if $verbose;
6026
6093
  # group delete of APP segments
6027
6094
  if ($$delGroup{$dirName}) {
6028
6095
  $verbose and print $out " Deleting $dirName segment\n";
@@ -6077,7 +6144,7 @@ sub WriteJPEG($$)
6077
6144
  }
6078
6145
  } elsif ($marker == 0xe1) { # APP1 (EXIF, XMP)
6079
6146
  # check for EXIF data
6080
- if ($$segDataPt =~ /^(.{0,4})$exifAPP1hdr/is) {
6147
+ if ($$segDataPt =~ /^(.{0,4})Exif\0./is) {
6081
6148
  my $hdrLen = length $exifAPP1hdr;
6082
6149
  if (length $1) {
6083
6150
  $hdrLen += length $1;
@@ -6812,6 +6879,36 @@ sub SetFileTime($$;$$$$)
6812
6879
  return 1; # (nothing to do)
6813
6880
  }
6814
6881
 
6882
+ #------------------------------------------------------------------------------
6883
+ # Add data to MD5 checksum
6884
+ # Inputs: 0) ExifTool ref, 1) RAF ref, 2) data size (or undef to read to end of file),
6885
+ # 3) data name (or undef for no warnings or messages), 4) flag for no verbose message
6886
+ # Returns: number of bytes read and MD5'd
6887
+ sub ImageDataMD5($$$;$$)
6888
+ {
6889
+ my ($self, $raf, $size, $type, $noMsg) = @_;
6890
+ my $md5 = $$self{ImageDataMD5} or return;
6891
+ my ($bytesRead, $n) = (0, 65536);
6892
+ my $buff;
6893
+ for (;;) {
6894
+ if (defined $size) {
6895
+ last unless $size;
6896
+ $n = $size > 65536 ? 65536 : $size;
6897
+ $size -= $n;
6898
+ }
6899
+ unless ($raf->Read($buff, $n)) {
6900
+ $self->Warn("Error reading $type data") if $type and defined $size;
6901
+ last;
6902
+ }
6903
+ $md5->add($buff);
6904
+ $bytesRead += length $buff;
6905
+ }
6906
+ if ($$self{OPTIONS}{Verbose} and $bytesRead and $type and not $noMsg) {
6907
+ $self->VPrint(0, "$$self{INDENT}(ImageDataMD5: $bytesRead bytes of $type data)\n");
6908
+ }
6909
+ return $bytesRead;
6910
+ }
6911
+
6815
6912
  #------------------------------------------------------------------------------
6816
6913
  # Copy data block from RAF to output file in max 64kB chunks
6817
6914
  # Inputs: 0) RAF ref, 1) outfile ref, 2) block size
@@ -6870,6 +6967,7 @@ sub WriteBinaryData($$$)
6870
6967
 
6871
6968
  # get default format ('int8u' unless specified)
6872
6969
  my $dataPt = $$dirInfo{DataPt} or return undef;
6970
+ my $dataLen = length $$dataPt;
6873
6971
  my $defaultFormat = $$tagTablePtr{FORMAT} || 'int8u';
6874
6972
  my $increment = FormatSize($defaultFormat);
6875
6973
  unless ($increment) {
@@ -6886,7 +6984,8 @@ sub WriteBinaryData($$$)
6886
6984
  delete $$dirInfo{VarFormatData};
6887
6985
  }
6888
6986
  my $dirStart = $$dirInfo{DirStart} || 0;
6889
- my $dirLen = $$dirInfo{DirLen} || length($$dataPt) - $dirStart;
6987
+ my $dirLen = $$dirInfo{DirLen};
6988
+ $dirLen = $dataLen - $dirStart if not defined $dirLen or $dirLen > $dataLen - $dirStart;
6890
6989
  my $newData = substr($$dataPt, $dirStart, $dirLen) or return undef;
6891
6990
  my $dirName = $$dirInfo{DirName};
6892
6991
  my $varSize = 0;
@@ -7022,11 +7121,34 @@ sub WriteBinaryData($$$)
7022
7121
  $tagInfo = $self->GetTagInfo($tagTablePtr, $tagID, \$v);
7023
7122
  next unless $tagInfo;
7024
7123
  }
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;
7124
+ my $subdir = $$tagInfo{SubDirectory} or next;
7125
+ my $start = $$subdir{Start};
7126
+ my $len;
7127
+ if (not $start) {
7128
+ $start = $entry;
7129
+ $len = $dirLen - $start;
7130
+ } elsif ($start =~ /\$/) {
7131
+ my $count = 1;
7132
+ my $format = $$tagInfo{Format} || $defaultFormat;
7133
+ $format =~ /(.*)\[(.*)\]/ and ($format, $count) = ($1, $2);
7134
+ my $val = ReadValue($dataPt, $entry, $format, $count, $dirLen - $entry);
7135
+ # ignore directories with a zero offset (ie. missing Nikon ShotInfo entries)
7136
+ next unless $val;
7137
+ my $dirStart = 0;
7138
+ #### eval Start ($val, $dirStart)
7139
+ $start = eval($start);
7140
+ next if $start < $dirStart or $start > $dataLen;
7141
+ $len = $$subdir{DirLen};
7142
+ $len = $dataLen - $start unless $len and $len <= $dataLen - $start;
7143
+ }
7144
+ my %subdirInfo = (
7145
+ DataPt => \$newData,
7146
+ DirStart => $start,
7147
+ DirLen => $len,
7148
+ TagInfo => $tagInfo,
7149
+ );
7150
+ my $dat = $self->WriteDirectory(\%subdirInfo, GetTagTable($$subdir{TagTable}));
7151
+ substr($newData, $start, $len) = $dat if defined $dat and length $dat;
7030
7152
  }
7031
7153
  }
7032
7154
  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.59';
54
54
  @ISA = qw(Exporter);
55
55
  @EXPORT_OK = qw(EscapeXML UnescapeXML);
56
56
 
@@ -248,7 +248,11 @@ my %boolConv = (
248
248
 
249
249
  # XMP namespaces which we don't want to contribute to generated EXIF tag names
250
250
  # (Note: namespaces with non-standard prefixes aren't currently ignored)
251
- my %ignoreNamespace = ( 'x'=>1, rdf=>1, xmlns=>1, xml=>1, svg=>1, et=>1, office=>1 );
251
+ my %ignoreNamespace = ( 'x'=>1, rdf=>1, xmlns=>1, xml=>1, svg=>1, office=>1 );
252
+
253
+ # ExifTool properties that don't generate tag names (et:tagid is historic)
254
+ my %ignoreEtProp = ( 'et:desc'=>1, 'et:prt'=>1, 'et:val'=>1 , 'et:id'=>1, 'et:tagid'=>1,
255
+ 'et:toolkit'=>1, 'et:table'=>1, 'et:index'=>1 );
252
256
 
253
257
  # XMP properties to ignore (set dynamically via dirInfo IgnoreProp)
254
258
  my %ignoreProp;
@@ -753,6 +757,10 @@ my %sRangeMask = (
753
757
  Name => 'album',
754
758
  SubDirectory => { TagTable => 'Image::ExifTool::XMP::Album' },
755
759
  },
760
+ et => {
761
+ Name => 'et',
762
+ SubDirectory => { TagTable => 'Image::ExifTool::XMP::ExifTool' },
763
+ },
756
764
  prism => {
757
765
  Name => 'prism',
758
766
  SubDirectory => { TagTable => 'Image::ExifTool::XMP::prism' },
@@ -2481,6 +2489,9 @@ my %sPantryItem = (
2481
2489
  EnhanceSuperResolutionAlreadyApplied => { Writable => 'boolean' },
2482
2490
  EnhanceSuperResolutionVersion => { }, # integer?
2483
2491
  EnhanceSuperResolutionScale => { Writable => 'rational' },
2492
+ EnhanceDenoiseAlreadyApplied => { Writable => 'boolean' }, #forum14760
2493
+ EnhanceDenoiseVersion => { }, #forum14760 integer?
2494
+ EnhanceDenoiseLumaAmount => { }, #forum14760 integer?
2484
2495
  );
2485
2496
 
2486
2497
  # IPTC Core namespace properties (Iptc4xmpCore) (ref 4)
@@ -2550,6 +2561,14 @@ my %sPantryItem = (
2550
2561
  Notes => { },
2551
2562
  );
2552
2563
 
2564
+ # ExifTool namespace properties (et)
2565
+ %Image::ExifTool::XMP::ExifTool = (
2566
+ %xmpTableDefaults,
2567
+ GROUPS => { 1 => 'XMP-et', 2 => 'Image' },
2568
+ NAMESPACE => 'et',
2569
+ OriginalImageMD5 => { Notes => 'used to store ExifTool ImageDataMD5 digest' },
2570
+ );
2571
+
2553
2572
  # table to add tags in other namespaces
2554
2573
  %Image::ExifTool::XMP::other = (
2555
2574
  GROUPS => { 2 => 'Unknown' },
@@ -2838,7 +2857,7 @@ sub GetXMPTagID($;$$)
2838
2857
  # split name into namespace and property name
2839
2858
  # (Note: namespace can be '' for property qualifiers)
2840
2859
  my ($ns, $nm) = ($prop =~ /(.*?):(.*)/) ? ($1, $2) : ('', $prop);
2841
- if ($ignoreNamespace{$ns} or $ignoreProp{$prop}) {
2860
+ if ($ignoreNamespace{$ns} or $ignoreProp{$prop} or $ignoreEtProp{$prop}) {
2842
2861
  # special case: don't ignore rdf numbered items
2843
2862
  # (not technically allowed in XMP, but used in RDF/XML)
2844
2863
  unless ($prop =~ /^rdf:(_\d+)$/) {
@@ -3097,7 +3116,7 @@ sub ScanForXMP($$)
3097
3116
  undef $buff;
3098
3117
  }
3099
3118
  }
3100
- unless ($$et{VALUE}{FileType}) {
3119
+ unless ($$et{FileType}) {
3101
3120
  $$et{FILE_TYPE} = $$et{FILE_EXT};
3102
3121
  $et->SetFileType('<unknown file containing XMP>', undef, '');
3103
3122
  }
@@ -3408,7 +3427,10 @@ NoLoop:
3408
3427
  my %grps = ( 0 => $1, 1 => $2 );
3409
3428
  # apply a little magic to recover original group names
3410
3429
  # from this exiftool-written RDF/XML file
3411
- if ($grps{1} =~ /^\d/) {
3430
+ if ($grps{1} eq 'System') {
3431
+ $grps{1} = 'XML-System';
3432
+ $grps{0} = 'XML';
3433
+ } elsif ($grps{1} =~ /^\d/) {
3412
3434
  # URI's with only family 0 are internal tags from the source file,
3413
3435
  # so change the group name to avoid confusion with tags from this file
3414
3436
  $grps{1} = "XML-$grps{0}";
@@ -3539,6 +3561,7 @@ NoLoop:
3539
3561
  DirLen => length $$dataPt,
3540
3562
  IgnoreProp => $$subdir{IgnoreProp}, # (allow XML to ignore specified properties)
3541
3563
  IsExtended => 1, # (hack to avoid Duplicate warning for embedded XMP)
3564
+ NoStruct => 1, # (don't try to build structures since this isn't true XMP)
3542
3565
  );
3543
3566
  my $oldOrder = GetByteOrder();
3544
3567
  SetByteOrder($$subdir{ByteOrder}) if $$subdir{ByteOrder};
@@ -3875,7 +3898,9 @@ sub ParseXMPElement($$$;$$$$)
3875
3898
  }
3876
3899
  }
3877
3900
  my $shortVal = $attrs{$shortName};
3878
- if ($ignoreNamespace{$ns} or $ignoreProp{$prop}) {
3901
+ # Note: $prop is the containing property in this loop (not the shorthand property)
3902
+ # so $ignoreProp ignores all attributes of the ignored property
3903
+ if ($ignoreNamespace{$ns} or $ignoreProp{$prop} or $ignoreEtProp{$propName}) {
3879
3904
  $ignored = $propName;
3880
3905
  # handle special attributes (extract as tags only once if not empty)
3881
3906
  if (ref $recognizedAttrs{$propName} and $shortVal) {
@@ -4375,8 +4400,10 @@ sub ProcessXMP($$;$)
4375
4400
 
4376
4401
  # restore structures if necessary
4377
4402
  if ($$et{IsStruct}) {
4378
- require 'Image/ExifTool/XMPStruct.pl';
4379
- RestoreStruct($et, $keepFlat);
4403
+ unless ($$dirInfo{NoStruct}) {
4404
+ require 'Image/ExifTool/XMPStruct.pl';
4405
+ RestoreStruct($et, $keepFlat);
4406
+ }
4380
4407
  delete $$et{IsStruct};
4381
4408
  }
4382
4409
  # 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)