exiftool-vendored.exe 12.67.0 → 12.70.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.
- package/bin/exiftool_files/Changes +79 -10
- package/bin/exiftool_files/README +7 -7
- package/bin/exiftool_files/exiftool.pl +37 -31
- package/bin/exiftool_files/lib/Image/ExifTool/CBOR.pm +18 -2
- package/bin/exiftool_files/lib/Image/ExifTool/Canon.pm +87 -16
- package/bin/exiftool_files/lib/Image/ExifTool/DJI.pm +3 -2
- package/bin/exiftool_files/lib/Image/ExifTool/DNG.pm +25 -2
- package/bin/exiftool_files/lib/Image/ExifTool/EXE.pm +54 -6
- package/bin/exiftool_files/lib/Image/ExifTool/Exif.pm +175 -14
- package/bin/exiftool_files/lib/Image/ExifTool/FujiFilm.pm +158 -20
- package/bin/exiftool_files/lib/Image/ExifTool/GIF.pm +5 -1
- package/bin/exiftool_files/lib/Image/ExifTool/Geotag.pm +16 -11
- package/bin/exiftool_files/lib/Image/ExifTool/ID3.pm +70 -7
- package/bin/exiftool_files/lib/Image/ExifTool/InDesign.pm +1 -1
- package/bin/exiftool_files/lib/Image/ExifTool/JPEG.pm +1 -1
- package/bin/exiftool_files/lib/Image/ExifTool/Jpeg2000.pm +30 -15
- package/bin/exiftool_files/lib/Image/ExifTool/MakerNotes.pm +2 -2
- package/bin/exiftool_files/lib/Image/ExifTool/Nikon.pm +82 -22
- package/bin/exiftool_files/lib/Image/ExifTool/Olympus.pm +7 -1
- package/bin/exiftool_files/lib/Image/ExifTool/PNG.pm +3 -1
- package/bin/exiftool_files/lib/Image/ExifTool/Panasonic.pm +22 -9
- package/bin/exiftool_files/lib/Image/ExifTool/Pentax.pm +6 -1
- package/bin/exiftool_files/lib/Image/ExifTool/PhotoMechanic.pm +2 -1
- package/bin/exiftool_files/lib/Image/ExifTool/QuickTime.pm +92 -55
- package/bin/exiftool_files/lib/Image/ExifTool/README +14 -5
- package/bin/exiftool_files/lib/Image/ExifTool/RIFF.pm +60 -10
- package/bin/exiftool_files/lib/Image/ExifTool/Sony.pm +152 -46
- package/bin/exiftool_files/lib/Image/ExifTool/TagLookup.pm +6955 -6713
- package/bin/exiftool_files/lib/Image/ExifTool/TagNames.pod +878 -334
- package/bin/exiftool_files/lib/Image/ExifTool/Text.pm +4 -5
- package/bin/exiftool_files/lib/Image/ExifTool/Validate.pm +23 -20
- package/bin/exiftool_files/lib/Image/ExifTool/WriteCanonRaw.pl +2 -2
- package/bin/exiftool_files/lib/Image/ExifTool/WriteExif.pl +14 -4
- package/bin/exiftool_files/lib/Image/ExifTool/WriteQuickTime.pl +3 -0
- package/bin/exiftool_files/lib/Image/ExifTool/WriteRIFF.pl +31 -6
- package/bin/exiftool_files/lib/Image/ExifTool/Writer.pl +40 -14
- package/bin/exiftool_files/lib/Image/ExifTool/XISF.pm +185 -0
- package/bin/exiftool_files/lib/Image/ExifTool/XMP.pm +67 -2
- package/bin/exiftool_files/lib/Image/ExifTool/XMP2.pl +35 -0
- package/bin/exiftool_files/lib/Image/ExifTool.pm +92 -45
- package/bin/exiftool_files/lib/Image/ExifTool.pod +14 -8
- package/package.json +3 -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
|
|
29
|
-
tags are not produced. To avoid long processing delays, ExifTool will
|
|
30
|
-
a minor warning and process only the first 64
|
|
31
|
-
|
|
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.
|
|
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)',
|
|
147
|
-
0xc61a => '(int16u|int32u|rational64u)',
|
|
148
|
-
0xc61d => 'int(16|32)u',
|
|
149
|
-
0xc61f => '(int16u|int32u|rational64u)',
|
|
150
|
-
0xc620 => '(int16u|int32u|rational64u)',
|
|
151
|
-
0xc628 => '(int16u|rational64u)',
|
|
152
|
-
0xc634 => 'int8u',
|
|
153
|
-
0xc640 => '',
|
|
154
|
-
0xc660 => '',
|
|
155
|
-
0xc68b => '(string|int8u)',
|
|
156
|
-
0xc68d => 'int(16|32)u',
|
|
157
|
-
0xc68e => 'int(16|32)u',
|
|
158
|
-
0xc6d2 => '',
|
|
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 <=
|
|
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 $$
|
|
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
|
-
|
|
934
|
-
|
|
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->
|
|
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
|
-
|
|
174
|
-
|
|
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 =>
|
|
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
|
|
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
|
-
$$
|
|
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})
|
|
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
|
-
|
|
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 $
|
|
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
|
|
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
|
+
|