exiftool_vendored 11.99.0 → 12.11.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 (67) hide show
  1. checksums.yaml +4 -4
  2. data/bin/Changes +201 -2
  3. data/bin/MANIFEST +8 -0
  4. data/bin/META.json +1 -1
  5. data/bin/META.yml +1 -1
  6. data/bin/README +43 -42
  7. data/bin/exiftool +172 -99
  8. data/bin/lib/Image/ExifTool.pm +170 -117
  9. data/bin/lib/Image/ExifTool.pod +132 -97
  10. data/bin/lib/Image/ExifTool/AIFF.pm +2 -2
  11. data/bin/lib/Image/ExifTool/APE.pm +2 -2
  12. data/bin/lib/Image/ExifTool/BuildTagLookup.pm +21 -10
  13. data/bin/lib/Image/ExifTool/Canon.pm +202 -13
  14. data/bin/lib/Image/ExifTool/CanonCustom.pm +82 -16
  15. data/bin/lib/Image/ExifTool/DPX.pm +56 -2
  16. data/bin/lib/Image/ExifTool/DarwinCore.pm +22 -3
  17. data/bin/lib/Image/ExifTool/EXE.pm +8 -5
  18. data/bin/lib/Image/ExifTool/Exif.pm +15 -6
  19. data/bin/lib/Image/ExifTool/Font.pm +9 -2
  20. data/bin/lib/Image/ExifTool/GIF.pm +6 -1
  21. data/bin/lib/Image/ExifTool/GeoTiff.pm +2 -0
  22. data/bin/lib/Image/ExifTool/Geotag.pm +2 -2
  23. data/bin/lib/Image/ExifTool/GoPro.pm +48 -22
  24. data/bin/lib/Image/ExifTool/H264.pm +1 -1
  25. data/bin/lib/Image/ExifTool/ID3.pm +86 -12
  26. data/bin/lib/Image/ExifTool/IPTC.pm +1 -0
  27. data/bin/lib/Image/ExifTool/Import.pm +12 -9
  28. data/bin/lib/Image/ExifTool/JSON.pm +27 -4
  29. data/bin/lib/Image/ExifTool/Lang/de.pm +3 -1
  30. data/bin/lib/Image/ExifTool/Lang/es.pm +1 -1
  31. data/bin/lib/Image/ExifTool/M2TS.pm +1 -1
  32. data/bin/lib/Image/ExifTool/MPF.pm +2 -2
  33. data/bin/lib/Image/ExifTool/MacOS.pm +154 -38
  34. data/bin/lib/Image/ExifTool/Matroska.pm +3 -1
  35. data/bin/lib/Image/ExifTool/Minolta.pm +7 -2
  36. data/bin/lib/Image/ExifTool/Nikon.pm +143 -17
  37. data/bin/lib/Image/ExifTool/Olympus.pm +40 -17
  38. data/bin/lib/Image/ExifTool/PNG.pm +14 -3
  39. data/bin/lib/Image/ExifTool/PPM.pm +5 -5
  40. data/bin/lib/Image/ExifTool/Panasonic.pm +148 -14
  41. data/bin/lib/Image/ExifTool/PanasonicRaw.pm +34 -0
  42. data/bin/lib/Image/ExifTool/Parrot.pm +2 -1
  43. data/bin/lib/Image/ExifTool/Pentax.pm +11 -3
  44. data/bin/lib/Image/ExifTool/Photoshop.pm +2 -1
  45. data/bin/lib/Image/ExifTool/QuickTime.pm +240 -37
  46. data/bin/lib/Image/ExifTool/QuickTimeStream.pl +419 -60
  47. data/bin/lib/Image/ExifTool/README +25 -21
  48. data/bin/lib/Image/ExifTool/RSRC.pm +17 -11
  49. data/bin/lib/Image/ExifTool/Radiance.pm +7 -2
  50. data/bin/lib/Image/ExifTool/Ricoh.pm +19 -1
  51. data/bin/lib/Image/ExifTool/Shift.pl +1 -0
  52. data/bin/lib/Image/ExifTool/SigmaRaw.pm +40 -33
  53. data/bin/lib/Image/ExifTool/Sony.pm +423 -39
  54. data/bin/lib/Image/ExifTool/Stim.pm +2 -2
  55. data/bin/lib/Image/ExifTool/TagLookup.pm +5798 -5675
  56. data/bin/lib/Image/ExifTool/TagNames.pod +575 -100
  57. data/bin/lib/Image/ExifTool/Validate.pm +4 -4
  58. data/bin/lib/Image/ExifTool/WriteExif.pl +1 -0
  59. data/bin/lib/Image/ExifTool/WriteQuickTime.pl +30 -21
  60. data/bin/lib/Image/ExifTool/Writer.pl +49 -24
  61. data/bin/lib/Image/ExifTool/XMP.pm +99 -17
  62. data/bin/lib/Image/ExifTool/XMP2.pl +1 -0
  63. data/bin/lib/Image/ExifTool/XMPStruct.pl +3 -1
  64. data/bin/lib/Image/ExifTool/ZISRAW.pm +123 -0
  65. data/bin/perl-Image-ExifTool.spec +42 -41
  66. data/lib/exiftool_vendored/version.rb +1 -1
  67. metadata +9 -8
@@ -17,7 +17,7 @@ package Image::ExifTool::Validate;
17
17
  use strict;
18
18
  use vars qw($VERSION %exifSpec);
19
19
 
20
- $VERSION = '1.17';
20
+ $VERSION = '1.18';
21
21
 
22
22
  use Image::ExifTool qw(:Utils);
23
23
  use Image::ExifTool::Exif;
@@ -214,11 +214,11 @@ my %validValue = (
214
214
  IFD0 => {
215
215
  0x100 => 'defined $val', # ImageWidth
216
216
  0x101 => 'defined $val', # ImageLength
217
- 0x102 => 'defined $val', # BitsPerSample
217
+ # (default is 1) 0x102 => 'defined $val', # BitsPerSample
218
218
  0x103 => q{
219
219
  not defined $val or $val =~ /^(1|5|6|32773)$/ or
220
220
  ($val == 2 and (not defined $val{0x102} or $val{0x102} == 1));
221
- }, # Compression
221
+ }, # Compression
222
222
  0x106 => '$val =~ /^[0123]$/', # PhotometricInterpretation
223
223
  0x111 => 'defined $val', # StripOffsets
224
224
  # SamplesPerPixel
@@ -237,7 +237,7 @@ my %validValue = (
237
237
  0x117 => 'defined $val', # StripByteCounts
238
238
  0x11a => 'defined $val', # XResolution
239
239
  0x11b => 'defined $val', # YResolution
240
- 0x128 => '$val =~ /^[123]$/', # ResolutionUnit
240
+ 0x128 => 'not defined $val or $val =~ /^[123]$/', # ResolutionUnit
241
241
  # ColorMap (must be palette image with correct number of colors)
242
242
  0x140 => q{
243
243
  return '' if defined $val{0x106} and $val{0x106} == 3 xor defined $val;
@@ -1832,6 +1832,7 @@ NoOverwrite: next if $isNew > 0;
1832
1832
  warn "Internal error writing offsets for $$newInfo{Name}\n";
1833
1833
  return undef;
1834
1834
  }
1835
+ $newValuePt = \$newValue;
1835
1836
  }
1836
1837
  $offsetInfo or $offsetInfo = $offsetInfo[$ifd] = { };
1837
1838
  # save location of valuePtr in new directory
@@ -144,7 +144,7 @@ sub PrintInvGPSCoordinates($)
144
144
  $v[2] = Image::ExifTool::ToFloat($v[2]) * ($below ? -1 : 1) if @v == 3;
145
145
  return "@v";
146
146
  }
147
- return $val if $val =~ /^([-+]\d+(\.\d*)?){2,3}(CRS.*)?$/; # already in ISO6709 format?
147
+ return $val if $val =~ /^([-+]\d+(\.\d*)?){2,3}(CRS.*)?\/?$/; # already in ISO6709 format?
148
148
  return undef;
149
149
  }
150
150
 
@@ -159,15 +159,16 @@ sub ConvInvISO6709($)
159
159
  my @a = split ' ', $val;
160
160
  if (@a == 2 or @a == 3) {
161
161
  # latitude must have 2 digits before the decimal, and longitude 3,
162
- # and all values must start with a "+" or "-"
163
- my @fmt = ('%s%02d','%s%03d','%s%d');
162
+ # and all values must start with a "+" or "-", and Google Photos
163
+ # requires at least 3 digits after the decimal point
164
+ my @fmt = ('%s%02d.%s%s','%s%03d.%s%s','%s%d.%s%s');
164
165
  foreach (@a) {
165
166
  return undef unless Image::ExifTool::IsFloat($_);
166
- $_ =~ s/^([-+]?)(\d+)/sprintf(shift(@fmt), $1 || '+', $2)/e;
167
+ $_ =~ s/^([-+]?)(\d+)\.?(\d*)/sprintf(shift(@fmt),$1||'+',$2,$3,length($3)<3 ? '0'x(3-length($3)) : '')/e;
167
168
  }
168
- return join '', @a;
169
+ return join '', @a, '/';
169
170
  }
170
- return $val if $val =~ /^([-+]\d+(\.\d*)?){2,3}(CRS.*)?$/; # already in ISO6709 format?
171
+ return $val if $val =~ /^([-+]\d+(\.\d*)?){2,3}(CRS.*)?\/?$/; # already in ISO6709 format?
171
172
  return undef;
172
173
  }
173
174
 
@@ -300,15 +301,19 @@ sub CheckQTValue($$$)
300
301
 
301
302
  #------------------------------------------------------------------------------
302
303
  # Format QuickTime value for writing
303
- # Inputs: 0) ExifTool ref, 1) value ref, 2) Format (or undef)
304
+ # Inputs: 0) ExifTool ref, 1) value ref, 2) Format (or undef), 3) Writable (or undef)
304
305
  # Returns: Flags for QT data type, and reformats value as required
305
- sub FormatQTValue($$;$)
306
+ sub FormatQTValue($$;$$)
306
307
  {
307
- my ($et, $valPt, $format) = @_;
308
+ my ($et, $valPt, $format, $writable) = @_;
308
309
  my $flags;
309
310
  if ($format and $format ne 'string') {
310
311
  $$valPt = WriteValue($$valPt, $format);
311
- $flags = $qtFormat{$format} || 0;
312
+ if ($writable and $qtFormat{$writable}) {
313
+ $flags = $qtFormat{$writable};
314
+ } else {
315
+ $flags = $qtFormat{$format} || 0;
316
+ }
312
317
  } elsif ($$valPt =~ /^\xff\xd8\xff/) {
313
318
  $flags = 0x0d; # JPG
314
319
  } elsif ($$valPt =~ /^(\x89P|\x8aM|\x8bJ)NG\r\n\x1a\n/) {
@@ -1122,9 +1127,9 @@ sub WriteQuickTime($$$)
1122
1127
  my $newVal = $et->GetNewValue($nvHash);
1123
1128
  next unless defined $newVal;
1124
1129
  my $prVal = $newVal;
1125
- my $flags = FormatQTValue($et, \$newVal, $format);
1130
+ my $flags = FormatQTValue($et, \$newVal, $format, $$tagInfo{Writable});
1126
1131
  next unless defined $newVal;
1127
- my ($ctry, $lang) = (0, $undLang);
1132
+ my ($ctry, $lang) = (0, 0);
1128
1133
  if ($$ti{LangCode}) {
1129
1134
  unless ($$ti{LangCode} =~ /^([A-Z]{3})?[-_]?([A-Z]{2})?$/i) {
1130
1135
  $et->Warn("Invalid language code for $$ti{Name}");
@@ -1180,7 +1185,11 @@ sub WriteQuickTime($$$)
1180
1185
  } else {
1181
1186
  if ($format) {
1182
1187
  # update flags for the format we are writing
1183
- $flags = $qtFormat{$format} if $qtFormat{$format};
1188
+ if ($$tagInfo{Writable} and $qtFormat{$$tagInfo{Writable}}) {
1189
+ $flags = $qtFormat{$$tagInfo{Writable}};
1190
+ } elsif ($qtFormat{$format}) {
1191
+ $flags = $qtFormat{$format};
1192
+ }
1184
1193
  } else {
1185
1194
  $format = QuickTimeFormat($flags, $len);
1186
1195
  }
@@ -1199,12 +1208,13 @@ sub WriteQuickTime($$$)
1199
1208
  }
1200
1209
  my $prVal = $newVal;
1201
1210
  # format new value for writing (and get new flags)
1202
- $flags = FormatQTValue($et, \$newVal, $format);
1211
+ $flags = FormatQTValue($et, \$newVal, $format, $$tagInfo{Writable});
1203
1212
  my $grp = $et->GetGroup($langInfo, 1);
1204
1213
  $et->VerboseValue("- $grp:$$langInfo{Name}", $val);
1205
1214
  $et->VerboseValue("+ $grp:$$langInfo{Name}", $prVal);
1206
1215
  $newData = substr($buff, 0, $pos-16) unless defined $newData;
1207
- $newData .= pack('Na4Nnn', length($newVal)+16, $type, $flags, $ctry, $lang);
1216
+ my $wLang = $lang eq $undLang ? 0 : $lang;
1217
+ $newData .= pack('Na4Nnn', length($newVal)+16, $type, $flags, $ctry, $wLang);
1208
1218
  $newData .= $newVal;
1209
1219
  ++$$et{CHANGED};
1210
1220
  } elsif (defined $newData) {
@@ -1268,10 +1278,11 @@ sub WriteQuickTime($$$)
1268
1278
  # add back necessary header and encode as necessary
1269
1279
  if (defined $lang) {
1270
1280
  $newData = $et->Encode($newData, $lang < 0x400 ? $charsetQuickTime : 'UTF8');
1281
+ my $wLang = $lang eq $undLang ? 0 : $lang;
1271
1282
  if ($$tagInfo{IText} and $$tagInfo{IText} == 6) {
1272
- $newData = pack('Nn', 0, $lang) . $newData . "\0";
1283
+ $newData = pack('Nn', 0, $wLang) . $newData . "\0";
1273
1284
  } else {
1274
- $newData = pack('nn', length($newData), $lang) . $newData;
1285
+ $newData = pack('nn', length($newData), $wLang) . $newData;
1275
1286
  }
1276
1287
  } elsif (not $format or $format =~ /^string/ and
1277
1288
  not $$tagInfo{Binary} and not $$tagInfo{ValueConv})
@@ -1406,9 +1417,9 @@ sub WriteQuickTime($$$)
1406
1417
  my $newVal = $et->GetNewValue($nvHash);
1407
1418
  next unless defined $newVal;
1408
1419
  my $prVal = $newVal;
1409
- my $flags = FormatQTValue($et, \$newVal, $$tagInfo{Format});
1420
+ my $flags = FormatQTValue($et, \$newVal, $$tagInfo{Format}, $$tagInfo{Writable});
1410
1421
  next unless defined $newVal;
1411
- my ($ctry, $lang) = (0,0);
1422
+ my ($ctry, $lang) = (0, 0);
1412
1423
  # handle alternate languages
1413
1424
  if ($$tagInfo{LangCode}) {
1414
1425
  $tag = substr($tag, 0, 4); # strip language code from tag ID
@@ -1424,10 +1435,8 @@ sub WriteQuickTime($$$)
1424
1435
  }
1425
1436
  if ($$dirInfo{HasData}) {
1426
1437
  # add 'data' header
1427
- $lang or $lang = $undLang;
1428
1438
  $newVal = pack('Na4Nnn',16+length($newVal),'data',$flags,$ctry,$lang).$newVal;
1429
1439
  } elsif ($tag =~ /^\xa9/ or $$tagInfo{IText}) {
1430
- $lang or $lang = $undLang;
1431
1440
  if ($ctry) {
1432
1441
  my $grp = $et->GetGroup($tagInfo,1);
1433
1442
  $et->Warn("Can't use country code for $grp:$$tagInfo{Name}");
@@ -359,21 +359,25 @@ sub SetNewValue($;$$%)
359
359
  my $convType = $options{Type} || ($$self{OPTIONS}{PrintConv} ? 'PrintConv' : 'ValueConv');
360
360
 
361
361
  # filter value if necessary
362
- $self->Filter($$self{OPTIONS}{FilterW}, \$value) if $convType eq 'PrintConv';
362
+ $self->Filter($$self{OPTIONS}{FilterW}, \$value) or return 0 if $convType eq 'PrintConv';
363
363
 
364
364
  my (@wantGroup, $family2);
365
365
  my $wantGroup = $options{Group};
366
366
  if ($wantGroup) {
367
367
  foreach (split /:/, $wantGroup) {
368
368
  next unless length($_) and /^(\d+)?(.*)/; # separate family number and group name
369
- my ($f, $g) = ($1, lc $2);
369
+ my ($f, $g) = ($1, $2);
370
+ my $lcg = lc $g;
370
371
  # save group/family unless '*' or 'all'
371
- push @wantGroup, [ $f, $g ] unless $g eq '*' or $g eq 'all';
372
- if (defined $f) {
373
- $f > 2 and return 0; # only allow family 0, 1 or 2
374
- $family2 = 1 if $f == 2; # set flag indicating family 2 was used
372
+ push @wantGroup, [ $f, $lcg ] unless $lcg eq '*' or $lcg eq 'all';
373
+ if ($g =~ s/^ID-//i) { # family 7 is a tag ID
374
+ return 0 if defined $f and $f ne 7;
375
+ $wantGroup[-1] = [ 7, $g ]; # group name with 'ID-' removed and case preserved
376
+ } elsif (defined $f) {
377
+ $f > 2 and return 0; # only allow family 0, 1 or 2
378
+ $family2 = 1 if $f == 2; # set flag indicating family 2 was used
375
379
  } else {
376
- $family2 = 1 if $family2groups{$g};
380
+ $family2 = 1 if $family2groups{$lcg};
377
381
  }
378
382
  }
379
383
  undef $wantGroup unless @wantGroup;
@@ -622,6 +626,8 @@ TAG: foreach $tagInfo (@matchingTags) {
622
626
  next;
623
627
  }
624
628
  next if $lcWant eq lc $grp[2];
629
+ } elsif ($fam == 7) {
630
+ next if IsSameID($$tagInfo{TagID}, $lcWant);
625
631
  } elsif ($fam != 1 and not $$tagInfo{AllowGroup}) {
626
632
  next if $lcWant eq lc $grp[$fam];
627
633
  if ($wgAll and not $fam and $allFam0{$lcWant}) {
@@ -1258,6 +1264,7 @@ sub SetNewValuesFromFile($$;@)
1258
1264
  Filter => $$options{Filter},
1259
1265
  FixBase => $$options{FixBase},
1260
1266
  GlobalTimeShift => $$options{GlobalTimeShift},
1267
+ HexTagIDs => $$options{HexTagIDs},
1261
1268
  IgnoreMinorErrors=>$$options{IgnoreMinorErrors},
1262
1269
  Lang => $$options{Lang},
1263
1270
  LargeFileSupport=> $$options{LargeFileSupport},
@@ -1409,7 +1416,9 @@ sub SetNewValuesFromFile($$;@)
1409
1416
  foreach (split /:/, $grp) {
1410
1417
  # save family/groups in list (ignoring 'all' and '*')
1411
1418
  next unless length($_) and /^(\d+)?(.*)/;
1412
- push @fg, [ $1, $2 ] unless $2 eq '*' or $2 eq 'all';
1419
+ my ($f, $g) = ($1, $2);
1420
+ $f = 7 if $g =~ s/^ID-//i;
1421
+ push @fg, [ $f, $g ] unless $g eq '*' or $g eq 'all';
1413
1422
  }
1414
1423
  }
1415
1424
  # allow ValueConv to be specified by a '#' on the tag name
@@ -1475,10 +1484,12 @@ SET: foreach $set (@setList) {
1475
1484
  }
1476
1485
  foreach (@{$$set[0]}) {
1477
1486
  my ($f, $g) = @$_;
1478
- if (defined $f) {
1479
- next SET unless defined $grp[$f] and $g eq $grp[$f];
1480
- } else {
1487
+ if (not defined $f) {
1481
1488
  next SET unless $grp{$g};
1489
+ } elsif ($f == 7) {
1490
+ next SET unless IsSameID($srcExifTool->GetTagID($tag), $g);
1491
+ } else {
1492
+ next SET unless defined $grp[$f] and $g eq $grp[$f];
1482
1493
  }
1483
1494
  }
1484
1495
  }
@@ -1598,21 +1609,25 @@ sub GetNewValue($$;$)
1598
1609
  $nvHash = $self->GetNewValueHash($tagInfo);
1599
1610
  } else {
1600
1611
  # separate group from tag name
1601
- $group = $1 if $tag =~ s/(.*)://;
1612
+ my @groups;
1613
+ @groups = split ':', $1 if $tag =~ s/(.*)://;
1602
1614
  my @tagInfoList = FindTagInfo($tag);
1603
1615
  # decide which tag we want
1604
1616
  GNV_TagInfo: foreach $tagInfo (@tagInfoList) {
1605
1617
  my $nvh = $self->GetNewValueHash($tagInfo) or next;
1606
- # select tag in specified group if necessary
1607
- while ($group and $group ne $$nvh{WriteGroup}) {
1618
+ # select tag in specified group(s) if necessary
1619
+ foreach (@groups) {
1620
+ next if $_ eq $$nvh{WriteGroup};
1608
1621
  my @grps = $self->GetGroup($tagInfo);
1609
1622
  if ($grps[0] eq $$nvh{WriteGroup}) {
1610
1623
  # check family 1 group only if WriteGroup is not specific
1611
- last if $group eq $grps[1];
1624
+ next if $_ eq $grps[1];
1612
1625
  } else {
1613
1626
  # otherwise check family 0 group
1614
- last if $group eq $grps[0];
1627
+ next if $_ eq $grps[0];
1615
1628
  }
1629
+ # also check family 7
1630
+ next if /^ID-(.*)/i and IsSameID($$tagInfo{TagID}, $1);
1616
1631
  # step to next entry in list
1617
1632
  $nvh = $$nvh{Next} or next GNV_TagInfo;
1618
1633
  }
@@ -2007,7 +2022,7 @@ sub SetFileName($$;$$$)
2007
2022
 
2008
2023
  #------------------------------------------------------------------------------
2009
2024
  # Set file permissions, group/user id and various MDItem tags from new tag values
2010
- # Inputs: 0) Exiftool ref, 1) file name or glob (must be a name for MDItem tags)
2025
+ # Inputs: 0) ExifTool ref, 1) file name or glob (must be a name for MDItem tags)
2011
2026
  # Returns: 1=something was set OK, 0=didn't try, -1=error (and warning set)
2012
2027
  # Notes: There may be errors even if 1 is returned
2013
2028
  sub SetSystemTags($$)
@@ -3269,7 +3284,7 @@ sub IsSameFile($$$)
3269
3284
 
3270
3285
  #------------------------------------------------------------------------------
3271
3286
  # Is this a raw file type?
3272
- # Inputs: 0) Exiftool ref
3287
+ # Inputs: 0) ExifTool ref
3273
3288
  # Returns: true if FileType is a type of RAW image
3274
3289
  sub IsRawType($)
3275
3290
  {
@@ -4020,6 +4035,7 @@ sub WriteDirectory($$$;$)
4020
4035
  if ($dataPt or $$dirInfo{RAF}) {
4021
4036
  ++$$self{CHANGED};
4022
4037
  $out and print $out " Deleting $grp1\n";
4038
+ $self->Warn('ICC_Profile deleted. Image colors may be affected') if $grp1 eq 'ICC_Profile';
4023
4039
  # can no longer validate TIFF_END if deleting an entire IFD
4024
4040
  delete $$self{TIFF_END} if $dirName =~ /IFD/;
4025
4041
  }
@@ -4691,17 +4707,17 @@ sub InverseDateTime($$;$$)
4691
4707
  {
4692
4708
  my ($self, $val, $tzFlag, $dateOnly) = @_;
4693
4709
  my ($rtnVal, $tz);
4710
+ my $fmt = $$self{OPTIONS}{DateFormat};
4694
4711
  # strip off timezone first if it exists
4695
- if ($val =~ s/([+-])(\d{1,2}):?(\d{2})\s*(DST)?$//i) {
4712
+ if (not $fmt and $val =~ s/([+-])(\d{1,2}):?(\d{2})\s*(DST)?$//i) {
4696
4713
  $tz = sprintf("$1%.2d:$3", $2);
4697
- } elsif ($val =~ s/Z$//i) {
4714
+ } elsif (not $fmt and $val =~ s/Z$//i) {
4698
4715
  $tz = 'Z';
4699
4716
  } else {
4700
4717
  $tz = '';
4701
4718
  # allow special value of 'now'
4702
4719
  return $self->TimeNow($tzFlag) if lc($val) eq 'now';
4703
4720
  }
4704
- my $fmt = $$self{OPTIONS}{DateFormat};
4705
4721
  # only convert date if a format was specified and the date is recognizable
4706
4722
  if ($fmt) {
4707
4723
  unless (defined $strptimeLib) {
@@ -5525,7 +5541,11 @@ sub WriteJPEG($$)
5525
5541
  my $buff = $self->WriteDirectory(\%dirInfo, $tagTablePtr, \&WriteTIFF);
5526
5542
  if (defined $buff and length $buff) {
5527
5543
  if (length($buff) + length($exifAPP1hdr) > $maxSegmentLen) {
5528
- $self->Warn('Creating multi-segment EXIF',1);
5544
+ if ($self->Options('NoMultiExif')) {
5545
+ $self->Error('EXIF is too large for JPEG segment');
5546
+ } else {
5547
+ $self->Warn('Creating multi-segment EXIF',1);
5548
+ }
5529
5549
  }
5530
5550
  # switch to buffered output if required
5531
5551
  if (($$self{PREVIEW_INFO} or $$self{LeicaTrailer}) and not $oldOutfile) {
@@ -5891,6 +5911,7 @@ sub WriteJPEG($$)
5891
5911
  # group delete of APP segments
5892
5912
  if ($$delGroup{$dirName}) {
5893
5913
  $verbose and print $out " Deleting $dirName segment\n";
5914
+ $self->Warn('ICC_Profile deleted. Image colors may be affected') if $dirName eq 'ICC_Profile';
5894
5915
  ++$$self{CHANGED};
5895
5916
  next Marker;
5896
5917
  }
@@ -5997,7 +6018,11 @@ sub WriteJPEG($$)
5997
6018
  # delete segment if IFD contains no entries
5998
6019
  length $$segDataPt or $del = 1, last;
5999
6020
  if (length($$segDataPt) + length($exifAPP1hdr) > $maxSegmentLen) {
6000
- $self->Warn('Writing multi-segment EXIF',1);
6021
+ if ($self->Options('NoMultiExif')) {
6022
+ $self->Error('EXIF is too large for JPEG segment');
6023
+ } else {
6024
+ $self->Warn('Writing multi-segment EXIF',1);
6025
+ }
6001
6026
  }
6002
6027
  # switch to buffered output if required
6003
6028
  if (($$self{PREVIEW_INFO} or $$self{LeicaTrailer}) and not $oldOutfile) {
@@ -6779,7 +6804,7 @@ sub WriteBinaryData($$$)
6779
6804
  my $val = ReadValue($dataPt, $entry, $format, $count, $dirLen-$entry);
6780
6805
  next unless defined $val;
6781
6806
  my $nvHash = $self->GetNewValueHash($tagInfo, $$self{CUR_WRITE_GROUP});
6782
- next unless $self->IsOverwriting($nvHash, $val);
6807
+ next unless $self->IsOverwriting($nvHash, $val) > 0;
6783
6808
  my $newVal = $self->GetNewValue($nvHash);
6784
6809
  next unless defined $newVal; # can't delete from a binary table
6785
6810
  # update DataMember with new value if necessary
@@ -25,7 +25,8 @@
25
25
  # 10) http://www.adobe.com/devnet/xmp/pdfs/XMPSpecificationPart2.pdf (Oct 2008)
26
26
  # 11) http://www.extensis.com/en/support/kb_article.jsp?articleNumber=6102211
27
27
  # 12) http://www.cipa.jp/std/documents/e/DC-010-2012_E.pdf
28
- # 13) http://www.cipa.jp/std/documents/e/DC-010-2017_E.pdf
28
+ # 13) http://www.cipa.jp/std/documents/e/DC-010-2017_E.pdf (changed to
29
+ # http://www.cipa.jp/std/documents/e/DC-X010-2017.pdf)
29
30
  #
30
31
  # Notes: - Property qualifiers are handled as if they were separate
31
32
  # properties (with no associated namespace).
@@ -49,7 +50,7 @@ use Image::ExifTool::Exif;
49
50
  use Image::ExifTool::GPS;
50
51
  require Exporter;
51
52
 
52
- $VERSION = '3.32';
53
+ $VERSION = '3.36';
53
54
  @ISA = qw(Exporter);
54
55
  @EXPORT_OK = qw(EscapeXML UnescapeXML);
55
56
 
@@ -889,6 +890,7 @@ my %sRetouchArea = (
889
890
  ModifyDate => { Groups => { 2 => 'Time' }, %dateTimeInfo, Priority => 0 },
890
891
  Nickname => { },
891
892
  Rating => { Writable => 'real', Notes => 'a value from 0 to 5, or -1 for "rejected"' },
893
+ RatingPercent=>{ Writable => 'real', Avoid => 1, Notes => 'non-standard' },
892
894
  Thumbnails => {
893
895
  FlatName => 'Thumbnail',
894
896
  Struct => \%sThumbnail,
@@ -1144,7 +1146,7 @@ my %sPantryItem = (
1144
1146
  LayerText => { },
1145
1147
  },
1146
1148
  },
1147
- TransmissionReference => { },
1149
+ TransmissionReference => { Notes => 'Now used as a job identifier' },
1148
1150
  Urgency => {
1149
1151
  Writable => 'integer',
1150
1152
  Notes => 'should be in the range 1-8 to conform with the XMP spec',
@@ -1289,11 +1291,11 @@ my %sPantryItem = (
1289
1291
  SharpenDetail => { Writable => 'integer' },
1290
1292
  SharpenEdgeMasking => { Writable => 'integer' },
1291
1293
  SharpenRadius => { Writable => 'real' },
1292
- SplitToningBalance => { Writable => 'integer' },
1293
- SplitToningHighlightHue => { Writable => 'integer' },
1294
- SplitToningHighlightSaturation => { Writable => 'integer' },
1295
- SplitToningShadowHue => { Writable => 'integer' },
1296
- SplitToningShadowSaturation => { Writable => 'integer' },
1294
+ SplitToningBalance => { Writable => 'integer', Notes => 'also used for newer ColorGrade settings' },
1295
+ SplitToningHighlightHue => { Writable => 'integer', Notes => 'also used for newer ColorGrade settings' },
1296
+ SplitToningHighlightSaturation => { Writable => 'integer', Notes => 'also used for newer ColorGrade settings' },
1297
+ SplitToningShadowHue => { Writable => 'integer', Notes => 'also used for newer ColorGrade settings' },
1298
+ SplitToningShadowSaturation => { Writable => 'integer', Notes => 'also used for newer ColorGrade settings' },
1297
1299
  Vibrance => { Writable => 'integer' },
1298
1300
  # new tags written by LR 1.4 (not sure in what version they first appeared)
1299
1301
  GrayMixerRed => { Writable => 'integer' },
@@ -1482,9 +1484,71 @@ my %sPantryItem = (
1482
1484
  Struct => {
1483
1485
  STRUCT_NAME => 'Look',
1484
1486
  NAMESPACE => 'crs',
1485
- Name => { },
1487
+ Name => { },
1488
+ Amount => { },
1489
+ Cluster => { },
1490
+ UUID => { },
1491
+ SupportsMonochrome => { },
1492
+ SupportsAmount => { },
1493
+ SupportsOutputReferred => { },
1494
+ Copyright => { },
1495
+ Group => { Writable => 'lang-alt' },
1496
+ Parameters => {
1497
+ Struct => {
1498
+ STRUCT_NAME => 'LookParms',
1499
+ NAMESPACE => 'crs',
1500
+ Version => { },
1501
+ ProcessVersion => { },
1502
+ Clarity2012 => { },
1503
+ ConvertToGrayscale => { },
1504
+ CameraProfile => { },
1505
+ LookTable => { },
1506
+ ToneCurvePV2012 => { List => 'Seq' },
1507
+ },
1508
+ },
1486
1509
  }
1487
1510
  },
1511
+ # more again (ref forum11258)
1512
+ GrainSeed => { },
1513
+ ClipboardOrientation => { Writable => 'integer' },
1514
+ ClipboardAspectRatio => { Writable => 'integer' },
1515
+ PresetType => { },
1516
+ Cluster => { },
1517
+ UUID => { Avoid => 1 },
1518
+ SupportsAmount => { Writable => 'boolean' },
1519
+ SupportsColor => { Writable => 'boolean' },
1520
+ SupportsMonochrome => { Writable => 'boolean' },
1521
+ SupportsHighDynamicRange=> { Writable => 'boolean' },
1522
+ SupportsNormalDynamicRange=> { Writable => 'boolean' },
1523
+ SupportsSceneReferred => { Writable => 'boolean' },
1524
+ SupportsOutputReferred => { Writable => 'boolean' },
1525
+ CameraModelRestriction => { },
1526
+ Copyright => { Avoid => 1 },
1527
+ ContactInfo => { },
1528
+ GrainSeed => { Writable => 'integer' },
1529
+ Name => { Writable => 'lang-alt', Avoid => 1 },
1530
+ ShortName => { Writable => 'lang-alt' },
1531
+ SortName => { Writable => 'lang-alt' },
1532
+ Group => { Writable => 'lang-alt', Avoid => 1 },
1533
+ Description => { Writable => 'lang-alt', Avoid => 1 },
1534
+ # new for DNG converter 13.0
1535
+ LookName => { NotFlat => 1 }, # (grr... conflicts with "Name" element of "Look" struct!)
1536
+ # new for Lightroom CC 2021 (ref forum11745)
1537
+ ColorGradeMidtoneHue => { Writable => 'integer' },
1538
+ ColorGradeMidtoneSat => { Writable => 'integer' },
1539
+ ColorGradeShadowLum => { Writable => 'integer' },
1540
+ ColorGradeMidtoneLum => { Writable => 'integer' },
1541
+ ColorGradeHighlightLum => { Writable => 'integer' },
1542
+ ColorGradeBlending => { Writable => 'integer' },
1543
+ ColorGradeGlobalHue => { Writable => 'integer' },
1544
+ ColorGradeGlobalSat => { Writable => 'integer' },
1545
+ ColorGradeGlobalLum => { Writable => 'integer' },
1546
+ # new for Adobe Camera Raw 13 (ref forum11745)
1547
+ LensProfileIsEmbedded => { Writable => 'boolean'},
1548
+ AutoToneDigest => { },
1549
+ AutoToneDigestNoSat => { },
1550
+ ToggleStyleDigest => { },
1551
+ ToggleStyleAmount => { Writable => 'integer' },
1488
1552
  );
1489
1553
 
1490
1554
  # Tiff namespace properties (tiff)
@@ -2042,7 +2106,7 @@ my %sPantryItem = (
2042
2106
  PRIORITY => 0, # not as reliable as actual EXIF tags
2043
2107
  NOTES => q{
2044
2108
  EXIF tags added by the EXIF 2.31 for XMP specification (see
2045
- L<http://www.cipa.jp/std/documents/e/DC-010-2017_E.pdf>).
2109
+ L<http://www.cipa.jp/std/documents/e/DC-X010-2017.pdf>).
2046
2110
  },
2047
2111
  Gamma => { Writable => 'rational' },
2048
2112
  PhotographicSensitivity => { Writable => 'integer' },
@@ -2747,8 +2811,9 @@ sub AddFlattenedTags($;$$)
2747
2811
  if ($flatInfo) {
2748
2812
  ref $flatInfo eq 'HASH' or warn("$flatInfo is not a HASH!\n"), next; # (to be safe)
2749
2813
  # pre-defined flattened tags should have Flat flag set
2750
- if (not defined $$flatInfo{Flat} and $Image::ExifTool::debug) {
2751
- warn "Missing Flat flag for $$flatInfo{Name}\n";
2814
+ if (not defined $$flatInfo{Flat}) {
2815
+ next if $$flatInfo{NotFlat};
2816
+ warn "Missing Flat flag for $$flatInfo{Name}\n" if $Image::ExifTool::debug;
2752
2817
  }
2753
2818
  $$flatInfo{Flat} = 0;
2754
2819
  # copy all missing entries from field information
@@ -2935,13 +3000,15 @@ sub PrintLensID(@)
2935
3000
  # for Pentax, CS4 stores an int16u, but we use 2 x int8u
2936
3001
  $id = join(' ', unpack('C*', pack('n', $id)));
2937
3002
  }
2938
- my $str = $$printConv{$id} || "Unknown ($id)";
2939
3003
  # Nikon is a special case because Adobe doesn't store the full LensID
3004
+ # (Apple Photos does, but we have to convert back to hex)
2940
3005
  if ($mk eq 'Nikon') {
2941
- my $hex = sprintf("%.2X", $id);
3006
+ $id = sprintf('%X', $id);
3007
+ $id = "0$id" if length($id) & 0x01; # pad with leading 0 if necessary
3008
+ $id =~ s/(..)/$1 /g and $id =~ s/ $//; # put spaces between bytes
2942
3009
  my (%newConv, %used);
2943
3010
  my $i = 0;
2944
- foreach (grep /^$hex /, keys %$printConv) {
3011
+ foreach (grep /^$id/, keys %$printConv) {
2945
3012
  my $lens = $$printConv{$_};
2946
3013
  next if $used{$lens}; # avoid duplicates
2947
3014
  $used{$lens} = 1;
@@ -2950,6 +3017,7 @@ sub PrintLensID(@)
2950
3017
  }
2951
3018
  $printConv = \%newConv;
2952
3019
  }
3020
+ my $str = $$printConv{$id} || "Unknown ($id)";
2953
3021
  return Image::ExifTool::Exif::PrintLensID($et, $str, $printConv,
2954
3022
  undef, $id, $focalLength, $sa, $maxAv, $sf, $lf, $lensModel);
2955
3023
  }
@@ -3187,8 +3255,9 @@ NoLoop:
3187
3255
  #} elsif (grep / /, @$props) {
3188
3256
  # $$tagInfo{List} = 1;
3189
3257
  }
3258
+ # save property list for verbose "adding" message unless this tag already exists
3259
+ $added = \@tagList unless $$tagTablePtr{$tagID};
3190
3260
  AddTagToTable($tagTablePtr, $tagID, $tagInfo);
3191
- $added = 1;
3192
3261
  last;
3193
3262
  }
3194
3263
  # decode value if necessary (et:encoding was used before exiftool 7.71)
@@ -3272,8 +3341,16 @@ NoLoop:
3272
3341
  }
3273
3342
  if ($$et{OPTIONS}{Verbose}) {
3274
3343
  if ($added) {
3344
+ my $props;
3345
+ if (@$added > 1) {
3346
+ $$tagInfo{Flat} = 0; # this is a flattened tag
3347
+ my @props = map { $$_[0] } @$added;
3348
+ $props = ' (' . join('/',@props) . ')';
3349
+ } else {
3350
+ $props = '';
3351
+ }
3275
3352
  my $g1 = $et->GetGroup($key, 1);
3276
- $et->VPrint(0, $$et{INDENT}, "[adding $g1:$tag]\n");
3353
+ $et->VPrint(0, $$et{INDENT}, "[adding $g1:$tag]$props\n");
3277
3354
  }
3278
3355
  my $tagID = join('/',@$props);
3279
3356
  $et->VerboseInfo($tagID, $tagInfo, Value => $rawVal || $val);
@@ -3531,6 +3608,11 @@ sub ParseXMPElement($$$;$$$$)
3531
3608
  # add svg namespace prefix if missing to ignore these entries in the tag name
3532
3609
  $$propList[-1] = "svg:$prop";
3533
3610
  }
3611
+ } elsif ($$et{XmpIgnoreProps}) { # ignore specified properties for tag name
3612
+ foreach (@{$$et{XmpIgnoreProps}}) {
3613
+ last unless @$propList;
3614
+ pop @$propList if $_ eq $$propList[0];
3615
+ }
3534
3616
  }
3535
3617
 
3536
3618
  # handle properties inside element attributes (RDF shorthand format):