exiftool_vendored 13.14.0 → 13.17.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.
- checksums.yaml +4 -4
- data/bin/Changes +45 -3
- data/bin/MANIFEST +4 -2
- data/bin/META.json +1 -1
- data/bin/META.yml +1 -1
- data/bin/README +2 -2
- data/bin/exiftool +4 -3
- data/bin/lib/Image/ExifTool/BuildTagLookup.pm +16 -16
- data/bin/lib/Image/ExifTool/Canon.pm +7 -4
- data/bin/lib/Image/ExifTool/EXE.pm +16 -2
- data/bin/lib/Image/ExifTool/ID3.pm +4 -4
- data/bin/lib/Image/ExifTool/JPEG.pm +7 -1
- data/bin/lib/Image/ExifTool/MIE.pm +6 -3
- data/bin/lib/Image/ExifTool/Nikon.pm +399 -179
- data/bin/lib/Image/ExifTool/NikonCustom.pm +5 -5
- data/bin/lib/Image/ExifTool/Panasonic.pm +7 -1
- data/bin/lib/Image/ExifTool/QuickTime.pm +70 -24
- data/bin/lib/Image/ExifTool/QuickTimeStream.pl +10 -10
- data/bin/lib/Image/ExifTool/README +4 -1
- data/bin/lib/Image/ExifTool/RIFF.pm +11 -1
- data/bin/lib/Image/ExifTool/Samsung.pm +4 -0
- data/bin/lib/Image/ExifTool/Sony.pm +1 -1
- data/bin/lib/Image/ExifTool/TagLookup.pm +4818 -4811
- data/bin/lib/Image/ExifTool/TagNames.pod +8080 -7908
- data/bin/lib/Image/ExifTool/Trailer.pm +318 -0
- data/bin/lib/Image/ExifTool/WriteQuickTime.pl +49 -23
- data/bin/lib/Image/ExifTool/WriteRIFF.pl +3 -1
- data/bin/lib/Image/ExifTool/Writer.pl +8 -7
- data/bin/lib/Image/ExifTool/XMP2.pl +3 -2
- data/bin/lib/Image/ExifTool.pm +138 -71
- data/bin/lib/Image/ExifTool.pod +53 -52
- data/bin/perl-Image-ExifTool.spec +1 -1
- data/lib/exiftool_vendored/version.rb +1 -1
- metadata +3 -3
- data/bin/lib/Image/ExifTool/Vivo.pm +0 -124
@@ -0,0 +1,318 @@
|
|
1
|
+
#------------------------------------------------------------------------------
|
2
|
+
# File: Trailer.pm
|
3
|
+
#
|
4
|
+
# Description: Read JPEG trailer written by various makes of phone
|
5
|
+
#
|
6
|
+
# Revisions: 2025-01-27 - P. Harvey Created
|
7
|
+
#------------------------------------------------------------------------------
|
8
|
+
|
9
|
+
package Image::ExifTool::Trailer;
|
10
|
+
|
11
|
+
use strict;
|
12
|
+
use vars qw($VERSION);
|
13
|
+
use Image::ExifTool qw(:DataAccess :Utils);
|
14
|
+
|
15
|
+
$VERSION = '1.00';
|
16
|
+
|
17
|
+
%Image::ExifTool::Trailer::Vivo = (
|
18
|
+
GROUPS => { 0 => 'Trailer', 1 => 'Vivo', 2 => 'Image' },
|
19
|
+
VARS => { NO_ID => 1 },
|
20
|
+
NOTES => 'Information written in JPEG trailer by some Vivo phones.',
|
21
|
+
# (don't know for sure what type of image this is, but it is in JPEG format)
|
22
|
+
HDRImage => {
|
23
|
+
Notes => 'highlights of HDR image',
|
24
|
+
Groups => { 2 => 'Preview' },
|
25
|
+
Binary => 1,
|
26
|
+
},
|
27
|
+
JSONInfo => { },
|
28
|
+
HiddenData => {
|
29
|
+
Notes => 'hidden in EXIF, not in trailer. This data is lost if the file is edited',
|
30
|
+
Groups => { 0 => 'EXIF' },
|
31
|
+
},
|
32
|
+
);
|
33
|
+
|
34
|
+
%Image::ExifTool::Trailer::OnePlus = (
|
35
|
+
GROUPS => { 0 => 'Trailer', 1 => 'OnePlus', 2 => 'Image' },
|
36
|
+
NOTES => 'Information written in JPEG trailer by some OnePlus phones.',
|
37
|
+
JSONInfo => { },
|
38
|
+
'private.emptyspace' => { # length of the entire OnePlus trailer
|
39
|
+
Name => 'OnePlusTrailerLen',
|
40
|
+
ValueConv => 'length $val == 4 ? unpack("N", $val) : $val',
|
41
|
+
Unknown => 1,
|
42
|
+
},
|
43
|
+
'watermark.device' => {
|
44
|
+
Name => 'Device',
|
45
|
+
ValueConv => '"0x" . join(" ", unpack("H10Z*", $val))',
|
46
|
+
Format => 'string',
|
47
|
+
},
|
48
|
+
);
|
49
|
+
|
50
|
+
# Google and/or Android information in JPEG trailer
|
51
|
+
%Image::ExifTool::Trailer::Google = (
|
52
|
+
GROUPS => { 0 => 'Trailer', 1 => 'Google', 2 => 'Image' },
|
53
|
+
NOTES => q{
|
54
|
+
Google-defined information written in the trailer of JPEG images by some
|
55
|
+
phones. This information is referenced by DirectoryItem entries in the XMP.
|
56
|
+
Note that some of this information may also be referenced from other
|
57
|
+
metadata formats, and hence may be extracted twice. For example,
|
58
|
+
MotionPhotoVideo may also exist within a Samsung trailer as
|
59
|
+
EmbbededVideoFile, or GainMapImage may also exist in an MPF trailer as
|
60
|
+
MPImage2.
|
61
|
+
},
|
62
|
+
MotionPhoto => { Name => 'MotionPhotoVideo', Groups => { 2 => 'Video' } },
|
63
|
+
GainMap => { Name => 'GainMapImage', Groups => { 2 => 'Preview' } },
|
64
|
+
Depth => { Name => 'DepthMapImage', Groups => { 2 => 'Preview' } },
|
65
|
+
Confidence => { Name => 'ConfidenceMapImage',Groups => { 2 => 'Preview' } },
|
66
|
+
'android/depthmap' => { Name => 'DepthMapImage', Groups => { 2 => 'Preview' } },
|
67
|
+
'android/confidencemap' => { Name => 'ConfidenceMapImage', Groups => { 2 => 'Preview' } },
|
68
|
+
);
|
69
|
+
|
70
|
+
#------------------------------------------------------------------------------
|
71
|
+
# Process Vivo trailer
|
72
|
+
# Inputs: 0) ExifTool object reference, 1) dirInfo reference
|
73
|
+
# Returns: 1 on success, 0 on failure, -1 if we must scan for the start
|
74
|
+
# of the trailer to set the ExifTool TrailerStart member
|
75
|
+
# - takes Offset as positive offset from end of trailer to end of file,
|
76
|
+
# and returns DataPos and DirLen, and updates OutFile when writing
|
77
|
+
sub ProcessVivo($$)
|
78
|
+
{
|
79
|
+
my ($et, $dirInfo) = @_;
|
80
|
+
my $raf = $$dirInfo{RAF};
|
81
|
+
my $buff;
|
82
|
+
|
83
|
+
# return now unless we are at a position to scan for the trailer
|
84
|
+
# (must scan because the trailer footer doesn't indicate the trailer length)
|
85
|
+
return -1 unless $$dirInfo{ScanForTrailer};
|
86
|
+
|
87
|
+
my $pos = $$et{TrailerStart} or return 0;
|
88
|
+
my $len = $$et{FileEnd} - $pos - $$dirInfo{Offset};
|
89
|
+
$raf->Seek($pos, 0) or return 0;
|
90
|
+
return 0 unless $len > 0 and $len < 1e7 and $raf->Read($buff, $len) == $len and
|
91
|
+
$buff =~ /\xff{4}\x1b\*9HWfu\x84\x93\xa2\xb1$/ and # validate footer
|
92
|
+
$buff =~ /(streamdata|vivo\{")/g; # find start
|
93
|
+
my $start = pos($buff) - length($1);
|
94
|
+
if ($start) {
|
95
|
+
$pos += $start;
|
96
|
+
$len -= $start;
|
97
|
+
$buff = substr($buff, $start);
|
98
|
+
}
|
99
|
+
# set trailer position and length
|
100
|
+
@$dirInfo{'DataPos','DirLen'} = ($pos, $len);
|
101
|
+
|
102
|
+
# let ProcessTrailers copy or delete this trailer
|
103
|
+
return -1 if $$dirInfo{OutFile};
|
104
|
+
|
105
|
+
$et->DumpTrailer($dirInfo) if $$et{OPTIONS}{Verbose} or $$et{HTML_DUMP};
|
106
|
+
my $tbl = GetTagTable('Image::ExifTool::Trailer::Vivo');
|
107
|
+
pos($buff) = 0; # rewind search to start of buffer
|
108
|
+
if ($buff =~ /^streamdata\xff\xd8\xff/ and $buff =~ /\xff\xd9stream(info|coun)/g) {
|
109
|
+
$et->HandleTag($tbl, HDRImage => substr($buff, 10, pos($buff)-20));
|
110
|
+
}
|
111
|
+
# continue looking for Vivo JSON data
|
112
|
+
if ($buff =~ /vivo\{"/g) {
|
113
|
+
my $jsonStart = pos($buff) - 2;
|
114
|
+
if ($buff =~ /\}\0/g) {
|
115
|
+
my $jsonLen = pos($buff) - 1 - $jsonStart;
|
116
|
+
$et->HandleTag($tbl, JSONInfo => substr($buff, $jsonStart, $jsonLen));
|
117
|
+
}
|
118
|
+
}
|
119
|
+
return 1;
|
120
|
+
}
|
121
|
+
|
122
|
+
#------------------------------------------------------------------------------
|
123
|
+
# Process OnePlus trailer
|
124
|
+
# Inputs: 0) ExifTool object reference, 1) dirInfo reference
|
125
|
+
# Returns: 1 on success, 0 on failure, -1 if we must scan for the start
|
126
|
+
# of the trailer to set the ExifTool TrailerStart member
|
127
|
+
# - takes Offset as positive offset from end of trailer to end of file,
|
128
|
+
# and returns DataPos and DirLen, and updates OutFile when writing
|
129
|
+
sub ProcessOnePlus($$)
|
130
|
+
{
|
131
|
+
my ($et, $dirInfo) = @_;
|
132
|
+
my $raf = $$dirInfo{RAF};
|
133
|
+
my ($buff, $buf2);
|
134
|
+
|
135
|
+
# return now unless we are at a position to scan for the trailer
|
136
|
+
# (must scan because the trailer footer doesn't indicate the entire trailer length)
|
137
|
+
return -1 unless $$dirInfo{ScanForTrailer};
|
138
|
+
|
139
|
+
# return -1 to let ProcessTrailers copy or delete the entire trailer
|
140
|
+
return -1 if $$dirInfo{OutFile};
|
141
|
+
|
142
|
+
my $start = $$et{TrailerStart} or return 0;
|
143
|
+
$raf->Seek(-8-$$dirInfo{Offset}, 2) and $raf->Read($buff, 8) == 8 or return 0;
|
144
|
+
my $end = $raf->Tell(); # (same as FileEnd - Offset)
|
145
|
+
|
146
|
+
my $dump = ($$et{OPTIONS}{Verbose} or $$et{HTML_DUMP});
|
147
|
+
my $tagTable = GetTagTable('Image::ExifTool::Trailer::OnePlus');
|
148
|
+
my $trailLen = 0;
|
149
|
+
if ($buff =~ /^jxrs...\0$/) {
|
150
|
+
my $jlen = unpack('x4V', $buff);
|
151
|
+
my $maxOff = 0;
|
152
|
+
if ($jlen < $end-$start and $jlen > 8 and $raf->Seek($end-$jlen) and
|
153
|
+
$raf->Read($buff, $jlen-8) == $jlen-8)
|
154
|
+
{
|
155
|
+
$buff =~ s/\0+$//; # remove trailing null(s)
|
156
|
+
require Image::ExifTool::Import;
|
157
|
+
my $list = Image::ExifTool::Import::ReadJSONObject(undef, \$buff);
|
158
|
+
if (ref $list eq 'ARRAY') {
|
159
|
+
$$_{offset} and $$_{offset} > $maxOff and $maxOff = $$_{offset} foreach @$list;
|
160
|
+
$trailLen = $maxOff + $jlen;
|
161
|
+
if ($dump and $trailLen) {
|
162
|
+
$et->DumpTrailer({
|
163
|
+
RAF => $raf,
|
164
|
+
DirName => 'OnePlus',
|
165
|
+
DataPos => $end-$trailLen,
|
166
|
+
DirLen => $trailLen,
|
167
|
+
});
|
168
|
+
}
|
169
|
+
$et->HandleTag($tagTable, JSONInfo => $buff);
|
170
|
+
foreach (@$list) {
|
171
|
+
my ($off, $name, $len) = @$_{qw(offset name length)};
|
172
|
+
next unless $off and $name and $len;
|
173
|
+
if ($raf->Seek($end-$jlen-$off) and $raf->Read($buf2, $len) == $len) {
|
174
|
+
$et->HandleTag($tagTable, $name, $buf2, DataPos => $end-$jlen-$off, DataPt => \$buf2);
|
175
|
+
}
|
176
|
+
}
|
177
|
+
} else {
|
178
|
+
$et->HandleTag($tagTable, JSONInfo => $buff);
|
179
|
+
$et->Warn('Error parsing OnePlus JSON information');
|
180
|
+
}
|
181
|
+
}
|
182
|
+
}
|
183
|
+
@$dirInfo{'DataPos','DirLen'} = ($end - $trailLen, $trailLen);
|
184
|
+
|
185
|
+
return 1;
|
186
|
+
}
|
187
|
+
|
188
|
+
#------------------------------------------------------------------------------
|
189
|
+
# Process Google trailer
|
190
|
+
# Inputs: 0) ExifTool object reference, 1) dirInfo reference
|
191
|
+
# Returns: 1 on success, 0 on failure, -1 if we must scan for the start
|
192
|
+
# of the trailer to set the ExifTool TrailerStart member
|
193
|
+
# - this trailer won't be identified when writing because XMP isn't extracted then
|
194
|
+
sub ProcessGoogle($$)
|
195
|
+
{
|
196
|
+
my ($et, $dirInfo) = @_;
|
197
|
+
my $raf = $$dirInfo{RAF};
|
198
|
+
my $info = $$et{VALUE};
|
199
|
+
|
200
|
+
my ($tag, $mime, $len, $pad) = @$info{qw(DirectoryItemSemantic DirectoryItemMime
|
201
|
+
DirectoryItemLength DirectoryItemPadding)};
|
202
|
+
|
203
|
+
unless (ref $tag eq 'ARRAY' and ref $mime eq 'ARRAY') {
|
204
|
+
undef $pad;
|
205
|
+
($tag, $mime, $len) = @$info{qw(ContainerDirectoryItemDataURI
|
206
|
+
ContainerDirectoryItemMime ContainerDirectoryItemLength)};
|
207
|
+
unless (ref $mime eq 'ARRAY' and ref $tag eq 'ARRAY') {
|
208
|
+
delete $$et{ProcessGoogleTrailer};
|
209
|
+
return 0;
|
210
|
+
}
|
211
|
+
}
|
212
|
+
# we need to know TrailerStart to be able to read/write this trailer
|
213
|
+
return -1 unless $$dirInfo{ScanForTrailer};
|
214
|
+
|
215
|
+
delete $$et{ProcessGoogleTrailer}; # reset flag to process the Google trailer
|
216
|
+
|
217
|
+
return -1 if $$dirInfo{OutFile};
|
218
|
+
|
219
|
+
# sometimes DirectoryItemLength is missing the Primary entry
|
220
|
+
$len = [ $len ] unless ref $len eq 'ARRAY';
|
221
|
+
unshift @$len, 0 while @$len < @$mime;
|
222
|
+
|
223
|
+
my $start = $$et{TrailerStart} or return 0;
|
224
|
+
my $end = $$et{FileEnd}; # (ignore Offset for now because some entries may run into other trailers)
|
225
|
+
|
226
|
+
my $dump = ($$et{OPTIONS}{Verbose} or $$et{HTML_DUMP});
|
227
|
+
my $tagTable = GetTagTable('Image::ExifTool::Trailer::Google');
|
228
|
+
|
229
|
+
# (ignore first entry: "Primary" or "primary_image")
|
230
|
+
my ($i, $pos, $buff, $regex, $grp, $type);
|
231
|
+
for ($i=1, $pos=0; defined $$mime[$i]; ++$i) {
|
232
|
+
my $more = $end - $start - $pos;
|
233
|
+
last if $more < 16;
|
234
|
+
next unless $$len[$i] and defined $$tag[$i];
|
235
|
+
last if $$len[$i] > $more;
|
236
|
+
$raf->Seek($start+$pos) and $raf->Read($buff, 16) == 16 and $raf->Seek($start+$pos) or last;
|
237
|
+
if ($$mime[$i] eq 'image/jpeg') {
|
238
|
+
$regex = '\xff\xd8\xff[\xdb\xe0\xe1]';
|
239
|
+
} elsif ($$mime[$i] eq 'video/mp4') {
|
240
|
+
$regex = '\0\0\0.ftyp(mp42|isom)';
|
241
|
+
} else {
|
242
|
+
$et->Warn("Google trailer $$tag[$i] $$mime[$i] not handled");
|
243
|
+
next;
|
244
|
+
}
|
245
|
+
if ($buff =~ /^$regex/s) {
|
246
|
+
last unless $raf->Read($buff, $$len[$i]) == $$len[$i];
|
247
|
+
} else {
|
248
|
+
last if $pos; # don't skip unknown information again
|
249
|
+
last unless $raf->Read($buff, $more) == $more;
|
250
|
+
last unless $buff =~ /($regex)/sg;
|
251
|
+
$pos += pos($buff) - length($1);
|
252
|
+
$more = $end - $start - $pos;
|
253
|
+
last if $$len[$i] > $end - $start - $pos;
|
254
|
+
$buff = substr($buff, $pos, $$len[$i]);
|
255
|
+
}
|
256
|
+
unless ($$tagTable{$$tag[$i]}) {
|
257
|
+
my $name = $$tag[$i];
|
258
|
+
$name =~ s/([^A-Za-z])([a-z])/$1\u$2/g; # capitalize words
|
259
|
+
$name = Image::ExifTool::MakeTagName($$tag[$i]);
|
260
|
+
if ($$mime[$i] eq 'image/jpeg') {
|
261
|
+
($type, $grp) = ('Image', 'Preview');
|
262
|
+
} else {
|
263
|
+
($type, $grp) = ('Video', 'Video');
|
264
|
+
}
|
265
|
+
$et->VPrint(0, $$et{INDENT}, "[adding Google:$name]\n");
|
266
|
+
AddTagToTable($tagTable, $$tag[$i], { Name => "$name$type", Groups => { 2 => $grp } });
|
267
|
+
}
|
268
|
+
$dump and $et->DumpTrailer({
|
269
|
+
RAF => $raf,
|
270
|
+
DirName => $$tag[$i],
|
271
|
+
DataPos => $start + $pos,
|
272
|
+
DirLen => $$len[$i],
|
273
|
+
});
|
274
|
+
$et->HandleTag($tagTable, $$tag[$i], \$buff, { DataPos => $start + $pos, DataPt => \$buff });
|
275
|
+
# (haven't seen non-zero padding, but I assume this is how it works
|
276
|
+
$pos += $$len[$i] + (($pad and $$pad[$i]) ? $$pad[$i] : 0);
|
277
|
+
}
|
278
|
+
if (defined $$tag[$i] and defined $$mime[$i]) {
|
279
|
+
$et->Warn("Error reading $$tag[$i] $$mime[$i] from trailer", 1);
|
280
|
+
}
|
281
|
+
return 0 unless $pos;
|
282
|
+
|
283
|
+
@$dirInfo{'DataPos','DirLen'} = ($start, $pos);
|
284
|
+
|
285
|
+
return 1;
|
286
|
+
}
|
287
|
+
|
288
|
+
1; # end
|
289
|
+
|
290
|
+
__END__
|
291
|
+
|
292
|
+
=head1 NAME
|
293
|
+
|
294
|
+
Image::ExifTool::Trailer - Read JPEG trailer written by various phone makes
|
295
|
+
|
296
|
+
=head1 SYNOPSIS
|
297
|
+
|
298
|
+
This module is used by Image::ExifTool
|
299
|
+
|
300
|
+
=head1 DESCRIPTION
|
301
|
+
|
302
|
+
This module contains definitions required by Image::ExifTool to read
|
303
|
+
metadata the trailer written by some Vivo, OnePlus and Google phones.
|
304
|
+
|
305
|
+
=head1 AUTHOR
|
306
|
+
|
307
|
+
Copyright 2003-2025, Phil Harvey (philharvey66 at gmail.com)
|
308
|
+
|
309
|
+
This library is free software; you can redistribute it and/or modify it
|
310
|
+
under the same terms as Perl itself.
|
311
|
+
|
312
|
+
=head1 SEE ALSO
|
313
|
+
|
314
|
+
L<Image::ExifTool::TagNames/Trailer Tags>,
|
315
|
+
L<Image::ExifTool(3pm)|Image::ExifTool>
|
316
|
+
|
317
|
+
=cut
|
318
|
+
|
@@ -378,6 +378,9 @@ sub WriteNextbase($$$)
|
|
378
378
|
# Write Meta Keys to add/delete entries as necessary ('mdta' handler) (ref PH)
|
379
379
|
# Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
|
380
380
|
# Returns: updated keys box data
|
381
|
+
# Note: Residual entries may be left in the 'keys' directory when deleting tags
|
382
|
+
# with language codes because the language code(s) are not known until the
|
383
|
+
# corresponding ItemList entry(s) are processed
|
381
384
|
sub WriteKeys($$$)
|
382
385
|
{
|
383
386
|
my ($et, $dirInfo, $tagTablePtr) = @_;
|
@@ -901,15 +904,10 @@ sub WriteQuickTime($$$)
|
|
901
904
|
my $createKeys = 0;
|
902
905
|
my ($rtnVal, $rtnErr) = $dataPt ? (undef, undef) : (1, 0);
|
903
906
|
|
904
|
-
# check for
|
907
|
+
# check for trailer at end of file
|
905
908
|
if ($raf) {
|
906
|
-
|
907
|
-
|
908
|
-
substr($buf2, 8) eq '8db42d694ccc418790edff439fe026bf')
|
909
|
-
{
|
910
|
-
$trailer = [ 'Insta360', $raf->Tell() - unpack('V',$buf2) ];
|
911
|
-
}
|
912
|
-
$raf->Seek($pos, 0) or return 0;
|
909
|
+
$trailer = IdentifyTrailers($raf);
|
910
|
+
$trailer and not ref $trailer and $et->Error($trailer), return 1;
|
913
911
|
}
|
914
912
|
if ($dataPt) {
|
915
913
|
$raf = File::RandomAccess->new($dataPt);
|
@@ -1063,6 +1061,10 @@ sub WriteQuickTime($$$)
|
|
1063
1061
|
$et->Error("Can't yet write compressed movie metadata");
|
1064
1062
|
return $rtnVal;
|
1065
1063
|
} elsif ($tag eq 'wide') {
|
1064
|
+
if ($size) {
|
1065
|
+
$et->Warn("Incorrect size for 'wide' atom ($size bytes)");
|
1066
|
+
$raf->Seek($size, 1) or $et->Error('Truncated wide atom');
|
1067
|
+
}
|
1066
1068
|
next; # drop 'wide' tag
|
1067
1069
|
}
|
1068
1070
|
|
@@ -1598,16 +1600,26 @@ sub WriteQuickTime($$$)
|
|
1598
1600
|
if (($lastTag eq 'mdat' or $lastTag eq 'moov') and not $dataPt and (not $$tagTablePtr{$tag} or
|
1599
1601
|
ref $$tagTablePtr{$tag} eq 'HASH' and $$tagTablePtr{$tag}{Unknown}))
|
1600
1602
|
{
|
1601
|
-
# identify other known trailers
|
1603
|
+
# identify other known trailers from their first bytes
|
1602
1604
|
$buf2 = '';
|
1603
1605
|
$raf->Seek($lastPos,0) and $raf->Read($buf2,8);
|
1606
|
+
my ($type, $len);
|
1604
1607
|
if ($buf2 eq 'CCCCCCCC') {
|
1605
|
-
$
|
1608
|
+
$type = 'Kenwood';
|
1606
1609
|
} elsif ($buf2 =~ /^(gpsa|gps0|gsen|gsea)...\0/s) {
|
1607
|
-
$
|
1610
|
+
$type = 'RIFF';
|
1608
1611
|
} else {
|
1609
|
-
$
|
1612
|
+
$type = 'Unknown';
|
1610
1613
|
}
|
1614
|
+
# determine length of this trailer
|
1615
|
+
if ($trailer) {
|
1616
|
+
$len = $$trailer[1] - $lastPos; # runs to start of next trailer
|
1617
|
+
} else {
|
1618
|
+
$raf->Seek(0, 2) or $et->Error('Seek error'), return $dataPt ? undef : 1;
|
1619
|
+
$len = $raf->Tell() - $lastPos; # runs to end of file
|
1620
|
+
}
|
1621
|
+
# add to start of linked list of trailers
|
1622
|
+
$trailer = [ $type, $lastPos, $len, $trailer ];
|
1611
1623
|
} else {
|
1612
1624
|
$et->Error($errStr);
|
1613
1625
|
return $dataPt ? undef : 1;
|
@@ -2069,21 +2081,35 @@ sub WriteQuickTime($$$)
|
|
2069
2081
|
# write the stuff that must come last
|
2070
2082
|
Write($outfile, $writeLast) or $rtnVal = 0 if $writeLast;
|
2071
2083
|
|
2072
|
-
# copy
|
2073
|
-
|
2074
|
-
# are we deleting the
|
2084
|
+
# copy trailers if necessary
|
2085
|
+
while ($rtnVal and $trailer) {
|
2086
|
+
# are we deleting the trailers?
|
2075
2087
|
my $nvTrail = $et->GetNewValueHash($Image::ExifTool::Extra{Trailer});
|
2076
|
-
if ($$et{DEL_GROUP}{Trailer} or
|
2088
|
+
if ($$et{DEL_GROUP}{Trailer} or $$et{DEL_GROUP}{$$trailer[0]} or
|
2089
|
+
($nvTrail and not ($$nvTrail{Value} and $$nvTrail{Value}[0])))
|
2090
|
+
{
|
2077
2091
|
$et->Warn("Deleted $$trailer[0] trailer", 1);
|
2078
|
-
|
2079
|
-
$
|
2080
|
-
|
2081
|
-
|
2082
|
-
|
2092
|
+
++$$et{CHANGED};
|
2093
|
+
$trailer = $$trailer[3];
|
2094
|
+
next;
|
2095
|
+
}
|
2096
|
+
$raf->Seek($$trailer[1], 0) or $rtnVal = 0, last;
|
2097
|
+
if ($$trailer[0] eq 'MIE') {
|
2098
|
+
require Image::ExifTool::MIE;
|
2099
|
+
my %dirInfo = ( RAF => $raf, OutFile => $outfile );
|
2100
|
+
my $result = Image::ExifTool::MIE::ProcessMIE($et, \%dirInfo);
|
2101
|
+
$result > 0 or $et->Error('Error writing MIE trailer'), $rtnVal = 0, last;
|
2083
2102
|
} else {
|
2084
|
-
$
|
2103
|
+
$et->Warn(sprintf('Copying %s trailer from offset 0x%x (%d bytes)', @$trailer[0..2]), 1);
|
2104
|
+
my $len = $$trailer[2];
|
2105
|
+
while ($len) {
|
2106
|
+
my $n = $len > 65536 ? 65536 : $len;
|
2107
|
+
$raf->Read($buf2, $n) == $n and Write($outfile, $buf2) or $rtnVal = 0, last;
|
2108
|
+
$len -= $n;
|
2109
|
+
}
|
2110
|
+
$rtnVal or $et->Error("Error copying $$trailer[0] trailer"), last;
|
2085
2111
|
}
|
2086
|
-
$
|
2112
|
+
$trailer = $$trailer[3]; # step to next trailer in linked list
|
2087
2113
|
}
|
2088
2114
|
return $rtnVal;
|
2089
2115
|
}
|
@@ -324,8 +324,10 @@ sub WriteRIFF($$)
|
|
324
324
|
$raf->Read($buff, 6) == 6 or $et->Error('Truncated VP8L chunk'), return 1;
|
325
325
|
$outsize += 6;
|
326
326
|
if ($buff =~ /^\x2f/s) {
|
327
|
+
my $word = Get32u(\$buff, 2);
|
327
328
|
$imageWidth = (Get16u(\$buff, 1) & 0x3fff) + 1;
|
328
|
-
$imageHeight = ((
|
329
|
+
$imageHeight = (($word >> 6) & 0x3fff) + 1;
|
330
|
+
$has{ALPH} = 1 if $word & 0x100000; # set alpha flag if necessary
|
329
331
|
}
|
330
332
|
$len2 -= 6;
|
331
333
|
}
|
@@ -140,10 +140,10 @@ my @delGroups = qw(
|
|
140
140
|
Adobe AFCP APP0 APP1 APP2 APP3 APP4 APP5 APP6 APP7 APP8 APP9 APP10 APP11 APP12
|
141
141
|
APP13 APP14 APP15 AudioKeys CanonVRD CIFF Ducky EXIF ExifIFD File FlashPix
|
142
142
|
FotoStation GlobParamIFD GPS ICC_Profile IFD0 IFD1 Insta360 InteropIFD IPTC
|
143
|
-
ItemList JFIF Jpeg2000 JUMBF Keys MakerNotes Meta MetaIFD Microsoft
|
144
|
-
Nextbase NikonApp NikonCapture PDF PDF-update PhotoMechanic
|
145
|
-
PNG-pHYs PrintIM QuickTime RMETA RSRC SEAL SubIFD Trailer
|
146
|
-
Vivo XML XML-* XMP XMP-*
|
143
|
+
ItemList iTunes JFIF Jpeg2000 JUMBF Keys MakerNotes Meta MetaIFD Microsoft
|
144
|
+
MIE MPF Nextbase NikonApp NikonCapture PDF PDF-update PhotoMechanic
|
145
|
+
Photoshop PNG PNG-pHYs PrintIM QuickTime RMETA RSRC SEAL SubIFD Trailer
|
146
|
+
UserData VideoKeys Vivo XML XML-* XMP XMP-*
|
147
147
|
);
|
148
148
|
# family 2 group names that we can delete
|
149
149
|
my @delGroup2 = qw(
|
@@ -2856,7 +2856,7 @@ sub GetAllGroups($;$)
|
|
2856
2856
|
}
|
2857
2857
|
}
|
2858
2858
|
delete $allGroups{'*'}; # (not a real group)
|
2859
|
-
return sort keys %allGroups;
|
2859
|
+
return sort { lc $a cmp lc $b } keys %allGroups;
|
2860
2860
|
}
|
2861
2861
|
|
2862
2862
|
#------------------------------------------------------------------------------
|
@@ -2874,7 +2874,7 @@ sub GetNewGroups($)
|
|
2874
2874
|
# Returns: List of group names (sorted alphabetically)
|
2875
2875
|
sub GetDeleteGroups()
|
2876
2876
|
{
|
2877
|
-
return sort @delGroups, @delGroup2;
|
2877
|
+
return sort { lc $a cmp lc $b } @delGroups, @delGroup2;
|
2878
2878
|
}
|
2879
2879
|
|
2880
2880
|
#------------------------------------------------------------------------------
|
@@ -3340,7 +3340,8 @@ sub InsertTagValues($$;$$$$)
|
|
3340
3340
|
} elsif ($tag eq 'self') {
|
3341
3341
|
$val = $et; # ("$self{var}" or "$file1:self{var}" in string)
|
3342
3342
|
} else {
|
3343
|
-
# get the tag value
|
3343
|
+
# get the tag value (note: this direct access allows excluded tags
|
3344
|
+
# to be accessed if the case is correct and a group name is not used)
|
3344
3345
|
$val = $et->GetValue($tag, $type);
|
3345
3346
|
unless (defined $val) {
|
3346
3347
|
# check for tag name with different case
|
@@ -1995,7 +1995,8 @@ my %sACDSeeRegionStruct = (
|
|
1995
1995
|
Struct => {
|
1996
1996
|
STRUCT_NAME => 'DeviceItem',
|
1997
1997
|
NAMESPACE => { Item => 'http://ns.google.com/photos/dd/1.0/item/' },
|
1998
|
-
|
1998
|
+
# use this as a key to process Google trailer
|
1999
|
+
Mime => { RawConv => '$$self{ProcessGoogleTrailer} = $val' },
|
1999
2000
|
Length => { Writable => 'integer' },
|
2000
2001
|
Padding => { Writable => 'integer' },
|
2001
2002
|
DataURI => { },
|
@@ -2149,7 +2150,7 @@ my %sACDSeeRegionStruct = (
|
|
2149
2150
|
STRUCT_NAME => 'Item',
|
2150
2151
|
# (use 'GItem' to avoid conflict with Google Device Container Item)
|
2151
2152
|
NAMESPACE => { GItem => 'http://ns.google.com/photos/1.0/container/item/'},
|
2152
|
-
Mime
|
2153
|
+
Mime => { RawConv => '$$self{ProcessGoogleTrailer} = $val' },
|
2153
2154
|
Semantic => { },
|
2154
2155
|
Length => { Writable => 'integer' },
|
2155
2156
|
Label => { },
|