exiftool-vendored.pl 12.30.0 → 12.38.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 (57) hide show
  1. package/bin/Changes +129 -3
  2. package/bin/MANIFEST +2 -0
  3. package/bin/META.json +1 -1
  4. package/bin/META.yml +1 -1
  5. package/bin/README +2 -2
  6. package/bin/arg_files/xmp2exif.args +2 -1
  7. package/bin/config_files/example.config +1 -1
  8. package/bin/exiftool +88 -55
  9. package/bin/fmt_files/gpx.fmt +1 -1
  10. package/bin/fmt_files/gpx_wpt.fmt +1 -1
  11. package/bin/lib/Image/ExifTool/BuildTagLookup.pm +13 -3
  12. package/bin/lib/Image/ExifTool/CBOR.pm +331 -0
  13. package/bin/lib/Image/ExifTool/Canon.pm +175 -12
  14. package/bin/lib/Image/ExifTool/CanonCustom.pm +12 -2
  15. package/bin/lib/Image/ExifTool/Charset.pm +2 -0
  16. package/bin/lib/Image/ExifTool/DPX.pm +13 -2
  17. package/bin/lib/Image/ExifTool/DarwinCore.pm +2 -2
  18. package/bin/lib/Image/ExifTool/Exif.pm +109 -3
  19. package/bin/lib/Image/ExifTool/FLIR.pm +33 -8
  20. package/bin/lib/Image/ExifTool/GIF.pm +5 -1
  21. package/bin/lib/Image/ExifTool/GPS.pm +14 -10
  22. package/bin/lib/Image/ExifTool/Geotag.pm +13 -2
  23. package/bin/lib/Image/ExifTool/GoPro.pm +16 -1
  24. package/bin/lib/Image/ExifTool/ICC_Profile.pm +96 -4
  25. package/bin/lib/Image/ExifTool/JSON.pm +7 -3
  26. package/bin/lib/Image/ExifTool/Jpeg2000.pm +154 -24
  27. package/bin/lib/Image/ExifTool/M2TS.pm +27 -12
  28. package/bin/lib/Image/ExifTool/MacOS.pm +2 -2
  29. package/bin/lib/Image/ExifTool/Nikon.pm +1204 -96
  30. package/bin/lib/Image/ExifTool/NikonCustom.pm +5 -1
  31. package/bin/lib/Image/ExifTool/NikonSettings.pm +135 -71
  32. package/bin/lib/Image/ExifTool/Olympus.pm +5 -1
  33. package/bin/lib/Image/ExifTool/OpenEXR.pm +4 -2
  34. package/bin/lib/Image/ExifTool/PDF.pm +11 -12
  35. package/bin/lib/Image/ExifTool/PNG.pm +4 -1
  36. package/bin/lib/Image/ExifTool/Panasonic.pm +2 -2
  37. package/bin/lib/Image/ExifTool/Pentax.pm +2 -1
  38. package/bin/lib/Image/ExifTool/QuickTime.pm +69 -10
  39. package/bin/lib/Image/ExifTool/QuickTimeStream.pl +141 -111
  40. package/bin/lib/Image/ExifTool/README +9 -2
  41. package/bin/lib/Image/ExifTool/Sony.pm +56 -13
  42. package/bin/lib/Image/ExifTool/TagInfoXML.pm +9 -4
  43. package/bin/lib/Image/ExifTool/TagLookup.pm +6473 -5827
  44. package/bin/lib/Image/ExifTool/TagNames.pod +1401 -54
  45. package/bin/lib/Image/ExifTool/WritePDF.pl +1 -0
  46. package/bin/lib/Image/ExifTool/WritePNG.pl +2 -0
  47. package/bin/lib/Image/ExifTool/WriteQuickTime.pl +10 -0
  48. package/bin/lib/Image/ExifTool/WriteXMP.pl +10 -11
  49. package/bin/lib/Image/ExifTool/Writer.pl +50 -5
  50. package/bin/lib/Image/ExifTool/XMP.pm +125 -31
  51. package/bin/lib/Image/ExifTool/XMP2.pl +3 -1
  52. package/bin/lib/Image/ExifTool/XMPStruct.pl +3 -1
  53. package/bin/lib/Image/ExifTool.pm +89 -22
  54. package/bin/lib/Image/ExifTool.pod +21 -13
  55. package/bin/perl-Image-ExifTool.spec +1 -1
  56. package/bin/pp_build_exe.args +5 -4
  57. package/package.json +3 -3
@@ -293,6 +293,7 @@ sub WritePDF($$)
293
293
  my $newTool = new Image::ExifTool;
294
294
  $newTool->Options(List => 1);
295
295
  $newTool->Options(Password => $et->Options('Password'));
296
+ $newTool->Options(NoPDFList => $et->Options('NoPDFList'));
296
297
  $$newTool{PDF_CAPTURE} = \%capture;
297
298
  my $info = $newTool->ImageInfo($raf, 'XMP', 'PDF:*', 'Error', 'Warning');
298
299
  # not a valid PDF file unless we got a version number
@@ -179,6 +179,8 @@ sub BuildTextChunk($$$$$)
179
179
  $tag =~ s/-$lang$//; # remove language code from tagID
180
180
  } elsif ($$et{OPTIONS}{Charset} ne 'Latin' and $val =~ /[\x80-\xff]/) {
181
181
  $iTXt = 1; # write as iTXt if it contains non-Latin special characters
182
+ } elsif ($$tagInfo{iTXt}) {
183
+ $iTXt = 1; # write as iTXt if specified in user-defined tag
182
184
  }
183
185
  }
184
186
  if ($comp) {
@@ -1067,6 +1067,9 @@ sub WriteQuickTime($$$)
1067
1067
  # 3=optional base offset, 4=optional item ID)
1068
1068
  ChunkOffset => \@chunkOffset,
1069
1069
  );
1070
+ # set InPlace flag so XMP will be padded properly when
1071
+ # QuickTimePad is used if this is an XMP directory
1072
+ $subdirInfo{InPlace} = 2 if $et->Options('QuickTimePad');
1070
1073
  # pass the header pointer if necessary (for EXIF IFD's
1071
1074
  # where the Base offset is at the end of the header)
1072
1075
  if ($hdrLen and $hdrLen < $size) {
@@ -1331,6 +1334,13 @@ sub WriteQuickTime($$$)
1331
1334
  }
1332
1335
  # write the new atom if it was modified
1333
1336
  if (defined $newData) {
1337
+ my $sizeDiff = length($buff) - length($newData);
1338
+ if ($sizeDiff > 0 and $$tagInfo{PreservePadding} and $et->Options('QuickTimePad')) {
1339
+ $newData .= "\0" x $sizeDiff;
1340
+ $et->VPrint(1, " ($$tagInfo{Name} padded to original size)");
1341
+ } elsif ($sizeDiff) {
1342
+ $et->VPrint(1, " ($$tagInfo{Name} changed size)");
1343
+ }
1334
1344
  my $len = length($newData) + 8;
1335
1345
  $len > 0x7fffffff and $et->Error("$$tagInfo{Name} to large to write"), last;
1336
1346
  # update size in ChunkOffset list for modified 'uuid' atom
@@ -1418,9 +1418,15 @@ sub WriteXMP($$;$)
1418
1418
  unless ($uri) {
1419
1419
  $uri = $nsURI{$1}; # we must have added a namespace
1420
1420
  unless ($uri) {
1421
- # (namespace may be empty if trying to write empty XMP structure, forum12384)
1422
- $xmpErr = "Undefined XMP namespace: $1" if length $uri;
1423
- next;
1421
+ # (namespace prefix may be empty if trying to write empty XMP structure, forum12384)
1422
+ if (length $1) {
1423
+ my $err = "Undefined XMP namespace: $1";
1424
+ if (not $xmpErr or $err ne $xmpErr) {
1425
+ $xmpFile ? $et->Error($err) : $et->Warn($err);
1426
+ $xmpErr = $err;
1427
+ }
1428
+ }
1429
+ next;
1424
1430
  }
1425
1431
  }
1426
1432
  $nsNew{$1} = $uri;
@@ -1586,14 +1592,7 @@ sub WriteXMP($$;$)
1586
1592
  unless (%capture or $xmpFile or $$dirInfo{InPlace} or $$dirInfo{NoDelete}) {
1587
1593
  $long[-2] = '';
1588
1594
  }
1589
- if ($xmpErr) {
1590
- if ($xmpFile) {
1591
- $et->Error($xmpErr);
1592
- return -1;
1593
- }
1594
- $et->Warn($xmpErr);
1595
- return undef;
1596
- }
1595
+ return($xmpFile ? -1 : undef) if $xmpErr;
1597
1596
  $$et{CHANGED} += $changed;
1598
1597
  $debug > 1 and $long[-2] and print $long[-2],"\n";
1599
1598
  return $long[-2] unless $xmpFile;
@@ -154,7 +154,7 @@ my %delMore = (
154
154
  );
155
155
 
156
156
  # family 0 groups where directories should never be deleted
157
- my %permanentDir = ( QuickTime => 1 );
157
+ my %permanentDir = ( QuickTime => 1, Jpeg2000 => 1 );
158
158
 
159
159
  # lookup for all valid family 2 groups (lower case)
160
160
  my %family2groups = map { lc $_ => 1 } @delGroup2, 'Unknown';
@@ -1400,7 +1400,10 @@ sub SetNewValuesFromFile($$;@)
1400
1400
  }
1401
1401
  }
1402
1402
  # validate tag name(s)
1403
- $$opts{EXPR} or ValidTagName($tag) or $self->Warn("Invalid tag name '${tag}'"), next;
1403
+ unless ($$opts{EXPR} or ValidTagName($tag)) {
1404
+ $self->Warn("Invalid tag name '${tag}'. Use '=' not '<' to assign a tag value");
1405
+ next;
1406
+ }
1404
1407
  ValidTagName($dstTag) or $self->Warn("Invalid tag name '${dstTag}'"), next;
1405
1408
  # translate '+' and '-' to appropriate SetNewValue option
1406
1409
  if ($opt) {
@@ -2074,6 +2077,46 @@ sub SetSystemTags($$)
2074
2077
  last;
2075
2078
  }
2076
2079
  }
2080
+ # delete Windows Zone.Identifier if specified
2081
+ my $zhash = $self->GetNewValueHash($Image::ExifTool::Extra{ZoneIdentifier});
2082
+ if ($zhash) {
2083
+ my $res = -1;
2084
+ if ($^O ne 'MSWin32') {
2085
+ $self->Warn('ZoneIdentifer is a Windows-only tag');
2086
+ } elsif (ref $file) {
2087
+ $self->Warn('Writing ZoneIdentifer requires a file name');
2088
+ } elsif (defined $self->GetNewValue('ZoneIdentifier', \$zhash)) {
2089
+ $self->Warn('ZoneIndentifier may only be delted');
2090
+ } elsif (not eval { require Win32API::File }) {
2091
+ $self->Warn('Install Win32API::File to write ZoneIdentifier');
2092
+ } else {
2093
+ my ($wattr, $wide);
2094
+ my $zfile = "${file}:Zone.Identifier";
2095
+ if ($self->EncodeFileName($zfile)) {
2096
+ $wide = 1;
2097
+ $wattr = eval { Win32API::File::GetFileAttributesW($zfile) };
2098
+ } else {
2099
+ $wattr = eval { Win32API::File::GetFileAttributes($zfile) };
2100
+ }
2101
+ if ($wattr == Win32API::File::INVALID_FILE_ATTRIBUTES()) {
2102
+ $res = 0; # file doesn't exist, nothing to do
2103
+ } elsif ($wattr & Win32API::File::FILE_ATTRIBUTE_READONLY()) {
2104
+ $self->Warn('Zone.Identifier stream is read-only');
2105
+ } else {
2106
+ if ($wide) {
2107
+ $res = 1 if eval { Win32API::File::DeleteFileW($zfile) };
2108
+ } else {
2109
+ $res = 1 if eval { Win32API::File::DeleteFile($zfile) };
2110
+ }
2111
+ if ($res > 0) {
2112
+ $self->VPrint(0, " Deleting Zone.Identifier stream\n");
2113
+ } else {
2114
+ $self->Warn('Error deleting Zone.Identifier stream');
2115
+ }
2116
+ }
2117
+ }
2118
+ $result = $res if $res == 1 or not $result;
2119
+ }
2077
2120
  return $result;
2078
2121
  }
2079
2122
 
@@ -4735,7 +4778,7 @@ sub InverseDateTime($$;$$)
4735
4778
  my ($rtnVal, $tz);
4736
4779
  my $fmt = $$self{OPTIONS}{DateFormat};
4737
4780
  # strip off timezone first if it exists
4738
- if (not $fmt and $val =~ s/([+-])(\d{1,2}):?(\d{2})\s*(DST)?$//i) {
4781
+ if (not $fmt and $val =~ s/([-+])(\d{1,2}):?(\d{2})\s*(DST)?$//i) {
4739
4782
  $tz = sprintf("$1%.2d:$3", $2);
4740
4783
  } elsif (not $fmt and $val =~ s/Z$//i) {
4741
4784
  $tz = 'Z';
@@ -4758,6 +4801,8 @@ sub InverseDateTime($$;$$)
4758
4801
  $strptimeLib = '';
4759
4802
  }
4760
4803
  }
4804
+ # handle factional seconds (%f), but only at the end of the string
4805
+ my $fs = ($fmt =~ s/%f$// and $val =~ s/(\.\d+)\s*$//) ? $1 : '';
4761
4806
  my ($lib, $wrn, @a);
4762
4807
  TryLib: for ($lib=$strptimeLib; ; $lib='') {
4763
4808
  if (not $lib) {
@@ -4794,10 +4839,10 @@ TryLib: for ($lib=$strptimeLib; ; $lib='') {
4794
4839
  next TryLib;
4795
4840
  }
4796
4841
  } elsif (length($a[$i]) < 2) {
4797
- $$a[$i] = "0$a[$i]";# pad to 2 digits if necessary
4842
+ $a[$i] = "0$a[$i]"; # pad to 2 digits if necessary
4798
4843
  }
4799
4844
  }
4800
- $val = join(':', @a[5,4,3]) . ' ' . join(':', @a[2,1,0]);
4845
+ $val = join(':', @a[5,4,3]) . ' ' . join(':', @a[2,1,0]) . $fs;
4801
4846
  last;
4802
4847
  }
4803
4848
  }
@@ -50,7 +50,7 @@ use Image::ExifTool::Exif;
50
50
  use Image::ExifTool::GPS;
51
51
  require Exporter;
52
52
 
53
- $VERSION = '3.44';
53
+ $VERSION = '3.50';
54
54
  @ISA = qw(Exporter);
55
55
  @EXPORT_OK = qw(EscapeXML UnescapeXML);
56
56
 
@@ -436,9 +436,38 @@ my %sOECF = (
436
436
  Names => { List => 'Seq' },
437
437
  Values => { List => 'Seq', Writable => 'rational' },
438
438
  );
439
-
439
+ my %sAreaModels = (
440
+ STRUCT_NAME => 'AreaModels',
441
+ NAMESPACE => 'crs',
442
+ ColorRangeMaskAreaSampleInfo => { FlatName => 'ColorSampleInfo' },
443
+ AreaComponents => { FlatName => 'Components', List => 'Seq' },
444
+ );
445
+ my %sCorrRangeMask = (
446
+ STRUCT_NAME => 'CorrRangeMask',
447
+ NAMESPACE => 'crs',
448
+ NOTES => 'Called CorrectionRangeMask by the spec.',
449
+ Version => { },
450
+ Type => { },
451
+ ColorAmount => { Writable => 'real' },
452
+ LumMin => { Writable => 'real' },
453
+ LumMax => { Writable => 'real' },
454
+ LumFeather => { Writable => 'real' },
455
+ DepthMin => { Writable => 'real' },
456
+ DepthMax => { Writable => 'real' },
457
+ DepthFeather=> { Writable => 'real' },
458
+ # new in LR 11.0
459
+ Invert => { Writable => 'boolean' },
460
+ SampleType => { Writable => 'integer' },
461
+ AreaModels => {
462
+ List => 'Seq',
463
+ Struct => \%sAreaModels,
464
+ },
465
+ LumRange => { },
466
+ LuminanceDepthSampleInfo => { },
467
+ );
440
468
  # new LR2 crs structures (PH)
441
- my %sCorrectionMask = (
469
+ my %sCorrectionMask;
470
+ %sCorrectionMask = (
442
471
  STRUCT_NAME => 'CorrectionMask',
443
472
  NAMESPACE => 'crs',
444
473
  # disable List behaviour of flattened Gradient/PaintBasedCorrections
@@ -473,19 +502,26 @@ my %sCorrectionMask = (
473
502
  Alpha => { Writable => 'real', List => 0 },
474
503
  CenterValue => { Writable => 'real', List => 0 },
475
504
  PerimeterValue=>{ Writable => 'real', List => 0 },
476
- );
477
- my %sCorrectionRangeMask = (
478
- STRUCT_NAME => 'CorrectionRangeMask',
479
- NAMESPACE => 'crs',
480
- Version => { },
481
- Type => { },
482
- ColorAmount => { Writable => 'real' },
483
- LumMin => { Writable => 'real' },
484
- LumMax => { Writable => 'real' },
485
- LumFeather => { Writable => 'real' },
486
- DepthMin => { Writable => 'real' },
487
- DepthMax => { Writable => 'real' },
488
- DepthFeather=> { Writable => 'real' },
505
+ # new in LR 11.0 MaskGroupBasedCorrections
506
+ MaskActive => { Writable => 'boolean', List => 0 },
507
+ MaskName => { List => 0 },
508
+ MaskBlendMode=> { Writable => 'integer', List => 0 },
509
+ MaskInverted => { Writable => 'boolean', List => 0 },
510
+ MaskSyncID => { List => 0 },
511
+ MaskVersion => { List => 0 },
512
+ MaskSubType => { List => 0 },
513
+ ReferencePoint => { List => 0 },
514
+ InputDigest => { List => 0 },
515
+ MaskDigest => { List => 0 },
516
+ WholeImageArea => { List => 0 },
517
+ Origin => { List => 0 },
518
+ Masks => { Struct => \%sCorrectionMask, NoSubStruct => 1 },
519
+ CorrectionRangeMask => {
520
+ Name => 'CorrRangeMask',
521
+ Notes => 'called CorrectionRangeMask by the spec',
522
+ FlatName => 'Range',
523
+ Struct => \%sCorrRangeMask,
524
+ },
489
525
  );
490
526
  my %sCorrection = (
491
527
  STRUCT_NAME => 'Correction',
@@ -499,8 +535,8 @@ my %sCorrection = (
499
535
  LocalClarity => { FlatName => 'Clarity', Writable => 'real', List => 0 },
500
536
  LocalSharpness => { FlatName => 'Sharpness', Writable => 'real', List => 0 },
501
537
  LocalBrightness => { FlatName => 'Brightness', Writable => 'real', List => 0 },
502
- LocalToningHue => { FlatName => 'Hue', Writable => 'real', List => 0 },
503
- LocalToningSaturation => { FlatName => 'Saturation', Writable => 'real', List => 0 },
538
+ LocalToningHue => { FlatName => 'ToningHue', Writable => 'real', List => 0 },
539
+ LocalToningSaturation => { FlatName => 'ToningSaturation', Writable => 'real', List => 0 },
504
540
  LocalExposure2012 => { FlatName => 'Exposure2012', Writable => 'real', List => 0 },
505
541
  LocalContrast2012 => { FlatName => 'Contrast2012', Writable => 'real', List => 0 },
506
542
  LocalHighlights2012 => { FlatName => 'Highlights2012', Writable => 'real', List => 0 },
@@ -516,15 +552,20 @@ my %sCorrection = (
516
552
  LocalBlacks2012 => { FlatName => 'Blacks2012', Writable => 'real', List => 0 },
517
553
  LocalDehaze => { FlatName => 'Dehaze', Writable => 'real', List => 0 },
518
554
  LocalTexture => { FlatName => 'Texture', Writable => 'real', List => 0 },
555
+ # new in LR 11.0
519
556
  CorrectionRangeMask => {
557
+ Name => 'CorrRangeMask',
558
+ Notes => 'called CorrectionRangeMask by the spec',
520
559
  FlatName => 'RangeMask',
521
- Struct => \%sCorrectionRangeMask,
560
+ Struct => \%sCorrRangeMask,
522
561
  },
523
562
  CorrectionMasks => {
524
563
  FlatName => 'Mask',
525
564
  Struct => \%sCorrectionMask,
526
565
  List => 'Seq',
527
566
  },
567
+ CorrectionName => { },
568
+ CorrectionSyncID => { },
528
569
  );
529
570
  my %sRetouchArea = (
530
571
  STRUCT_NAME => 'RetouchArea',
@@ -543,6 +584,30 @@ my %sRetouchArea = (
543
584
  List => 'Seq',
544
585
  },
545
586
  );
587
+ my %sMapInfo = (
588
+ STRUCT_NAME => 'MapInfo',
589
+ NAMESPACE => 'crs',
590
+ NOTES => q{
591
+ Called RangeMaskMapInfo by the specification, the same as the containing
592
+ structure.
593
+ },
594
+ RGBMin => { },
595
+ RGBMax => { },
596
+ LabMin => { },
597
+ LabMax => { },
598
+ LumEq => { List => 'Seq' },
599
+ );
600
+ my %sRangeMask = (
601
+ STRUCT_NAME => 'RangeMask',
602
+ NAMESPACE => 'crs',
603
+ NOTES => q{
604
+ This structure is actually called RangeMaskMapInfo, but it only contains one
605
+ element which is a RangeMaskMapInfo structure (Yes, really!). So these are
606
+ renamed to RangeMask and MapInfo respectively to avoid confusion and
607
+ redundancy in the tag names.
608
+ },
609
+ RangeMaskMapInfo => { FlatName => 'MapInfo', Struct => \%sMapInfo },
610
+ );
546
611
 
547
612
  # main XMP tag table (tag ID's are used for the family 1 group names)
548
613
  %Image::ExifTool::XMP::Main = (
@@ -1533,6 +1598,9 @@ my %sPantryItem = (
1533
1598
  CameraProfile => { },
1534
1599
  LookTable => { },
1535
1600
  ToneCurvePV2012 => { List => 'Seq' },
1601
+ ToneCurvePV2012Red => { List => 'Seq' },
1602
+ ToneCurvePV2012Green => { List => 'Seq' },
1603
+ ToneCurvePV2012Blue => { List => 'Seq' },
1536
1604
  },
1537
1605
  },
1538
1606
  }
@@ -1578,6 +1646,14 @@ my %sPantryItem = (
1578
1646
  AutoToneDigestNoSat => { },
1579
1647
  ToggleStyleDigest => { },
1580
1648
  ToggleStyleAmount => { Writable => 'integer' },
1649
+ # new for LightRoom 11.0
1650
+ CompatibleVersion => { },
1651
+ MaskGroupBasedCorrections => {
1652
+ FlatName => 'MaskGroupBasedCorr',
1653
+ Struct => \%sCorrection,
1654
+ List => 'Seq',
1655
+ },
1656
+ RangeMaskMapInfo => { Name => 'RangeMask', Struct => \%sRangeMask, FlatName => 'RangeMask' },
1581
1657
  );
1582
1658
 
1583
1659
  # Tiff namespace properties (tiff)
@@ -1669,7 +1745,7 @@ my %sPantryItem = (
1669
1745
  Software => { },
1670
1746
  Artist => { Groups => { 2 => 'Author' } },
1671
1747
  Copyright => { Groups => { 2 => 'Author' }, Writable => 'lang-alt' },
1672
- NativeDigest => { }, #PH
1748
+ NativeDigest => { Avoid => 1 }, #PH
1673
1749
  );
1674
1750
 
1675
1751
  # Exif namespace properties (exif)
@@ -2058,8 +2134,8 @@ my %sPantryItem = (
2058
2134
  Groups => { 2 => 'Location' },
2059
2135
  Writable => 'integer',
2060
2136
  PrintConv => {
2061
- 2 => '2-Dimensional',
2062
- 3 => '3-Dimensional',
2137
+ 2 => '2-Dimensional Measurement',
2138
+ 3 => '3-Dimensional Measurement',
2063
2139
  },
2064
2140
  },
2065
2141
  GPSDOP => { Groups => { 2 => 'Location' }, Writable => 'rational' },
@@ -2139,8 +2215,8 @@ my %sPantryItem = (
2139
2215
  NAMESPACE => 'exifEX',
2140
2216
  PRIORITY => 0, # not as reliable as actual EXIF tags
2141
2217
  NOTES => q{
2142
- EXIF tags added by the EXIF 2.31 for XMP specification (see
2143
- L<http://www.cipa.jp/std/documents/e/DC-X010-2017.pdf>).
2218
+ EXIF tags added by the EXIF 2.32 for XMP specification (see
2219
+ L<https://cipa.jp/std/documents/download_e.html?DC-010-2020_E>).
2144
2220
  },
2145
2221
  Gamma => { Writable => 'rational' },
2146
2222
  PhotographicSensitivity => { Writable => 'integer' },
@@ -2304,6 +2380,7 @@ my %sPantryItem = (
2304
2380
  VignetteCorrectionAlreadyApplied => { Writable => 'boolean' },
2305
2381
  LateralChromaticAberrationCorrectionAlreadyApplied => { Writable => 'boolean' },
2306
2382
  LensDistortInfo => { }, # (LR 7.5.1, 4 signed rational values)
2383
+ NeutralDensityFactor => { }, # (LR 11.0 - rational value, but denominator seems significant)
2307
2384
  );
2308
2385
 
2309
2386
  # IPTC Core namespace properties (Iptc4xmpCore) (ref 4)
@@ -2346,6 +2423,9 @@ my %sPantryItem = (
2346
2423
  Scene => { Groups => { 2 => 'Other' }, List => 'Bag' },
2347
2424
  SubjectCode => { Groups => { 2 => 'Other' }, List => 'Bag' },
2348
2425
  # Copyright - have seen this in a sample (Jan 2021), but I think it is non-standard
2426
+ # new IPTC Core 1.3 properties
2427
+ AltTextAccessibility => { Groups => { 2 => 'Other' }, Writable => 'lang-alt' },
2428
+ ExtDescrAccessibility => { Groups => { 2 => 'Other' }, Writable => 'lang-alt' },
2349
2429
  );
2350
2430
 
2351
2431
  # Adobe Lightroom namespace properties (lr) (ref PH)
@@ -2357,6 +2437,7 @@ my %sPantryItem = (
2357
2437
  NOTES => 'Adobe Lightroom "lr" namespace tags.',
2358
2438
  privateRTKInfo => { },
2359
2439
  hierarchicalSubject => { List => 'Bag' },
2440
+ weightedFlatSubject => { List => 'Bag' },
2360
2441
  );
2361
2442
 
2362
2443
  # Adobe Album namespace properties (album) (ref PH)
@@ -3499,6 +3580,17 @@ sub ParseXMPElement($$$;$$$$)
3499
3580
  }
3500
3581
  $start = pos($$dataPt); # start from here the next time around
3501
3582
 
3583
+ # ignore specified XMP namespaces/properties
3584
+ if ($$et{EXCL_XMP_LOOKUP} and not $isWriting and $prop =~ /^(.+):(.*)/) {
3585
+ my ($ns, $nm) = (lc($stdXlatNS{$1} || $1), lc($2));
3586
+ if ($$et{EXCL_XMP_LOOKUP}{"xmp-$ns:all"} or $$et{EXCL_XMP_LOOKUP}{"xmp-$ns:$nm"} or
3587
+ $$et{EXCL_XMP_LOOKUP}{"xmp-all:$nm"})
3588
+ {
3589
+ ++$count; # (pretend we found something so we don't store as a tag value)
3590
+ next;
3591
+ }
3592
+ }
3593
+
3502
3594
  # extract property attributes
3503
3595
  my ($parseResource, %attrs, @attrs);
3504
3596
  while ($attrs =~ m/(\S+?)\s*=\s*(['"])(.*?)\2/sg) {
@@ -3596,9 +3688,9 @@ sub ParseXMPElement($$$;$$$$)
3596
3688
  if ($nItems == 1000) {
3597
3689
  my ($tg,$ns) = GetXMPTagID($propList);
3598
3690
  if ($isWriting) {
3599
- $et->Warn("Excessive number of items for $ns:$tg. Processing may be slow", 1);
3691
+ $et->WarnOnce("Excessive number of items for $ns:$tg. Processing may be slow", 1);
3600
3692
  } elsif (not $$et{OPTIONS}{IgnoreMinorErrors}) {
3601
- $et->Warn("Extracted only 1000 $ns:$tg items. Ignore minor errors to extract all", 2);
3693
+ $et->WarnOnce("Extracted only 1000 $ns:$tg items. Ignore minor errors to extract all", 2);
3602
3694
  last;
3603
3695
  }
3604
3696
  }
@@ -3960,7 +4052,7 @@ sub ProcessXMP($$;$)
3960
4052
  } elsif ($1 eq 'REDXIF') {
3961
4053
  $type = 'RMD';
3962
4054
  $mime = 'application/xml';
3963
- } else {
4055
+ } elsif ($1 ne 'fcpxml') { # Final Cut Pro XML
3964
4056
  return 0;
3965
4057
  }
3966
4058
  } elsif ($buf2 =~ /<svg[\s>]/) {
@@ -3970,14 +4062,16 @@ sub ProcessXMP($$;$)
3970
4062
  } elsif ($buf2 =~ /<plist[\s>]/) {
3971
4063
  $type = 'PLIST';
3972
4064
  }
3973
- if ($isSVG and $$et{XMP_CAPTURE}) {
3974
- $et->Error("ExifTool does not yet support writing of SVG images");
3975
- return 0;
3976
- }
3977
4065
  }
3978
4066
  $isXML = 1;
3979
4067
  } elsif ($2 eq '<rdf:RDF') {
3980
4068
  $isRDF = 1; # recognize XMP without x:xmpmeta element
4069
+ } elsif ($2 eq '<svg') {
4070
+ $isSVG = $isXML = 1;
4071
+ }
4072
+ if ($isSVG and $$et{XMP_CAPTURE}) {
4073
+ $et->Error("ExifTool does not yet support writing of SVG images");
4074
+ return 0;
3981
4075
  }
3982
4076
  if ($buff =~ /^\0\0/) {
3983
4077
  $fmt = 'N'; # UTF-32 MM with or without BOM
@@ -537,7 +537,7 @@ my %sImageRegion = ( # new in 1.5
537
537
  NAMESPACE => 'Iptc4xmpExt',
538
538
  TABLE_DESC => 'XMP IPTC Extension',
539
539
  NOTES => q{
540
- This table contains tags defined by the IPTC Extension schema version 1.5.
540
+ This table contains tags defined by the IPTC Extension schema version 1.6.
541
541
  The actual namespace prefix is "Iptc4xmpExt", but ExifTool shortens this for
542
542
  the family 1 group name. (see
543
543
  L<http://www.iptc.org/standards/photo-metadata/iptc-standard/>)
@@ -797,6 +797,8 @@ my %sImageRegion = ( # new in 1.5
797
797
  audioBitsPerSample => { Groups => { 2 => 'Audio' }, Writable => 'integer' },
798
798
  # new IPTC Extension schema 1.5 property
799
799
  ImageRegion => { Groups => { 2 => 'Image' }, List => 'Bag', Struct => \%sImageRegion },
800
+ # new Extension 1.6 property
801
+ EventId => { Name => 'EventID', List => 'Bag' },
800
802
  );
801
803
 
802
804
  #------------------------------------------------------------------------------
@@ -32,7 +32,9 @@ sub SerializeStruct($;$)
32
32
  my ($key, $val, @vals, $rtnVal);
33
33
 
34
34
  if (ref $obj eq 'HASH') {
35
- foreach $key (sort keys %$obj) {
35
+ # support hashes with ordered keys
36
+ my @keys = $$obj{_ordered_keys_} ? @{$$obj{_ordered_keys_}} : sort keys %$obj;
37
+ foreach $key (@keys) {
36
38
  push @vals, $key . '=' . SerializeStruct($$obj{$key}, '}');
37
39
  }
38
40
  $rtnVal = '{' . join(',', @vals) . '}';