exiftool_vendored 12.67.0 → 12.70.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/bin/Changes +79 -10
  3. data/bin/MANIFEST +5 -0
  4. data/bin/META.json +1 -1
  5. data/bin/META.yml +1 -1
  6. data/bin/README +7 -7
  7. data/bin/exiftool +32 -27
  8. data/bin/lib/Image/ExifTool/CBOR.pm +18 -2
  9. data/bin/lib/Image/ExifTool/Canon.pm +87 -16
  10. data/bin/lib/Image/ExifTool/DJI.pm +3 -2
  11. data/bin/lib/Image/ExifTool/DNG.pm +25 -2
  12. data/bin/lib/Image/ExifTool/EXE.pm +54 -6
  13. data/bin/lib/Image/ExifTool/Exif.pm +175 -14
  14. data/bin/lib/Image/ExifTool/FujiFilm.pm +158 -20
  15. data/bin/lib/Image/ExifTool/GIF.pm +5 -1
  16. data/bin/lib/Image/ExifTool/Geotag.pm +16 -11
  17. data/bin/lib/Image/ExifTool/ID3.pm +70 -7
  18. data/bin/lib/Image/ExifTool/InDesign.pm +1 -1
  19. data/bin/lib/Image/ExifTool/JPEG.pm +1 -1
  20. data/bin/lib/Image/ExifTool/Jpeg2000.pm +30 -15
  21. data/bin/lib/Image/ExifTool/MakerNotes.pm +2 -2
  22. data/bin/lib/Image/ExifTool/Nikon.pm +82 -22
  23. data/bin/lib/Image/ExifTool/Olympus.pm +7 -1
  24. data/bin/lib/Image/ExifTool/PNG.pm +3 -1
  25. data/bin/lib/Image/ExifTool/Panasonic.pm +22 -9
  26. data/bin/lib/Image/ExifTool/Pentax.pm +6 -1
  27. data/bin/lib/Image/ExifTool/PhotoMechanic.pm +2 -1
  28. data/bin/lib/Image/ExifTool/QuickTime.pm +92 -55
  29. data/bin/lib/Image/ExifTool/README +14 -5
  30. data/bin/lib/Image/ExifTool/RIFF.pm +60 -10
  31. data/bin/lib/Image/ExifTool/Sony.pm +152 -46
  32. data/bin/lib/Image/ExifTool/TagLookup.pm +6955 -6713
  33. data/bin/lib/Image/ExifTool/TagNames.pod +878 -334
  34. data/bin/lib/Image/ExifTool/Text.pm +4 -5
  35. data/bin/lib/Image/ExifTool/Validate.pm +23 -20
  36. data/bin/lib/Image/ExifTool/WriteCanonRaw.pl +2 -2
  37. data/bin/lib/Image/ExifTool/WriteExif.pl +14 -4
  38. data/bin/lib/Image/ExifTool/WriteQuickTime.pl +3 -0
  39. data/bin/lib/Image/ExifTool/WriteRIFF.pl +31 -6
  40. data/bin/lib/Image/ExifTool/Writer.pl +40 -14
  41. data/bin/lib/Image/ExifTool/XISF.pm +185 -0
  42. data/bin/lib/Image/ExifTool/XMP.pm +67 -2
  43. data/bin/lib/Image/ExifTool/XMP2.pl +35 -0
  44. data/bin/lib/Image/ExifTool.pm +92 -45
  45. data/bin/lib/Image/ExifTool.pod +14 -8
  46. data/bin/perl-Image-ExifTool.spec +6 -6
  47. data/lib/exiftool_vendored/version.rb +1 -1
  48. metadata +4 -3
@@ -25,11 +25,10 @@ $VERSION = '1.04';
25
25
  Although basic text files contain no metadata, the following tags are
26
26
  determined from a simple analysis of the data in TXT and CSV files.
27
27
  Statistics are generated only for 8-bit encodings, but the L<FastScan|../ExifTool.html#FastScan> (-fast)
28
- option may be used to limit processing to the first 64 kB in which case some
29
- tags are not produced. To avoid long processing delays, ExifTool will issue
30
- a minor warning and process only the first 64 kB of any file larger than 20
31
- MB unless the L<IgnoreMinorErrors|../ExifTool.html#IgnoreMinorErrors> (-m)
32
- option is used.
28
+ option may be used to limit processing to the first 64 KiB in which case
29
+ some tags are not produced. To avoid long processing delays, ExifTool will
30
+ issue a minor warning and process only the first 64 KiB of any file larger
31
+ than 20 MiB unless the L<IgnoreMinorErrors|../ExifTool.html#IgnoreMinorErrors> (-m) option is used.
33
32
  },
34
33
  MIMEEncoding => { Groups => { 2 => 'Other' } },
35
34
  Newlines => {
@@ -17,7 +17,7 @@ package Image::ExifTool::Validate;
17
17
  use strict;
18
18
  use vars qw($VERSION %exifSpec);
19
19
 
20
- $VERSION = '1.21';
20
+ $VERSION = '1.23';
21
21
 
22
22
  use Image::ExifTool qw(:Utils);
23
23
  use Image::ExifTool::Exif;
@@ -86,7 +86,7 @@ my %verCheck = (
86
86
  GPS => { GPSVersionID => \%gpsVer },
87
87
  );
88
88
 
89
- # tags standard in various RAW file formats
89
+ # tags standard in various RAW file formats or IFD's
90
90
  my %otherSpec = (
91
91
  CR2 => { 0xc5d8 => 1, 0xc5d9 => 1, 0xc5e0 => 1, 0xc640 => 1, 0xc6dc => 1, 0xc6dd => 1 },
92
92
  NEF => { 0x9216 => 1, 0x9217 => 1 },
@@ -103,6 +103,7 @@ my %otherSpec = (
103
103
  SRW => { 0xa010 => 1, 0xa011 => 1, 0xa101 => 1, 0xa102 => 1 },
104
104
  NRW => { 0x9216 => 1, 0x9217 => 1 },
105
105
  X3F => { 0xa500 => 1 },
106
+ CameraIFD => { All => 1 }, # (exists in JPG and DNG of Leica Q3 images)
106
107
  );
107
108
 
108
109
  # standard format for tags (not necessary for exifSpec or GPS tags where Writable is defined)
@@ -142,20 +143,22 @@ my %stdFormat = (
142
143
  # GeoTiff tags:
143
144
  0x830e => 'double', 0x8482 => 'double', 0x87af => 'int16u', 0x87b1 => 'string',
144
145
  0x8480 => 'double', 0x85d8 => 'double', 0x87b0 => 'double',
145
- # DNG tags:
146
- 0xc615 => '(string|int8u)', 0xc6d3 => '',
147
- 0xc61a => '(int16u|int32u|rational64u)', 0xc6f4 => '(string|int8u)',
148
- 0xc61d => 'int(16|32)u', 0xc6f6 => '(string|int8u)',
149
- 0xc61f => '(int16u|int32u|rational64u)', 0xc6f8 => '(string|int8u)',
150
- 0xc620 => '(int16u|int32u|rational64u)', 0xc6fe => '(string|int8u)',
151
- 0xc628 => '(int16u|rational64u)', 0xc716 => '(string|int8u)',
152
- 0xc634 => 'int8u', 0xc717 => '(string|int8u)',
153
- 0xc640 => '', 0xc718 => '(string|int8u)',
154
- 0xc660 => '', 0xc71e => 'int(16|32)u',
155
- 0xc68b => '(string|int8u)', 0xc71f => 'int(16|32)u',
156
- 0xc68d => 'int(16|32)u', 0xc791 => 'int(16|32)u',
157
- 0xc68e => 'int(16|32)u', 0xc792 => 'int(16|32)u',
158
- 0xc6d2 => '', 0xc793 => '(int16u|int32u|rational64u)',
146
+ # DNG tags: (use '' for non-DNG tags in the range 0xc612-0xcd48)
147
+ 0xc615 => '(string|int8u)', 0xc6f4 => '(string|int8u)',
148
+ 0xc61a => '(int16u|int32u|rational64u)', 0xc6f6 => '(string|int8u)',
149
+ 0xc61d => 'int(16|32)u', 0xc6f8 => '(string|int8u)',
150
+ 0xc61f => '(int16u|int32u|rational64u)', 0xc6fe => '(string|int8u)',
151
+ 0xc620 => '(int16u|int32u|rational64u)', 0xc716 => '(string|int8u)',
152
+ 0xc628 => '(int16u|rational64u)', 0xc717 => '(string|int8u)',
153
+ 0xc634 => 'int8u', 0xc718 => '(string|int8u)',
154
+ 0xc640 => '', 0xc71e => 'int(16|32)u',
155
+ 0xc660 => '', 0xc71f => 'int(16|32)u',
156
+ 0xc68b => '(string|int8u)', 0xc791 => 'int(16|32)u',
157
+ 0xc68d => 'int(16|32)u', 0xc792 => 'int(16|32)u',
158
+ 0xc68e => 'int(16|32)u', 0xc793 => '(int16u|int32u|rational64u)',
159
+ 0xc6d2 => '', 0xcd43 => 'int(16|32)u',
160
+ 0xc6d3 => '', 0xcd48 => '(string|int8u)',
161
+
159
162
  # Exif 3.0 spec
160
163
  0x10e => 'string|utf8', 0xa430 => 'string|utf8', 0xa439 => 'string|utf8',
161
164
  0x10f => 'string|utf8', 0xa433 => 'string|utf8', 0xa43a => 'string|utf8',
@@ -430,7 +433,7 @@ sub ValidateExif($$$$$$$$)
430
433
  my $stdFmt = $stdFormat{$ifd} || $stdFormat{IFD};
431
434
  if (defined $$stdFmt{All} or ($tagTablePtr eq \%Image::ExifTool::Exif::Main and
432
435
  ($exifSpec{$tag} or $$stdFmt{$tag} or
433
- ($tag >= 0xc612 and $tag <= 0xc7b5 and not defined $$stdFmt{$tag}))) or # (DNG tags)
436
+ ($tag >= 0xc612 and $tag <= 0xcd48 and not defined $$stdFmt{$tag}))) or # (DNG tags)
434
437
  $$tagTablePtr{SHORT_NAME} eq 'GPS::Main')
435
438
  {
436
439
  my $wgp = $$ti{WriteGroup} || $$tagTablePtr{WRITE_GROUP};
@@ -456,8 +459,8 @@ sub ValidateExif($$$$$$$$)
456
459
  } elsif (not $otherSpec{$$et{FileType}} or
457
460
  (not $otherSpec{$$et{FileType}}{$tag} and not $otherSpec{$$et{FileType}}{All}))
458
461
  {
459
- if ($tagTablePtr eq \%Image::ExifTool::Exif::Main or $$tagInfo{Unknown}) {
460
- $et->Warn(sprintf('Non-standard %s tag 0x%.4x %s', $ifd, $tag, $$ti{Name}), 1);
462
+ if ($tagTablePtr eq \%Image::ExifTool::Exif::Main or $$ti{Unknown}) {
463
+ $et->Warn(sprintf('Non-standard %s tag 0x%.4x %s', $ifd, $tag, $$ti{Name}), 1) unless $otherSpec{$ifd};
461
464
  }
462
465
  }
463
466
  # change expected count from read Format to Writable size
@@ -478,7 +481,7 @@ sub ValidateExif($$$$$$$$)
478
481
  } elsif (not $otherSpec{$$et{FileType}} or
479
482
  (not $otherSpec{$$et{FileType}}{$tag} and not $otherSpec{$$et{FileType}}{All}))
480
483
  {
481
- $et->Warn(sprintf('Unknown %s tag 0x%.4x', $ifd, $tag), 1);
484
+ $et->Warn(sprintf('Unknown %s tag 0x%.4x', $ifd, $tag), 1) unless $otherSpec{$ifd};
482
485
  }
483
486
  }
484
487
 
@@ -142,7 +142,6 @@ sub SaveMakerNotes($)
142
142
  }
143
143
  # save position of maker notes for pointer fixups
144
144
  $fixup->{Shift} += length($makerNotes);
145
- $$et{MAKER_NOTE_FIXUP} = $fixup;
146
145
  $$et{MAKER_NOTE_BYTE_ORDER} = GetByteOrder();
147
146
  # add value data
148
147
  $makerNotes .= $makerInfo->{ValBuff};
@@ -150,7 +149,8 @@ sub SaveMakerNotes($)
150
149
  my $tagTablePtr = Image::ExifTool::GetTagTable('Image::ExifTool::Exif::Main');
151
150
  my $tagInfo = $et->GetTagInfo($tagTablePtr, 0x927c, \$makerNotes);
152
151
  # save the MakerNotes
153
- $et->FoundTag($tagInfo, $makerNotes);
152
+ my $key = $et->FoundTag($tagInfo, $makerNotes);
153
+ $$et{TAG_EXTRA}{$key}{Fixup} = $fixup;
154
154
  # save the garbage collection some work later
155
155
  delete $makerInfo->{Entries};
156
156
  delete $makerInfo->{ValBuff};
@@ -930,8 +930,16 @@ Entry: for (;;) {
930
930
  }
931
931
  }
932
932
  unless ($success) {
933
- return undef if $et->Error("Error reading value for $name entry $index", $inMakerNotes);
934
- ++$index; $oldID = $newID; next; # drop this tag
933
+ my $wrn = sprintf("Error reading value for $name entry $index, ID 0x%.4x", $oldID);
934
+ my $truncOK;
935
+ if ($oldInfo and not $$oldInfo{Unknown}) {
936
+ $wrn .= " $$oldInfo{Name}";
937
+ $truncOK = $$oldInfo{TruncateOK};
938
+ }
939
+ return undef if $et->Error($wrn, $inMakerNotes || $truncOK);
940
+ unless ($truncOK) {
941
+ ++$index; $oldID = $newID; next; # drop this tag
942
+ }
935
943
  }
936
944
  } elsif (not $invalidPreview) {
937
945
  return undef if $et->Error("Bad $name offset for $tagStr", $inMakerNotes);
@@ -1094,6 +1102,8 @@ Entry: for (;;) {
1094
1102
  # add, edit or delete this tag
1095
1103
  shift @newTags; # remove from list
1096
1104
  my $curInfo = $set{$newID};
1105
+ # don't allow MakerNotes to be added to ExifIFD of CR3 file
1106
+ next if $newID == 0x927c and $isNew > 0 and $$et{FileType} eq 'CR3';
1097
1107
  unless ($curInfo or $$addDirs{$newID}) {
1098
1108
  # we can finally get the specific tagInfo reference for this tag
1099
1109
  # (because we can now evaluate the Condition statement since all
@@ -1429,8 +1439,8 @@ NoOverwrite: next if $isNew > 0;
1429
1439
  if ($$et{DEL_GROUP}{MakerNotes} and
1430
1440
  ($$et{DEL_GROUP}{MakerNotes} != 2 or $isNew <= 0))
1431
1441
  {
1432
- if ($et->IsRawType()) {
1433
- $et->WarnOnce("Can't delete MakerNotes from $$et{FileType}",1);
1442
+ if ($et->IsRawType() and not ($et->IsRawType() == 2 and $dirName eq 'ExifIFD')) {
1443
+ $et->Warn("Can't delete MakerNotes from $$et{FileType}",1);
1434
1444
  } else {
1435
1445
  if ($isNew <= 0) {
1436
1446
  ++$$et{CHANGED};
@@ -96,6 +96,8 @@ my %ctboID = (
96
96
  "\xbe\x7a\xcf\xcb\x97\xa9\x42\xe8\x9c\x71\x99\x94\x91\xe3\xaf\xac" => 1, # XMP
97
97
  "\xea\xf4\x2b\x5e\x1c\x98\x4b\x88\xb9\xfb\xb7\xdc\x40\x6e\x4d\x16" => 2, # PreviewImage
98
98
  # ID 3 is used for 'mdat' atom (not a uuid)
99
+ # (haven't seen ID 4 yet)
100
+ "\x57\x66\xb8\x29\xbb\x6a\x47\xc5\xbc\xfb\x8b\x9f\x22\x60\xd0\x6d" => 5, # something to do with burst-roll image
99
101
  );
100
102
 
101
103
  # mark UserData tags that don't have ItemList counterparts as Preferred
@@ -1054,6 +1056,7 @@ sub WriteQuickTime($$$)
1054
1056
  Parent => $dirName,
1055
1057
  DirName => $subName,
1056
1058
  Name => $$tagInfo{Name},
1059
+ TagInfo => $tagInfo,
1057
1060
  DirID => $tag,
1058
1061
  DataPt => \$buff,
1059
1062
  DataLen => $size,
@@ -19,6 +19,8 @@ my %webpMap = (
19
19
  'XMP ' => 'RIFF', # (the RIFF chunk name is 'XMP ')
20
20
  EXIF => 'RIFF',
21
21
  ICCP => 'RIFF',
22
+ C2PA => 'RIFF',
23
+ JUMBF => 'C2PA',
22
24
  XMP => 'XMP ',
23
25
  IFD0 => 'EXIF',
24
26
  IFD1 => 'IFD0',
@@ -66,6 +68,7 @@ sub WriteRIFF($$)
66
68
  $et->InitWriteDirs(\%webpMap);
67
69
  my $addDirs = $$et{ADD_DIRS};
68
70
  my $editDirs = $$et{EDIT_DIRS};
71
+ $$addDirs{IFD0} = 'EXIF' if $$addDirs{EXIF}; # set flag to add IFD0 if adding EXIF (don't ask)
69
72
  my ($createVP8X, $deleteVP8X);
70
73
 
71
74
  # write header
@@ -142,6 +145,17 @@ sub WriteRIFF($$)
142
145
  }
143
146
  # RIFF chunks are padded to an even number of bytes
144
147
  my $len2 = $len + ($len & 0x01);
148
+ # handle incorrect "XMP\0" chunk ID written by Google software
149
+ if ($tag eq "XMP\0") {
150
+ if ($$et{DEL_GROUP}{XMP}) {
151
+ # just ignore this chunk if deleting XMP
152
+ $raf->Seek($len2, 1) or $et->Error('Seek error'), last;
153
+ ++$$et{CHANGED};
154
+ next;
155
+ } else {
156
+ $et->Warn('Incorrect XMP tag ID',1) if $pass;
157
+ }
158
+ }
145
159
  # edit/add/delete necessary metadata chunks (EXIF must come before XMP)
146
160
  if ($$editDirs{$tag} or $tag eq '' or ($tag eq 'XMP ' and $$addDirs{EXIF})) {
147
161
  my $handledTag;
@@ -156,13 +170,12 @@ sub WriteRIFF($$)
156
170
  #
157
171
  # add/edit/delete EXIF/XMP/ICCP (note: EXIF must come before XMP, and ICCP is written elsewhere)
158
172
  #
159
- my %dirName = ( EXIF => 'IFD0', 'XMP ' => 'XMP', ICCP => 'ICC_Profile' );
160
- my %tblName = ( EXIF => 'Exif', 'XMP ' => 'XMP', ICCP => 'ICC_Profile' );
173
+ my %dirName = ( EXIF => 'IFD0', 'XMP ' => 'XMP', ICCP => 'ICC_Profile', C2PA => 'JUMBF' );
174
+ my %tblName = ( EXIF => 'Exif', 'XMP ' => 'XMP', ICCP => 'ICC_Profile', C2PA => 'Jpeg2000' );
161
175
  my $dir;
162
- foreach $dir ('EXIF', 'XMP ', 'ICCP' ) {
176
+ foreach $dir ('EXIF', 'XMP ', 'ICCP', 'C2PA' ) {
163
177
  next unless $tag eq $dir or ($$addDirs{$dir} and
164
178
  ($tag eq '' or ($tag eq 'XMP ' and $dir eq 'EXIF')));
165
- delete $$addDirs{$dir}; # (don't try to add again)
166
179
  my $start;
167
180
  unless ($pass) {
168
181
  # write the EXIF and save the result for the next pass
@@ -170,8 +183,15 @@ sub WriteRIFF($$)
170
183
  if ($tag eq 'EXIF') {
171
184
  # (only need to set directory $start for EXIF)
172
185
  if ($buff =~ /^Exif\0\0/) {
173
- $et->Warn('Improper EXIF header') unless $pass;
174
- $start = 6;
186
+ if ($$et{DEL_GROUP}{EXIF}) {
187
+ # remove incorrect header if rewriting anyway
188
+ $buff = substr($buff, 6);
189
+ $len -= 6;
190
+ $len2 -= 6;
191
+ } else {
192
+ $et->Warn('Improper EXIF header',1) unless $pass;
193
+ $start = 6;
194
+ }
175
195
  } else {
176
196
  $start = 0;
177
197
  }
@@ -189,11 +209,16 @@ sub WriteRIFF($$)
189
209
  Parent => $dir,
190
210
  DirName => $dirName{$dir},
191
211
  );
212
+ # must pass the TagInfo to enable deletion of C2PA information
213
+ if (ref $Image::ExifTool::RIFF::Main{$dir} eq 'HASH') {
214
+ $dirInfo{TagInfo} = $Image::ExifTool::RIFF::Main{$dir};
215
+ }
192
216
  my $tagTablePtr = GetTagTable("Image::ExifTool::$tblName{$dir}::Main");
193
217
  # (override writeProc for EXIF because it has the TIFF header)
194
218
  my $writeProc = $dir eq 'EXIF' ? \&Image::ExifTool::WriteTIFF : undef;
195
219
  $dirDat{$dir} = $et->WriteDirectory(\%dirInfo, $tagTablePtr, $writeProc);
196
220
  }
221
+ delete $$addDirs{$dir}; # (don't try to add again)
197
222
  if (defined $dirDat{$dir}) {
198
223
  if ($dir eq $tag) {
199
224
  $handledTag = 1; # set flag indicating we edited this tag
@@ -122,9 +122,9 @@ my %writableType = (
122
122
  XMP => [ undef, 'WriteXMP' ],
123
123
  );
124
124
 
125
- # RAW file types
125
+ # RAW file types (2 = raw file where we can delete maker notes from ExifIFD)
126
126
  my %rawType = (
127
- '3FR'=> 1, CR3 => 1, IIQ => 1, NEF => 1, RW2 => 1,
127
+ '3FR'=> 1, CR3 => 2, IIQ => 1, NEF => 1, RW2 => 1,
128
128
  ARQ => 1, CRW => 1, K25 => 1, NRW => 1, RWL => 1,
129
129
  ARW => 1, DCR => 1, KDC => 1, ORF => 1, SR2 => 1,
130
130
  ARW => 1, ERF => 1, MEF => 1, PEF => 1, SRF => 1,
@@ -278,6 +278,7 @@ my %ignorePrintConv = map { $_ => 1 } qw(OTHER BITMASK Notes);
278
278
  # ListOnly => [internal use] set only list or non-list tags
279
279
  # SetTags => [internal use] hash ref to return tagInfo refs of set tags
280
280
  # Sanitized => [internal use] set to avoid double-sanitizing the value
281
+ # Fixup => [internal use] fixup information when writing maker notes
281
282
  # Returns: number of tags set (plus error string in list context)
282
283
  # Notes: For tag lists (like Keywords), call repeatedly with the same tag name for
283
284
  # each value in the list. Internally, the new information is stored in
@@ -1002,10 +1003,8 @@ TAG: foreach $tagInfo (@matchingTags) {
1002
1003
  $$nvHash{NoReplace} = 1 if $$tagInfo{List} and not $options{Replace};
1003
1004
  $$nvHash{WantGroup} = $wantGroup;
1004
1005
  $$nvHash{EditOnly} = 1 if $editOnly;
1005
- # save maker note information if writing maker notes
1006
- if ($$tagInfo{MakerNotes}) {
1007
- $$nvHash{MAKER_NOTE_FIXUP} = $$self{MAKER_NOTE_FIXUP};
1008
- }
1006
+ # save maker note fixup information if writing maker notes
1007
+ $$nvHash{MAKER_NOTE_FIXUP} = $options{Fixup} if $$tagInfo{MakerNotes};
1009
1008
  if ($createOnly) { # create only (never edit)
1010
1009
  # empty item in DelValue list to never edit existing value
1011
1010
  $$nvHash{DelValue} = [ '' ];
@@ -1272,6 +1271,7 @@ sub SetNewValuesFromFile($$;@)
1272
1271
  # +------------------------------------------+
1273
1272
  $srcExifTool->Options(
1274
1273
  Binary => 1,
1274
+ ByteUnit => $$options{ByteUnit},
1275
1275
  Charset => $$options{Charset},
1276
1276
  CharsetEXIF => $$options{CharsetEXIF},
1277
1277
  CharsetFileName => $$options{CharsetFileName},
@@ -1372,8 +1372,8 @@ sub SetNewValuesFromFile($$;@)
1372
1372
  #
1373
1373
  unless (@setTags) {
1374
1374
  # transfer maker note information to this object
1375
- $$self{MAKER_NOTE_FIXUP} = $$srcExifTool{MAKER_NOTE_FIXUP};
1376
1375
  $$self{MAKER_NOTE_BYTE_ORDER} = $$srcExifTool{MAKER_NOTE_BYTE_ORDER};
1376
+ my $tagExtra = $$srcExifTool{TAG_EXTRA};
1377
1377
  foreach $tag (@tags) {
1378
1378
  # don't try to set errors or warnings
1379
1379
  next if $tag =~ /^(Error|Warning)\b/;
@@ -1381,10 +1381,13 @@ sub SetNewValuesFromFile($$;@)
1381
1381
  if ($opts{SrcType} and $opts{SrcType} ne $srcType) {
1382
1382
  $$info{$tag} = $srcExifTool->GetValue($tag, $opts{SrcType});
1383
1383
  }
1384
+ my $fixup = $$tagExtra{$tag}{Fixup};
1385
+ $opts{Fixup} = $fixup if $fixup;
1384
1386
  # set value for this tag
1385
1387
  my ($n, $e) = $self->SetNewValue($tag, $$info{$tag}, %opts);
1386
1388
  # delete this tag if we couldn't set it
1387
1389
  $n or delete $$info{$tag};
1390
+ delete $opts{Fixup} if $fixup;
1388
1391
  }
1389
1392
  return $info;
1390
1393
  }
@@ -1617,7 +1620,7 @@ SET: foreach $set (@setList) {
1617
1620
  }
1618
1621
  # transfer maker note information if setting this tag
1619
1622
  if ($$srcExifTool{TAG_INFO}{$tag}{MakerNotes}) {
1620
- $$self{MAKER_NOTE_FIXUP} = $$srcExifTool{MAKER_NOTE_FIXUP};
1623
+ $$opts{Fixup} = $$srcExifTool{TAG_EXTRA}{$tag}{Fixup};
1621
1624
  $$self{MAKER_NOTE_BYTE_ORDER} = $$srcExifTool{MAKER_NOTE_BYTE_ORDER};
1622
1625
  }
1623
1626
  if ($dstTag eq '*') {
@@ -1649,6 +1652,7 @@ SET: foreach $set (@setList) {
1649
1652
  $rtnInfo{NextFreeTagKey(\%rtnInfo, 'Warning')} = $wrn;
1650
1653
  $noWarn = 1;
1651
1654
  }
1655
+ delete $$opts{Fixup};
1652
1656
  $rtnInfo{$tag} = $val if $rtn; # tag was set successfully
1653
1657
  }
1654
1658
  }
@@ -4176,6 +4180,7 @@ sub WriteDirectory($$$;$)
4176
4180
  $out = $$self{OPTIONS}{TextOut} if $$self{OPTIONS}{Verbose};
4177
4181
  # set directory name from default group0 name if not done already
4178
4182
  my $dirName = $$dirInfo{DirName};
4183
+ my $parent = $$dirInfo{Parent} || '';
4179
4184
  my $dataPt = $$dirInfo{DataPt};
4180
4185
  my $grp0 = $$tagTablePtr{GROUPS}{0};
4181
4186
  $dirName or $dirName = $$dirInfo{DirName} = $grp0;
@@ -4183,14 +4188,19 @@ sub WriteDirectory($$$;$)
4183
4188
  my $delGroup = $$self{DEL_GROUP};
4184
4189
  # delete entire directory if specified
4185
4190
  my $grp1 = $dirName;
4186
- $delFlag = ($$delGroup{$grp0} or $$delGroup{$grp1}) unless $permanentDir{$grp0};
4191
+ $delFlag = ($$delGroup{$grp0} or $$delGroup{$grp1});
4192
+ if ($permanentDir{$grp0} and not ($$dirInfo{TagInfo} and $$dirInfo{TagInfo}{Deletable})) {
4193
+ undef $delFlag;
4194
+ }
4187
4195
  # (never delete an entire QuickTime group)
4188
4196
  if ($delFlag) {
4189
4197
  if (($grp0 =~ /^(MakerNotes)$/ or $grp1 =~ /^(IFD0|ExifIFD|MakerNotes)$/) and
4190
4198
  $self->IsRawType() and
4191
4199
  # allow non-permanent MakerNote directories to be deleted (ie. NikonCapture)
4192
4200
  (not $$dirInfo{TagInfo} or not defined $$dirInfo{TagInfo}{Permanent} or
4193
- $$dirInfo{TagInfo}{Permanent}))
4201
+ $$dirInfo{TagInfo}{Permanent}) and
4202
+ # allow MakerNotes to be deleted from ExifIFD of CR3 file
4203
+ not ($self->IsRawType() == 2 and $parent eq 'ExifIFD'))
4194
4204
  {
4195
4205
  $self->WarnOnce("Can't delete $1 from $$self{FileType}",1);
4196
4206
  undef $grp1;
@@ -4226,7 +4236,6 @@ sub WriteDirectory($$$;$)
4226
4236
  if ($delFlag == 2 and $right) {
4227
4237
  # also check grandparent because some routines create 2 levels in 1
4228
4238
  my $right2 = $$self{ADD_DIRS}{$right} || '';
4229
- my $parent = $$dirInfo{Parent};
4230
4239
  if (not $parent or $parent eq $right or $parent eq $right2) {
4231
4240
  # prevent duplicate directories from being recreated at the same path
4232
4241
  my $path = join '-', @{$$self{PATH}}, $dirName;
@@ -4284,10 +4293,27 @@ sub WriteDirectory($$$;$)
4284
4293
  last unless $self->IsOverwriting($nvHash, $dataPt ? $$dataPt : '');
4285
4294
  my $verb = 'Writing';
4286
4295
  my $newVal = $self->GetNewValue($nvHash);
4287
- unless (defined $newVal and length $newVal) {
4296
+ if (defined $newVal and length $newVal) {
4297
+ # hack to add back TIFF header when writing MakerNoteCanon to CMT3 in CR3 images
4298
+ if ($$tagInfo{Name} eq 'MakerNoteCanon') {
4299
+ require Image::ExifTool::Canon;
4300
+ if ($tagInfo eq $Image::ExifTool::Canon::uuid{CMT3}) {
4301
+ my $hdr;
4302
+ if (substr($newVal, 0, 1) eq "\0") {
4303
+ $hdr = "MM\0\x2a" . pack('N', 8);
4304
+ } else {
4305
+ $hdr = "II\x2a\0" . pack('V', 8);
4306
+ }
4307
+ $newVal = $hdr . $newVal;
4308
+ }
4309
+ }
4310
+ } else {
4288
4311
  return '' unless $dataPt or $$dirInfo{RAF}; # nothing to do if block never existed
4289
4312
  # don't allow MakerNotes to be removed from RAW files
4290
- if ($blockName eq 'MakerNotes' and $rawType{$$self{FileType}}) {
4313
+ if ($blockName eq 'MakerNotes' and $self->IsRawType() and
4314
+ # but allow MakerNotes to be deleted from ExifIFD of CR3 image (shouldn't be there)
4315
+ not ($self->IsRawType() == 2 and $parent eq 'ExifIFD'))
4316
+ {
4291
4317
  $self->Warn("Can't delete MakerNotes from $$self{FileType}",1);
4292
4318
  return undef;
4293
4319
  }
@@ -7067,7 +7093,7 @@ sub WriteBinaryData($$$)
7067
7093
  $newVal = length($data) if defined $data;
7068
7094
  my $format = $$tagInfo{Format} || $$tagTablePtr{FORMAT} || 'int32u';
7069
7095
  if ($format =~ /^int16/ and $newVal > 0xffff) {
7070
- $self->Error("$$tagInfo{DataTag} is too large (64 kB max. for this file)");
7096
+ $self->Error("$$tagInfo{DataTag} is too large (64 KiB max. for this file)");
7071
7097
  }
7072
7098
  }
7073
7099
  my $rtnVal = WriteValue($newVal, $format, $count, $dataPt, $entry);
@@ -0,0 +1,185 @@
1
+ #------------------------------------------------------------------------------
2
+ # File: XISF.pm
3
+ #
4
+ # Description: Read Extensible Image Serialization Format metadata
5
+ #
6
+ # Revisions: 2023-10-10 - P. Harvey Created
7
+ #
8
+ # References: 1) https://pixinsight.com/doc/docs/XISF-1.0-spec/XISF-1.0-spec.html
9
+ #------------------------------------------------------------------------------
10
+
11
+ package Image::ExifTool::XISF;
12
+
13
+ use strict;
14
+ use vars qw($VERSION);
15
+ use Image::ExifTool qw(:DataAccess :Utils);
16
+ use Image::ExifTool::XMP;
17
+
18
+ $VERSION = '1.00';
19
+
20
+ # XISF tags (ref 1)
21
+ %Image::ExifTool::XISF::Main = (
22
+ GROUPS => { 0 => 'XML', 1 => 'XML', 2 => 'Image' },
23
+ VARS => { LONG_TAGS => 1 },
24
+ NOTES => q{
25
+ This table lists some standard Extensible Image Serialization Format (XISF)
26
+ tags, but ExifTool will extract any other tags found. See
27
+ L<https://pixinsight.com/xisf/> for the specification.
28
+ },
29
+ ImageGeometry => { },
30
+ ImageSampleFormat => { },
31
+ ImageBounds => { },
32
+ ImageImageType => { Name => 'ImageType' },
33
+ ImageColorSpace => { Name => 'ColorSpace' },
34
+ ImageLocation => { },
35
+ ImageResolutionHorizontal => 'XResolution',
36
+ ImageResolutionVertical => 'YResolution',
37
+ ImageResolutionUnit => 'ResolutionUnit',
38
+ ImageICCProfile => {
39
+ Name => 'ICC_Profile',
40
+ ValueConv => 'Image::ExifTool::XMP::DecodeBase64($val)',
41
+ Binary => 1,
42
+ },
43
+ ImageICCProfileLocation => { Name => 'ICCProfileLocation' },
44
+ ImagePixelStorage => { },
45
+ ImageOffset => { Name => 'ImagePixelOffset' },
46
+ ImageOrientation => { Name => 'Orientation' },
47
+ ImageId => { Name => 'ImageID' },
48
+ ImageUuid => { Name => 'UUID' },
49
+ ImageData => { Binary => 1 },
50
+ 'CreationTime' => {
51
+ Name => 'CreateDate',
52
+ Shift => 'Time',
53
+ Groups => { 2 => 'Time' },
54
+ ValueConv => 'Image::ExifTool::XMP::ConvertXMPDate($val)',
55
+ PrintConv => '$self->ConvertDateTime($val)',
56
+ },
57
+ CreatorApplication => { },
58
+ Abstract => { },
59
+ AccessRights => { },
60
+ Authors => { Groups => { 2 => 'Author' } },
61
+ BibliographicReferences => { },
62
+ BriefDescription => { },
63
+ CompressionLevel => { },
64
+ CompressionCodecs => { },
65
+ Contributors => { Groups => { 2 => 'Author' } },
66
+ Copyright => { Groups => { 2 => 'Author' } },
67
+ CreatorModule => { },
68
+ CreatorOS => { },
69
+ Description => { },
70
+ Keywords => { },
71
+ Languages => { },
72
+ License => { },
73
+ OriginalCreationTime => {
74
+ Name => 'DateTimeOriginal',
75
+ Description => 'Date/Time Original',
76
+ Shift => 'Time',
77
+ Groups => { 2 => 'Time' },
78
+ ValueConv => 'Image::ExifTool::XMP::ConvertXMPDate($val)',
79
+ PrintConv => '$self->ConvertDateTime($val)',
80
+ },
81
+ RelatedResources => { },
82
+ Title => { },
83
+ );
84
+
85
+ #------------------------------------------------------------------------------
86
+ # Handle properties in XISF metadata structures
87
+ # Inputs: 0) attribute list ref, 1) attr hash ref,
88
+ # 2) property name ref, 3) property value ref
89
+ # Returns: true if value was changed
90
+ sub HandleXISFAttrs($$$$)
91
+ {
92
+ my ($attrList, $attrs, $prop, $valPt) = @_;
93
+ return 0 unless defined $$attrs{id};
94
+ my ($changed, $a);
95
+ # use "id" as the tag name, "value" as the value, and ignore "type"
96
+ $$prop = $$attrs{id};
97
+ $$prop =~ s/^XISF://; # remove XISF namespace
98
+ if (defined $$attrs{value}) {
99
+ $$valPt = $$attrs{value};
100
+ $changed = 1;
101
+ }
102
+ my @attrs = @$attrList;
103
+ @$attrList = ( );
104
+ foreach $a (@attrs) {
105
+ if ($a eq 'id' or $a eq 'value' or $a eq 'type') {
106
+ delete $$attrs{$a};
107
+ } else {
108
+ push @$attrList, $a;
109
+ }
110
+ }
111
+ return $changed;
112
+ }
113
+
114
+ #------------------------------------------------------------------------------
115
+ # Read information in a XISF document
116
+ # Inputs: 0) ExifTool ref, 1) dirInfo ref
117
+ # Returns: 1 on success, 0 if this wasn't a valid XISF file
118
+ sub ProcessXISF($$)
119
+ {
120
+ my ($et, $dirInfo) = @_;
121
+ my $raf = $$dirInfo{RAF};
122
+ my $buff;
123
+
124
+ return 0 unless $raf->Read($buff, 16) == 16 and $buff =~ /^XISF0100/;
125
+ $et->SetFileType();
126
+ SetByteOrder('II');
127
+ my $tagTablePtr = GetTagTable('Image::ExifTool::XISF::Main');
128
+ my $hdrLen = Get32u(\$buff, 8);
129
+ $raf->Read($buff, $hdrLen) == $hdrLen or $et->Warn('Error reading XISF header'), return 1;
130
+ $et->FoundTag(XML => $buff);
131
+ my %dirInfo = (
132
+ DataPt => \$buff,
133
+ IgnoreProp => { xisf => 1, Metadata => 1, Property => 1 },
134
+ XMPParseOpts => { AttrProc => \&HandleXISFAttrs },
135
+ );
136
+ Image::ExifTool::XMP::ProcessXMP($et, \%dirInfo, $tagTablePtr);
137
+ my $geo = $$et{VALUE}{ImageGeometry};
138
+ if ($geo) {
139
+ my ($w, $h, $n) = split /:/, $geo;
140
+ $et->FoundTag(ImageWidth => $w);
141
+ $et->FoundTag(ImageHeight => $h);
142
+ $et->FoundTag(NumPlanes => $n);
143
+ }
144
+ return 1;
145
+ }
146
+
147
+ 1; # end
148
+
149
+ __END__
150
+
151
+ =head1 NAME
152
+
153
+ Image::ExifTool::XISF - Read Extensible Image Serialization Format metadata
154
+
155
+ =head1 SYNOPSIS
156
+
157
+ This module is used by Image::ExifTool
158
+
159
+ =head1 DESCRIPTION
160
+
161
+ This module contains definitions required by Image::ExifTool to read meta
162
+ information from XISF (Extensible Image Serialization Format) images.
163
+
164
+ =head1 AUTHOR
165
+
166
+ Copyright 2003-2023, Phil Harvey (philharvey66 at gmail.com)
167
+
168
+ This library is free software; you can redistribute it and/or modify it
169
+ under the same terms as Perl itself.
170
+
171
+ =head1 REFERENCES
172
+
173
+ =over 4
174
+
175
+ =item L<https://pixinsight.com/doc/docs/XISF-1.0-spec/XISF-1.0-spec.html>
176
+
177
+ =back
178
+
179
+ =head1 SEE ALSO
180
+
181
+ L<Image::ExifTool::TagNames/XISF Tags>,
182
+ L<Image::ExifTool(3pm)|Image::ExifTool>
183
+
184
+ =cut
185
+