exiftool_vendored 12.16.0 → 12.25.0
Sign up to get free protection for your applications and to get access to all the features.
- 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__
|