exiftool_vendored 13.21.0 → 13.22.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 +13 -1
- data/bin/MANIFEST +2 -0
- data/bin/META.json +1 -1
- data/bin/META.yml +1 -1
- data/bin/README +2 -2
- data/bin/exiftool +30 -18
- data/bin/lib/Image/ExifTool/DJI.pm +12 -1
- data/bin/lib/Image/ExifTool/GM.pm +1 -1
- data/bin/lib/Image/ExifTool/JPEG.pm +1 -1
- data/bin/lib/Image/ExifTool/Nikon.pm +6 -6
- data/bin/lib/Image/ExifTool/PNG.pm +7 -1
- data/bin/lib/Image/ExifTool/Plot.pm +350 -130
- data/bin/lib/Image/ExifTool/RIFF.pm +3 -3
- data/bin/lib/Image/ExifTool/TagNames.pod +9 -1
- data/bin/lib/Image/ExifTool/Trailer.pm +2 -2
- data/bin/lib/Image/ExifTool/WriteExif.pl +5 -0
- data/bin/lib/Image/ExifTool.pm +1 -1
- data/bin/lib/Image/ExifTool.pod +6 -2
- data/bin/perl-Image-ExifTool.spec +1 -1
- data/lib/exiftool_vendored/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ad918781e3d8bd85a5482713e9f0007a5c6a49baf150bc89899989a38d2976b4
|
4
|
+
data.tar.gz: 6c73a902a729e31ca68eb9cfb10162cc9cb58e4f5530a8383c791bd00f358f93
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7140b651487bbf72d3977a7af1dff81162a4e11de366121a9fcfffd6ef767df7c91e5e84c4f184ee2f0691fd014f90bdf1ed24bb71262d9b2b10225e3ae59374
|
7
|
+
data.tar.gz: 607ce339d770ac81eb2bea3d1cf1a2029c3916bd717ebc300c20807ad7c443218c34ede0769f3329dea40fba4abdd1fedf6e7e18ae367c5fa6901a790f6048fd
|
data/bin/Changes
CHANGED
@@ -7,10 +7,22 @@ RSS feed: https://exiftool.org/rss.xml
|
|
7
7
|
Note: The most recent production release is Version 13.10. (Other versions are
|
8
8
|
considered development releases, and are not uploaded to MetaCPAN.)
|
9
9
|
|
10
|
+
Mar. 1, 2025 - Version 13.22
|
11
|
+
|
12
|
+
- Improvements and additions to the new plot feature
|
13
|
+
- Decode a few more DJI timed metadata tags
|
14
|
+
- Extract GainMapImage from PNG images
|
15
|
+
- Reverted WebP FileType change of version 13.20
|
16
|
+
- Set $^W in the exiftool application to re-enable global warnings (they have
|
17
|
+
been disabled since version 12.92 due to the shebang change)
|
18
|
+
- Fixed possible incorrect IFD number in "No size tag" warning
|
19
|
+
- Fixed "File is empty" error when using -plot with -w to write the output
|
20
|
+
file into the same directory as the input files
|
21
|
+
|
10
22
|
Feb. 20, 2025 - Version 13.21
|
11
23
|
|
12
24
|
- Patched issue that could result in runtime warning for some video files
|
13
|
-
- Fixed a bug that could
|
25
|
+
- Fixed a bug with the new -plot option that could generate a compiler error
|
14
26
|
|
15
27
|
Feb. 20, 2025 - Version 13.20
|
16
28
|
|
data/bin/MANIFEST
CHANGED
@@ -208,6 +208,7 @@ html/htmldump.html
|
|
208
208
|
html/idiosyncracies.html
|
209
209
|
html/index.html
|
210
210
|
html/install.html
|
211
|
+
html/markers.svg
|
211
212
|
html/metafiles.html
|
212
213
|
html/mistakes.html
|
213
214
|
html/overview.png
|
@@ -216,6 +217,7 @@ html/plot1.svg
|
|
216
217
|
html/plot2.svg
|
217
218
|
html/plot3.svg
|
218
219
|
html/plot4.svg
|
220
|
+
html/plot5.svg
|
219
221
|
html/standards.html
|
220
222
|
html/struct.html
|
221
223
|
html/style.css
|
data/bin/META.json
CHANGED
data/bin/META.yml
CHANGED
data/bin/README
CHANGED
@@ -109,8 +109,8 @@ your home directory, then you would type the following commands in a
|
|
109
109
|
terminal window to extract and run ExifTool:
|
110
110
|
|
111
111
|
cd ~/Desktop
|
112
|
-
gzip -dc Image-ExifTool-13.
|
113
|
-
cd Image-ExifTool-13.
|
112
|
+
gzip -dc Image-ExifTool-13.22.tar.gz | tar -xf -
|
113
|
+
cd Image-ExifTool-13.22
|
114
114
|
./exiftool t/images/ExifTool.jpg
|
115
115
|
|
116
116
|
Note: These commands extract meta information from one of the test images.
|
data/bin/exiftool
CHANGED
@@ -11,7 +11,9 @@ use strict;
|
|
11
11
|
use warnings;
|
12
12
|
require 5.004;
|
13
13
|
|
14
|
-
my $version = '13.
|
14
|
+
my $version = '13.22';
|
15
|
+
|
16
|
+
$^W = 1; # enable global warnings
|
15
17
|
|
16
18
|
# add our 'lib' directory to the include list BEFORE 'use Image::ExifTool'
|
17
19
|
my $exePath;
|
@@ -1551,10 +1553,11 @@ if ($textOut2) {
|
|
1551
1553
|
undef $textOut2;
|
1552
1554
|
next;
|
1553
1555
|
}
|
1556
|
+
# make sure we can write the output text file before processing all input files
|
1554
1557
|
CreateDirectory($textOut2); # create directory if necessary
|
1555
1558
|
if ($mt->Open(\*OUTFILE, $textOut2, '>')) {
|
1556
|
-
|
1557
|
-
$textOut2
|
1559
|
+
close(\*OUTFILE);
|
1560
|
+
unlink($textOut2); # (this was just a test)
|
1558
1561
|
} else {
|
1559
1562
|
Error("Error creating $textOut2\n");
|
1560
1563
|
undef $textOut2;
|
@@ -1968,27 +1971,36 @@ if ($textOut) {
|
|
1968
1971
|
} else {
|
1969
1972
|
print $sectTrailer if $sectTrailer;
|
1970
1973
|
print $fileTrailer if $fileTrailer and not $fileHeader;
|
1971
|
-
# print CSV
|
1972
|
-
my $err;
|
1973
|
-
|
1974
|
-
|
1975
|
-
|
1976
|
-
|
1977
|
-
|
1978
|
-
Error("Error: $$plot{Error}\n");
|
1974
|
+
# print CSV or SVG output file if necessary
|
1975
|
+
my ($fp, $err);
|
1976
|
+
if ($textOut2) {
|
1977
|
+
if ($mt->Open(\*OUTFILE, $textOut2, '>')) {
|
1978
|
+
$fp = \*OUTFILE;
|
1979
|
+
} else {
|
1980
|
+
Error("Error creating $textOut2\n");
|
1979
1981
|
$err = 1;
|
1980
|
-
} elsif ($$plot{Warn}) {
|
1981
|
-
Warn("Warning: $$plot{Warn}\n");
|
1982
1982
|
}
|
1983
1983
|
}
|
1984
|
-
|
1985
|
-
|
1984
|
+
unless ($err) {
|
1985
|
+
PrintCSV($fp) if $csv and not $isWriting;
|
1986
|
+
# print SVG plot
|
1987
|
+
if ($plot) {
|
1988
|
+
$plot->Draw($fp || \*STDOUT);
|
1989
|
+
if ($$plot{Error}) {
|
1990
|
+
Error("Error: $$plot{Error}\n");
|
1991
|
+
$err = 1;
|
1992
|
+
} elsif ($$plot{Warn}) {
|
1993
|
+
Warn("Warning: $$plot{Warn}\n");
|
1994
|
+
}
|
1995
|
+
}
|
1996
|
+
}
|
1997
|
+
if ($fp) {
|
1998
|
+
close($fp) or $err = 1;
|
1986
1999
|
if ($err) {
|
1987
|
-
$mt->Unlink($
|
2000
|
+
$mt->Unlink($textOut2);
|
1988
2001
|
} else {
|
1989
2002
|
$created{$textOut2} = 1;
|
1990
2003
|
}
|
1991
|
-
undef $tmpText;
|
1992
2004
|
}
|
1993
2005
|
}
|
1994
2006
|
|
@@ -6041,7 +6053,7 @@ with this command:
|
|
6041
6053
|
|
6042
6054
|
produces output like this:
|
6043
6055
|
|
6044
|
-
-- Generated by ExifTool 13.
|
6056
|
+
-- Generated by ExifTool 13.22 --
|
6045
6057
|
File: a.jpg - 2003:10:31 15:44:19
|
6046
6058
|
(f/5.6, 1/60s, ISO 100)
|
6047
6059
|
File: b.jpg - 2006:05:23 11:57:38
|
@@ -18,7 +18,7 @@ use Image::ExifTool::XMP;
|
|
18
18
|
use Image::ExifTool::GPS;
|
19
19
|
use Image::ExifTool::Protobuf;
|
20
20
|
|
21
|
-
$VERSION = '1.
|
21
|
+
$VERSION = '1.14';
|
22
22
|
|
23
23
|
sub ProcessDJIInfo($$$);
|
24
24
|
sub ProcessSettings($$$);
|
@@ -266,6 +266,9 @@ my %convFloat2 = (
|
|
266
266
|
},
|
267
267
|
'dvtm_ac203_3-2-6-1' => { Name => 'ColorTemperature', Format => 'unsigned' }, # (NC)
|
268
268
|
# dvtm_ac203_3-2-9-1 - looks like Z accerometer measurement, but 2 and 3 don't look like other components
|
269
|
+
'dvtm_ac203_3-2-10-2' => { Name => 'AccelerometerX', Format => 'float' } , # (NC) left/right
|
270
|
+
'dvtm_ac203_3-2-10-3' => { Name => 'AccelerometerY', Format => 'float' } , # (NC) front/back
|
271
|
+
'dvtm_ac203_3-2-10-4' => { Name => 'AccelerometerZ', Format => 'float' } , # (NC) up/down
|
269
272
|
# dvtm_ac203_3-4-1-4 - model code?
|
270
273
|
'dvtm_ac203_3-4-2-1' => {
|
271
274
|
Name => 'GPSInfo',
|
@@ -301,6 +304,9 @@ my %convFloat2 = (
|
|
301
304
|
PrintConv => 'Image::ExifTool::Exif::PrintExposureTime($val)',
|
302
305
|
},
|
303
306
|
'dvtm_ac204_3-2-6-1' => { Name => 'ColorTemperature', Format => 'unsigned' }, # (NC)
|
307
|
+
'dvtm_ac204_3-2-10-2' => { Name => 'AccelerometerX', Format => 'float' } , # (NC) left/right
|
308
|
+
'dvtm_ac204_3-2-10-3' => { Name => 'AccelerometerY', Format => 'float' } , # (NC) front/back
|
309
|
+
'dvtm_ac204_3-2-10-4' => { Name => 'AccelerometerZ', Format => 'float' } , # (NC) up/down
|
304
310
|
# dvtm_ac204_3-4-1-4 - model code?
|
305
311
|
'dvtm_ac204_3-4-2-1' => {
|
306
312
|
Name => 'GPSInfo',
|
@@ -413,6 +419,11 @@ my %convFloat2 = (
|
|
413
419
|
Format => 'rational',
|
414
420
|
PrintConv => 'Image::ExifTool::Exif::PrintExposureTime($val)',
|
415
421
|
},
|
422
|
+
'dvtm_pm320_3-2-4-1' => { # (NC)
|
423
|
+
Name => 'FNumber',
|
424
|
+
Format => 'rational',
|
425
|
+
PrintConv => 'Image::ExifTool::Exif::PrintFNumber($val)',
|
426
|
+
},
|
416
427
|
'dvtm_pm320_3-2-6-1' => { Name => 'DigitalZoom', Format => 'float' },
|
417
428
|
'dvtm_pm320_3-3-4-1' => {
|
418
429
|
Name => 'GPSInfo',
|
@@ -437,7 +437,7 @@ sub Process_marl($$$)
|
|
437
437
|
{
|
438
438
|
my ($et, $dirInfo, $tagTablePtr) = @_;
|
439
439
|
my $dataPt = $$dirInfo{DataPt};
|
440
|
-
my $dataPos = $$dirInfo{DataPos} + $$dirInfo{Base};
|
440
|
+
my $dataPos = ($$dirInfo{DataPos} || 0) + ($$dirInfo{Base} || 0);
|
441
441
|
my $dataLen = length $$dataPt;
|
442
442
|
my $vals = $$et{GMVals}; # running values for each channel (0=TimeStamp)
|
443
443
|
my $chan = $$et{GMChan}; # running channel number
|
@@ -202,7 +202,7 @@ sub ProcessJPEG_HDR($$$);
|
|
202
202
|
Groups => { 0 => 'APP6', 1 => 'DJI' },
|
203
203
|
Notes => 'DJI Thermal Analysis Tool record',
|
204
204
|
ValueConv => 'substr($val,7)',
|
205
|
-
# also seen Motorola APP6 "MMIMETA\0", with sub-types: AL3A,ALED,MMI0,MOTD,QC3A
|
205
|
+
# also seen Motorola APP6 "MMIMETA\0", with sub-types: AL3A,ALED,MMI0,MOTD,QC3A,LMB1
|
206
206
|
}],
|
207
207
|
APP7 => [{
|
208
208
|
Name => 'Pentax',
|
@@ -4833,17 +4833,17 @@ my %base64coord = (
|
|
4833
4833
|
0x2f => [
|
4834
4834
|
{
|
4835
4835
|
Name => 'FocusPositionHorizontal', # 209/231 focus point cameras
|
4836
|
-
Condition => '$$self{Model} =~ /^NIKON (Z 30|Z 50|Z fc)\b/i
|
4836
|
+
Condition => '$$self{Model} =~ /^NIKON (Z 30|Z 50|Z fc)\b/i and $$self{AFAreaXPosition}', #models Z30, Z50, Zfc
|
4837
4837
|
ValueConv => 'int($$self{AFAreaXPosition} / 260 )', #divisor is an estimate (chosen to cause center point to report 'C')
|
4838
4838
|
PrintConv => sub { my ($val) = @_; PrintAFPointsLeftRight($val, 19 ) },
|
4839
4839
|
},{
|
4840
4840
|
Name => 'FocusPositionHorizontal', #273/299 focus point cameras
|
4841
|
-
Condition => '$$self{Model} =~ /^NIKON (Z 5|Z 6|Z 6_2|D780)\b/i
|
4841
|
+
Condition => '$$self{Model} =~ /^NIKON (Z 5|Z 6|Z 6_2|D780)\b/i and $$self{AFAreaXPosition}', #models Z5, Z6, Z6ii, D780
|
4842
4842
|
ValueConv => 'int($$self{AFAreaXPosition} / 260 )', #divisor is an estimate (chosen to cause center point to report 'C')
|
4843
4843
|
PrintConv => sub { my ($val) = @_; PrintAFPointsLeftRight($val, 21 ) },
|
4844
4844
|
},{
|
4845
4845
|
Name => 'FocusPositionHorizontal', #405/493 focus point cameras
|
4846
|
-
Condition => '$$self{Model} =~ /^NIKON (Z 7|Z 7_2)\b/i
|
4846
|
+
Condition => '$$self{Model} =~ /^NIKON (Z 7|Z 7_2)\b/i and $$self{AFAreaXPosition}', #models Z7/Z7ii
|
4847
4847
|
ValueConv => 'int($$self{AFAreaXPosition} / 260 )', #divisor is the measured horizontal pixel separation between adjacent points
|
4848
4848
|
PrintConv => sub { my ($val) = @_; PrintAFPointsLeftRight($val, 29 ) },
|
4849
4849
|
},
|
@@ -4858,17 +4858,17 @@ my %base64coord = (
|
|
4858
4858
|
0x31 => [
|
4859
4859
|
{
|
4860
4860
|
Name => 'FocusPositionVertical', # 209/233 focus point cameras
|
4861
|
-
Condition => '$$self{Model} =~ /^NIKON (Z 30|Z 50|Z fc)\b/i
|
4861
|
+
Condition => '$$self{Model} =~ /^NIKON (Z 30|Z 50|Z fc)\b/i and $$self{AFAreaYPosition}', #models Z30, Z50, Zfc
|
4862
4862
|
ValueConv => 'int($$self{AFAreaYPosition} / 286 )', #divisor is an estimate (chosen to cause center point to report 'C')
|
4863
4863
|
PrintConv => sub { my ($val) = @_; PrintAFPointsUpDown($val, 11 ) },
|
4864
4864
|
},{
|
4865
4865
|
Name => 'FocusPositionVertical', #273/299 focus point cameras
|
4866
|
-
Condition => '$$self{Model} =~ /^NIKON (Z 5|Z 6|Z 6_2|D780)\b/i
|
4866
|
+
Condition => '$$self{Model} =~ /^NIKON (Z 5|Z 6|Z 6_2|D780)\b/i and $$self{AFAreaYPosition}', #models Z5, Z6, Z6ii, D780
|
4867
4867
|
ValueConv => 'int($$self{AFAreaYPosition} / 286 )', #divisor is an estimate (chosen to cause center point to report 'C')
|
4868
4868
|
PrintConv => sub { my ($val) = @_; PrintAFPointsUpDown($val, 13 ) },
|
4869
4869
|
},{
|
4870
4870
|
Name => 'FocusPositionVertical', #405/493 focus point cameras
|
4871
|
-
Condition => '$$self{Model} =~ /^NIKON (Z 7|Z 7_2)\b/i
|
4871
|
+
Condition => '$$self{Model} =~ /^NIKON (Z 7|Z 7_2)\b/i and $$self{AFAreaYPosition}', #models Z7/Z7ii
|
4872
4872
|
ValueConv => 'int($$self{AFAreaYPosition} / 292 )', #divisor is the measured vertical pixel separation between adjacent points
|
4873
4873
|
PrintConv => sub { my ($val) = @_; PrintAFPointsUpDown($val, 17 ) },
|
4874
4874
|
},
|
@@ -36,7 +36,7 @@ use strict;
|
|
36
36
|
use vars qw($VERSION $AUTOLOAD %stdCase);
|
37
37
|
use Image::ExifTool qw(:DataAccess :Utils);
|
38
38
|
|
39
|
-
$VERSION = '1.
|
39
|
+
$VERSION = '1.71';
|
40
40
|
|
41
41
|
sub ProcessPNG_tEXt($$$);
|
42
42
|
sub ProcessPNG_iTXt($$$);
|
@@ -371,6 +371,12 @@ my %noLeapFrog = ( SAVE => 1, SEEK => 1, IHDR => 1, JHDR => 1, IEND => 1, MEND =
|
|
371
371
|
IgnoreProp => { meta => 1 }, # ignore 'meta' container
|
372
372
|
},
|
373
373
|
},
|
374
|
+
gdAT => {
|
375
|
+
Name => 'GainMapImage',
|
376
|
+
Groups => { 2 => 'Preview' },
|
377
|
+
Binary => 1,
|
378
|
+
},
|
379
|
+
# gmAP - https://github.com/w3c/png/issues/380 does't correspond to my only sample
|
374
380
|
seAl => {
|
375
381
|
Name => 'SEAL',
|
376
382
|
SubDirectory => { TagTable => 'Image::ExifTool::XMP::SEAL' },
|
@@ -18,16 +18,17 @@ my %defaults = (
|
|
18
18
|
size => [ 800, 600 ], # width,height of output image
|
19
19
|
margin => [ 60, 15, 15, 30 ], # left,top,right,bottom margins around plot area
|
20
20
|
legend => [ 0, 0 ], # top,right offset for legend
|
21
|
-
|
22
|
-
|
23
|
-
#
|
24
|
-
cols => [ qw(red green blue black orange gray
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
21
|
+
txtpad => [ 10, 10 ], # padding between text and x,y scale
|
22
|
+
linespacing => 20, # text line spacing
|
23
|
+
# colours for plot lines
|
24
|
+
cols => [ qw(red green blue black orange gray fuchsia brown turquoise gold
|
25
|
+
lime violet maroon aqua navy pink olive indigo silver teal) ],
|
26
|
+
marks => [ qw(circle square triangle diamond star plus pentagon left down right) ],
|
27
|
+
stroke => 1, # stroke width and marker scaling
|
28
|
+
grid => 'darkgray', # grid colour
|
29
|
+
text => 'black', # text and plot frame colour
|
29
30
|
type => 'line', # plot type, 'line' or 'scatter'
|
30
|
-
style => '
|
31
|
+
style => '', # 'line', 'marker' or 'line+marker'
|
31
32
|
xlabel => '', # x axis label
|
32
33
|
ylabel => '', # y axis label
|
33
34
|
title => '', # plot title
|
@@ -35,24 +36,33 @@ my %defaults = (
|
|
35
36
|
# xmin, xmax # x axis minimum,maximum
|
36
37
|
# ymin, ymax # y axis minimum,maximum
|
37
38
|
# split # split list of numbers into separate plot lines
|
38
|
-
# bkg # background
|
39
|
+
# bkg # background colour
|
40
|
+
# multi # flag to make one plot per dataset
|
41
|
+
#
|
42
|
+
# members containing capital letters are used internally
|
43
|
+
#
|
39
44
|
Data => { }, # data arrays for each variable
|
40
45
|
Name => [ ], # variable names
|
41
|
-
|
46
|
+
# XMin, XMax # min/max data index
|
47
|
+
# YMin, YMax # min/max data value
|
48
|
+
# SaveName, Save # saved variables between plots
|
42
49
|
);
|
43
50
|
|
44
|
-
my
|
45
|
-
|
46
|
-
'<
|
47
|
-
'<path
|
48
|
-
'<path
|
49
|
-
'<path
|
50
|
-
'<path
|
51
|
-
'<path
|
52
|
-
'<path
|
51
|
+
my %markerData = (
|
52
|
+
circle => '<circle cx="4" cy="4" r="2.667"',
|
53
|
+
square => '<path d="M1.667 1.667 l4.667 0 0 4.667 -4.667 0 z"',
|
54
|
+
triangle => '<path d="M4 0.8 l2.667 5.333 -5.333 0 z"',
|
55
|
+
diamond => '<path d="M4 1 l3 3 -3 3 -3 -3 z"',
|
56
|
+
star => '<path d="M4 0.8 L5 2.625 7.043 3.011 5.617 4.525 5.881 6.589 4 5.7 2.119 6.589 2.383 4.525 0.957 3.011 3 2.625 z"',
|
57
|
+
plus => '<path d="M2.75 1 l2.5 0 0 1.75 1.75 0 0 2.5 -1.75 0 0 1.75 -2.5 0 0 -1.75 -1.75 0 0 -2.5 1.75 0 z"',
|
58
|
+
pentagon => '<path d="M4 1 L6.853 3.073 5.763 6.427 2.237 6.427 1.147 3.073 z"',
|
59
|
+
left => '<path d="M0.8 4 l5.333 2.667 0 -5.333 z"',
|
60
|
+
down => '<path d="M4 7.2 l2.667 -5.333 -5.333 0 z"',
|
61
|
+
right => '<path d="M7.2 4 l-5.333 2.667 0 -5.333 z"',
|
53
62
|
);
|
54
|
-
|
55
|
-
my (
|
63
|
+
|
64
|
+
my @ng = (20, 15); # optimal number grid lines in X and Y for a 800x600 plot
|
65
|
+
my $wch = 8; # nominal width of a character (measured at 7.92)
|
56
66
|
|
57
67
|
#------------------------------------------------------------------------------
|
58
68
|
# Create new plot object
|
@@ -78,17 +88,40 @@ sub Settings($$)
|
|
78
88
|
return unless $set;
|
79
89
|
foreach (split /,\s*/, $set) {
|
80
90
|
next unless /^([a-z].*?)(=(.*))?$/i;
|
81
|
-
my ($name, $val) = ($1, $3);
|
91
|
+
my ($name, $val) = (lc $1, $3);
|
82
92
|
if (ref $$self{$name} eq 'ARRAY') {
|
83
93
|
next unless defined $val;
|
84
|
-
$$self{
|
94
|
+
my $isNum = $$self{$name}[0] =~ /^\d+$/;
|
95
|
+
# also allow numbers to also be separated by 'x'
|
96
|
+
my @vals = $isNum ? split(/\s*[x\s\/+]\s*/, $val) : split(/\s*[\s\/+]\s*/, $val);
|
97
|
+
my $i;
|
98
|
+
for ($i=0; @vals; ++$i) {
|
99
|
+
my $val = lc shift @vals;
|
100
|
+
next unless length $val;
|
101
|
+
if ($name eq 'marks') {
|
102
|
+
my @v = split /-/, $val;
|
103
|
+
if ($v[0]) {
|
104
|
+
if ($v[0] =~ /^n/) {
|
105
|
+
$v[0] = 'none';
|
106
|
+
} else {
|
107
|
+
($v[0]) = grep /^$v[0]/, @{$defaults{marks}};
|
108
|
+
$v[0] or $$self{Warn} = 'Invalid marker name', next;
|
109
|
+
}
|
110
|
+
} else {
|
111
|
+
# cycle through default markers if none specified
|
112
|
+
$v[0] = $defaults{marks}[$i % @{$defaults{marks}}];
|
113
|
+
}
|
114
|
+
$val = join '-', @v;
|
115
|
+
}
|
116
|
+
$$self{$name}[$i] = $val;
|
117
|
+
}
|
85
118
|
} else {
|
86
119
|
$val = 1 unless defined $val; # default to 1 if no "="
|
87
120
|
my %charName = ('&'=>'amp', '<'=>'lt', '>'=>'gt');
|
88
121
|
# escape necessary XML characters, but allow numerical entities
|
89
122
|
$val =~ s/([&><])/&$charName{$1};/sg and $val =~ s/&(#(\d+|x[0-9a-fA-F]+);)/&$1/;
|
90
123
|
undef $val unless length $val;
|
91
|
-
$$self{
|
124
|
+
$$self{$name} = $val;
|
92
125
|
}
|
93
126
|
}
|
94
127
|
}
|
@@ -102,12 +135,13 @@ sub AddPoints($$$)
|
|
102
135
|
my ($tag, $name, %num, $index, $mod, $val, @vals);
|
103
136
|
my ($ee, $docNum, $data, $xmin, $xmax) = @$self{qw(EE DocNum Data XMin XMax)};
|
104
137
|
$$self{type} or $$self{type} = 'line';
|
105
|
-
my $scat = $$self{type} =~ /^s/
|
138
|
+
my $scat = $$self{type} =~ /^s/ ? 1 : 0;
|
106
139
|
my $xname = $$self{Name}[0]; # (x-axis name if using scatter plot)
|
107
|
-
my $maxLines = $$self{type} =~ /^h/
|
140
|
+
my $maxLines = ($$self{type} =~ /^h/ and not $$self{multi}) ? 1 : 20;
|
108
141
|
for (;;) {
|
109
142
|
if (@vals) {
|
110
143
|
$val = shift @vals;
|
144
|
+
next unless $val =~ /^[+-]?(?=\.?\d)\d*\.?\d*(?:e[+-]?\d+)?$/;
|
111
145
|
} else {
|
112
146
|
$tag = shift @$tags or last;
|
113
147
|
# ignore non-floating-point values
|
@@ -127,7 +161,7 @@ sub AddPoints($$$)
|
|
127
161
|
}
|
128
162
|
}
|
129
163
|
}
|
130
|
-
next unless defined $val and $val =~ /^[+-]?(?=\.?\d)\d*\.?\d*(?:e[+-]?\d+)?([ ,;\t\n\r]
|
164
|
+
next unless defined $val and $val =~ /^[+-]?(?=\.?\d)\d*\.?\d*(?:e[+-]?\d+)?([ ,;\t\n\r]+|$)/i;
|
131
165
|
if ($1) {
|
132
166
|
# split a string of numbers into separate plot points (eg. histogram tags)
|
133
167
|
if ($$self{'split'}) {
|
@@ -145,10 +179,10 @@ sub AddPoints($$$)
|
|
145
179
|
my $docNum = $docNum ? $$docNum{$tag} || 0 : 0;
|
146
180
|
next if $docNum and not $ee;
|
147
181
|
unless ($$data{$name}) {
|
148
|
-
if (@{$$self{Name}} >= $maxLines) {
|
182
|
+
if (@{$$self{Name}} >= $maxLines + $scat) {
|
149
183
|
unless ($$self{MaxTags}) {
|
150
|
-
if ($$self{type} =~ /^h/
|
151
|
-
$$self{Warn} = '
|
184
|
+
if ($$self{type} =~ /^h/ and not $$self{multi}) {
|
185
|
+
$$self{Warn} = 'Use the Multi setting to make a separate histogram for each dataset';
|
152
186
|
} else {
|
153
187
|
$$self{Warn} = 'Too many variables to plot all of them';
|
154
188
|
}
|
@@ -158,10 +192,11 @@ sub AddPoints($$$)
|
|
158
192
|
}
|
159
193
|
push @{$$self{Name}}, $name;
|
160
194
|
$xname or $xname = $name; # x-axis data for scatter plot
|
161
|
-
unless (
|
162
|
-
$$self{
|
163
|
-
|
195
|
+
unless ($scat and $name eq $xname) {
|
196
|
+
$$self{Max} = $val if not defined $$self{Max} or $val > $$self{Max};
|
197
|
+
$$self{Min} = $val if not defined $$self{Min} or $val < $$self{Min};
|
164
198
|
}
|
199
|
+
$xmin = $xmax = $docNum unless defined $xmin;
|
165
200
|
$num{$name} = $xmax;
|
166
201
|
$$data{$name}[$xmax - $xmin] = $val if $xmax >= $xmin;
|
167
202
|
next;
|
@@ -186,20 +221,40 @@ sub AddPoints($$$)
|
|
186
221
|
|
187
222
|
#------------------------------------------------------------------------------
|
188
223
|
# Calculate a nice round number for grid spacing
|
189
|
-
# Inputs: 0) nominal spacing (must be positive)
|
224
|
+
# Inputs: 0) nominal spacing (must be positive), 1) flag to increment to next number
|
190
225
|
# Returns: spacing rounded to an even number
|
191
|
-
sub GetGridSpacing(
|
226
|
+
sub GetGridSpacing($;$)
|
192
227
|
{
|
193
|
-
my $nom =
|
194
|
-
my $rounded;
|
228
|
+
my ($nom, $inc) = @_;
|
229
|
+
my ($rounded, $spc);
|
195
230
|
my $div = sprintf('%.3e', $nom);
|
196
231
|
my $num = substr($div, 0, 1);
|
197
232
|
my $exp = $div =~ s/.*e// ? $div : 0;
|
198
|
-
|
199
|
-
|
233
|
+
if ($inc) {
|
234
|
+
# increment to next highest even number
|
235
|
+
$num = $num < 2 ? 2 : ($num < 5 ? 5 : (++$exp, 1));
|
236
|
+
} else {
|
237
|
+
# look for nearest factor to 1, 2 or 5 * 10^x
|
238
|
+
$num = $num < 8 ? 5 : (++$exp, 1) if $num > 2;
|
239
|
+
}
|
200
240
|
return $exp >= 0 ? $num . ('0' x $exp) : '.' . ('0' x (-$exp - 1)) . $num;
|
201
241
|
}
|
202
242
|
|
243
|
+
#------------------------------------------------------------------------------
|
244
|
+
# Get plot range
|
245
|
+
# Inputs: 0) minimum, 1) maximum
|
246
|
+
# Returns: difference
|
247
|
+
# Notes: Adjusts min/max if necessary to make difference positive
|
248
|
+
sub GetRange($$)
|
249
|
+
{
|
250
|
+
if ($_[0] >= $_[1]) {
|
251
|
+
$_[0] = ($_[0] + $_[1]) / 2;
|
252
|
+
$_[0] -= 0.5 if $_[0];
|
253
|
+
$_[1] = $_[0] + 1;
|
254
|
+
}
|
255
|
+
return $_[1] - $_[0];
|
256
|
+
}
|
257
|
+
|
203
258
|
#------------------------------------------------------------------------------
|
204
259
|
# Draw SVG plot
|
205
260
|
# Inputs: 0) Plot ref, 1) Output file reference
|
@@ -208,29 +263,85 @@ sub Draw($$)
|
|
208
263
|
my ($self, $fp) = @_;
|
209
264
|
my ($min, $max, $xmin, $xmax, $name, $style) = @$self{qw(Min Max XMin XMax Name style)};
|
210
265
|
|
211
|
-
if (not defined $min or not defined $xmin
|
266
|
+
if (not defined $min or not defined $xmin) {
|
212
267
|
$$self{Error} = 'Nothing to plot';
|
213
268
|
return;
|
214
269
|
}
|
215
|
-
my ($
|
216
|
-
my ($
|
217
|
-
my
|
218
|
-
my $
|
219
|
-
my $
|
270
|
+
my ($i, $n, %col, %class, $dx, $dy, $dx2, $xAxis, $x, $y, $px, $py, @og);
|
271
|
+
my ($noLegend, $xname, $xdat, $xdiff, $diff, %markID, $plotNum);
|
272
|
+
my $scat = $$self{type} =~ /^s/ ? 1 : 0;
|
273
|
+
my $hist = $$self{type} =~ /^h/ ? [ ] : 0;
|
274
|
+
my $multi = int($$self{multi} || 0);
|
275
|
+
$multi = 0 unless $multi > 0;
|
276
|
+
$style or $style = $hist ? 'line+fill' : 'line';
|
277
|
+
unless ($style =~ /\b[mpl]/ or ($hist and $style =~ /\bf/)) {
|
278
|
+
$$self{Error} = 'Invalid plot Style setting';
|
279
|
+
return;
|
280
|
+
}
|
281
|
+
my $numPlots = $multi ? scalar(@$name) - $scat : 1;
|
282
|
+
my @size = @{$$self{size}};
|
283
|
+
my $sy = $size[1];
|
284
|
+
if ($multi) {
|
285
|
+
$sy *= int(($numPlots + $multi - 1) / $multi) / $multi;
|
286
|
+
$_ /= $multi foreach @size;
|
287
|
+
}
|
288
|
+
my $tmp = $$self{title} || "Plot by ExifTool $Image::ExifTool::VERSION";
|
289
|
+
print $fp qq{<?xml version="1.0" standalone="no"?>
|
290
|
+
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
291
|
+
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="$$self{size}[0]" height="$sy"
|
292
|
+
preserveAspectRatio="xMidYMid meet" viewBox="0 0 $$self{size}[0] $sy">
|
293
|
+
<title>$tmp</title>};
|
294
|
+
# loop through all plots
|
295
|
+
for ($plotNum=0; $plotNum<$numPlots; ++$plotNum) {
|
296
|
+
if ($numPlots > 1) {
|
297
|
+
print $fp "\n<g transform='translate(", ($plotNum % $multi) * $size[0],
|
298
|
+
',', int($plotNum/$multi) * $size[1], ")'>";
|
299
|
+
if ($plotNum) {
|
300
|
+
@$self{qw(XMin XMax title xlabel ylabel)} = @{$$self{Save}};
|
301
|
+
} else {
|
302
|
+
$$self{Save} = [ @$self{qw(XMin XMax title xlabel ylabel)} ];
|
303
|
+
$$self{SaveName} = [ @{$$self{Name}} ];
|
304
|
+
}
|
305
|
+
$name = $$self{Name} = [ ];
|
306
|
+
push @{$$self{Name}}, $$self{SaveName}[0] if $scat;
|
307
|
+
push @{$$self{Name}}, $$self{SaveName}[$scat + $plotNum];
|
308
|
+
my $dat = $$self{Data}{$$self{Name}[$scat]};
|
309
|
+
undef $min; undef $max;
|
310
|
+
foreach (@$dat) {
|
311
|
+
defined or next;
|
312
|
+
defined $min or $min = $max = $_, next;
|
313
|
+
$min > $_ and $min = $_;
|
314
|
+
$max < $_ and $max = $_;
|
315
|
+
}
|
316
|
+
}
|
317
|
+
my ($data, $title, $xlabel, $ylabel, $cols, $marks, $tpad, $wid) =
|
318
|
+
@$self{qw(Data title xlabel ylabel cols marks txtpad stroke)};
|
220
319
|
my @name = @$name;
|
221
320
|
my @margin = ( @{$$self{margin}} );
|
222
321
|
|
223
322
|
# set reasonable default titles and labels
|
224
323
|
$xname = shift @name if $scat;
|
225
324
|
$title = "$name[0] vs $xname" if $scat and defined $title and not $title and @name == 1;
|
226
|
-
|
227
|
-
|
325
|
+
if ($scat || $hist and defined $xlabel and not $xlabel) {
|
326
|
+
$xlabel = $$name[0];
|
327
|
+
$noLegend = 1 if $hist;
|
328
|
+
}
|
329
|
+
if (defined $ylabel and not $ylabel and @name == 1) {
|
330
|
+
$ylabel = $hist ? 'Count' : $name[0];
|
331
|
+
$noLegend = 1 unless $hist;
|
332
|
+
}
|
228
333
|
|
229
334
|
# make room for title/labels
|
230
|
-
$margin[1] += $$self{
|
231
|
-
$margin[3] += $$self{
|
232
|
-
$margin[0] += $$self{
|
335
|
+
$margin[1] += $$self{linespacing} * 1.5 if $title;
|
336
|
+
$margin[3] += $$self{linespacing} if $xlabel;
|
337
|
+
$margin[0] += $$self{linespacing} if $ylabel;
|
233
338
|
|
339
|
+
# calculate optimal number of X/Y grid lines
|
340
|
+
for ($i=0; $i<2; ++$i) {
|
341
|
+
$og[$i] = $ng[$i] * ($size[$i] - $margin[$i] - $margin[$i+2]) /
|
342
|
+
($defaults{size}[$i] - $defaults{margin}[$i] - $defaults{margin}[$i+2]);
|
343
|
+
$og[$i] <= 0 and $$self{Error} = 'Invalid plot size', return;
|
344
|
+
}
|
234
345
|
if ($scat) {
|
235
346
|
$xdat = $$self{Data}{$xname};
|
236
347
|
unless (defined $$self{xmin} and defined $$self{xmax}) {
|
@@ -241,7 +352,7 @@ sub Draw($$)
|
|
241
352
|
$xmin = $_ if $xmin > $_;
|
242
353
|
$xmax = $_ if $xmax < $_;
|
243
354
|
}
|
244
|
-
my $dnx2 = ($xmax - $xmin) / ($
|
355
|
+
my $dnx2 = ($xmax - $xmin) / ($og[0] * 2);
|
245
356
|
# leave a bit of a left/right margin, but don't pass 0
|
246
357
|
$xmin = ($xmin >= 0 and $xmin < $dnx2) ? 0 : $xmin - $dnx2;
|
247
358
|
$xmax = ($xmax <= 0 and -$xmax < $dnx2) ? 0 : $xmax + $dnx2;
|
@@ -261,7 +372,7 @@ sub Draw($$)
|
|
261
372
|
$max = $$self{xmax} if defined $$self{xmax};
|
262
373
|
} else {
|
263
374
|
# leave a bit of a margin above/below data when autoscaling but don't pass 0
|
264
|
-
my $dny2 = ($max - $min) / ($
|
375
|
+
my $dny2 = ($max - $min) / ($og[1] * 2);
|
265
376
|
$min = ($min >= 0 and $min < $dny2) ? 0 : $min - $dny2;
|
266
377
|
$max = ($max <= 0 and -$max < $dny2) ? 0 : $max + $dny2;
|
267
378
|
# adjust to user-defined range if specified
|
@@ -269,15 +380,15 @@ sub Draw($$)
|
|
269
380
|
$max = $$self{ymax} if defined $$self{ymax};
|
270
381
|
}
|
271
382
|
# generate random colors if we need more
|
272
|
-
while (@$cols < @$name) {
|
383
|
+
while (@$cols < @$name) {
|
273
384
|
$$self{seeded} or srand(141), $$self{seeded} = 1;
|
274
385
|
push @$cols, sprintf("#%.2x%.2x%.2x",int(rand(220)),int(rand(220)),int(rand(220)));
|
275
386
|
}
|
276
|
-
$diff = $
|
277
|
-
$xdiff = $
|
387
|
+
$diff = GetRange($min, $max);
|
388
|
+
$xdiff = GetRange($xmin, $xmax);
|
278
389
|
|
279
390
|
# determine y grid spacing (nice even numbers)
|
280
|
-
$dy = GetGridSpacing($diff / ($hist ? $$self{nbins} : $
|
391
|
+
$dy = GetGridSpacing($diff / ($hist ? $$self{nbins} : $og[1]));
|
281
392
|
# expand plot min/max to the nearest even multiple of our grid spacing
|
282
393
|
$min = ($min > 0 ? int($min/$dy) : int($min/$dy-0.9999)) * $dy;
|
283
394
|
$max = ($max > 0 ? int($max/$dy+0.9999) : int($max/$dy)) * $dy;
|
@@ -300,125 +411,137 @@ sub Draw($$)
|
|
300
411
|
} else {
|
301
412
|
$max < $_ and $max = $_ foreach @$hist; # find max count
|
302
413
|
}
|
303
|
-
$diff = $
|
414
|
+
$diff = GetRange($min, $max);
|
304
415
|
$dx = $dy;
|
305
|
-
$dy = GetGridSpacing($diff / $
|
416
|
+
$dy = GetGridSpacing($diff / $og[1]);
|
306
417
|
$max = ($max > 0 ? int($max/$dy+0.9999) : int($max/$dy)) * $dy;
|
307
418
|
$$data{$name[0]} = $hist;
|
308
419
|
} else {
|
309
|
-
$dx = GetGridSpacing($xdiff / $
|
420
|
+
$dx = GetGridSpacing($xdiff / $og[0]);
|
310
421
|
}
|
311
422
|
if ($scat) {
|
312
423
|
$xmin = ($xmin > 0 ? int($xmin/$dx) : int($xmin/$dx-0.9999)) * $dx;
|
313
424
|
$xmax = ($xmax > 0 ? int($xmax/$dx+0.9999) : int($xmax/$dx)) * $dx;
|
314
425
|
}
|
315
|
-
$diff = $
|
316
|
-
$xdiff = $
|
426
|
+
$diff = GetRange($min, $max);
|
427
|
+
$xdiff = GetRange($xmin, $xmax);
|
317
428
|
# width/height of plot area
|
318
|
-
my $width =
|
319
|
-
my $height =
|
429
|
+
my $width = $size[0] - $margin[0] - $margin[2];
|
430
|
+
my $height = $size[1] - $margin[1] - $margin[3];
|
320
431
|
my $yscl = $height / $diff;
|
321
432
|
my $xscl = $width / $xdiff;
|
322
433
|
my $px0 = $margin[0] - $xmin * $xscl;
|
323
434
|
my $py0 = $margin[1] + $height + $min * $yscl;
|
324
|
-
my
|
325
|
-
print $fp
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
435
|
+
my @clip = ($margin[0]-6*$wid, $margin[1]-6*$wid, $width+12*$wid, $height+12*$wid);
|
436
|
+
print $fp "\n<!-- Definitions -->\n<defs>\n<clipPath id='plot-area'>";
|
437
|
+
print $fp "<rect x='$clip[0]' y='$clip[1]' width='$clip[2]' height='$clip[3]'/></clipPath>";
|
438
|
+
if ($style =~ /\b[mp]/) { # 'm' for 'marker' or 'p' for 'point' (undocumented)
|
439
|
+
for ($i=0; $i<@name; ++$i) {
|
440
|
+
my @m = split /-/, ($$marks[$i] || $defaults{marks}[$i % @{$defaults{marks}}]);
|
441
|
+
my ($fill, $mark);
|
442
|
+
$fill = $m[2] || $$cols[$i] if $m[1] ? $m[1] =~ /^f/ : $style =~ /\bf/;
|
443
|
+
$mark = $markerData{$m[0]};
|
444
|
+
$mark or $markID{$mark} = '', next; # skip 'none' or unrecognized marker name
|
445
|
+
if ($fill and $fill ne 'none') {
|
446
|
+
my $op = $m[3] || ($$cols[$i] eq 'none' ? 50 : 20);
|
447
|
+
$mark .= qq( fill="$fill" style="fill-opacity: $op%");
|
448
|
+
} else {
|
449
|
+
$mark .= ' fill="none"';
|
450
|
+
}
|
451
|
+
$mark .= ' stroke="context-stroke"/>';
|
452
|
+
# don't re-define mark if it is the same as a previous one
|
453
|
+
$markID{$mark} and $markID{$i} = $markID{$mark}, next;
|
454
|
+
$markID{$mark} = $markID{$i} = "mark$i";
|
455
|
+
print $fp "\n<marker id='$markID{$i}' markerWidth='8' markerHeight='8' refX='4'",
|
456
|
+
" refY='4'>\n$mark\n</marker>";
|
457
|
+
}
|
458
|
+
print $fp "\n</defs>\n<style>";
|
459
|
+
for ($i=0; $i<@name; ++$i) {
|
460
|
+
next unless $markID{$i} eq "mark$i";
|
461
|
+
print $fp "\n path.mark$i { marker: url(#mark$i) }";
|
462
|
+
}
|
463
|
+
} else {
|
464
|
+
print $fp "\n</defs>\n<style>";
|
465
|
+
}
|
466
|
+
print $fp "\n text { fill: $$self{text} }\n</style>";
|
467
|
+
print $fp "\n<rect x='0' y='0' width='$size[0]' height='$size[1]' fill='$$self{bkg}'/>" if $$self{bkg};
|
331
468
|
print $fp "\n<!-- X axis -->";
|
332
469
|
print $fp "\n<g dominant-baseline='hanging' text-anchor='middle'>";
|
333
|
-
$py = int(($margin[1] + $height + $$
|
470
|
+
$py = int(($margin[1] + $height + $$tpad[1]) * 10 + 0.5) / 10;
|
334
471
|
$px = int(($margin[0] + $width / 2) * 10 + 0.5) / 10;
|
335
472
|
if ($title) {
|
336
473
|
print $fp "\n<text x='${px}' y='14' font-size='150%'>$title</text>";
|
337
474
|
}
|
338
475
|
if ($xlabel) {
|
339
|
-
$y = $py + $$self{
|
476
|
+
$y = $py + $$self{linespacing};
|
340
477
|
print $fp "\n<text x='${px}' y='${y}'>$xlabel</text>";
|
341
478
|
}
|
342
479
|
if ($ylabel) {
|
343
480
|
$y = $margin[1] + $height / 2;
|
344
481
|
print $fp "\n<text x='10' y='${y}' transform='rotate(-90,10,$y)'>$ylabel</text>";
|
345
482
|
}
|
346
|
-
#
|
347
|
-
my $
|
348
|
-
for (
|
349
|
-
|
350
|
-
$len =
|
483
|
+
# make sure the X labels will fit
|
484
|
+
my $spc = $dx;
|
485
|
+
for (;;) {
|
486
|
+
# find longest label at current spacing
|
487
|
+
my $len = 0;
|
488
|
+
my $x0 = int($xmax / $spc + 0.5) * $spc; # get value of last x label
|
489
|
+
for ($i=0, $x=$x0; $i<3; ++$i, $x-=$spc) {
|
490
|
+
$n = length sprintf('%g', $x);
|
491
|
+
$len = $n if $len < $n;
|
492
|
+
}
|
493
|
+
last if $spc >= ($len + 1) * $wch * $xdiff / $width;
|
494
|
+
# increase label spacing by one increment and try again
|
495
|
+
$spc = $dx2 = GetGridSpacing($spc, 1);
|
351
496
|
}
|
352
|
-
my $
|
353
|
-
$dx2 = GetGridSpacing($dx * $n * 1.5 / 500) if $n > 500; # use larger label spacing
|
354
|
-
($grid, $lastLen) = ('', 0);
|
497
|
+
my ($grid, $lastLen) = ('', 0);
|
355
498
|
for ($x=int($xmin/$dx-1)*$dx; ; $x+=$dx) {
|
356
499
|
$px = int(($margin[0] + ($x - $xmin) * $width / $xdiff) * 10 + 0.5) / 10;
|
357
500
|
next if $px < $margin[0] - 0.5;
|
358
501
|
last if $px > $margin[0] + $width + 0.5;
|
359
|
-
|
502
|
+
my $h = $height;
|
503
|
+
if (not $dx2 or abs($x/$dx2 - int($x/$dx2+($x>0 ? 0.5 : -0.5))) < 0.01) {
|
360
504
|
printf $fp "\n<text x='${px}' y='${py}'>%g</text>", $x;
|
505
|
+
$h += $$tpad[1]/2;
|
361
506
|
}
|
362
507
|
length($grid) - $lastLen > 80 and $grid .= "\n", $lastLen = length($grid);
|
363
|
-
$grid .= sprintf("M$px $margin[1] v$
|
508
|
+
$grid .= sprintf("M$px $margin[1] v$h ");
|
364
509
|
}
|
365
510
|
print $fp "\n<path stroke='$$self{grid}' stroke-width='0.5' d='\n${grid}'/>";
|
366
511
|
print $fp "\n</g>\n<!-- Y axis -->\n<g dominant-baseline='middle' text-anchor='end'>";
|
367
|
-
$px = int(($margin[0] - $$
|
512
|
+
$px = int(($margin[0] - $$tpad[0]) * 10 + 0.5) / 10;
|
368
513
|
($grid, $lastLen) = ('', 0);
|
514
|
+
my ($gx, $gw) = ($margin[0]-$$tpad[0]/2, $width + $$tpad[0]/2);
|
369
515
|
for ($y=$min; ; $y+=$dy) {
|
370
516
|
$py = int(($margin[1] + $height - ($y - $min) * $yscl) * 10 + 0.5) / 10;
|
371
517
|
last if $py < $margin[1] - 0.5;
|
372
|
-
$y = 0 if $y < $dy/2 and $y > -$dy/2;
|
518
|
+
$y = 0 if $y < $dy/2 and $y > -$dy/2; # (avoid round-off errors)
|
373
519
|
printf $fp "\n<text x='${px}' y='${py}'>%g</text>", $y;
|
374
|
-
$y < $dy/2 and $y > -$dy/2 and $xAxis = 1
|
520
|
+
$y < $dy/2 and $y > -$dy/2 and $xAxis = 1; # redraw x axis later
|
375
521
|
length($grid) - $lastLen > 80 and $grid .= "\n", $lastLen = length($grid);
|
376
|
-
$grid .= "M$
|
522
|
+
$grid .= "M$gx $py h$gw ";
|
377
523
|
}
|
378
524
|
if ($xAxis and $min!=0) {
|
379
525
|
$py = $margin[1] + $height + $min * $yscl;
|
380
|
-
print $fp "\n<path stroke='$$self{text}' d='M$margin[0] $py h$
|
526
|
+
print $fp "\n<path stroke='$$self{text}' d='M$margin[0] $py h$width'/>";
|
381
527
|
}
|
382
528
|
print $fp "\n<path stroke='$$self{grid}' stroke-width='0.5' d='\n${grid}'/>";
|
383
|
-
print $fp "\n</g>\n<!-- Plot box and legend-->\n<g dominant-baseline='middle' text-anchor='start'>";
|
529
|
+
print $fp "\n</g>\n<!-- Plot box and legend -->\n<g dominant-baseline='middle' text-anchor='start'>";
|
384
530
|
print $fp "\n<path stroke='$$self{text}' fill='none' d='M$margin[0] $margin[1] l0 $height $width 0 0 -$height z'/>";
|
385
531
|
for ($i=0; $i<@name and not $noLegend; ++$i) {
|
386
|
-
|
387
|
-
$
|
388
|
-
$y = $margin[1] + $$self{legend}[1] + 15 + $$self{lineSpacing} * ($i - $scat + 0.5);
|
532
|
+
$x = $size[0] - $margin[2] - 175 + $$self{legend}[0];
|
533
|
+
$y = $margin[1] + $$self{legend}[1] + 15 + $$self{linespacing} * ($i + 0.5);
|
389
534
|
my $col = $$cols[$i];
|
390
|
-
my $mark = '';
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
}
|
395
|
-
my $line = ($style =~ /\bl/i) ? ' l-20 0' : '';
|
396
|
-
print $fp "\n<path$mark stroke-width='2' stroke='${col}' d='M$x $y m-7 -1${line}'/>";
|
535
|
+
my $mark = $markID{$i} ? " marker-end='url(#$markID{$i})' fill='none'" : '';
|
536
|
+
my $line = ($style =~ /\bl/) ? ' l-20 0' : sprintf(' m%.4g 0', -5 * $wid);
|
537
|
+
my $sw = ($style =~ /\bm/ ? 1.5 : 2) * $wid; # (wider for line-only style so colour is more visible)
|
538
|
+
print $fp "\n<path$mark stroke-width='${sw}' stroke='${col}' d='M$x $y m-7 -1${line}'/>";
|
397
539
|
print $fp "\n<text x='${x}' y='${y}'>$name[$i]</text>";
|
398
540
|
}
|
399
|
-
|
400
|
-
print $fp "\n</g>\n<!-- Definitions -->\n<defs>";
|
401
|
-
print $fp "\n<clipPath id='plot-area'><rect x='$clip[0]' y='$clip[1]' width='$clip[2]' height='$clip[3]' /></clipPath>";
|
402
|
-
if ($style =~ /\b[mp]/i) {
|
403
|
-
for ($i=0; $i<@markerName and $i<@name; ++$i) {
|
404
|
-
print $fp "\n<marker id='@markerName[$i]' markerWidth='12' markerHeight='12' refX='6' refY='6' markerUnits='userSpaceOnUse'>";
|
405
|
-
my $mark = $markerData[$i];
|
406
|
-
$mark =~ s/"none"/"$$cols[$i]"/ if $style =~ /\bf/i;
|
407
|
-
print $fp "\n$mark\n</marker>";
|
408
|
-
}
|
409
|
-
print $fp "\n</defs>\n<style>";
|
410
|
-
for ($i=0; $i<@markerName and $i<@name; ++$i) {
|
411
|
-
print $fp "\n path.$markerName[$i] { marker: url(#$markerName[$i]) }";
|
412
|
-
}
|
413
|
-
print $fp "\n text { fill: $$self{text}] }";
|
414
|
-
print $fp "\n</style>";
|
415
|
-
} else {
|
416
|
-
print $fp "\n</defs><style>\n text { fill: $$self{text} }\n</style>";
|
417
|
-
}
|
418
|
-
print $fp "\n<g fill='none' clip-path='url(#plot-area)' stroke-linejoin='round' stroke-linecap='round' stroke-width='1.5'>";
|
541
|
+
# print the data
|
419
542
|
foreach (0..$#name) {
|
420
543
|
$col{$name[$_]} = $$cols[$_];
|
421
|
-
$class{$name[$_]} = $
|
544
|
+
$class{$name[$_]} = $markID{$_} ? " class='$markID{$_}'" : '';
|
422
545
|
}
|
423
546
|
my ($i0, $i1, $xsclr);
|
424
547
|
my $fill = '';
|
@@ -429,17 +552,25 @@ sub Draw($$)
|
|
429
552
|
$xscl = $width / @$hist;
|
430
553
|
$px0 = $margin[0];
|
431
554
|
$xsclr = int($xscl * 100 + 0.5) / 100;
|
432
|
-
|
555
|
+
if ($style =~ /\bf/) {
|
556
|
+
my @m = split /-/, $$marks[0];
|
557
|
+
my $op = $m[3] || ($style =~ /\bl/ ? 20 : 50);
|
558
|
+
$fill = " fill='$$cols[0]'";
|
559
|
+
$fill .= " style='fill-opacity: $op%'" if $$cols[0] ne 'none';
|
560
|
+
}
|
433
561
|
} else {
|
434
562
|
$i0 = int($xmin) - 1;
|
435
563
|
$i0 = 0 if $i0 < 0;
|
436
564
|
$i1 = int($xmax) + 1;
|
437
565
|
}
|
566
|
+
print $fp "\n</g>\n<!-- Datasets -->\n<g fill='none' clip-path='url(#plot-area)'",
|
567
|
+
" stroke-linejoin='round' stroke-linecap='round' stroke-width='",1.5*$wid,"'>";
|
568
|
+
my $doLines = $style =~ /\bl/;
|
438
569
|
foreach (@name) {
|
570
|
+
my $stroke = ($hist and not $doLines) ? 'none' : $col{$_};
|
439
571
|
my $dat = $$data{$_};
|
440
|
-
my $doLines = $style =~ /\bl/i;
|
441
572
|
print $fp "\n<!-- $_ -->";
|
442
|
-
print $fp "\n<path$class{$_}$fill stroke='$
|
573
|
+
print $fp "\n<path$class{$_}$fill stroke='${stroke}' d='";
|
443
574
|
print $fp 'M' if $doLines;
|
444
575
|
my $m = $doLines ? '' : ' M';
|
445
576
|
for ($i=$i0; $i<=$i1; ++$i) {
|
@@ -456,12 +587,15 @@ sub Draw($$)
|
|
456
587
|
next;
|
457
588
|
}
|
458
589
|
}
|
459
|
-
print $fp $m, ($i %
|
590
|
+
print $fp $m, ($i % 10 ? ' ' : "\n"), "$x $y";
|
460
591
|
}
|
461
592
|
print $fp ' V', $margin[1]+$height, " H$margin[0] z" if $hist and $fill;
|
462
593
|
print $fp "'/>";
|
463
594
|
}
|
464
|
-
print $fp "\n</g
|
595
|
+
print $fp "\n</g>";
|
596
|
+
print $fp "\n</g>" if $numPlots > 1;
|
597
|
+
}
|
598
|
+
print $fp "</svg>\n" or $$self{Error} = 'Error writing output plot file';
|
465
599
|
}
|
466
600
|
|
467
601
|
1; # end
|
@@ -472,14 +606,92 @@ __END__
|
|
472
606
|
|
473
607
|
Image::ExifTool::Plot - Plot tag values in SVG format
|
474
608
|
|
475
|
-
=head1
|
609
|
+
=head1 DESCRIPTION
|
476
610
|
|
477
|
-
|
611
|
+
Output plots in SVG format based on ExifTool tag information.
|
478
612
|
|
479
|
-
=head1
|
613
|
+
=head1 METHODS
|
614
|
+
|
615
|
+
=head2 new
|
616
|
+
|
617
|
+
Create a new Plot object.
|
618
|
+
|
619
|
+
$plot = Image::ExifTool::Plot->new;
|
620
|
+
|
621
|
+
=head2 Settings
|
622
|
+
|
623
|
+
Change plot settings.
|
624
|
+
|
625
|
+
=over 4
|
626
|
+
|
627
|
+
=item Inputs:
|
628
|
+
|
629
|
+
0) Plot object reference
|
630
|
+
|
631
|
+
1) Comma-delimited string of options
|
632
|
+
|
633
|
+
=item Options:
|
634
|
+
|
635
|
+
"Type=Line" - plot type (Line, Scatter or Histogram)
|
636
|
+
"Style=Line" - data style (Line, Marker and/or Fill)
|
637
|
+
"NBins=20" - number of bins for histogram plot
|
638
|
+
"Size=800 600" - width,height of output image
|
639
|
+
"Margin=60 15 15 30" - left,top,right,bottom margins around plot area
|
640
|
+
"Legend=0 0" - x,y offset to shift plot legend
|
641
|
+
"TxtPad=10 10" - padding between text and x,y scale
|
642
|
+
"LineSpacing=20" - spacing between text lines
|
643
|
+
"Stroke=1" - plot stroke width and marker-size scaling factor
|
644
|
+
Title, XLabel, YLabel - plot title and x/y axis labels (no default)
|
645
|
+
XMin, XMax - x axis minimum/maximum (autoscaling if not set)
|
646
|
+
YMin, YMax - y axis minimum/maximum
|
647
|
+
Multi - flag to draw multiple plots, one for each dataset
|
648
|
+
Split - flag to split strings of numbers into lists
|
649
|
+
(> 1 to split into lists of N items)
|
650
|
+
"Grid=darkgray" - grid color
|
651
|
+
"Text=black" - color of text and plot border
|
652
|
+
"Bkg=" - background color (default is transparent)
|
653
|
+
"Cols=red green blue black orange gray fuchsia brown turquoise gold"
|
654
|
+
- colors for plot data
|
655
|
+
"Marks=circle square triangle diamond star plus pentagon left down right"
|
656
|
+
- marker-shape names for each dataset
|
657
|
+
|
658
|
+
=back
|
480
659
|
|
481
|
-
|
482
|
-
|
660
|
+
=head2 AddPoints
|
661
|
+
|
662
|
+
Add points to be plotted.
|
663
|
+
|
664
|
+
=over 4
|
665
|
+
|
666
|
+
=item Inputs:
|
667
|
+
|
668
|
+
0) Plot object reference
|
669
|
+
|
670
|
+
1) Tag information hash reference from ExifTool
|
671
|
+
|
672
|
+
2) List of tag keys to plot
|
673
|
+
|
674
|
+
=back
|
675
|
+
|
676
|
+
=head2 Draw
|
677
|
+
|
678
|
+
Draw the SVG plot to the specified output file.
|
679
|
+
|
680
|
+
=over 4
|
681
|
+
|
682
|
+
=item Inputs:
|
683
|
+
|
684
|
+
0) Plot object reference
|
685
|
+
|
686
|
+
1) Output file reference
|
687
|
+
|
688
|
+
=item Notes:
|
689
|
+
|
690
|
+
On return, the Plot Error and Warn members contain error or warning strings
|
691
|
+
if there were any problems. If an Error is set, then the output SVG is
|
692
|
+
invalid.
|
693
|
+
|
694
|
+
=back
|
483
695
|
|
484
696
|
=head1 AUTHOR
|
485
697
|
|
@@ -488,5 +700,13 @@ Copyright 2003-2025, Phil Harvey (philharvey66 at gmail.com)
|
|
488
700
|
This library is free software; you can redistribute it and/or modify it
|
489
701
|
under the same terms as Perl itself.
|
490
702
|
|
703
|
+
=head1 SEE ALSO
|
704
|
+
|
705
|
+
=over 4
|
706
|
+
|
707
|
+
=item L<https://exiftool.org/plot.html>
|
708
|
+
|
709
|
+
=back
|
710
|
+
|
491
711
|
=cut
|
492
712
|
|
@@ -30,7 +30,7 @@ use strict;
|
|
30
30
|
use vars qw($VERSION $AUTOLOAD);
|
31
31
|
use Image::ExifTool qw(:DataAccess :Utils);
|
32
32
|
|
33
|
-
$VERSION = '1.
|
33
|
+
$VERSION = '1.70';
|
34
34
|
|
35
35
|
sub ConvertTimecode($);
|
36
36
|
sub ProcessSGLT($$$);
|
@@ -1319,9 +1319,9 @@ my %code2charset = (
|
|
1319
1319
|
Name => 'ImageWidth',
|
1320
1320
|
Format => 'int16u',
|
1321
1321
|
Priority => 0,
|
1322
|
-
# add "
|
1322
|
+
# add " (lossless)" to FileType since image has a VP8L (lossless) chunk
|
1323
1323
|
RawConv => q{
|
1324
|
-
$self->OverrideFileType($$self{VALUE}{FileType} . '
|
1324
|
+
$self->OverrideFileType($$self{VALUE}{FileType} . ' (lossless)', undef, 'webp');
|
1325
1325
|
return $val;
|
1326
1326
|
},
|
1327
1327
|
ValueConv => '($val & 0x3fff) + 1',
|
@@ -12,7 +12,7 @@ meta information extracted from or written to a file.
|
|
12
12
|
=head1 TAG TABLES
|
13
13
|
|
14
14
|
The tables listed below give the names of all tags recognized by ExifTool.
|
15
|
-
They contain a total of
|
15
|
+
They contain a total of 28534 tags, with 17595 unique tag names.
|
16
16
|
|
17
17
|
B<Tag ID>, B<Index#> or B<Sequence> is given in the first column of each
|
18
18
|
table. A B<Tag ID> is the computer-readable equivalent of a tag name, and
|
@@ -11561,6 +11561,9 @@ for a given protocol should be considered to have the default value of 0.
|
|
11561
11561
|
'dvtm_ac203_3-2-2-1' ISO no
|
11562
11562
|
'dvtm_ac203_3-2-4-1' ShutterSpeed no
|
11563
11563
|
'dvtm_ac203_3-2-6-1' ColorTemperature no
|
11564
|
+
'dvtm_ac203_3-2-10-2' AccelerometerX no
|
11565
|
+
'dvtm_ac203_3-2-10-3' AccelerometerY no
|
11566
|
+
'dvtm_ac203_3-2-10-4' AccelerometerZ no
|
11564
11567
|
'dvtm_ac203_3-4-2-1' GPSInfo DJI GPSInfo
|
11565
11568
|
'dvtm_ac203_3-4-2-2' GPSAltitude no
|
11566
11569
|
'dvtm_ac203_3-4-2-6-1' GPSDateTime no
|
@@ -11569,6 +11572,9 @@ for a given protocol should be considered to have the default value of 0.
|
|
11569
11572
|
'dvtm_ac204_2-3' FrameInfo DJI FrameInfo
|
11570
11573
|
'dvtm_ac204_3-2-4-1' ShutterSpeed no
|
11571
11574
|
'dvtm_ac204_3-2-6-1' ColorTemperature no
|
11575
|
+
'dvtm_ac204_3-2-10-2' AccelerometerX no
|
11576
|
+
'dvtm_ac204_3-2-10-3' AccelerometerY no
|
11577
|
+
'dvtm_ac204_3-2-10-4' AccelerometerZ no
|
11572
11578
|
'dvtm_ac204_3-4-2-1' GPSInfo DJI GPSInfo
|
11573
11579
|
'dvtm_ac204_3-4-2-2' GPSAltitude no
|
11574
11580
|
'dvtm_ac204_3-4-2-6-1' GPSDateTime no
|
@@ -11577,6 +11583,7 @@ for a given protocol should be considered to have the default value of 0.
|
|
11577
11583
|
'dvtm_pm320_2-2' FrameInfo DJI FrameInfo
|
11578
11584
|
'dvtm_pm320_3-2-2-1' ISO no
|
11579
11585
|
'dvtm_pm320_3-2-3-1' ShutterSpeed no
|
11586
|
+
'dvtm_pm320_3-2-4-1' FNumber no
|
11580
11587
|
'dvtm_pm320_3-2-6-1' DigitalZoom no
|
11581
11588
|
'dvtm_pm320_3-3-3' DroneInfo DJI DroneInfo
|
11582
11589
|
'dvtm_pm320_3-3-4-1' GPSInfo DJI GPSInfo
|
@@ -28670,6 +28677,7 @@ check if speed is more of a concern.
|
|
28670
28677
|
'gIFg' GIFGraphicControlExtension no
|
28671
28678
|
'gIFt' GIFPlainTextExtension no
|
28672
28679
|
'gIFx' GIFApplicationExtension no
|
28680
|
+
'gdAT' GainMapImage no
|
28673
28681
|
'hIST' PaletteHistogram no
|
28674
28682
|
'iCCP' ICC_Profile ICC_Profile
|
28675
28683
|
'iCCP-name' ProfileName yes
|
@@ -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.01';
|
16
16
|
|
17
17
|
%Image::ExifTool::Trailer::Vivo = (
|
18
18
|
GROUPS => { 0 => 'Trailer', 1 => 'Vivo', 2 => 'Image' },
|
@@ -271,7 +271,7 @@ sub ProcessGoogle($$)
|
|
271
271
|
DataPos => $start + $pos,
|
272
272
|
DirLen => $$len[$i],
|
273
273
|
});
|
274
|
-
$et->HandleTag($tagTable, $$tag[$i], \$buff,
|
274
|
+
$et->HandleTag($tagTable, $$tag[$i], \$buff, DataPos => $start + $pos, DataPt => \$buff);
|
275
275
|
# (haven't seen non-zero padding, but I assume this is how it works
|
276
276
|
$pos += $$len[$i] + (($pad and $$pad[$i]) ? $$pad[$i] : 0);
|
277
277
|
}
|
@@ -2269,6 +2269,11 @@ NoOverwrite: next if $isNew > 0;
|
|
2269
2269
|
# build list of offsets to process
|
2270
2270
|
my @offsetList;
|
2271
2271
|
if ($ifd >= 0) {
|
2272
|
+
$dirName = $$dirInfo{DirName} || 'unknown';
|
2273
|
+
if ($ifd) {
|
2274
|
+
$dirName =~ s/\d+$//;
|
2275
|
+
$dirName .= $ifd;
|
2276
|
+
}
|
2272
2277
|
my $offsetInfo = $offsetInfo[$ifd] or next;
|
2273
2278
|
if ($$offsetInfo{0x111} and $$offsetInfo{0x144}) {
|
2274
2279
|
# SubIFD may contain double-referenced data as both strips and tiles
|
data/bin/lib/Image/ExifTool.pm
CHANGED
@@ -29,7 +29,7 @@ use vars qw($VERSION $RELEASE @ISA @EXPORT_OK %EXPORT_TAGS $AUTOLOAD @fileTypes
|
|
29
29
|
%jpegMarker %specialTags %fileTypeLookup $testLen $exeDir
|
30
30
|
%static_vars $advFmtSelf);
|
31
31
|
|
32
|
-
$VERSION = '13.
|
32
|
+
$VERSION = '13.22';
|
33
33
|
$RELEASE = '';
|
34
34
|
@ISA = qw(Exporter);
|
35
35
|
%EXPORT_TAGS = (
|
data/bin/lib/Image/ExifTool.pod
CHANGED
@@ -1009,16 +1009,20 @@ values are:
|
|
1009
1009
|
"Legend=0 0" - x,y offset to shift plot legend
|
1010
1010
|
"TxtPad=10 10" - padding between text and x,y scale
|
1011
1011
|
"LineSpacing=20" - spacing between text lines
|
1012
|
+
"Stroke=1" - plot stroke width and marker-size scaling
|
1012
1013
|
Title, XLabel, YLabel - plot title and x/y axis labels (no default)
|
1013
1014
|
XMin, XMax - x axis minimum/maximum (autoscaling if not set)
|
1014
1015
|
YMin, YMax - y axis minimum/maximum
|
1016
|
+
Multi - flag to draw multiple plots, one for each dataset
|
1015
1017
|
Split - flag to split strings of numbers into lists
|
1016
1018
|
(> 1 to split into lists of N items)
|
1017
1019
|
"Grid=darkgray" - grid color
|
1018
1020
|
"Text=black" - color of text and plot border
|
1019
1021
|
"Bkg=" - background color (default is transparent)
|
1020
|
-
"Cols=red green blue black orange gray
|
1021
|
-
|
1022
|
+
"Cols=red green blue black orange gray fuchsia brown turquoise gold"
|
1023
|
+
- colors for plot data
|
1024
|
+
"Marks=circle square triangle diamond star plus pentagon left down right"
|
1025
|
+
- marker-shape names for each dataset
|
1022
1026
|
|
1023
1027
|
=item PrintConv
|
1024
1028
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: exiftool_vendored
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 13.
|
4
|
+
version: 13.22.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Matthew McEachen
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2025-03-
|
12
|
+
date: 2025-03-02 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: exiftool
|