exiftool_vendored 12.16.0 → 12.25.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 +137 -1
- data/bin/MANIFEST +12 -0
- data/bin/META.json +1 -1
- data/bin/META.yml +1 -1
- data/bin/README +44 -43
- data/bin/config_files/acdsee.config +193 -6
- data/bin/config_files/cuepointlist.config +70 -0
- data/bin/config_files/example.config +1 -8
- data/bin/exiftool +139 -98
- data/bin/fmt_files/gpx.fmt +1 -1
- data/bin/fmt_files/gpx_wpt.fmt +1 -1
- data/bin/fmt_files/kml.fmt +1 -1
- data/bin/fmt_files/kml_track.fmt +1 -1
- data/bin/lib/Image/ExifTool.pm +158 -49
- data/bin/lib/Image/ExifTool.pod +94 -75
- data/bin/lib/Image/ExifTool/Apple.pm +3 -2
- data/bin/lib/Image/ExifTool/BuildTagLookup.pm +25 -14
- data/bin/lib/Image/ExifTool/Canon.pm +28 -3
- data/bin/lib/Image/ExifTool/CanonCustom.pm +19 -1
- data/bin/lib/Image/ExifTool/DJI.pm +6 -6
- data/bin/lib/Image/ExifTool/DjVu.pm +6 -5
- data/bin/lib/Image/ExifTool/Exif.pm +50 -22
- data/bin/lib/Image/ExifTool/FITS.pm +13 -2
- data/bin/lib/Image/ExifTool/FujiFilm.pm +19 -8
- data/bin/lib/Image/ExifTool/GPS.pm +24 -13
- data/bin/lib/Image/ExifTool/H264.pm +20 -5
- data/bin/lib/Image/ExifTool/ICC_Profile.pm +2 -2
- data/bin/lib/Image/ExifTool/JPEG.pm +6 -2
- data/bin/lib/Image/ExifTool/JSON.pm +24 -3
- data/bin/lib/Image/ExifTool/Jpeg2000.pm +361 -16
- data/bin/lib/Image/ExifTool/M2TS.pm +40 -4
- data/bin/lib/Image/ExifTool/MIE.pm +2 -2
- data/bin/lib/Image/ExifTool/MRC.pm +341 -0
- data/bin/lib/Image/ExifTool/MWG.pm +3 -3
- data/bin/lib/Image/ExifTool/MXF.pm +1 -1
- data/bin/lib/Image/ExifTool/MacOS.pm +1 -1
- data/bin/lib/Image/ExifTool/Microsoft.pm +298 -82
- data/bin/lib/Image/ExifTool/Nikon.pm +5 -5
- data/bin/lib/Image/ExifTool/NikonSettings.pm +25 -16
- data/bin/lib/Image/ExifTool/Olympus.pm +2 -2
- data/bin/lib/Image/ExifTool/PNG.pm +2 -2
- data/bin/lib/Image/ExifTool/Panasonic.pm +14 -1
- data/bin/lib/Image/ExifTool/PhaseOne.pm +4 -3
- data/bin/lib/Image/ExifTool/QuickTime.pm +148 -68
- data/bin/lib/Image/ExifTool/QuickTimeStream.pl +94 -34
- data/bin/lib/Image/ExifTool/README +5 -4
- data/bin/lib/Image/ExifTool/RIFF.pm +84 -12
- data/bin/lib/Image/ExifTool/Samsung.pm +2 -1
- data/bin/lib/Image/ExifTool/Shortcuts.pm +9 -0
- data/bin/lib/Image/ExifTool/Sony.pm +157 -49
- data/bin/lib/Image/ExifTool/TagInfoXML.pm +1 -0
- data/bin/lib/Image/ExifTool/TagLookup.pm +4079 -3987
- data/bin/lib/Image/ExifTool/TagNames.pod +642 -273
- data/bin/lib/Image/ExifTool/WriteExif.pl +1 -1
- data/bin/lib/Image/ExifTool/WritePostScript.pl +1 -0
- data/bin/lib/Image/ExifTool/WriteQuickTime.pl +44 -17
- data/bin/lib/Image/ExifTool/WriteXMP.pl +15 -8
- data/bin/lib/Image/ExifTool/Writer.pl +50 -14
- data/bin/lib/Image/ExifTool/XMP.pm +50 -11
- data/bin/perl-Image-ExifTool.spec +42 -42
- data/lib/exiftool_vendored/version.rb +1 -1
- metadata +52 -12
|
@@ -89,7 +89,7 @@ my $parsePictureTiming; # flag to enable parsing of picture timing information (
|
|
|
89
89
|
Notes => 'hours:minutes:seconds:frames',
|
|
90
90
|
ValueConv => 'sprintf("%.2x:%.2x:%.2x:%.2x",reverse unpack("C*",$val))',
|
|
91
91
|
},
|
|
92
|
-
# 0x14 - TitleBinaryGroup
|
|
92
|
+
# 0x14 - TitleBinaryGroup - val: 0x00000000,0x14200130
|
|
93
93
|
# 0x15 - TitleCassetteNo (ref 7)
|
|
94
94
|
# 0x16-0x17 - TitleSoftID (ref 7)
|
|
95
95
|
# (0x18,0x19 listed as TitleTextHeader/TitleText by ref 7)
|
|
@@ -134,8 +134,8 @@ my $parsePictureTiming; # flag to enable parsing of picture timing information (
|
|
|
134
134
|
Name => 'Camera2',
|
|
135
135
|
SubDirectory => { TagTable => 'Image::ExifTool::H264::Camera2' },
|
|
136
136
|
},
|
|
137
|
-
# 0x73 Lens - val:
|
|
138
|
-
# 0x74 Gain
|
|
137
|
+
# 0x73 Lens - val: 0x04ffffd3,0x0effffd3,0x15ffffd3,0x41ffffd3,0x52ffffd3,0x59ffffd3,0x65ffffd3,0x71ffffd3,0x75ffffd3,0x79ffffd3,0x7fffffd3,0xffffffd3...
|
|
138
|
+
# 0x74 Gain - val: 0xb8ffff0f
|
|
139
139
|
# 0x75 Pedestal
|
|
140
140
|
# 0x76 Gamma
|
|
141
141
|
# 0x77 Detail
|
|
@@ -376,34 +376,49 @@ my $parsePictureTiming; # flag to enable parsing of picture timing information (
|
|
|
376
376
|
Notes => 'combined with tag 0xc8',
|
|
377
377
|
},
|
|
378
378
|
# 0xc9-0xcf - GPSOption
|
|
379
|
+
# 0xc9 - val: 0x001d0203
|
|
380
|
+
0xca => { #PH (Sony DSC-HX7V)
|
|
381
|
+
Name => 'GPSDateStamp',
|
|
382
|
+
Format => 'string',
|
|
383
|
+
Groups => { 1 => 'GPS', 2 => 'Time' },
|
|
384
|
+
Combine => 2, # the next 2 tags contain the rest of the string
|
|
385
|
+
Notes => 'combined with tags 0xcb and 0xcc',
|
|
386
|
+
ValueConv => 'Image::ExifTool::Exif::ExifDate($val)',
|
|
387
|
+
},
|
|
379
388
|
0xe0 => {
|
|
380
389
|
Name => 'MakeModel',
|
|
381
390
|
SubDirectory => { TagTable => 'Image::ExifTool::H264::MakeModel' },
|
|
382
391
|
},
|
|
383
392
|
# 0xe1-0xef - MakerOption
|
|
384
393
|
# 0xe1 - val: 0x01000670,0x01000678,0x06ffffff,0x01ffffff,0x01000020,0x01000400...
|
|
385
|
-
# 0xe2-0xe8 - val: 0x00000000 in many samples
|
|
386
394
|
0xe1 => { #6
|
|
387
395
|
Name => 'RecInfo',
|
|
388
396
|
Condition => '$$self{Make} eq "Canon"',
|
|
389
397
|
Notes => 'Canon only',
|
|
390
398
|
SubDirectory => { TagTable => 'Image::ExifTool::H264::RecInfo' },
|
|
391
399
|
},
|
|
400
|
+
# 0xe2-0xe8 - val: 0x00000000 in many samples
|
|
401
|
+
# 0xe2 - val: 0x00000000,0x01000000,0x01010000,0x8080900c,0x8080a074
|
|
402
|
+
# 0xe3 - val: 0x00801f89,0x00801f8b,0x00c01f89,0xc9c01f80
|
|
392
403
|
0xe4 => { #PH
|
|
393
404
|
Name => 'Model',
|
|
394
405
|
Condition => '$$self{Make} eq "Sony"', # (possibly also Canon models?)
|
|
395
406
|
Description => 'Camera Model Name',
|
|
396
|
-
Notes => 'Sony
|
|
407
|
+
Notes => 'Sony only, combined with tags 0xe5 and 0xe6',
|
|
397
408
|
Format => 'string',
|
|
398
409
|
Combine => 2, # (not sure about 0xe6, but include it just in case)
|
|
399
410
|
RawConv => '$val eq "" ? undef : $val',
|
|
400
411
|
},
|
|
412
|
+
# 0xeb - val: 0x008a0a00,0x0a300000,0x508a0a00,0x52880a00,0x528a0a00
|
|
413
|
+
# 0xec - val: 0x0b700000
|
|
414
|
+
# 0xed - val: 0x0ce0f819
|
|
401
415
|
0xee => { #6 (HFS200)
|
|
402
416
|
Name => 'FrameInfo',
|
|
403
417
|
Condition => '$$self{Make} eq "Canon"',
|
|
404
418
|
Notes => 'Canon only',
|
|
405
419
|
SubDirectory => { TagTable => 'Image::ExifTool::H264::FrameInfo' },
|
|
406
420
|
},
|
|
421
|
+
# 0xef - val: 0x01c00000,0x0e00000c
|
|
407
422
|
);
|
|
408
423
|
|
|
409
424
|
# ConsumerCamera1 information (ref PH)
|
|
@@ -24,7 +24,7 @@ use strict;
|
|
|
24
24
|
use vars qw($VERSION);
|
|
25
25
|
use Image::ExifTool qw(:DataAccess :Utils);
|
|
26
26
|
|
|
27
|
-
$VERSION = '1.
|
|
27
|
+
$VERSION = '1.36';
|
|
28
28
|
|
|
29
29
|
sub ProcessICC($$);
|
|
30
30
|
sub ProcessICC_Profile($$$);
|
|
@@ -1225,7 +1225,7 @@ sub ProcessICC_Profile($$$)
|
|
|
1225
1225
|
my $type = substr($$dataPt, $valuePtr, 4);
|
|
1226
1226
|
#### eval Validate ($type)
|
|
1227
1227
|
if (defined $$subdir{Validate} and not eval $$subdir{Validate}) {
|
|
1228
|
-
$et->Warn("Invalid $name data");
|
|
1228
|
+
$et->Warn("Invalid ICC $name data");
|
|
1229
1229
|
} else {
|
|
1230
1230
|
$et->ProcessDirectory(\%subdirInfo, $newTagTable, $$subdir{ProcessProc});
|
|
1231
1231
|
}
|
|
@@ -193,11 +193,15 @@ sub ProcessJPEG_HDR($$$);
|
|
|
193
193
|
Condition => '$$valPt =~ /^UNICODE\0/',
|
|
194
194
|
Notes => 'PhotoStudio Unicode comment',
|
|
195
195
|
},
|
|
196
|
-
APP11 => {
|
|
196
|
+
APP11 => [{
|
|
197
197
|
Name => 'JPEG-HDR',
|
|
198
198
|
Condition => '$$valPt =~ /^HDR_RI /',
|
|
199
199
|
SubDirectory => { TagTable => 'Image::ExifTool::JPEG::HDR' },
|
|
200
|
-
},
|
|
200
|
+
},{
|
|
201
|
+
Name => 'JUMBF',
|
|
202
|
+
Condition => '$$valPt =~ /^JP/',
|
|
203
|
+
SubDirectory => { TagTable => 'Image::ExifTool::Jpeg2000::Main' },
|
|
204
|
+
}],
|
|
201
205
|
APP12 => [{
|
|
202
206
|
Name => 'PictureInfo',
|
|
203
207
|
Condition => '$$valPt =~ /(\[picture info\]|Type=)/',
|
|
@@ -14,13 +14,15 @@ 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.03';
|
|
18
18
|
|
|
19
|
+
sub ProcessJSON($$);
|
|
19
20
|
sub ProcessTag($$$$%);
|
|
20
21
|
|
|
21
22
|
%Image::ExifTool::JSON::Main = (
|
|
22
23
|
GROUPS => { 0 => 'JSON', 1 => 'JSON', 2 => 'Other' },
|
|
23
24
|
VARS => { NO_ID => 1 },
|
|
25
|
+
PROCESS_PROC => \&ProcessJSON,
|
|
24
26
|
NOTES => q{
|
|
25
27
|
Other than a few tags in the table below, JSON tags have not been
|
|
26
28
|
pre-defined. However, ExifTool will read any existing tags from basic
|
|
@@ -103,7 +105,26 @@ sub ProcessJSON($$)
|
|
|
103
105
|
my ($et, $dirInfo) = @_;
|
|
104
106
|
my $raf = $$dirInfo{RAF};
|
|
105
107
|
my $structOpt = $et->Options('Struct');
|
|
106
|
-
my (%database, $key, $tag);
|
|
108
|
+
my (%database, $key, $tag, $dataPt);
|
|
109
|
+
|
|
110
|
+
unless ($raf) {
|
|
111
|
+
$dataPt = $$dirInfo{DataPt};
|
|
112
|
+
if ($$dirInfo{DirStart} or ($$dirInfo{DirLen} and $$dirInfo{DirLen} ne length($$dataPt))) {
|
|
113
|
+
my $buff = substr(${$$dirInfo{DataPt}}, $$dirInfo{DirStart}, $$dirInfo{DirLen});
|
|
114
|
+
$dataPt = \$buff;
|
|
115
|
+
}
|
|
116
|
+
$raf = new File::RandomAccess($dataPt);
|
|
117
|
+
# extract as a block if requested
|
|
118
|
+
my $blockName = $$dirInfo{BlockInfo} ? $$dirInfo{BlockInfo}{Name} : '';
|
|
119
|
+
my $blockExtract = $et->Options('BlockExtract');
|
|
120
|
+
if ($blockName and ($blockExtract or $$et{REQ_TAG_LOOKUP}{lc $blockName} or
|
|
121
|
+
($$et{TAGS_FROM_FILE} and not $$et{EXCL_TAG_LOOKUP}{lc $blockName})))
|
|
122
|
+
{
|
|
123
|
+
$et->FoundTag($$dirInfo{BlockInfo}, $$dataPt);
|
|
124
|
+
return 1 if $blockExtract and $blockExtract > 1;
|
|
125
|
+
}
|
|
126
|
+
$et->VerboseDir('JSON');
|
|
127
|
+
}
|
|
107
128
|
|
|
108
129
|
# read information from JSON file into database structure
|
|
109
130
|
my $err = Image::ExifTool::Import::ReadJSON($raf, \%database,
|
|
@@ -111,7 +132,7 @@ sub ProcessJSON($$)
|
|
|
111
132
|
|
|
112
133
|
return 0 if $err or not %database;
|
|
113
134
|
|
|
114
|
-
$et->SetFileType();
|
|
135
|
+
$et->SetFileType() unless $dataPt;
|
|
115
136
|
|
|
116
137
|
my $tagTablePtr = GetTagTable('Image::ExifTool::JSON::Main');
|
|
117
138
|
|
|
@@ -16,9 +16,10 @@ use strict;
|
|
|
16
16
|
use vars qw($VERSION);
|
|
17
17
|
use Image::ExifTool qw(:DataAccess :Utils);
|
|
18
18
|
|
|
19
|
-
$VERSION = '1.
|
|
19
|
+
$VERSION = '1.29';
|
|
20
20
|
|
|
21
21
|
sub ProcessJpeg2000Box($$$);
|
|
22
|
+
sub ProcessJUMD($$$);
|
|
22
23
|
|
|
23
24
|
my %resolutionUnit = (
|
|
24
25
|
-3 => 'km',
|
|
@@ -54,6 +55,22 @@ my %jp2Map = (
|
|
|
54
55
|
MakerNotes => 'ExifIFD',
|
|
55
56
|
);
|
|
56
57
|
|
|
58
|
+
# map of where information is written in a JXL image
|
|
59
|
+
my %jxlMap = (
|
|
60
|
+
IFD0 => 'Exif',
|
|
61
|
+
XMP => 'XML',
|
|
62
|
+
'Exif' => 'JP2',
|
|
63
|
+
IFD1 => 'IFD0',
|
|
64
|
+
EXIF => 'IFD0', # to write EXIF as a block
|
|
65
|
+
ExifIFD => 'IFD0',
|
|
66
|
+
GPS => 'IFD0',
|
|
67
|
+
SubIFD => 'IFD0',
|
|
68
|
+
GlobParamIFD => 'IFD0',
|
|
69
|
+
PrintIM => 'IFD0',
|
|
70
|
+
InteropIFD => 'ExifIFD',
|
|
71
|
+
MakerNotes => 'ExifIFD',
|
|
72
|
+
);
|
|
73
|
+
|
|
57
74
|
# UUID's for writable UUID directories (by tag name)
|
|
58
75
|
my %uuid = (
|
|
59
76
|
'UUID-EXIF' => 'JpgTiffExif->JP2',
|
|
@@ -99,6 +116,9 @@ my %j2cMarker = (
|
|
|
99
116
|
0x76 => 'NLT', # non-linearity point transformation
|
|
100
117
|
);
|
|
101
118
|
|
|
119
|
+
my %jumbfTypes = (
|
|
120
|
+
);
|
|
121
|
+
|
|
102
122
|
# JPEG 2000 "box" (ie. atom) names
|
|
103
123
|
# Note: only tags with a defined "Format" are extracted
|
|
104
124
|
%Image::ExifTool::Jpeg2000::Main = (
|
|
@@ -107,8 +127,9 @@ my %j2cMarker = (
|
|
|
107
127
|
WRITE_PROC => \&ProcessJpeg2000Box,
|
|
108
128
|
PREFERRED => 1, # always add these tags when writing
|
|
109
129
|
NOTES => q{
|
|
110
|
-
The tags below are extracted from JPEG 2000 images
|
|
111
|
-
currently writes only EXIF, IPTC and XMP
|
|
130
|
+
The tags below are extracted from JPEG 2000 images and the JUMBF metadata in
|
|
131
|
+
JPEG images. Note that ExifTool currently writes only EXIF, IPTC and XMP
|
|
132
|
+
tags in Jpeg2000 images.
|
|
112
133
|
},
|
|
113
134
|
'jP ' => 'JP2Signature', # (ref 1)
|
|
114
135
|
"jP\x1a\x1a" => 'JP2Signature', # (ref 2)
|
|
@@ -199,13 +220,22 @@ my %j2cMarker = (
|
|
|
199
220
|
chck => 'DigitalSignature',
|
|
200
221
|
mp7b => 'MPEG7Binary',
|
|
201
222
|
free => 'Free',
|
|
202
|
-
jp2c =>
|
|
223
|
+
jp2c => [{
|
|
224
|
+
Name => 'ContiguousCodestream',
|
|
225
|
+
Condition => 'not $$self{jumd_level}',
|
|
226
|
+
},{
|
|
227
|
+
Name => 'PreviewImage',
|
|
228
|
+
Groups => { 2 => 'Preview' },
|
|
229
|
+
Format => 'undef',
|
|
230
|
+
Binary => 1,
|
|
231
|
+
}],
|
|
203
232
|
jp2i => {
|
|
204
233
|
Name => 'IntellectualProperty',
|
|
205
234
|
SubDirectory => { TagTable => 'Image::ExifTool::XMP::Main' },
|
|
206
235
|
},
|
|
207
|
-
'xml '=> {
|
|
236
|
+
'xml '=> [{
|
|
208
237
|
Name => 'XML',
|
|
238
|
+
Condition => 'not $$self{IsJXL}',
|
|
209
239
|
Writable => 'undef',
|
|
210
240
|
Flags => [ 'Binary', 'Protected', 'BlockExtract' ],
|
|
211
241
|
List => 1,
|
|
@@ -213,12 +243,18 @@ my %j2cMarker = (
|
|
|
213
243
|
by default, the XML data in this tag is parsed using the ExifTool XMP module
|
|
214
244
|
to to allow individual tags to be accessed when reading, but it may also be
|
|
215
245
|
extracted as a block via the "XML" tag, which is also how this tag is
|
|
216
|
-
written and copied.
|
|
217
|
-
|
|
246
|
+
written and copied. It may also be extracted as a block by setting the API
|
|
247
|
+
BlockExtract option. This is a List-type tag because multiple XML blocks
|
|
248
|
+
may exist
|
|
218
249
|
},
|
|
219
250
|
# (note: extracting as a block was broken in 11.04, and finally fixed in 12.14)
|
|
220
251
|
SubDirectory => { TagTable => 'Image::ExifTool::XMP::XML' },
|
|
221
|
-
},
|
|
252
|
+
},{
|
|
253
|
+
Name => 'XMP',
|
|
254
|
+
Notes => 'used for XMP in JPEG XL files',
|
|
255
|
+
# NOTE: the hacked code relies on this being at index 1 of the tagInfo list!
|
|
256
|
+
SubDirectory => { TagTable => 'Image::ExifTool::XMP::Main' },
|
|
257
|
+
}],
|
|
222
258
|
uuid => [
|
|
223
259
|
{
|
|
224
260
|
Name => 'UUID-EXIF',
|
|
@@ -302,6 +338,12 @@ my %j2cMarker = (
|
|
|
302
338
|
Start => '$valuePtr + 16',
|
|
303
339
|
},
|
|
304
340
|
},
|
|
341
|
+
{
|
|
342
|
+
Name => 'UUID-Signature', # (seen in JUMB data of JPEG images)
|
|
343
|
+
Condition => '$$valPt=~/^casg\x00\x11\x00\x10\x80\x00\x00\xaa\x00\x38\x9b\x71/',
|
|
344
|
+
Format => 'undef',
|
|
345
|
+
ValueConv => 'substr($val,16)',
|
|
346
|
+
},
|
|
305
347
|
{
|
|
306
348
|
Name => 'UUID-Unknown',
|
|
307
349
|
},
|
|
@@ -321,6 +363,52 @@ my %j2cMarker = (
|
|
|
321
363
|
Name => 'URL',
|
|
322
364
|
Format => 'string',
|
|
323
365
|
},
|
|
366
|
+
# JUMBF boxes (ref https://github.com/thorfdbg/codestream-parser)
|
|
367
|
+
jumd => {
|
|
368
|
+
Name => 'JUMBFDescr',
|
|
369
|
+
SubDirectory => { TagTable => 'Image::ExifTool::Jpeg2000::JUMD' },
|
|
370
|
+
},
|
|
371
|
+
jumb => {
|
|
372
|
+
Name => 'JUMBFBox',
|
|
373
|
+
SubDirectory => {
|
|
374
|
+
TagTable => 'Image::ExifTool::Jpeg2000::Main',
|
|
375
|
+
ProcessProc => \&ProcessJUMB,
|
|
376
|
+
},
|
|
377
|
+
},
|
|
378
|
+
json => {
|
|
379
|
+
Name => 'JSONData',
|
|
380
|
+
Flags => [ 'Binary', 'Protected', 'BlockExtract' ],
|
|
381
|
+
Notes => q{
|
|
382
|
+
by default, data in this tag is parsed using the ExifTool JSON module to to
|
|
383
|
+
allow individual tags to be accessed when reading, but it may also be
|
|
384
|
+
extracted as a block via the "JSONData" tag or by setting the API
|
|
385
|
+
BlockExtract option
|
|
386
|
+
},
|
|
387
|
+
SubDirectory => { TagTable => 'Image::ExifTool::JSON::Main' },
|
|
388
|
+
},
|
|
389
|
+
#
|
|
390
|
+
# stuff seen in JPEG XL images:
|
|
391
|
+
#
|
|
392
|
+
# jbrd - JPEG Bitstream Reconstruction Data (allows lossless conversion back to original JPG)
|
|
393
|
+
jxlc => {
|
|
394
|
+
Name => 'JXLCodestream',
|
|
395
|
+
Format => 'undef',
|
|
396
|
+
Notes => q{
|
|
397
|
+
Codestream in JPEG XL image. Currently processed only to determine
|
|
398
|
+
ImageSize
|
|
399
|
+
},
|
|
400
|
+
RawConv => 'Image::ExifTool::Jpeg2000::ProcessJXLCodestream($self,\$val); undef',
|
|
401
|
+
},
|
|
402
|
+
Exif => {
|
|
403
|
+
Name => 'EXIF',
|
|
404
|
+
SubDirectory => {
|
|
405
|
+
TagTable => 'Image::ExifTool::Exif::Main',
|
|
406
|
+
ProcessProc => \&Image::ExifTool::ProcessTIFF,
|
|
407
|
+
WriteProc => \&Image::ExifTool::WriteTIFF,
|
|
408
|
+
DirName => 'EXIF',
|
|
409
|
+
Start => '$valuePtr + 4',
|
|
410
|
+
},
|
|
411
|
+
},
|
|
324
412
|
);
|
|
325
413
|
|
|
326
414
|
%Image::ExifTool::Jpeg2000::ImageHeader = (
|
|
@@ -375,6 +463,7 @@ my %j2cMarker = (
|
|
|
375
463
|
'jp2 ' => 'JPEG 2000 Image (.JP2)', # image/jp2
|
|
376
464
|
'jpm ' => 'JPEG 2000 Compound Image (.JPM)', # image/jpm
|
|
377
465
|
'jpx ' => 'JPEG 2000 with extensions (.JPX)', # image/jpx
|
|
466
|
+
'jxl ' => 'JPEG XL Image (.JXL)', # image/jxl
|
|
378
467
|
},
|
|
379
468
|
},
|
|
380
469
|
1 => {
|
|
@@ -513,6 +602,112 @@ my %j2cMarker = (
|
|
|
513
602
|
],
|
|
514
603
|
);
|
|
515
604
|
|
|
605
|
+
# JUMBF description box
|
|
606
|
+
%Image::ExifTool::Jpeg2000::JUMD = (
|
|
607
|
+
PROCESS_PROC => \&ProcessJUMD,
|
|
608
|
+
GROUPS => { 0 => 'JUMBF', 1 => 'JUMBF', 2 => 'Image' },
|
|
609
|
+
NOTES => 'Information extracted from the JUMBF description box.',
|
|
610
|
+
'jumd-type' => {
|
|
611
|
+
Name => 'JUMDType',
|
|
612
|
+
ValueConv => 'unpack "H*", $val',
|
|
613
|
+
PrintConv => q{
|
|
614
|
+
my @a = $val =~ /^(\w{8})(\w{4})(\w{4})(\w{16})$/;
|
|
615
|
+
return $val unless @a;
|
|
616
|
+
my $ascii = pack 'H*', $a[0];
|
|
617
|
+
$a[0] = $ascii if $ascii =~ /^[a-zA-Z0-9]{4}$/;
|
|
618
|
+
return join '-', @a;
|
|
619
|
+
},
|
|
620
|
+
# seen:
|
|
621
|
+
# cacb/cast/caas/cacl/casg/json-00110010800000aa00389b71
|
|
622
|
+
# 6579d6fbdba2446bb2ac1b82feeb89d1 - JPEG image
|
|
623
|
+
},
|
|
624
|
+
'jumd-label' => { Name => 'JUMDLabel' },
|
|
625
|
+
'jumd-flags' => {
|
|
626
|
+
Name => 'JUMDFlags',
|
|
627
|
+
PrintConv => { BITMASK => {
|
|
628
|
+
0 => 'Requestable',
|
|
629
|
+
1 => 'Label',
|
|
630
|
+
2 => 'ID',
|
|
631
|
+
3 => 'Signature',
|
|
632
|
+
}},
|
|
633
|
+
},
|
|
634
|
+
'jumd-id' => { Name => 'JUMDID', Description => 'JUMD ID' },
|
|
635
|
+
'jumd-sig' => { Name => 'JUMDSignature', PrintConv => 'unpack "H*", $val' },
|
|
636
|
+
);
|
|
637
|
+
|
|
638
|
+
#------------------------------------------------------------------------------
|
|
639
|
+
# Read JUMBF box to keep track of sub-document numbers
|
|
640
|
+
# Inputs: 0) ExifTool ref, 1) dirInfo ref, 2) tag table ref
|
|
641
|
+
# Returns: 1 on success
|
|
642
|
+
sub ProcessJUMB($$$)
|
|
643
|
+
{
|
|
644
|
+
my ($et, $dirInfo, $tagTablePtr) = @_;
|
|
645
|
+
if ($$et{jumd_level}) {
|
|
646
|
+
++$$et{jumd_level}[-1]; # increment current sub-document number
|
|
647
|
+
} else {
|
|
648
|
+
$$et{jumd_level} = [ ++$$et{DOC_COUNT} ]; # new top-level sub-document
|
|
649
|
+
$$et{SET_GROUP0} = 'JUMBF';
|
|
650
|
+
}
|
|
651
|
+
$$et{DOC_NUM} = join '-', @{$$et{jumd_level}};
|
|
652
|
+
push @{$$et{jumd_level}}, 0;
|
|
653
|
+
ProcessJpeg2000Box($et, $dirInfo, $tagTablePtr);
|
|
654
|
+
delete $$et{DOC_NUM};
|
|
655
|
+
delete $$et{JUMBFLabel};
|
|
656
|
+
pop @{$$et{jumd_level}};
|
|
657
|
+
if (@{$$et{jumd_level}} < 2) {
|
|
658
|
+
delete $$et{jumd_level};
|
|
659
|
+
delete $$et{SET_GROUP0};
|
|
660
|
+
}
|
|
661
|
+
return 1;
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
#------------------------------------------------------------------------------
|
|
665
|
+
# Read JUMBF description box (ref https://github.com/thorfdbg/codestream-parser)
|
|
666
|
+
# Inputs: 0) ExifTool ref, 1) dirInfo ref, 2) tag table ref
|
|
667
|
+
# Returns: 1 on success
|
|
668
|
+
sub ProcessJUMD($$$)
|
|
669
|
+
{
|
|
670
|
+
my ($et, $dirInfo, $tagTablePtr) = @_;
|
|
671
|
+
my $dataPt = $$dirInfo{DataPt};
|
|
672
|
+
my $pos = $$dirInfo{DirStart};
|
|
673
|
+
my $end = $pos + $$dirInfo{DirLen};
|
|
674
|
+
$et->VerboseDir('JUMD', 0, $end-$pos);
|
|
675
|
+
delete $$et{JUMBFLabel};
|
|
676
|
+
$$dirInfo{DirLen} < 17 and $et->Warn('Truncated JUMD directory'), return 0;
|
|
677
|
+
my $type = substr($$dataPt, $pos, 4);
|
|
678
|
+
$et->HandleTag($tagTablePtr, 'jumd-type', substr($$dataPt, $pos, 16));
|
|
679
|
+
$pos += 16;
|
|
680
|
+
my $flags = Get8u($dataPt, $pos++);
|
|
681
|
+
$et->HandleTag($tagTablePtr, 'jumd-flags', $flags);
|
|
682
|
+
if ($flags & 0x02) { # label exists?
|
|
683
|
+
pos($$dataPt) = $pos;
|
|
684
|
+
$$dataPt =~ /\0/g or $et->Warn('Missing JUMD label terminator'), return 0;
|
|
685
|
+
my $len = pos($$dataPt) - $pos;
|
|
686
|
+
my $name = substr($$dataPt, $pos, $len);
|
|
687
|
+
$et->HandleTag($tagTablePtr, 'jumd-label', $name);
|
|
688
|
+
$pos += $len;
|
|
689
|
+
if ($len) {
|
|
690
|
+
$name =~ s/[^-_a-zA-Z0-9]([a-z])/\U$1/g; # capitalize characters after illegal characters
|
|
691
|
+
$name =~ tr/-_a-zA-Z0-9//dc; # remove other illegal characters
|
|
692
|
+
$name = ucfirst $name; # capitalize first letter
|
|
693
|
+
$name = "Tag$name" if length($name) < 2; # must at least 2 characters long
|
|
694
|
+
$$et{JUMBFLabel} = $name;
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
if ($flags & 0x04) { # ID exists?
|
|
698
|
+
$pos + 4 > $end and $et->Warn('Missing JUMD ID'), return 0;
|
|
699
|
+
$et->HandleTag($tagTablePtr, 'jumd-id', Get32u($dataPt, $pos));
|
|
700
|
+
$pos += 4;
|
|
701
|
+
}
|
|
702
|
+
if ($flags & 0x08) { # signature exists?
|
|
703
|
+
$pos + 32 > $end and $et->Warn('Missing JUMD signature'), return 0;
|
|
704
|
+
$et->HandleTag($tagTablePtr, 'jumd-sig', substr($$dataPt, $pos, 32));
|
|
705
|
+
$pos += 32;
|
|
706
|
+
}
|
|
707
|
+
$pos == $end or $et->Warn('Extra data in JUMD box'." $pos $end", 1);
|
|
708
|
+
return 1;
|
|
709
|
+
}
|
|
710
|
+
|
|
516
711
|
#------------------------------------------------------------------------------
|
|
517
712
|
# Create new JPEG 2000 boxes when writing
|
|
518
713
|
# (Currently only supports adding top-level Writable JPEG2000 tags and certain UUID boxes)
|
|
@@ -542,8 +737,29 @@ sub CreateNewBoxes($$)
|
|
|
542
737
|
$et->VerboseValue("+ Jpeg2000:$$tagInfo{Name}", $val);
|
|
543
738
|
}
|
|
544
739
|
}
|
|
545
|
-
# add UUID boxes
|
|
740
|
+
# add UUID boxes (and/or JXL Exif/XML boxes)
|
|
546
741
|
foreach $dirName (sort keys %$addDirs) {
|
|
742
|
+
# handle JPEG XL XMP and EXIF
|
|
743
|
+
if ($dirName eq 'XML' or $dirName eq 'Exif') {
|
|
744
|
+
my ($tag, $dir) = $dirName eq 'XML' ? ('xml ', 'XMP') : ('Exif', 'EXIF');
|
|
745
|
+
my $tagInfo = $Image::ExifTool::Jpeg2000::Main{$tag};
|
|
746
|
+
$tagInfo = $$tagInfo[1] if ref $tagInfo eq 'ARRAY'; # (hack for stupid JXL XMP)
|
|
747
|
+
my $subdir = $$tagInfo{SubDirectory};
|
|
748
|
+
my $tagTable = GetTagTable($$subdir{TagTable});
|
|
749
|
+
$tagTable = GetTagTable('Image::ExifTool::XMP::Main') if $dir eq 'XMP';
|
|
750
|
+
my %dirInfo = (
|
|
751
|
+
DirName => $dir,
|
|
752
|
+
Parent => 'JP2',
|
|
753
|
+
);
|
|
754
|
+
my $newdir = $et->WriteDirectory(\%dirInfo, $tagTable, $$subdir{WriteProc});
|
|
755
|
+
if (defined $newdir and length $newdir) {
|
|
756
|
+
# not sure why, but EXIF box is padded with leading 0's in my sample
|
|
757
|
+
my $pad = $dirName eq 'Exif' ? "\0\0\0\0" : '';
|
|
758
|
+
my $boxhdr = pack('N', length($newdir) + length($pad) + 8) . $tag;
|
|
759
|
+
Write($outfile, $boxhdr, $pad, $newdir) or return 0;
|
|
760
|
+
next;
|
|
761
|
+
}
|
|
762
|
+
}
|
|
547
763
|
next unless $uuid{$dirName};
|
|
548
764
|
my $tagInfo;
|
|
549
765
|
foreach $tagInfo (@{$Image::ExifTool::Jpeg2000::Main{uuid}}) {
|
|
@@ -725,6 +941,13 @@ sub ProcessJpeg2000Box($$$)
|
|
|
725
941
|
);
|
|
726
942
|
next unless $tagInfo;
|
|
727
943
|
}
|
|
944
|
+
# create new tag for JUMBF data values with name corresponding to JUMBFLabel
|
|
945
|
+
if ($$et{JUMBFLabel} and (not $$tagInfo{SubDirectory} or $$tagInfo{BlockExtract})) {
|
|
946
|
+
$tagInfo = { %$tagInfo, Name => $$et{JUMBFLabel} };
|
|
947
|
+
AddTagToTable($tagTablePtr, '_JUMBF_' . $$et{JUMBFLabel}, $tagInfo);
|
|
948
|
+
delete $$tagInfo{Protected}; # (must do this so -j -b returns JUMBF binary data)
|
|
949
|
+
$$tagInfo{TagID} = $boxID;
|
|
950
|
+
}
|
|
728
951
|
if ($$tagInfo{SubDirectory}) {
|
|
729
952
|
my $subdir = $$tagInfo{SubDirectory};
|
|
730
953
|
my $subdirStart = $valuePtr;
|
|
@@ -752,8 +975,8 @@ sub ProcessJpeg2000Box($$$)
|
|
|
752
975
|
# remove this directory from our create list
|
|
753
976
|
delete $$et{AddJp2Dirs}{$$tagInfo{Name}};
|
|
754
977
|
my $newdir;
|
|
755
|
-
# only edit writable UUID boxes
|
|
756
|
-
if ($uuid) {
|
|
978
|
+
# only edit writable UUID and Exif boxes
|
|
979
|
+
if ($uuid or $boxID eq 'Exif' or ($boxID eq 'xml ' and $$et{IsJXL})) {
|
|
757
980
|
$newdir = $et->WriteDirectory(\%subdirInfo, $subTable, $$subdir{WriteProc});
|
|
758
981
|
next if defined $newdir and not length $newdir; # next if deleting the box
|
|
759
982
|
} elsif (defined $uuid) {
|
|
@@ -803,6 +1026,68 @@ sub ProcessJpeg2000Box($$$)
|
|
|
803
1026
|
return 1;
|
|
804
1027
|
}
|
|
805
1028
|
|
|
1029
|
+
#------------------------------------------------------------------------------
|
|
1030
|
+
# Return bits from a bitstream object
|
|
1031
|
+
# Inputs: 0) array ref, 1) number of bits
|
|
1032
|
+
# Returns: specified number of bits as an integer, and shifts input bitstream
|
|
1033
|
+
sub GetBits($$)
|
|
1034
|
+
{
|
|
1035
|
+
my ($a, $n) = @_;
|
|
1036
|
+
my $v = 0;
|
|
1037
|
+
my $bit = 1;
|
|
1038
|
+
my $i;
|
|
1039
|
+
while ($n--) {
|
|
1040
|
+
for ($i=0; $i<@$a; ++$i) {
|
|
1041
|
+
# consume bits LSB first
|
|
1042
|
+
my $set = $$a[$i] & 1;
|
|
1043
|
+
$$a[$i] >>= 1;
|
|
1044
|
+
if ($i) {
|
|
1045
|
+
$$a[$i-1] |= 0x80 if $set;
|
|
1046
|
+
} else {
|
|
1047
|
+
$v |= $bit if $set;
|
|
1048
|
+
$bit <<= 1;
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
1052
|
+
return $v;
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1055
|
+
#------------------------------------------------------------------------------
|
|
1056
|
+
# Extract parameters from JPEG XL codestream [unverified!]
|
|
1057
|
+
# Inputs: 0) ExifTool ref, 1) codestream ref
|
|
1058
|
+
# Returns: 1
|
|
1059
|
+
sub ProcessJXLCodestream($$)
|
|
1060
|
+
{
|
|
1061
|
+
my ($et, $dataPt) = @_;
|
|
1062
|
+
# add padding if necessary to avoid unpacking past end of data
|
|
1063
|
+
if (length $$dataPt < 14) {
|
|
1064
|
+
my $tmp = $$dataPt . ("\0" x 14);
|
|
1065
|
+
$dataPt = \$tmp;
|
|
1066
|
+
}
|
|
1067
|
+
my @a = unpack 'x2C12', $$dataPt;
|
|
1068
|
+
my ($x, $y);
|
|
1069
|
+
my $small = GetBits(\@a, 1);
|
|
1070
|
+
if ($small) {
|
|
1071
|
+
$y = (GetBits(\@a, 5) + 1) * 8;
|
|
1072
|
+
} else {
|
|
1073
|
+
$y = GetBits(\@a, [9, 13, 18, 30]->[GetBits(\@a, 2)]) + 1;
|
|
1074
|
+
}
|
|
1075
|
+
my $ratio = GetBits(\@a, 3);
|
|
1076
|
+
if ($ratio == 0) {
|
|
1077
|
+
if ($small) {
|
|
1078
|
+
$x = (GetBits(\@a, 5) + 1) * 8;;
|
|
1079
|
+
} else {
|
|
1080
|
+
$x = GetBits(\@a, [9, 13, 18, 30]->[GetBits(\@a, 2)]) + 1;
|
|
1081
|
+
}
|
|
1082
|
+
} else {
|
|
1083
|
+
my $r = [[1,1],[12,10],[4,3],[3,2],[16,9],[5,4],[2,1]]->[$ratio-1];
|
|
1084
|
+
$x = int($y * $$r[0] / $$r[1]);
|
|
1085
|
+
}
|
|
1086
|
+
$et->FoundTag(ImageWidth => $x);
|
|
1087
|
+
$et->FoundTag(ImageHeight => $y);
|
|
1088
|
+
return 1;
|
|
1089
|
+
}
|
|
1090
|
+
|
|
806
1091
|
#------------------------------------------------------------------------------
|
|
807
1092
|
# Read/write meta information from a JPEG 2000 image
|
|
808
1093
|
# Inputs: 0) ExifTool object reference, 1) dirInfo reference
|
|
@@ -817,8 +1102,9 @@ sub ProcessJP2($$)
|
|
|
817
1102
|
|
|
818
1103
|
# check to be sure this is a valid JPG2000 file
|
|
819
1104
|
return 0 unless $raf->Read($hdr,12) == 12;
|
|
820
|
-
unless ($hdr eq "\
|
|
821
|
-
$hdr eq "\
|
|
1105
|
+
unless ($hdr eq "\0\0\0\x0cjP \x0d\x0a\x87\x0a" or # (ref 1)
|
|
1106
|
+
$hdr eq "\0\0\0\x0cjP\x1a\x1a\x0d\x0a\x87\x0a" or # (ref 2)
|
|
1107
|
+
$$et{IsJXL})
|
|
822
1108
|
{
|
|
823
1109
|
return 0 unless $hdr =~ /^\xff\x4f\xff\x51\0/; # check for JP2 codestream format
|
|
824
1110
|
if ($outfile) {
|
|
@@ -835,17 +1121,23 @@ sub ProcessJP2($$)
|
|
|
835
1121
|
}
|
|
836
1122
|
if ($outfile) {
|
|
837
1123
|
Write($outfile, $hdr) or return -1;
|
|
838
|
-
|
|
1124
|
+
if ($$et{IsJXL}) {
|
|
1125
|
+
$et->InitWriteDirs(\%jxlMap);
|
|
1126
|
+
$$et{AddJp2Tags} = { }; # (don't add JP2 tags in JXL files)
|
|
1127
|
+
} else {
|
|
1128
|
+
$et->InitWriteDirs(\%jp2Map);
|
|
1129
|
+
$$et{AddJp2Tags} = $et->GetNewTagInfoHash(\%Image::ExifTool::Jpeg2000::Main);
|
|
1130
|
+
}
|
|
839
1131
|
# save list of directories to create
|
|
840
|
-
my %addDirs = %{$$et{ADD_DIRS}};
|
|
1132
|
+
my %addDirs = %{$$et{ADD_DIRS}}; # (make a copy)
|
|
841
1133
|
$$et{AddJp2Dirs} = \%addDirs;
|
|
842
|
-
$$et{AddJp2Tags} = $et->GetNewTagInfoHash(\%Image::ExifTool::Jpeg2000::Main);
|
|
843
1134
|
} else {
|
|
844
1135
|
my ($buff, $fileType);
|
|
845
1136
|
# recognize JPX and JPM as unique types of JP2
|
|
846
1137
|
if ($raf->Read($buff, 12) == 12 and $buff =~ /^.{4}ftyp(.{4})/s) {
|
|
847
1138
|
$fileType = 'JPX' if $1 eq 'jpx ';
|
|
848
1139
|
$fileType = 'JPM' if $1 eq 'jpm ';
|
|
1140
|
+
$fileType = 'JXL' if $1 eq 'jxl ';
|
|
849
1141
|
}
|
|
850
1142
|
$raf->Seek(-length($buff), 1) if defined $buff;
|
|
851
1143
|
$et->SetFileType($fileType);
|
|
@@ -860,6 +1152,59 @@ sub ProcessJP2($$)
|
|
|
860
1152
|
return $et->ProcessDirectory(\%dirInfo, $tagTablePtr);
|
|
861
1153
|
}
|
|
862
1154
|
|
|
1155
|
+
#------------------------------------------------------------------------------
|
|
1156
|
+
# Read meta information from a JPEG XL image
|
|
1157
|
+
# Inputs: 0) ExifTool object reference, 1) dirInfo reference
|
|
1158
|
+
# Returns: 1 on success, 0 if this wasn't a valid JPEG XL file, -1 on write error
|
|
1159
|
+
sub ProcessJXL($$)
|
|
1160
|
+
{
|
|
1161
|
+
my ($et, $dirInfo) = @_;
|
|
1162
|
+
my $raf = $$dirInfo{RAF};
|
|
1163
|
+
my $outfile = $$dirInfo{OutFile};
|
|
1164
|
+
my ($hdr, $buff);
|
|
1165
|
+
|
|
1166
|
+
return 0 unless $raf->Read($hdr,12) == 12;
|
|
1167
|
+
if ($hdr eq "\0\0\0\x0cJXL \x0d\x0a\x87\x0a") {
|
|
1168
|
+
# JPEG XL in ISO BMFF container
|
|
1169
|
+
$$et{IsJXL} = 1;
|
|
1170
|
+
} elsif ($hdr =~ /^\xff\x0a/) {
|
|
1171
|
+
# JPEG XL codestream
|
|
1172
|
+
if ($outfile) {
|
|
1173
|
+
if ($$et{OPTIONS}{IgnoreMinorErrors}) {
|
|
1174
|
+
$et->Warn('Wrapped JXL codestream in ISO BMFF container');
|
|
1175
|
+
} else {
|
|
1176
|
+
$et->Error('Will wrap JXL codestream in ISO BMFF container for writing',1);
|
|
1177
|
+
return 0;
|
|
1178
|
+
}
|
|
1179
|
+
$$et{IsJXL} = 2;
|
|
1180
|
+
my $buff = "\0\0\0\x0cJXL \x0d\x0a\x87\x0a\0\0\0\x14ftypjxl \0\0\0\0jxl ";
|
|
1181
|
+
# add metadata to empty ISO BMFF container
|
|
1182
|
+
$$dirInfo{RAF} = new File::RandomAccess(\$buff);
|
|
1183
|
+
} else {
|
|
1184
|
+
$et->SetFileType('JXL Codestream','image/jxl', 'jxl');
|
|
1185
|
+
return ProcessJXLCodestream($et, \$hdr);
|
|
1186
|
+
}
|
|
1187
|
+
} else {
|
|
1188
|
+
return 0;
|
|
1189
|
+
}
|
|
1190
|
+
$raf->Seek(0,0) or $et->Error('Seek error'), return 0;
|
|
1191
|
+
|
|
1192
|
+
my $success = ProcessJP2($et, $dirInfo);
|
|
1193
|
+
|
|
1194
|
+
if ($outfile and $success > 0 and $$et{IsJXL} == 2) {
|
|
1195
|
+
# attach the JXL codestream box to the ISO BMFF file
|
|
1196
|
+
$raf->Seek(0,2) or return -1;
|
|
1197
|
+
my $size = $raf->Tell();
|
|
1198
|
+
$raf->Seek(0,0) or return -1;
|
|
1199
|
+
SetByteOrder('MM');
|
|
1200
|
+
Write($outfile, Set32u($size + 8), 'jxlc') or return -1;
|
|
1201
|
+
while ($raf->Read($buff, 65536)) {
|
|
1202
|
+
Write($outfile, $buff) or return -1;
|
|
1203
|
+
}
|
|
1204
|
+
}
|
|
1205
|
+
return $success;
|
|
1206
|
+
}
|
|
1207
|
+
|
|
863
1208
|
1; # end
|
|
864
1209
|
|
|
865
1210
|
__END__
|