exiftool-vendored.exe 12.73.0 → 12.78.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 +94 -6
- package/bin/exiftool_files/README +46 -45
- package/bin/exiftool_files/config_files/example.config +10 -2
- package/bin/exiftool_files/exiftool.pl +138 -87
- package/bin/exiftool_files/lib/File/RandomAccess.pm +31 -5
- package/bin/exiftool_files/lib/File/RandomAccess.pod +4 -4
- package/bin/exiftool_files/lib/Image/ExifTool/7Z.pm +3 -3
- package/bin/exiftool_files/lib/Image/ExifTool/AFCP.pm +2 -2
- package/bin/exiftool_files/lib/Image/ExifTool/BZZ.pm +2 -2
- package/bin/exiftool_files/lib/Image/ExifTool/BuildTagLookup.pm +7 -7
- package/bin/exiftool_files/lib/Image/ExifTool/Canon.pm +17 -13
- package/bin/exiftool_files/lib/Image/ExifTool/CanonVRD.pm +8 -2
- package/bin/exiftool_files/lib/Image/ExifTool/DICOM.pm +2 -2
- package/bin/exiftool_files/lib/Image/ExifTool/DNG.pm +4 -4
- package/bin/exiftool_files/lib/Image/ExifTool/Exif.pm +54 -5
- package/bin/exiftool_files/lib/Image/ExifTool/FLIR.pm +2 -2
- package/bin/exiftool_files/lib/Image/ExifTool/Fixup.pm +3 -3
- package/bin/exiftool_files/lib/Image/ExifTool/FlashPix.pm +3 -3
- package/bin/exiftool_files/lib/Image/ExifTool/FujiFilm.pm +8 -3
- package/bin/exiftool_files/lib/Image/ExifTool/GPS.pm +5 -3
- package/bin/exiftool_files/lib/Image/ExifTool/Geolocation.dat +0 -0
- package/bin/exiftool_files/lib/Image/ExifTool/Geolocation.pm +237 -0
- package/bin/exiftool_files/lib/Image/ExifTool/Geotag.pm +4 -4
- package/bin/exiftool_files/lib/Image/ExifTool/HtmlDump.pm +7 -4
- package/bin/exiftool_files/lib/Image/ExifTool/ID3.pm +2 -2
- package/bin/exiftool_files/lib/Image/ExifTool/Import.pm +9 -6
- package/bin/exiftool_files/lib/Image/ExifTool/JSON.pm +11 -11
- package/bin/exiftool_files/lib/Image/ExifTool/Jpeg2000.pm +51 -12
- package/bin/exiftool_files/lib/Image/ExifTool/MIE.pm +3 -3
- package/bin/exiftool_files/lib/Image/ExifTool/MWG.pm +1 -0
- package/bin/exiftool_files/lib/Image/ExifTool/MacOS.pm +19 -4
- package/bin/exiftool_files/lib/Image/ExifTool/MinoltaRaw.pm +2 -2
- package/bin/exiftool_files/lib/Image/ExifTool/Nikon.pm +5 -3
- package/bin/exiftool_files/lib/Image/ExifTool/NikonCustom.pm +3 -3
- package/bin/exiftool_files/lib/Image/ExifTool/Ogg.pm +4 -3
- package/bin/exiftool_files/lib/Image/ExifTool/Olympus.pm +3 -1
- package/bin/exiftool_files/lib/Image/ExifTool/PDF.pm +59 -9
- package/bin/exiftool_files/lib/Image/ExifTool/PLIST.pm +3 -3
- package/bin/exiftool_files/lib/Image/ExifTool/PanasonicRaw.pm +3 -3
- package/bin/exiftool_files/lib/Image/ExifTool/Pentax.pm +1 -1
- package/bin/exiftool_files/lib/Image/ExifTool/PhaseOne.pm +2 -2
- package/bin/exiftool_files/lib/Image/ExifTool/Photoshop.pm +3 -3
- package/bin/exiftool_files/lib/Image/ExifTool/PostScript.pm +2 -2
- package/bin/exiftool_files/lib/Image/ExifTool/QuickTime.pm +223 -117
- package/bin/exiftool_files/lib/Image/ExifTool/QuickTimeStream.pl +260 -242
- package/bin/exiftool_files/lib/Image/ExifTool/RSRC.pm +2 -2
- package/bin/exiftool_files/lib/Image/ExifTool/Samsung.pm +4 -4
- package/bin/exiftool_files/lib/Image/ExifTool/Shift.pl +1 -2
- package/bin/exiftool_files/lib/Image/ExifTool/SigmaRaw.pm +3 -3
- package/bin/exiftool_files/lib/Image/ExifTool/Sony.pm +3 -3
- package/bin/exiftool_files/lib/Image/ExifTool/TagInfoXML.pm +2 -2
- package/bin/exiftool_files/lib/Image/ExifTool/TagLookup.pm +85 -8
- package/bin/exiftool_files/lib/Image/ExifTool/TagNames.pod +148 -8
- package/bin/exiftool_files/lib/Image/ExifTool/WriteCanonRaw.pl +1 -1
- package/bin/exiftool_files/lib/Image/ExifTool/WriteExif.pl +26 -23
- package/bin/exiftool_files/lib/Image/ExifTool/WritePDF.pl +1 -1
- package/bin/exiftool_files/lib/Image/ExifTool/WriteQuickTime.pl +15 -2
- package/bin/exiftool_files/lib/Image/ExifTool/WriteXMP.pl +4 -2
- package/bin/exiftool_files/lib/Image/ExifTool/Writer.pl +77 -52
- package/bin/exiftool_files/lib/Image/ExifTool/XMP.pm +2 -1
- package/bin/exiftool_files/lib/Image/ExifTool/XMP2.pl +9 -0
- package/bin/exiftool_files/lib/Image/ExifTool/ZIP.pm +6 -6
- package/bin/exiftool_files/lib/Image/ExifTool.pm +204 -63
- package/bin/exiftool_files/lib/Image/ExifTool.pod +118 -94
- package/package.json +5 -5
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
#------------------------------------------------------------------------------
|
|
2
|
+
# File: Geolocation.pm
|
|
3
|
+
#
|
|
4
|
+
# Description: Look up geolocation information based on a GPS position
|
|
5
|
+
#
|
|
6
|
+
# Revisions: 2024-03-03 - P. Harvey Created
|
|
7
|
+
#
|
|
8
|
+
# References: https://download.geonames.org/export/
|
|
9
|
+
#
|
|
10
|
+
# Notes: Set $Image::ExifTool::Geolocation::databaseFile to override
|
|
11
|
+
# default database file (lib/Image/ExifTool/Geolocation.dat)
|
|
12
|
+
#
|
|
13
|
+
# Based on data from geonames.org Creative Commons databases,
|
|
14
|
+
# reformatted as follows in the Geolocation.dat file:
|
|
15
|
+
#
|
|
16
|
+
# Header: GeolocationV.VV\tNNNN\n - V.VV=version, NNNN=num city entries
|
|
17
|
+
#
|
|
18
|
+
# NNNN City entries:
|
|
19
|
+
# 1. int16u[2] - longitude.latitude (converted to 0-64k range)
|
|
20
|
+
# 2. int8u - low byte of time zone number
|
|
21
|
+
# 3. int8u - 100's=time zone high bit, population: 10's=num zeros, 1's=sig digit
|
|
22
|
+
# 4. UTF8 City name, terminated by tab
|
|
23
|
+
# 5. 2-character country code
|
|
24
|
+
# 6. Region code, terminated by newline
|
|
25
|
+
# End of section marker - "\0\0\0\0\x01"
|
|
26
|
+
# Country entries:
|
|
27
|
+
# 1. 2-character country code
|
|
28
|
+
# 2. Country name, terminated by newline
|
|
29
|
+
# End of section marker - "\0\0\0\0\x02"
|
|
30
|
+
# Region entries:
|
|
31
|
+
# 1. 2-character country code
|
|
32
|
+
# 2. Region code, terminated by tab
|
|
33
|
+
# 3. Region name, terminated by newline
|
|
34
|
+
# End of section marker - "\0\0\0\0\x03"
|
|
35
|
+
# Time zone entries:
|
|
36
|
+
# 1. Time zone name, terminated by newline
|
|
37
|
+
# End of file marker - "\0\0\0\0\0"
|
|
38
|
+
#------------------------------------------------------------------------------
|
|
39
|
+
|
|
40
|
+
package Image::ExifTool::Geolocation;
|
|
41
|
+
|
|
42
|
+
use strict;
|
|
43
|
+
use vars qw($VERSION $databaseFile);
|
|
44
|
+
|
|
45
|
+
$VERSION = '1.00';
|
|
46
|
+
|
|
47
|
+
my (@cityLookup, %countryLookup, %adminLookup, @timezoneLookup);
|
|
48
|
+
|
|
49
|
+
# get path name for database file from lib/Image/ExifTool/Geolocation.dat by default,
|
|
50
|
+
# or according to $Image::ExifTool::Geolocation::databaseFile if specified
|
|
51
|
+
my $datfile = $databaseFile;
|
|
52
|
+
unless ($datfile) {
|
|
53
|
+
$datfile = $INC{'Image/ExifTool/Geolocation.pm'};
|
|
54
|
+
$datfile or $datfile = 'Geolocation.pm', warn("Error getting Geolocation directory\n");
|
|
55
|
+
$datfile =~ s/\.pm$/\.dat/;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
# open geolocation database and verify header
|
|
59
|
+
open DATFILE, "<$datfile" or warn("Error reading $datfile\n"), return 0;
|
|
60
|
+
binmode DATFILE;
|
|
61
|
+
my $line = <DATFILE>;
|
|
62
|
+
unless ($line =~ /^Geolocation(\d+\.\d+)\t(\d+)/) {
|
|
63
|
+
warn("Bad format Geolocation database\n");
|
|
64
|
+
close(DATFILE);
|
|
65
|
+
return 0;
|
|
66
|
+
}
|
|
67
|
+
my $ncity = $2;
|
|
68
|
+
|
|
69
|
+
# read city database
|
|
70
|
+
for (;;) {
|
|
71
|
+
$line = <DATFILE>;
|
|
72
|
+
last if length($line) == 6 and $line =~ /\0\0\0\0/;
|
|
73
|
+
$line .= <DATFILE> while length($line) < 7;
|
|
74
|
+
chomp $line;
|
|
75
|
+
push @cityLookup, $line;
|
|
76
|
+
}
|
|
77
|
+
@cityLookup == $ncity or warn("Bad number of entries in Geolocation database\n"), return 0;
|
|
78
|
+
# read countries
|
|
79
|
+
for (;;) {
|
|
80
|
+
$line = <DATFILE>;
|
|
81
|
+
last if length($line) == 6 and $line =~ /\0\0\0\0/;
|
|
82
|
+
chomp $line;
|
|
83
|
+
$countryLookup{substr($line,0,2)} = substr($line,2);
|
|
84
|
+
}
|
|
85
|
+
# read regions
|
|
86
|
+
for (;;) {
|
|
87
|
+
$line = <DATFILE>;
|
|
88
|
+
last if length($line) == 6 and $line =~ /\0\0\0\0/;
|
|
89
|
+
chomp $line;
|
|
90
|
+
my ($code, $region) = split /\t/, $line;
|
|
91
|
+
$adminLookup{$code} = $region;
|
|
92
|
+
}
|
|
93
|
+
# read time zones
|
|
94
|
+
for (;;) {
|
|
95
|
+
$line = <DATFILE>;
|
|
96
|
+
last if length($line) == 6 and $line =~ /\0\0\0\0/;
|
|
97
|
+
chomp $line;
|
|
98
|
+
push @timezoneLookup, $line;
|
|
99
|
+
}
|
|
100
|
+
close DATFILE;
|
|
101
|
+
|
|
102
|
+
#------------------------------------------------------------------------------
|
|
103
|
+
# Look up lat/lon in geolocation database
|
|
104
|
+
# Inputs: 0) Latitude, 1) longitude, 2) optional min population,
|
|
105
|
+
# 3) optional max distance (km)
|
|
106
|
+
# Returns: 0) UTF8 city name (or undef if geolocation is unsuccessful),
|
|
107
|
+
# 1) UTF8 state, province or region (or undef),
|
|
108
|
+
# 2) country code, 3) country name (undef is possible),
|
|
109
|
+
# 4) time zone name (empty string possible), 5) approx population,
|
|
110
|
+
# 6) approx distance (km), 7) approximate compass bearing (or undef),
|
|
111
|
+
# 8/9) approx lat/lon
|
|
112
|
+
sub Geolocate($$;$$)
|
|
113
|
+
{
|
|
114
|
+
my ($lat, $lon, $pop, $km) = @_;
|
|
115
|
+
my ($minPop, $maxDist2);
|
|
116
|
+
my $earthCirc = 40000; # earth circumference in km
|
|
117
|
+
|
|
118
|
+
if ($pop) {
|
|
119
|
+
# convert population minimum to a 2-digit code
|
|
120
|
+
my $dig = substr($pop, 0, 1);
|
|
121
|
+
my $zer = length($pop) - 1;
|
|
122
|
+
# round up if necessary
|
|
123
|
+
if (length($pop) > 1 and substr($pop, 1, 1) >= 5) {
|
|
124
|
+
++$dig > 9 and $dig = 1, ++$zer;
|
|
125
|
+
}
|
|
126
|
+
$minPop = $zer.$dig;
|
|
127
|
+
}
|
|
128
|
+
if ($km) {
|
|
129
|
+
# convert max distance to reduced coordinate units
|
|
130
|
+
my $tmp = $km * 2 * 65536 / $earthCirc;
|
|
131
|
+
$maxDist2 = $tmp * $tmp;
|
|
132
|
+
}
|
|
133
|
+
my $cos = cos($lat * 3.14159 / 180); # cosine factor for longitude distances
|
|
134
|
+
# reduce lat/lon to the range 0-65536
|
|
135
|
+
$lat = int(($lat + 90) / 180 * 65536 + 0.5) & 0xffff;
|
|
136
|
+
$lon = int(($lon + 180) / 360 * 65536 + 0.5) & 0xffff;
|
|
137
|
+
my $coord = pack('n2',$lon,$lat); # pack for comparison with binary database values
|
|
138
|
+
# binary search to find closest longitude
|
|
139
|
+
my ($n0, $n1) = (0, scalar(@cityLookup)-1);
|
|
140
|
+
while ($n1 - $n0 > 1) {
|
|
141
|
+
my $n = int(($n0 + $n1) / 2);
|
|
142
|
+
if ($coord lt $cityLookup[$n]) {
|
|
143
|
+
$n1 = $n;
|
|
144
|
+
} else {
|
|
145
|
+
$n0 = $n;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
# step backward then forward through database to find nearest city
|
|
149
|
+
my ($minDist2, $minN, @dxy);
|
|
150
|
+
my ($inc, $end, $n) = (-1, -1, $n0+1);
|
|
151
|
+
for (;;) {
|
|
152
|
+
if (($n += $inc) == $end) {
|
|
153
|
+
last if $inc == 1;
|
|
154
|
+
($inc, $end, $n) = (1, scalar(@cityLookup), $n1);
|
|
155
|
+
}
|
|
156
|
+
my ($x,$y) = unpack('n2', $cityLookup[$n]);
|
|
157
|
+
my ($dy,$dx) = ($y-$lat, $x-$lon);
|
|
158
|
+
$dx += 65536 if $dx < -32768; # measure the short way around the world
|
|
159
|
+
$dx -= 65536 if $dx > 32768;
|
|
160
|
+
$dx = 2 * $cos * $dx; # adjust for longitude spacing
|
|
161
|
+
my $dx2 = $dx * $dx;
|
|
162
|
+
my $dist2 = $dy * $dy + $dx2;
|
|
163
|
+
if (defined $minDist2) {
|
|
164
|
+
# searched far enough if longitude alone is further than best distance
|
|
165
|
+
$dx2 > $minDist2 and $n = $end - $inc, next;
|
|
166
|
+
} elsif (defined $maxDist2) {
|
|
167
|
+
$dx2 > $maxDist2 and $n = $end - $inc, next;
|
|
168
|
+
next if $dist2 > $maxDist2; # ignore if distance is too great
|
|
169
|
+
}
|
|
170
|
+
# ignore if population is below threshold
|
|
171
|
+
next if $minPop and $minPop > unpack('x5C', $cityLookup[$n]) % 100;
|
|
172
|
+
if (not defined $minDist2 or $minDist2 > $dist2) {
|
|
173
|
+
$minDist2 = $dist2;
|
|
174
|
+
@dxy = ($dx, $dy);
|
|
175
|
+
$minN = $n;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
return () unless defined $minN;
|
|
179
|
+
|
|
180
|
+
my ($ln,$lt,$tn,$pc) = unpack('n2C2', $cityLookup[$minN]);
|
|
181
|
+
my ($city, $code) = split /\t/, substr($cityLookup[$minN],6);
|
|
182
|
+
my $ctry = substr($code,0,2);
|
|
183
|
+
my $rgn = $adminLookup{$code};
|
|
184
|
+
my $po2 = substr($pc, -1) . (length($pc) > 1 ? '0' x substr($pc, -2, 1) : '');
|
|
185
|
+
$tn += 256 if $pc > 99;
|
|
186
|
+
my $be; # calculate bearing to geolocated city
|
|
187
|
+
if ($dxy[0] or $dxy[1]) {
|
|
188
|
+
$be = atan2($dxy[0],$dxy[1]) * 180 / 3.14159;
|
|
189
|
+
$be += 360 if $be < 0;
|
|
190
|
+
$be = int($be + 0.5);
|
|
191
|
+
}
|
|
192
|
+
$lt = sprintf('%.3f', $lt * 180 / 65536 - 90);
|
|
193
|
+
$ln = sprintf('%.3f', $ln * 360 / 65536 - 180);
|
|
194
|
+
$km = sprintf('%.1f', sqrt($minDist2) * $earthCirc / (2 * 65536));
|
|
195
|
+
|
|
196
|
+
return($city,$rgn,$ctry,$countryLookup{$ctry},$timezoneLookup[$tn],$po2,$km,$be,$lt,$ln);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
__END__
|
|
200
|
+
|
|
201
|
+
=head1 NAME
|
|
202
|
+
|
|
203
|
+
Image::ExifTool::Geolocation - Look up geolocation based on GPS position
|
|
204
|
+
|
|
205
|
+
=head1 SYNOPSIS
|
|
206
|
+
|
|
207
|
+
This module is used by the Image::ExifTool Geolocation feature.
|
|
208
|
+
|
|
209
|
+
=head1 DESCRIPTION
|
|
210
|
+
|
|
211
|
+
This module contains the code to convert GPS coordinates to city, region,
|
|
212
|
+
country, time zone, etc. It uses a database derived from geonames.org,
|
|
213
|
+
modified to reduce the size as much as possible.
|
|
214
|
+
|
|
215
|
+
=head1 AUTHOR
|
|
216
|
+
|
|
217
|
+
Copyright 2003-2024, Phil Harvey (philharvey66 at gmail.com)
|
|
218
|
+
|
|
219
|
+
This library is free software; you can redistribute it and/or modify it
|
|
220
|
+
under the same terms as Perl itself. Geolocation.dat is based on data
|
|
221
|
+
from geonames.org with a Creative Commons license.
|
|
222
|
+
|
|
223
|
+
=head1 REFERENCES
|
|
224
|
+
|
|
225
|
+
=over 4
|
|
226
|
+
|
|
227
|
+
=item L<https://download.geonames.org/export/>
|
|
228
|
+
|
|
229
|
+
=back
|
|
230
|
+
|
|
231
|
+
=head1 SEE ALSO
|
|
232
|
+
|
|
233
|
+
L<Image::ExifTool(3pm)|Image::ExifTool>
|
|
234
|
+
|
|
235
|
+
=cut
|
|
236
|
+
|
|
237
|
+
1; #end
|
|
@@ -29,7 +29,7 @@ use vars qw($VERSION);
|
|
|
29
29
|
use Image::ExifTool qw(:Public);
|
|
30
30
|
use Image::ExifTool::GPS;
|
|
31
31
|
|
|
32
|
-
$VERSION = '1.
|
|
32
|
+
$VERSION = '1.74';
|
|
33
33
|
|
|
34
34
|
sub JITTER() { return 2 } # maximum time jitter
|
|
35
35
|
|
|
@@ -174,7 +174,7 @@ sub LoadTrackLog($$;$)
|
|
|
174
174
|
# $val is track file name
|
|
175
175
|
if ($et->Open(\*EXIFTOOL_TRKFILE, $val)) {
|
|
176
176
|
$trackFile = $val;
|
|
177
|
-
$raf =
|
|
177
|
+
$raf = File::RandomAccess->new(\*EXIFTOOL_TRKFILE);
|
|
178
178
|
unless ($raf->Read($_, 256)) {
|
|
179
179
|
close EXIFTOOL_TRKFILE;
|
|
180
180
|
return "Empty track file '${val}'";
|
|
@@ -202,7 +202,7 @@ sub LoadTrackLog($$;$)
|
|
|
202
202
|
}
|
|
203
203
|
unless ($from) {
|
|
204
204
|
# set up RAF for reading log file in memory
|
|
205
|
-
$raf =
|
|
205
|
+
$raf = File::RandomAccess->new(\$val);
|
|
206
206
|
$from = 'data';
|
|
207
207
|
}
|
|
208
208
|
|
|
@@ -1109,7 +1109,7 @@ sub SetGeoValues($$;$)
|
|
|
1109
1109
|
$iExt = $i1;
|
|
1110
1110
|
}
|
|
1111
1111
|
if (abs($time - $tn) > $geoMaxExtSecs) {
|
|
1112
|
-
$err or $err = 'Time is too far from nearest GPS fix'.' '.abs($time-$tn).' '.$geoMaxExtSecs;
|
|
1112
|
+
$err or $err = 'Time is too far from nearest GPS fix'.' '.abs($time-$tn).' > '.$geoMaxExtSecs;
|
|
1113
1113
|
$et->VPrint(2, ' Nearest fix: ', PrintFixTime($tn), "\n") if $verbose > 2;
|
|
1114
1114
|
$fix = { } if $$geotag{DateTimeOnly};
|
|
1115
1115
|
} else {
|
|
@@ -13,7 +13,7 @@ use vars qw($VERSION);
|
|
|
13
13
|
use Image::ExifTool; # only for FinishTiffDump()
|
|
14
14
|
use Image::ExifTool::HTML qw(EscapeHTML);
|
|
15
15
|
|
|
16
|
-
$VERSION = '1.
|
|
16
|
+
$VERSION = '1.42';
|
|
17
17
|
|
|
18
18
|
sub DumpTable($$$;$$$$$$);
|
|
19
19
|
sub Open($$$;@);
|
|
@@ -314,6 +314,8 @@ sub Print($$;$$$$$)
|
|
|
314
314
|
$title = 'HtmlDump' unless $title;
|
|
315
315
|
$level or $level = 0;
|
|
316
316
|
my $tell = $raf->Tell();
|
|
317
|
+
$raf->Seek(0,2) or $$self{ERROR} = 'Seek error', return -1;
|
|
318
|
+
my $fileLen = $raf->Tell();
|
|
317
319
|
my $pos = 0;
|
|
318
320
|
my $dataEnd = $dataPos + ($dataPt ? length($$dataPt) : 0);
|
|
319
321
|
# initialize member variables
|
|
@@ -352,6 +354,7 @@ sub Print($$;$$$$$)
|
|
|
352
354
|
} else {
|
|
353
355
|
last;
|
|
354
356
|
}
|
|
357
|
+
$start = $fileLen if $start > $fileLen; # handle case of bad start offset
|
|
355
358
|
my $len = $start - $pos;
|
|
356
359
|
if ($len > 0 and not $wasUnused) {
|
|
357
360
|
# we have a unused bytes before this data block
|
|
@@ -432,8 +435,7 @@ sub Print($$;$$$$$)
|
|
|
432
435
|
{
|
|
433
436
|
$err = $msg;
|
|
434
437
|
# reset $len to the actual length of available data
|
|
435
|
-
$
|
|
436
|
-
$len = $raf->Tell() - $start;
|
|
438
|
+
$len = $fileLen - $start;
|
|
437
439
|
$tip .= "<br>Error: Only $len bytes available!" if $tip;
|
|
438
440
|
next;
|
|
439
441
|
}
|
|
@@ -767,6 +769,7 @@ sub FinishTiffDump($$$)
|
|
|
767
769
|
PreviewImageStart => 'PreviewImageLength',
|
|
768
770
|
JpgFromRawStart => 'JpgFromRawLength',
|
|
769
771
|
OtherImageStart => 'OtherImageLength',
|
|
772
|
+
PreviewJXLStart => 'PreviewJXLLength',
|
|
770
773
|
ImageOffset => 'ImageByteCount',
|
|
771
774
|
AlphaOffset => 'AlphaByteCount',
|
|
772
775
|
MPImageStart => 'MPImageLength',
|
|
@@ -894,7 +897,7 @@ Image::ExifTool::HtmlDump - Dump information in hex to HTML page
|
|
|
894
897
|
=head1 SYNOPSIS
|
|
895
898
|
|
|
896
899
|
use Image::ExifTool::HtmlDump;
|
|
897
|
-
my $dump =
|
|
900
|
+
my $dump = Image::ExifTool::HtmlDump->new;
|
|
898
901
|
$dump->Add($start, $size, $comment);
|
|
899
902
|
$dump->Print($dumpInfo, $raf, $dataPt, $dataPos, $outfile);
|
|
900
903
|
|
|
@@ -18,7 +18,7 @@ use strict;
|
|
|
18
18
|
use vars qw($VERSION);
|
|
19
19
|
use Image::ExifTool qw(:DataAccess :Utils);
|
|
20
20
|
|
|
21
|
-
$VERSION = '1.
|
|
21
|
+
$VERSION = '1.61';
|
|
22
22
|
|
|
23
23
|
sub ProcessID3v2($$$);
|
|
24
24
|
sub ProcessPrivate($$$);
|
|
@@ -1408,7 +1408,7 @@ sub ProcessID3($$)
|
|
|
1408
1408
|
$$et{DoneID3} = 1;
|
|
1409
1409
|
|
|
1410
1410
|
# allow this to be called with either RAF or DataPt
|
|
1411
|
-
my $raf = $$dirInfo{RAF} ||
|
|
1411
|
+
my $raf = $$dirInfo{RAF} || File::RandomAccess->new($$dirInfo{DataPt});
|
|
1412
1412
|
my ($buff, %id3Header, %id3Trailer, $hBuff, $tBuff, $eBuff, $tagTablePtr);
|
|
1413
1413
|
my $rtnVal = 0;
|
|
1414
1414
|
my $hdrEnd = 0;
|
|
@@ -12,7 +12,7 @@ require Exporter;
|
|
|
12
12
|
|
|
13
13
|
use vars qw($VERSION @ISA @EXPORT_OK);
|
|
14
14
|
|
|
15
|
-
$VERSION = '1.
|
|
15
|
+
$VERSION = '1.12';
|
|
16
16
|
@ISA = qw(Exporter);
|
|
17
17
|
@EXPORT_OK = qw(ReadCSV ReadJSON);
|
|
18
18
|
|
|
@@ -38,13 +38,13 @@ sub ReadCSV($$;$$)
|
|
|
38
38
|
$raf = $file;
|
|
39
39
|
$file = 'CSV file';
|
|
40
40
|
} elsif (ref $file eq 'GLOB') {
|
|
41
|
-
$raf =
|
|
41
|
+
$raf = File::RandomAccess->new($file);
|
|
42
42
|
$file = 'CSV file';
|
|
43
43
|
} else {
|
|
44
44
|
open CSVFILE, $file or return "Error opening CSV file '${file}'";
|
|
45
45
|
binmode CSVFILE;
|
|
46
46
|
$openedFile = 1;
|
|
47
|
-
$raf =
|
|
47
|
+
$raf = File::RandomAccess->new(\*CSVFILE);
|
|
48
48
|
}
|
|
49
49
|
$delim = ',' unless defined $delim;
|
|
50
50
|
# set input record separator by first newline found in the file
|
|
@@ -238,7 +238,7 @@ Tok: for (;;) {
|
|
|
238
238
|
|
|
239
239
|
#------------------------------------------------------------------------------
|
|
240
240
|
# Read JSON file
|
|
241
|
-
# Inputs: 0) JSON file name, file ref or
|
|
241
|
+
# Inputs: 0) JSON file name, file ref, RAF ref or SCALAR ref, 1) database hash ref,
|
|
242
242
|
# 2) flag to delete "-" tags, 3) character set
|
|
243
243
|
# Returns: undef on success, or error string
|
|
244
244
|
sub ReadJSON($$;$$)
|
|
@@ -253,13 +253,16 @@ sub ReadJSON($$;$$)
|
|
|
253
253
|
$raf = $file;
|
|
254
254
|
$file = 'JSON file';
|
|
255
255
|
} elsif (ref $file eq 'GLOB') {
|
|
256
|
-
$raf =
|
|
256
|
+
$raf = File::RandomAccess->new($file);
|
|
257
257
|
$file = 'JSON file';
|
|
258
|
+
} elsif (ref $file eq 'SCALAR') {
|
|
259
|
+
$raf = File::RandomAccess->new($file);
|
|
260
|
+
$file = 'in memory';
|
|
258
261
|
} else {
|
|
259
262
|
open JSONFILE, $file or return "Error opening JSON file '${file}'";
|
|
260
263
|
binmode JSONFILE;
|
|
261
264
|
$openedFile = 1;
|
|
262
|
-
$raf =
|
|
265
|
+
$raf = File::RandomAccess->new(\*JSONFILE);
|
|
263
266
|
}
|
|
264
267
|
my $obj = ReadJSONObject($raf);
|
|
265
268
|
close JSONFILE if $openedFile;
|
|
@@ -14,7 +14,7 @@ use vars qw($VERSION);
|
|
|
14
14
|
use Image::ExifTool qw(:DataAccess :Utils);
|
|
15
15
|
use Image::ExifTool::Import;
|
|
16
16
|
|
|
17
|
-
$VERSION = '1.
|
|
17
|
+
$VERSION = '1.07';
|
|
18
18
|
|
|
19
19
|
sub ProcessJSON($$);
|
|
20
20
|
sub ProcessTag($$$$%);
|
|
@@ -60,15 +60,15 @@ sub FoundTag($$$$%)
|
|
|
60
60
|
# avoid conflict with special table entries
|
|
61
61
|
$tag .= '!' if $Image::ExifTool::specialTags{$tag};
|
|
62
62
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
63
|
+
unless ($$tagTablePtr{$tag}) {
|
|
64
|
+
my $name = $tag;
|
|
65
|
+
$name =~ tr/:/_/; # use underlines in place of colons in tag name
|
|
66
|
+
AddTagToTable($tagTablePtr, $tag, {
|
|
67
|
+
Name => Image::ExifTool::MakeTagName($name),
|
|
68
|
+
%flags,
|
|
69
|
+
Temporary => 1,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
72
|
$et->HandleTag($tagTablePtr, $tag, $val);
|
|
73
73
|
}
|
|
74
74
|
|
|
@@ -120,7 +120,7 @@ sub ProcessJSON($$)
|
|
|
120
120
|
my $buff = substr(${$$dirInfo{DataPt}}, $$dirInfo{DirStart}, $$dirInfo{DirLen});
|
|
121
121
|
$dataPt = \$buff;
|
|
122
122
|
}
|
|
123
|
-
$raf =
|
|
123
|
+
$raf = File::RandomAccess->new($dataPt);
|
|
124
124
|
# extract as a block if requested
|
|
125
125
|
my $blockName = $$dirInfo{BlockInfo} ? $$dirInfo{BlockInfo}{Name} : '';
|
|
126
126
|
my $blockExtract = $et->Options('BlockExtract');
|
|
@@ -16,7 +16,7 @@ use strict;
|
|
|
16
16
|
use vars qw($VERSION);
|
|
17
17
|
use Image::ExifTool qw(:DataAccess :Utils);
|
|
18
18
|
|
|
19
|
-
$VERSION = '1.
|
|
19
|
+
$VERSION = '1.39';
|
|
20
20
|
|
|
21
21
|
sub ProcessJpeg2000Box($$$);
|
|
22
22
|
sub ProcessJUMD($$$);
|
|
@@ -141,10 +141,13 @@ my %j2cMarker = (
|
|
|
141
141
|
Authenticity Initiative) JUMBF (JPEG Universal Metadata Box Format) metdata
|
|
142
142
|
is currently extracted from JPEG, PNG, TIFF-based (eg. TIFF, DNG),
|
|
143
143
|
QuickTime-based (eg. MP4, MOV, HEIF, AVIF), RIFF-based (eg. WAV, AVI, WebP),
|
|
144
|
-
GIF files and ID3v2 metadata. The suggested ExifTool
|
|
145
|
-
for reading C2PA metadata are C<-jumbf:all -G3 -b -j
|
|
146
|
-
metadata may be deleted from writable JPEG, PNG, WebP,
|
|
147
|
-
QuickTime-based files by deleting the JUMBF group with
|
|
144
|
+
PDF, SVG and GIF files, and ID3v2 metadata. The suggested ExifTool
|
|
145
|
+
command-line arguments for reading C2PA metadata are C<-jumbf:all -G3 -b -j
|
|
146
|
+
-u -struct>. This metadata may be deleted from writable JPEG, PNG, WebP,
|
|
147
|
+
TIFF-based, and QuickTime-based files by deleting the JUMBF group with
|
|
148
|
+
C<-jumbf:all=>. The C2PA JUMBF metadata may be extracted as a block via the
|
|
149
|
+
JUMBF tag. See L<https://c2pa.org/specifications/> for the C2PA
|
|
150
|
+
specification.
|
|
148
151
|
},
|
|
149
152
|
#
|
|
150
153
|
# NOTE: ONLY TAGS WITH "Format" DEFINED ARE EXTRACTED!
|
|
@@ -419,7 +422,7 @@ my %j2cMarker = (
|
|
|
419
422
|
Flags => [ 'Binary', 'Protected' ],
|
|
420
423
|
SubDirectory => { TagTable => 'Image::ExifTool::CBOR::Main' },
|
|
421
424
|
},
|
|
422
|
-
bfdb => { # used in JUMBF
|
|
425
|
+
bfdb => { # used in JUMBF
|
|
423
426
|
Name => 'BinaryDataType',
|
|
424
427
|
Notes => 'JUMBF, MIME type and optional file name',
|
|
425
428
|
Format => 'undef',
|
|
@@ -748,6 +751,7 @@ my %j2cMarker = (
|
|
|
748
751
|
},
|
|
749
752
|
# seen:
|
|
750
753
|
# cacb/cast/caas/cacl/casg/json-00110010800000aa00389b71
|
|
754
|
+
# (also brob- but not yet tested)
|
|
751
755
|
# 6579d6fbdba2446bb2ac1b82feeb89d1 - JPEG image
|
|
752
756
|
},
|
|
753
757
|
'label' => { Name => 'JUMDLabel' },
|
|
@@ -821,6 +825,7 @@ sub ProcessJUMD($$$)
|
|
|
821
825
|
$name =~ tr/-_a-zA-Z0-9//dc; # remove other illegal characters
|
|
822
826
|
$name =~ s/__/_/; # collapse double underlines
|
|
823
827
|
$name = ucfirst $name; # capitalize first letter
|
|
828
|
+
$name =~ s/C2pa/C2PA/; # capitalize C2PA
|
|
824
829
|
$name = "Tag$name" if length($name) < 2; # must at least 2 characters long
|
|
825
830
|
$$et{JUMBFLabel} = $name;
|
|
826
831
|
}
|
|
@@ -1017,11 +1022,23 @@ sub ProcessJpeg2000Box($$$)
|
|
|
1017
1022
|
my $dirStart = $$dirInfo{DirStart} || 0;
|
|
1018
1023
|
my $base = $$dirInfo{Base} || 0;
|
|
1019
1024
|
my $outfile = $$dirInfo{OutFile};
|
|
1025
|
+
my $dirName = $$dirInfo{DirName} || '';
|
|
1020
1026
|
my $dirEnd = $dirStart + $dirLen;
|
|
1021
1027
|
my ($err, $outBuff, $verbose, $doColour, $hash, $raf);
|
|
1022
1028
|
|
|
1023
|
-
|
|
1024
|
-
|
|
1029
|
+
if ($dataPt) {
|
|
1030
|
+
# save C2PA JUMBF as a block if requested
|
|
1031
|
+
if ($dirName eq 'JUMBF' and $$et{REQ_TAG_LOOKUP}{jumbf} and not $$dirInfo{NoBlockSave}) {
|
|
1032
|
+
if ($dirStart or $dirLen ne length($$dataPt)) {
|
|
1033
|
+
my $dat = substr($$dataPt, $dirStart, $dirLen);
|
|
1034
|
+
$et->FoundTag(JUMBF => \$dat);
|
|
1035
|
+
} else {
|
|
1036
|
+
$et->FoundTag(JUMBF => $dataPt);
|
|
1037
|
+
}
|
|
1038
|
+
}
|
|
1039
|
+
} else {
|
|
1040
|
+
$raf = $$dirInfo{RAF}; # read from RAF
|
|
1041
|
+
}
|
|
1025
1042
|
|
|
1026
1043
|
if ($outfile) {
|
|
1027
1044
|
unless ($raf) {
|
|
@@ -1030,7 +1047,7 @@ sub ProcessJpeg2000Box($$$)
|
|
|
1030
1047
|
$outfile = \$outBuff;
|
|
1031
1048
|
}
|
|
1032
1049
|
# determine if we will be writing colr box
|
|
1033
|
-
if (
|
|
1050
|
+
if ($dirName eq 'JP2Header') {
|
|
1034
1051
|
$doColour = 2 if defined $et->GetNewValue('ColorSpecMethod') or $et->GetNewValue('ICC_Profile') or
|
|
1035
1052
|
defined $et->GetNewValue('ColorSpecPrecedence') or defined $et->GetNewValue('ColorSpace') or
|
|
1036
1053
|
defined $et->GetNewValue('ColorSpecApproximation') or defined $et->GetNewValue('ColorSpecData');
|
|
@@ -1038,7 +1055,7 @@ sub ProcessJpeg2000Box($$$)
|
|
|
1038
1055
|
} else {
|
|
1039
1056
|
# (must not set verbose flag when writing!)
|
|
1040
1057
|
$verbose = $$et{OPTIONS}{Verbose};
|
|
1041
|
-
$et->VerboseDir(
|
|
1058
|
+
$et->VerboseDir($dirName) if $verbose;
|
|
1042
1059
|
# do hash if requested, but only for top-level image data
|
|
1043
1060
|
$hash = $$et{ImageDataHash} if $raf;
|
|
1044
1061
|
}
|
|
@@ -1187,7 +1204,7 @@ sub ProcessJpeg2000Box($$$)
|
|
|
1187
1204
|
# create new tag for JUMBF data values with name corresponding to JUMBFLabel
|
|
1188
1205
|
if ($tagInfo and $$et{JUMBFLabel} and (not $$tagInfo{SubDirectory} or $$tagInfo{BlockExtract})) {
|
|
1189
1206
|
$tagInfo = { %$tagInfo, Name => $$et{JUMBFLabel} . ($$tagInfo{JUMBF_Suffix} || '') };
|
|
1190
|
-
|
|
1207
|
+
($$tagInfo{Description} = Image::ExifTool::MakeDescription($$tagInfo{Name})) =~ s/C2 PA/C2PA/;
|
|
1191
1208
|
AddTagToTable($tagTablePtr, '_JUMBF_' . $$et{JUMBFLabel}, $tagInfo);
|
|
1192
1209
|
delete $$tagInfo{Protected}; # (must do this so -j -b returns JUMBF binary data)
|
|
1193
1210
|
$$tagInfo{TagID} = $boxID;
|
|
@@ -1491,6 +1508,28 @@ sub ProcessJXLCodestream($$)
|
|
|
1491
1508
|
return 1;
|
|
1492
1509
|
}
|
|
1493
1510
|
|
|
1511
|
+
#------------------------------------------------------------------------------
|
|
1512
|
+
# Read/write meta information from a C2PA/JUMBF file
|
|
1513
|
+
# Inputs: 0) ExifTool object reference, 1) dirInfo reference
|
|
1514
|
+
# Returns: 1 on success, 0 if this wasn't a valid JUMBF file
|
|
1515
|
+
sub ProcessJUMBF($$)
|
|
1516
|
+
{
|
|
1517
|
+
my ($et, $dirInfo) = @_;
|
|
1518
|
+
my $raf = $$dirInfo{RAF};
|
|
1519
|
+
my $hdr;
|
|
1520
|
+
|
|
1521
|
+
# check to be sure this is a valid JPG2000 file
|
|
1522
|
+
return 0 unless $raf->Read($hdr,20) == 20 and $raf->Seek(0,0);
|
|
1523
|
+
return 0 unless $hdr =~ /^.{4}jumb\0.{3}jumd(.{4})/;
|
|
1524
|
+
$et->SetFileType($1 eq 'c2pa' ? 'C2PA' : 'JUMBF');
|
|
1525
|
+
my %dirInfo = (
|
|
1526
|
+
RAF => $raf,
|
|
1527
|
+
DirName => 'JUMBF',
|
|
1528
|
+
);
|
|
1529
|
+
my $tagTablePtr = GetTagTable('Image::ExifTool::Jpeg2000::Main');
|
|
1530
|
+
return $et->ProcessDirectory(\%dirInfo, $tagTablePtr);
|
|
1531
|
+
}
|
|
1532
|
+
|
|
1494
1533
|
#------------------------------------------------------------------------------
|
|
1495
1534
|
# Read/write meta information from a JPEG 2000 image
|
|
1496
1535
|
# Inputs: 0) ExifTool object reference, 1) dirInfo reference
|
|
@@ -1583,7 +1622,7 @@ sub ProcessJXL($$)
|
|
|
1583
1622
|
$$et{IsJXL} = 2;
|
|
1584
1623
|
my $buff = "\0\0\0\x0cJXL \x0d\x0a\x87\x0a\0\0\0\x14ftypjxl \0\0\0\0jxl ";
|
|
1585
1624
|
# add metadata to empty ISO BMFF container
|
|
1586
|
-
$$dirInfo{RAF} =
|
|
1625
|
+
$$dirInfo{RAF} = File::RandomAccess->new(\$buff);
|
|
1587
1626
|
} else {
|
|
1588
1627
|
$et->SetFileType('JXL Codestream','image/jxl', 'jxl');
|
|
1589
1628
|
if ($$et{ImageDataHash} and $raf->Seek(0,0)) {
|
|
@@ -14,7 +14,7 @@ use Image::ExifTool qw(:DataAccess :Utils);
|
|
|
14
14
|
use Image::ExifTool::Exif;
|
|
15
15
|
use Image::ExifTool::GPS;
|
|
16
16
|
|
|
17
|
-
$VERSION = '1.
|
|
17
|
+
$VERSION = '1.54';
|
|
18
18
|
|
|
19
19
|
sub ProcessMIE($$);
|
|
20
20
|
sub ProcessMIEGroup($$$);
|
|
@@ -1077,7 +1077,7 @@ sub WriteMIEGroup($$$)
|
|
|
1077
1077
|
$newVal = '';
|
|
1078
1078
|
%subdirInfo = (
|
|
1079
1079
|
OutFile => \$newVal,
|
|
1080
|
-
RAF =>
|
|
1080
|
+
RAF => File::RandomAccess->new(\$oldVal),
|
|
1081
1081
|
);
|
|
1082
1082
|
} elsif ($optCompress and not $$dirInfo{IsCompressed}) {
|
|
1083
1083
|
# write to memory so we can compress the new MIE group
|
|
@@ -1585,7 +1585,7 @@ sub ProcessMIEGroup($$$)
|
|
|
1585
1585
|
WasCompressed => $wasCompressed,
|
|
1586
1586
|
);
|
|
1587
1587
|
# read from uncompressed data instead if necessary
|
|
1588
|
-
$subdirInfo{RAF} =
|
|
1588
|
+
$subdirInfo{RAF} = File::RandomAccess->new(\$value) if $valLen;
|
|
1589
1589
|
|
|
1590
1590
|
my $oldOrder = GetByteOrder();
|
|
1591
1591
|
SetByteOrder($format & 0x08 ? 'II' : 'MM');
|
|
@@ -441,6 +441,7 @@ my %sRegionStruct = (
|
|
|
441
441
|
Writable => 'real',
|
|
442
442
|
Notes => 'not part of MWG 2.0 spec',
|
|
443
443
|
},
|
|
444
|
+
# Title - seen in sample XMP of MWG 2.0 specification, but not in spec itself
|
|
444
445
|
seeAlso => { Namespace => 'rdfs', Resource => 1 },
|
|
445
446
|
);
|
|
446
447
|
my %sKeywordStruct;
|
|
@@ -12,7 +12,7 @@ use strict;
|
|
|
12
12
|
use vars qw($VERSION);
|
|
13
13
|
use Image::ExifTool qw(:DataAccess :Utils);
|
|
14
14
|
|
|
15
|
-
$VERSION = '1.
|
|
15
|
+
$VERSION = '1.13';
|
|
16
16
|
|
|
17
17
|
sub MDItemLocalTime($);
|
|
18
18
|
sub ProcessATTR($$$);
|
|
@@ -22,6 +22,11 @@ my %mdDateInfo = (
|
|
|
22
22
|
PrintConv => '$self->ConvertDateTime($val)',
|
|
23
23
|
);
|
|
24
24
|
|
|
25
|
+
my %delXAttr = (
|
|
26
|
+
XAttrQuarantine => 'com.apple.quarantine',
|
|
27
|
+
XAttrMDItemWhereFroms => 'com.apple.metadata:kMDItemWhereFroms',
|
|
28
|
+
);
|
|
29
|
+
|
|
25
30
|
# Information decoded from Mac OS sidecar files
|
|
26
31
|
%Image::ExifTool::MacOS::Main = (
|
|
27
32
|
GROUPS => { 0 => 'File', 1 => 'MacOS' },
|
|
@@ -320,7 +325,17 @@ my %mdDateInfo = (
|
|
|
320
325
|
Groups => { 2 => 'Time' },
|
|
321
326
|
},
|
|
322
327
|
'com.apple.metadata:kMDItemFinderComment' => { Name => 'XAttrMDItemFinderComment' },
|
|
323
|
-
'com.apple.metadata:kMDItemWhereFroms'
|
|
328
|
+
'com.apple.metadata:kMDItemWhereFroms' => {
|
|
329
|
+
Name => 'XAttrMDItemWhereFroms',
|
|
330
|
+
Writable => 1,
|
|
331
|
+
WritePseudo => 1,
|
|
332
|
+
WriteCheck => '"May only delete this tag"',
|
|
333
|
+
Protected => 1,
|
|
334
|
+
Notes => q{
|
|
335
|
+
information about where the file came from. May only be deleted when
|
|
336
|
+
writing
|
|
337
|
+
},
|
|
338
|
+
},
|
|
324
339
|
'com.apple.metadata:kMDLabel' => { Name => 'XAttrMDLabel', Binary => 1 },
|
|
325
340
|
'com.apple.ResourceFork' => { Name => 'XAttrResourceFork', Binary => 1 },
|
|
326
341
|
'com.apple.lastuseddate#PS' => {
|
|
@@ -407,9 +422,9 @@ sub SetMacOSTags($$$)
|
|
|
407
422
|
$et->VPrint(1," - $tag = (all)\n") if $overwrite > 0;
|
|
408
423
|
undef $val if $val eq '';
|
|
409
424
|
}
|
|
410
|
-
} elsif ($tag
|
|
425
|
+
} elsif ($delXAttr{$tag}) {
|
|
411
426
|
($f = $file) =~ s/'/'\\''/g;
|
|
412
|
-
$cmd = "/usr/bin/xattr -d
|
|
427
|
+
$cmd = "/usr/bin/xattr -d $delXAttr{$tag} '${f}'";
|
|
413
428
|
$silentErr = 256; # (will get this error if attribute doesn't exist)
|
|
414
429
|
} else {
|
|
415
430
|
($f = $file) =~ s/(["\\])/\\$1/g; # escape necessary characters for script
|