exiftool_vendored 12.18.0 → 12.33.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 +236 -4
- data/bin/MANIFEST +23 -0
- data/bin/META.json +1 -1
- data/bin/META.yml +1 -1
- data/bin/README +45 -43
- data/bin/arg_files/xmp2exif.args +2 -1
- data/bin/config_files/acdsee.config +193 -6
- data/bin/config_files/convert_regions.config +25 -14
- data/bin/config_files/cuepointlist.config +70 -0
- data/bin/config_files/example.config +2 -9
- data/bin/exiftool +152 -97
- data/bin/fmt_files/gpx.fmt +2 -2
- data/bin/fmt_files/gpx_wpt.fmt +2 -2
- data/bin/fmt_files/kml.fmt +1 -1
- data/bin/fmt_files/kml_track.fmt +1 -1
- data/bin/lib/Image/ExifTool/Apple.pm +3 -2
- data/bin/lib/Image/ExifTool/BuildTagLookup.pm +31 -13
- data/bin/lib/Image/ExifTool/CBOR.pm +331 -0
- data/bin/lib/Image/ExifTool/Canon.pm +44 -19
- data/bin/lib/Image/ExifTool/DJI.pm +6 -6
- data/bin/lib/Image/ExifTool/DPX.pm +13 -2
- data/bin/lib/Image/ExifTool/DjVu.pm +6 -5
- data/bin/lib/Image/ExifTool/Exif.pm +124 -13
- data/bin/lib/Image/ExifTool/FITS.pm +13 -2
- data/bin/lib/Image/ExifTool/FlashPix.pm +35 -10
- data/bin/lib/Image/ExifTool/FujiFilm.pm +19 -8
- data/bin/lib/Image/ExifTool/GPS.pm +22 -11
- data/bin/lib/Image/ExifTool/Geotag.pm +13 -2
- data/bin/lib/Image/ExifTool/GoPro.pm +16 -1
- data/bin/lib/Image/ExifTool/ICC_Profile.pm +2 -2
- data/bin/lib/Image/ExifTool/ID3.pm +15 -3
- data/bin/lib/Image/ExifTool/JPEG.pm +74 -4
- data/bin/lib/Image/ExifTool/JSON.pm +30 -5
- data/bin/lib/Image/ExifTool/Jpeg2000.pm +395 -16
- data/bin/lib/Image/ExifTool/LIF.pm +153 -0
- data/bin/lib/Image/ExifTool/Lang/nl.pm +60 -59
- data/bin/lib/Image/ExifTool/M2TS.pm +137 -5
- data/bin/lib/Image/ExifTool/MIE.pm +4 -3
- 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 +3 -3
- data/bin/lib/Image/ExifTool/Microsoft.pm +298 -82
- data/bin/lib/Image/ExifTool/Nikon.pm +18 -5
- data/bin/lib/Image/ExifTool/NikonSettings.pm +19 -2
- data/bin/lib/Image/ExifTool/Olympus.pm +10 -3
- data/bin/lib/Image/ExifTool/Other.pm +93 -0
- data/bin/lib/Image/ExifTool/PDF.pm +9 -12
- data/bin/lib/Image/ExifTool/PNG.pm +8 -7
- data/bin/lib/Image/ExifTool/Panasonic.pm +28 -3
- data/bin/lib/Image/ExifTool/Pentax.pm +28 -5
- data/bin/lib/Image/ExifTool/PhaseOne.pm +4 -3
- data/bin/lib/Image/ExifTool/Photoshop.pm +6 -0
- data/bin/lib/Image/ExifTool/QuickTime.pm +234 -75
- data/bin/lib/Image/ExifTool/QuickTimeStream.pl +283 -141
- data/bin/lib/Image/ExifTool/README +5 -2
- data/bin/lib/Image/ExifTool/RIFF.pm +89 -12
- data/bin/lib/Image/ExifTool/Samsung.pm +48 -10
- data/bin/lib/Image/ExifTool/Shortcuts.pm +9 -0
- data/bin/lib/Image/ExifTool/Sony.pm +230 -69
- data/bin/lib/Image/ExifTool/TagInfoXML.pm +1 -0
- data/bin/lib/Image/ExifTool/TagLookup.pm +4145 -4029
- data/bin/lib/Image/ExifTool/TagNames.pod +671 -287
- data/bin/lib/Image/ExifTool/Torrent.pm +18 -11
- data/bin/lib/Image/ExifTool/WriteExif.pl +1 -1
- data/bin/lib/Image/ExifTool/WriteIPTC.pl +1 -1
- data/bin/lib/Image/ExifTool/WritePDF.pl +1 -0
- data/bin/lib/Image/ExifTool/WritePNG.pl +2 -0
- data/bin/lib/Image/ExifTool/WritePostScript.pl +1 -0
- data/bin/lib/Image/ExifTool/WriteQuickTime.pl +55 -21
- data/bin/lib/Image/ExifTool/WriteXMP.pl +7 -3
- data/bin/lib/Image/ExifTool/Writer.pl +47 -10
- data/bin/lib/Image/ExifTool/XMP.pm +45 -15
- data/bin/lib/Image/ExifTool/XMP2.pl +3 -1
- data/bin/lib/Image/ExifTool/XMPStruct.pl +3 -1
- data/bin/lib/Image/ExifTool/ZISRAW.pm +121 -2
- data/bin/lib/Image/ExifTool.pm +233 -81
- data/bin/lib/Image/ExifTool.pod +114 -93
- data/bin/perl-Image-ExifTool.spec +43 -42
- data/lib/exiftool_vendored/version.rb +1 -1
- metadata +28 -13
@@ -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.31';
|
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',
|
@@ -107,9 +124,13 @@ my %j2cMarker = (
|
|
107
124
|
WRITE_PROC => \&ProcessJpeg2000Box,
|
108
125
|
PREFERRED => 1, # always add these tags when writing
|
109
126
|
NOTES => q{
|
110
|
-
The tags below are
|
111
|
-
|
127
|
+
The tags below are found in JPEG 2000 images and the JUMBF metadata in JPEG
|
128
|
+
images, but not all of these are extracted. Note that ExifTool currently
|
129
|
+
writes only EXIF, IPTC and XMP tags in Jpeg2000 images.
|
112
130
|
},
|
131
|
+
#
|
132
|
+
# NOTE: ONLY TAGS WITH "Format" DEFINED ARE EXTRACTED!
|
133
|
+
#
|
113
134
|
'jP ' => 'JP2Signature', # (ref 1)
|
114
135
|
"jP\x1a\x1a" => 'JP2Signature', # (ref 2)
|
115
136
|
prfl => 'Profile',
|
@@ -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,22 @@ my %j2cMarker = (
|
|
302
338
|
Start => '$valuePtr + 16',
|
303
339
|
},
|
304
340
|
},
|
341
|
+
{
|
342
|
+
Name => 'UUID-Signature', # (seen in JUMB data of JPEG images)
|
343
|
+
# (may be able to remove this when JUMBF specification is finalized)
|
344
|
+
Condition => '$$valPt=~/^casg\x00\x11\x00\x10\x80\x00\x00\xaa\x00\x38\x9b\x71/',
|
345
|
+
Format => 'undef',
|
346
|
+
ValueConv => 'substr($val,16)',
|
347
|
+
},
|
348
|
+
{
|
349
|
+
Name => 'UUID-C2PAClaimSignature', # (seen in incorrectly-formatted JUMB data of JPEG images)
|
350
|
+
# (may be able to remove this when JUMBF specification is finalized)
|
351
|
+
Condition => '$$valPt=~/^c2cs\x00\x11\x00\x10\x80\x00\x00\xaa\x00\x38\x9b\x71/',
|
352
|
+
SubDirectory => {
|
353
|
+
TagTable => 'Image::ExifTool::CBOR::Main',
|
354
|
+
Start => '$valuePtr + 16',
|
355
|
+
},
|
356
|
+
},
|
305
357
|
{
|
306
358
|
Name => 'UUID-Unknown',
|
307
359
|
},
|
@@ -321,6 +373,73 @@ my %j2cMarker = (
|
|
321
373
|
Name => 'URL',
|
322
374
|
Format => 'string',
|
323
375
|
},
|
376
|
+
# JUMBF boxes (ref https://github.com/thorfdbg/codestream-parser)
|
377
|
+
jumd => {
|
378
|
+
Name => 'JUMBFDescr',
|
379
|
+
SubDirectory => { TagTable => 'Image::ExifTool::Jpeg2000::JUMD' },
|
380
|
+
},
|
381
|
+
jumb => {
|
382
|
+
Name => 'JUMBFBox',
|
383
|
+
SubDirectory => {
|
384
|
+
TagTable => 'Image::ExifTool::Jpeg2000::Main',
|
385
|
+
ProcessProc => \&ProcessJUMB,
|
386
|
+
},
|
387
|
+
},
|
388
|
+
json => {
|
389
|
+
Name => 'JSONData',
|
390
|
+
Flags => [ 'Binary', 'Protected', 'BlockExtract' ],
|
391
|
+
Notes => q{
|
392
|
+
by default, data in this tag is parsed using the ExifTool JSON module to to
|
393
|
+
allow individual tags to be accessed when reading, but it may also be
|
394
|
+
extracted as a block via the "JSONData" tag or by setting the API
|
395
|
+
BlockExtract option
|
396
|
+
},
|
397
|
+
SubDirectory => { TagTable => 'Image::ExifTool::JSON::Main' },
|
398
|
+
},
|
399
|
+
cbor => {
|
400
|
+
Name => 'CBORData',
|
401
|
+
Flags => [ 'Binary', 'Protected' ],
|
402
|
+
SubDirectory => { TagTable => 'Image::ExifTool::CBOR::Main' },
|
403
|
+
},
|
404
|
+
bfdb => { # used in JUMBF (see # (used when tag is renamed according to JUMDLabel)
|
405
|
+
Name => 'BinaryDataType',
|
406
|
+
Notes => 'JUMBF, MIME type and optional file name',
|
407
|
+
Format => 'undef',
|
408
|
+
# (ignore "toggles" byte and just extract MIME type and file name)
|
409
|
+
ValueConv => '$_=substr($val,1); s/\0+$//; s/\0/, /; $_',
|
410
|
+
JUMBF_Suffix => 'Type', # (used when tag is renamed according to JUMDLabel)
|
411
|
+
},
|
412
|
+
bidb => { # used in JUMBF
|
413
|
+
Name => 'BinaryData',
|
414
|
+
Notes => 'JUMBF',
|
415
|
+
Groups => { 2 => 'Preview' },
|
416
|
+
Format => 'undef',
|
417
|
+
Binary => 1,
|
418
|
+
JUMBF_Suffix => 'Data', # (used when tag is renamed according to JUMDLabel)
|
419
|
+
},
|
420
|
+
#
|
421
|
+
# stuff seen in JPEG XL images:
|
422
|
+
#
|
423
|
+
# jbrd - JPEG Bitstream Reconstruction Data (allows lossless conversion back to original JPG)
|
424
|
+
jxlc => {
|
425
|
+
Name => 'JXLCodestream',
|
426
|
+
Format => 'undef',
|
427
|
+
Notes => q{
|
428
|
+
Codestream in JPEG XL image. Currently processed only to determine
|
429
|
+
ImageSize
|
430
|
+
},
|
431
|
+
RawConv => 'Image::ExifTool::Jpeg2000::ProcessJXLCodestream($self,\$val); undef',
|
432
|
+
},
|
433
|
+
Exif => {
|
434
|
+
Name => 'EXIF',
|
435
|
+
SubDirectory => {
|
436
|
+
TagTable => 'Image::ExifTool::Exif::Main',
|
437
|
+
ProcessProc => \&Image::ExifTool::ProcessTIFF,
|
438
|
+
WriteProc => \&Image::ExifTool::WriteTIFF,
|
439
|
+
DirName => 'EXIF',
|
440
|
+
Start => '$valuePtr + 4',
|
441
|
+
},
|
442
|
+
},
|
324
443
|
);
|
325
444
|
|
326
445
|
%Image::ExifTool::Jpeg2000::ImageHeader = (
|
@@ -375,6 +494,7 @@ my %j2cMarker = (
|
|
375
494
|
'jp2 ' => 'JPEG 2000 Image (.JP2)', # image/jp2
|
376
495
|
'jpm ' => 'JPEG 2000 Compound Image (.JPM)', # image/jpm
|
377
496
|
'jpx ' => 'JPEG 2000 with extensions (.JPX)', # image/jpx
|
497
|
+
'jxl ' => 'JPEG XL Image (.JXL)', # image/jxl
|
378
498
|
},
|
379
499
|
},
|
380
500
|
1 => {
|
@@ -513,6 +633,114 @@ my %j2cMarker = (
|
|
513
633
|
],
|
514
634
|
);
|
515
635
|
|
636
|
+
# JUMBF description box
|
637
|
+
%Image::ExifTool::Jpeg2000::JUMD = (
|
638
|
+
PROCESS_PROC => \&ProcessJUMD,
|
639
|
+
GROUPS => { 0 => 'JUMBF', 1 => 'JUMBF', 2 => 'Image' },
|
640
|
+
NOTES => 'Information extracted from the JUMBF description box.',
|
641
|
+
'type' => {
|
642
|
+
Name => 'JUMDType',
|
643
|
+
ValueConv => 'unpack "H*", $val',
|
644
|
+
PrintConv => q{
|
645
|
+
my @a = $val =~ /^(\w{8})(\w{4})(\w{4})(\w{16})$/;
|
646
|
+
return $val unless @a;
|
647
|
+
my $ascii = pack 'H*', $a[0];
|
648
|
+
$a[0] = "($ascii)" if $ascii =~ /^[a-zA-Z0-9]{4}$/;
|
649
|
+
return join '-', @a;
|
650
|
+
},
|
651
|
+
# seen:
|
652
|
+
# cacb/cast/caas/cacl/casg/json-00110010800000aa00389b71
|
653
|
+
# 6579d6fbdba2446bb2ac1b82feeb89d1 - JPEG image
|
654
|
+
},
|
655
|
+
'label' => { Name => 'JUMDLabel' },
|
656
|
+
'toggles' => {
|
657
|
+
Name => 'JUMDToggles',
|
658
|
+
Unknown => 1,
|
659
|
+
PrintConv => { BITMASK => {
|
660
|
+
0 => 'Requestable',
|
661
|
+
1 => 'Label',
|
662
|
+
2 => 'ID',
|
663
|
+
3 => 'Signature',
|
664
|
+
}},
|
665
|
+
},
|
666
|
+
'id' => { Name => 'JUMDID', Description => 'JUMD ID' },
|
667
|
+
'sig' => { Name => 'JUMDSignature', PrintConv => 'unpack "H*", $val' },
|
668
|
+
);
|
669
|
+
|
670
|
+
#------------------------------------------------------------------------------
|
671
|
+
# Read JUMBF box to keep track of sub-document numbers
|
672
|
+
# Inputs: 0) ExifTool ref, 1) dirInfo ref, 2) tag table ref
|
673
|
+
# Returns: 1 on success
|
674
|
+
sub ProcessJUMB($$$)
|
675
|
+
{
|
676
|
+
my ($et, $dirInfo, $tagTablePtr) = @_;
|
677
|
+
if ($$et{jumd_level}) {
|
678
|
+
++$$et{jumd_level}[-1]; # increment current sub-document number
|
679
|
+
} else {
|
680
|
+
$$et{jumd_level} = [ ++$$et{DOC_COUNT} ]; # new top-level sub-document
|
681
|
+
$$et{SET_GROUP0} = 'JUMBF';
|
682
|
+
}
|
683
|
+
$$et{DOC_NUM} = join '-', @{$$et{jumd_level}};
|
684
|
+
push @{$$et{jumd_level}}, 0;
|
685
|
+
ProcessJpeg2000Box($et, $dirInfo, $tagTablePtr);
|
686
|
+
delete $$et{DOC_NUM};
|
687
|
+
delete $$et{JUMBFLabel};
|
688
|
+
pop @{$$et{jumd_level}};
|
689
|
+
if (@{$$et{jumd_level}} < 2) {
|
690
|
+
delete $$et{jumd_level};
|
691
|
+
delete $$et{SET_GROUP0};
|
692
|
+
}
|
693
|
+
return 1;
|
694
|
+
}
|
695
|
+
|
696
|
+
#------------------------------------------------------------------------------
|
697
|
+
# Read JUMBF description box (ref https://github.com/thorfdbg/codestream-parser)
|
698
|
+
# Inputs: 0) ExifTool ref, 1) dirInfo ref, 2) tag table ref
|
699
|
+
# Returns: 1 on success
|
700
|
+
sub ProcessJUMD($$$)
|
701
|
+
{
|
702
|
+
my ($et, $dirInfo, $tagTablePtr) = @_;
|
703
|
+
my $dataPt = $$dirInfo{DataPt};
|
704
|
+
my $pos = $$dirInfo{DirStart};
|
705
|
+
my $end = $pos + $$dirInfo{DirLen};
|
706
|
+
$et->VerboseDir('JUMD', 0, $end-$pos);
|
707
|
+
delete $$et{JUMBFLabel};
|
708
|
+
$$dirInfo{DirLen} < 17 and $et->Warn('Truncated JUMD directory'), return 0;
|
709
|
+
my $type = substr($$dataPt, $pos, 4);
|
710
|
+
$et->HandleTag($tagTablePtr, 'type', substr($$dataPt, $pos, 16));
|
711
|
+
$pos += 16;
|
712
|
+
my $flags = Get8u($dataPt, $pos++);
|
713
|
+
$et->HandleTag($tagTablePtr, 'toggles', $flags);
|
714
|
+
if ($flags & 0x02) { # label exists?
|
715
|
+
pos($$dataPt) = $pos;
|
716
|
+
$$dataPt =~ /\0/g or $et->Warn('Missing JUMD label terminator'), return 0;
|
717
|
+
my $len = pos($$dataPt) - $pos;
|
718
|
+
my $name = substr($$dataPt, $pos, $len);
|
719
|
+
$et->HandleTag($tagTablePtr, 'label', $name);
|
720
|
+
$pos += $len;
|
721
|
+
if ($len) {
|
722
|
+
$name =~ s/[^-_a-zA-Z0-9]([a-z])/\U$1/g; # capitalize characters after illegal characters
|
723
|
+
$name =~ tr/-_a-zA-Z0-9//dc; # remove other illegal characters
|
724
|
+
$name =~ s/__/_/; # collapse double underlines
|
725
|
+
$name = ucfirst $name; # capitalize first letter
|
726
|
+
$name = "Tag$name" if length($name) < 2; # must at least 2 characters long
|
727
|
+
$$et{JUMBFLabel} = $name;
|
728
|
+
}
|
729
|
+
}
|
730
|
+
if ($flags & 0x04) { # ID exists?
|
731
|
+
$pos + 4 > $end and $et->Warn('Missing JUMD ID'), return 0;
|
732
|
+
$et->HandleTag($tagTablePtr, 'id', Get32u($dataPt, $pos));
|
733
|
+
$pos += 4;
|
734
|
+
}
|
735
|
+
if ($flags & 0x08) { # signature exists?
|
736
|
+
$pos + 32 > $end and $et->Warn('Missing JUMD signature'), return 0;
|
737
|
+
$et->HandleTag($tagTablePtr, 'sig', substr($$dataPt, $pos, 32));
|
738
|
+
$pos += 32;
|
739
|
+
}
|
740
|
+
$pos == $end or $et->Warn('Extra data in JUMD box'." $pos $end", 1);
|
741
|
+
return 1;
|
742
|
+
}
|
743
|
+
|
516
744
|
#------------------------------------------------------------------------------
|
517
745
|
# Create new JPEG 2000 boxes when writing
|
518
746
|
# (Currently only supports adding top-level Writable JPEG2000 tags and certain UUID boxes)
|
@@ -542,8 +770,29 @@ sub CreateNewBoxes($$)
|
|
542
770
|
$et->VerboseValue("+ Jpeg2000:$$tagInfo{Name}", $val);
|
543
771
|
}
|
544
772
|
}
|
545
|
-
# add UUID boxes
|
773
|
+
# add UUID boxes (and/or JXL Exif/XML boxes)
|
546
774
|
foreach $dirName (sort keys %$addDirs) {
|
775
|
+
# handle JPEG XL XMP and EXIF
|
776
|
+
if ($dirName eq 'XML' or $dirName eq 'Exif') {
|
777
|
+
my ($tag, $dir) = $dirName eq 'XML' ? ('xml ', 'XMP') : ('Exif', 'EXIF');
|
778
|
+
my $tagInfo = $Image::ExifTool::Jpeg2000::Main{$tag};
|
779
|
+
$tagInfo = $$tagInfo[1] if ref $tagInfo eq 'ARRAY'; # (hack for stupid JXL XMP)
|
780
|
+
my $subdir = $$tagInfo{SubDirectory};
|
781
|
+
my $tagTable = GetTagTable($$subdir{TagTable});
|
782
|
+
$tagTable = GetTagTable('Image::ExifTool::XMP::Main') if $dir eq 'XMP';
|
783
|
+
my %dirInfo = (
|
784
|
+
DirName => $dir,
|
785
|
+
Parent => 'JP2',
|
786
|
+
);
|
787
|
+
my $newdir = $et->WriteDirectory(\%dirInfo, $tagTable, $$subdir{WriteProc});
|
788
|
+
if (defined $newdir and length $newdir) {
|
789
|
+
# not sure why, but EXIF box is padded with leading 0's in my sample
|
790
|
+
my $pad = $dirName eq 'Exif' ? "\0\0\0\0" : '';
|
791
|
+
my $boxhdr = pack('N', length($newdir) + length($pad) + 8) . $tag;
|
792
|
+
Write($outfile, $boxhdr, $pad, $newdir) or return 0;
|
793
|
+
next;
|
794
|
+
}
|
795
|
+
}
|
547
796
|
next unless $uuid{$dirName};
|
548
797
|
my $tagInfo;
|
549
798
|
foreach $tagInfo (@{$Image::ExifTool::Jpeg2000::Main{uuid}}) {
|
@@ -715,6 +964,14 @@ sub ProcessJpeg2000Box($$$)
|
|
715
964
|
}
|
716
965
|
}
|
717
966
|
}
|
967
|
+
# create new tag for JUMBF data values with name corresponding to JUMBFLabel
|
968
|
+
if ($tagInfo and $$et{JUMBFLabel} and (not $$tagInfo{SubDirectory} or $$tagInfo{BlockExtract})) {
|
969
|
+
$tagInfo = { %$tagInfo, Name => $$et{JUMBFLabel} . ($$tagInfo{JUMBF_Suffix} || '') };
|
970
|
+
delete $$tagInfo{Description};
|
971
|
+
AddTagToTable($tagTablePtr, '_JUMBF_' . $$et{JUMBFLabel}, $tagInfo);
|
972
|
+
delete $$tagInfo{Protected}; # (must do this so -j -b returns JUMBF binary data)
|
973
|
+
$$tagInfo{TagID} = $boxID;
|
974
|
+
}
|
718
975
|
if ($verbose) {
|
719
976
|
$et->VerboseInfo($boxID, $tagInfo,
|
720
977
|
Table => $tagTablePtr,
|
@@ -752,8 +1009,8 @@ sub ProcessJpeg2000Box($$$)
|
|
752
1009
|
# remove this directory from our create list
|
753
1010
|
delete $$et{AddJp2Dirs}{$$tagInfo{Name}};
|
754
1011
|
my $newdir;
|
755
|
-
# only edit writable UUID boxes
|
756
|
-
if ($uuid) {
|
1012
|
+
# only edit writable UUID and Exif boxes
|
1013
|
+
if ($uuid or $boxID eq 'Exif' or ($boxID eq 'xml ' and $$et{IsJXL})) {
|
757
1014
|
$newdir = $et->WriteDirectory(\%subdirInfo, $subTable, $$subdir{WriteProc});
|
758
1015
|
next if defined $newdir and not length $newdir; # next if deleting the box
|
759
1016
|
} elsif (defined $uuid) {
|
@@ -803,6 +1060,68 @@ sub ProcessJpeg2000Box($$$)
|
|
803
1060
|
return 1;
|
804
1061
|
}
|
805
1062
|
|
1063
|
+
#------------------------------------------------------------------------------
|
1064
|
+
# Return bits from a bitstream object
|
1065
|
+
# Inputs: 0) array ref, 1) number of bits
|
1066
|
+
# Returns: specified number of bits as an integer, and shifts input bitstream
|
1067
|
+
sub GetBits($$)
|
1068
|
+
{
|
1069
|
+
my ($a, $n) = @_;
|
1070
|
+
my $v = 0;
|
1071
|
+
my $bit = 1;
|
1072
|
+
my $i;
|
1073
|
+
while ($n--) {
|
1074
|
+
for ($i=0; $i<@$a; ++$i) {
|
1075
|
+
# consume bits LSB first
|
1076
|
+
my $set = $$a[$i] & 1;
|
1077
|
+
$$a[$i] >>= 1;
|
1078
|
+
if ($i) {
|
1079
|
+
$$a[$i-1] |= 0x80 if $set;
|
1080
|
+
} else {
|
1081
|
+
$v |= $bit if $set;
|
1082
|
+
$bit <<= 1;
|
1083
|
+
}
|
1084
|
+
}
|
1085
|
+
}
|
1086
|
+
return $v;
|
1087
|
+
}
|
1088
|
+
|
1089
|
+
#------------------------------------------------------------------------------
|
1090
|
+
# Extract parameters from JPEG XL codestream [unverified!]
|
1091
|
+
# Inputs: 0) ExifTool ref, 1) codestream ref
|
1092
|
+
# Returns: 1
|
1093
|
+
sub ProcessJXLCodestream($$)
|
1094
|
+
{
|
1095
|
+
my ($et, $dataPt) = @_;
|
1096
|
+
# add padding if necessary to avoid unpacking past end of data
|
1097
|
+
if (length $$dataPt < 14) {
|
1098
|
+
my $tmp = $$dataPt . ("\0" x 14);
|
1099
|
+
$dataPt = \$tmp;
|
1100
|
+
}
|
1101
|
+
my @a = unpack 'x2C12', $$dataPt;
|
1102
|
+
my ($x, $y);
|
1103
|
+
my $small = GetBits(\@a, 1);
|
1104
|
+
if ($small) {
|
1105
|
+
$y = (GetBits(\@a, 5) + 1) * 8;
|
1106
|
+
} else {
|
1107
|
+
$y = GetBits(\@a, [9, 13, 18, 30]->[GetBits(\@a, 2)]) + 1;
|
1108
|
+
}
|
1109
|
+
my $ratio = GetBits(\@a, 3);
|
1110
|
+
if ($ratio == 0) {
|
1111
|
+
if ($small) {
|
1112
|
+
$x = (GetBits(\@a, 5) + 1) * 8;;
|
1113
|
+
} else {
|
1114
|
+
$x = GetBits(\@a, [9, 13, 18, 30]->[GetBits(\@a, 2)]) + 1;
|
1115
|
+
}
|
1116
|
+
} else {
|
1117
|
+
my $r = [[1,1],[12,10],[4,3],[3,2],[16,9],[5,4],[2,1]]->[$ratio-1];
|
1118
|
+
$x = int($y * $$r[0] / $$r[1]);
|
1119
|
+
}
|
1120
|
+
$et->FoundTag(ImageWidth => $x);
|
1121
|
+
$et->FoundTag(ImageHeight => $y);
|
1122
|
+
return 1;
|
1123
|
+
}
|
1124
|
+
|
806
1125
|
#------------------------------------------------------------------------------
|
807
1126
|
# Read/write meta information from a JPEG 2000 image
|
808
1127
|
# Inputs: 0) ExifTool object reference, 1) dirInfo reference
|
@@ -817,8 +1136,9 @@ sub ProcessJP2($$)
|
|
817
1136
|
|
818
1137
|
# check to be sure this is a valid JPG2000 file
|
819
1138
|
return 0 unless $raf->Read($hdr,12) == 12;
|
820
|
-
unless ($hdr eq "\
|
821
|
-
$hdr eq "\
|
1139
|
+
unless ($hdr eq "\0\0\0\x0cjP \x0d\x0a\x87\x0a" or # (ref 1)
|
1140
|
+
$hdr eq "\0\0\0\x0cjP\x1a\x1a\x0d\x0a\x87\x0a" or # (ref 2)
|
1141
|
+
$$et{IsJXL})
|
822
1142
|
{
|
823
1143
|
return 0 unless $hdr =~ /^\xff\x4f\xff\x51\0/; # check for JP2 codestream format
|
824
1144
|
if ($outfile) {
|
@@ -835,17 +1155,23 @@ sub ProcessJP2($$)
|
|
835
1155
|
}
|
836
1156
|
if ($outfile) {
|
837
1157
|
Write($outfile, $hdr) or return -1;
|
838
|
-
|
1158
|
+
if ($$et{IsJXL}) {
|
1159
|
+
$et->InitWriteDirs(\%jxlMap);
|
1160
|
+
$$et{AddJp2Tags} = { }; # (don't add JP2 tags in JXL files)
|
1161
|
+
} else {
|
1162
|
+
$et->InitWriteDirs(\%jp2Map);
|
1163
|
+
$$et{AddJp2Tags} = $et->GetNewTagInfoHash(\%Image::ExifTool::Jpeg2000::Main);
|
1164
|
+
}
|
839
1165
|
# save list of directories to create
|
840
|
-
my %addDirs = %{$$et{ADD_DIRS}};
|
1166
|
+
my %addDirs = %{$$et{ADD_DIRS}}; # (make a copy)
|
841
1167
|
$$et{AddJp2Dirs} = \%addDirs;
|
842
|
-
$$et{AddJp2Tags} = $et->GetNewTagInfoHash(\%Image::ExifTool::Jpeg2000::Main);
|
843
1168
|
} else {
|
844
1169
|
my ($buff, $fileType);
|
845
1170
|
# recognize JPX and JPM as unique types of JP2
|
846
1171
|
if ($raf->Read($buff, 12) == 12 and $buff =~ /^.{4}ftyp(.{4})/s) {
|
847
1172
|
$fileType = 'JPX' if $1 eq 'jpx ';
|
848
1173
|
$fileType = 'JPM' if $1 eq 'jpm ';
|
1174
|
+
$fileType = 'JXL' if $1 eq 'jxl ';
|
849
1175
|
}
|
850
1176
|
$raf->Seek(-length($buff), 1) if defined $buff;
|
851
1177
|
$et->SetFileType($fileType);
|
@@ -860,6 +1186,59 @@ sub ProcessJP2($$)
|
|
860
1186
|
return $et->ProcessDirectory(\%dirInfo, $tagTablePtr);
|
861
1187
|
}
|
862
1188
|
|
1189
|
+
#------------------------------------------------------------------------------
|
1190
|
+
# Read meta information from a JPEG XL image
|
1191
|
+
# Inputs: 0) ExifTool object reference, 1) dirInfo reference
|
1192
|
+
# Returns: 1 on success, 0 if this wasn't a valid JPEG XL file, -1 on write error
|
1193
|
+
sub ProcessJXL($$)
|
1194
|
+
{
|
1195
|
+
my ($et, $dirInfo) = @_;
|
1196
|
+
my $raf = $$dirInfo{RAF};
|
1197
|
+
my $outfile = $$dirInfo{OutFile};
|
1198
|
+
my ($hdr, $buff);
|
1199
|
+
|
1200
|
+
return 0 unless $raf->Read($hdr,12) == 12;
|
1201
|
+
if ($hdr eq "\0\0\0\x0cJXL \x0d\x0a\x87\x0a") {
|
1202
|
+
# JPEG XL in ISO BMFF container
|
1203
|
+
$$et{IsJXL} = 1;
|
1204
|
+
} elsif ($hdr =~ /^\xff\x0a/) {
|
1205
|
+
# JPEG XL codestream
|
1206
|
+
if ($outfile) {
|
1207
|
+
if ($$et{OPTIONS}{IgnoreMinorErrors}) {
|
1208
|
+
$et->Warn('Wrapped JXL codestream in ISO BMFF container');
|
1209
|
+
} else {
|
1210
|
+
$et->Error('Will wrap JXL codestream in ISO BMFF container for writing',1);
|
1211
|
+
return 0;
|
1212
|
+
}
|
1213
|
+
$$et{IsJXL} = 2;
|
1214
|
+
my $buff = "\0\0\0\x0cJXL \x0d\x0a\x87\x0a\0\0\0\x14ftypjxl \0\0\0\0jxl ";
|
1215
|
+
# add metadata to empty ISO BMFF container
|
1216
|
+
$$dirInfo{RAF} = new File::RandomAccess(\$buff);
|
1217
|
+
} else {
|
1218
|
+
$et->SetFileType('JXL Codestream','image/jxl', 'jxl');
|
1219
|
+
return ProcessJXLCodestream($et, \$hdr);
|
1220
|
+
}
|
1221
|
+
} else {
|
1222
|
+
return 0;
|
1223
|
+
}
|
1224
|
+
$raf->Seek(0,0) or $et->Error('Seek error'), return 0;
|
1225
|
+
|
1226
|
+
my $success = ProcessJP2($et, $dirInfo);
|
1227
|
+
|
1228
|
+
if ($outfile and $success > 0 and $$et{IsJXL} == 2) {
|
1229
|
+
# attach the JXL codestream box to the ISO BMFF file
|
1230
|
+
$raf->Seek(0,2) or return -1;
|
1231
|
+
my $size = $raf->Tell();
|
1232
|
+
$raf->Seek(0,0) or return -1;
|
1233
|
+
SetByteOrder('MM');
|
1234
|
+
Write($outfile, Set32u($size + 8), 'jxlc') or return -1;
|
1235
|
+
while ($raf->Read($buff, 65536)) {
|
1236
|
+
Write($outfile, $buff) or return -1;
|
1237
|
+
}
|
1238
|
+
}
|
1239
|
+
return $success;
|
1240
|
+
}
|
1241
|
+
|
863
1242
|
1; # end
|
864
1243
|
|
865
1244
|
__END__
|
@@ -0,0 +1,153 @@
|
|
1
|
+
#------------------------------------------------------------------------------
|
2
|
+
# File: LIF.pm
|
3
|
+
#
|
4
|
+
# Description: Read LIF (Leica Image File) files
|
5
|
+
#
|
6
|
+
# Revisions: 2021-06-21 - P. Harvey Created
|
7
|
+
#------------------------------------------------------------------------------
|
8
|
+
|
9
|
+
package Image::ExifTool::LIF;
|
10
|
+
|
11
|
+
use strict;
|
12
|
+
use vars qw($VERSION);
|
13
|
+
use Image::ExifTool qw(:DataAccess :Utils);
|
14
|
+
use Image::ExifTool::XMP;
|
15
|
+
|
16
|
+
$VERSION = '1.00';
|
17
|
+
|
18
|
+
%Image::ExifTool::LIF::Main = (
|
19
|
+
GROUPS => { 0 => 'XML', 1 => 'XML', 2 => 'Image' },
|
20
|
+
PROCESS_PROC => \&Image::ExifTool::XMP::ProcessXMP,
|
21
|
+
VARS => { NO_ID => 1 },
|
22
|
+
NOTES => q{
|
23
|
+
Tags extracted from Leica Image Format (LIF) imaging files. As well as the
|
24
|
+
tags listed below, all available information is extracted from the
|
25
|
+
XML-format metadata in the LIF header.
|
26
|
+
},
|
27
|
+
TimeStampList => {
|
28
|
+
Groups => { 2 => 'Time' },
|
29
|
+
ValueConv => q{
|
30
|
+
my $unixTimeZero = 134774 * 24 * 3600;
|
31
|
+
my @vals = split ' ', $val;
|
32
|
+
foreach (@vals) {
|
33
|
+
$_ = 1e-7 * hex($_);
|
34
|
+
# shift from Jan 1, 1601 to Jan 1, 1970
|
35
|
+
$_ = Image::ExifTool::ConvertUnixTime($_ - $unixTimeZero);
|
36
|
+
}
|
37
|
+
return \@vals;
|
38
|
+
},
|
39
|
+
},
|
40
|
+
);
|
41
|
+
|
42
|
+
#------------------------------------------------------------------------------
|
43
|
+
# Shorten obscenely long LIF tag names
|
44
|
+
# Inputs: Tag name
|
45
|
+
# Returns: Shortened tag name
|
46
|
+
sub ShortenTagNames($)
|
47
|
+
{
|
48
|
+
local $_;
|
49
|
+
$_ = shift;
|
50
|
+
s/DescriptionDimensionsDimensionDescription/Dimensions/;
|
51
|
+
s/DescriptionChannelsChannelDescription/Channel/;
|
52
|
+
s/ShutterListShutter/Shutter/;
|
53
|
+
s/SettingDefinition/Setting/;
|
54
|
+
s/AdditionalZPositionListAdditionalZPosition/AdditionalZPosition/;
|
55
|
+
s/LMSDataContainerHeader//g;
|
56
|
+
s/FilterWheelWheel/FilterWheel/;
|
57
|
+
s/FilterWheelFilter/FilterWheel/;
|
58
|
+
s/DetectorListDetector/Detector/;
|
59
|
+
s/OnlineDyeSeparationOnlineDyeSeparation/OnlineDyeSeparation/;
|
60
|
+
s/AotfListAotf/Aotf/;
|
61
|
+
s/SettingAotfLaserLineSetting/SettingAotfLaser/;
|
62
|
+
s/DataROISetROISet/DataROISet/;
|
63
|
+
s/AdditionalZPosition/AddZPos/;
|
64
|
+
s/FRAPplusBlock_FRAPBlock_FRAP_PrePost_Info/FRAP_/;
|
65
|
+
s/FRAPplusBlock_FRAPBlock_FRAP_(Master)?/FRAP_/;
|
66
|
+
s/LDM_Block_SequentialLDM_Block_Sequential_/LDM_/;
|
67
|
+
s/ATLConfocalSetting/ATLConfocal/;
|
68
|
+
s/LaserArrayLaser/Laser/;
|
69
|
+
s/LDM_Master/LDM_/;
|
70
|
+
s/(List)?ATLConfocal/ATL_/;
|
71
|
+
s/Separation/Sep/;
|
72
|
+
s/BleachPointsElement/BleachPoint/;
|
73
|
+
s/BeamPositionBeamPosition/BeamPosition/;
|
74
|
+
s/DataROISetPossible(ROI)?/DataROISet/;
|
75
|
+
s/RoiElementChildrenElementDataROISingle(Roi)?/Roi/;
|
76
|
+
s/InfoLaserLineSettingArrayLaserLineSetting/LastLineSetting/;
|
77
|
+
s/FilterWheelWheelNameFilterName/FilterWheelFilterName/;
|
78
|
+
s/LUT_ListLut/Lut/;
|
79
|
+
s/ROI_ListRoiRoidata/ROI_/;
|
80
|
+
s/LaserLineSettingArrayLaserLineSetting/LaserLineSetting/;
|
81
|
+
return $_;
|
82
|
+
}
|
83
|
+
|
84
|
+
#------------------------------------------------------------------------------
|
85
|
+
# Extract metadata from a LIF image
|
86
|
+
# Inputs: 0) ExifTool object reference, 1) dirInfo reference
|
87
|
+
# Returns: 1 on success, 0 if this wasn't a valid LIF file
|
88
|
+
sub ProcessLIF($$)
|
89
|
+
{
|
90
|
+
my ($et, $dirInfo) = @_;
|
91
|
+
my $raf = $$dirInfo{RAF};
|
92
|
+
my $buff;
|
93
|
+
|
94
|
+
# verify this is a valid LIF file
|
95
|
+
return 0 unless $raf->Read($buff, 15) == 15 and $buff =~ /^\x70\0{3}.{4}\x2a.{4}<\0/s;
|
96
|
+
|
97
|
+
$et->SetFileType();
|
98
|
+
SetByteOrder('II');
|
99
|
+
|
100
|
+
my $size = Get32u(\$buff, 4); # XML chunk size
|
101
|
+
my $len = Get32u(\$buff, 9) * 2; # XML data length
|
102
|
+
|
103
|
+
$size < $len and $et->Error('Corrupted LIF XML block'), return 1;
|
104
|
+
$size > 100000000 and $et->Error('LIF XML block too large'), return 1;
|
105
|
+
|
106
|
+
$raf->Seek(-2, 1) and $raf->Read($buff, $len) == $len or $et->Error('Truncated LIF XML block'), return 1;
|
107
|
+
|
108
|
+
my $tagTablePtr = GetTagTable('Image::ExifTool::LIF::Main');
|
109
|
+
|
110
|
+
# convert from UCS2 to UTF8
|
111
|
+
my $xml = Image::ExifTool::Decode($et, $buff, 'UCS2', 'II', 'UTF8');
|
112
|
+
|
113
|
+
my %dirInfo = ( DataPt => \$xml );
|
114
|
+
|
115
|
+
$$et{XmpIgnoreProps} = [ 'LMSDataContainerHeader', 'Element', 'Children', 'Data', 'Image', 'Attachment' ];
|
116
|
+
$$et{ShortenXmpTags} = \&ShortenTagNames;
|
117
|
+
|
118
|
+
$et->ProcessDirectory(\%dirInfo, $tagTablePtr);
|
119
|
+
|
120
|
+
return 1;
|
121
|
+
}
|
122
|
+
|
123
|
+
1; # end
|
124
|
+
|
125
|
+
__END__
|
126
|
+
|
127
|
+
=head1 NAME
|
128
|
+
|
129
|
+
Image::ExifTool::LIF - Read LIF meta information
|
130
|
+
|
131
|
+
=head1 SYNOPSIS
|
132
|
+
|
133
|
+
This module is used by Image::ExifTool
|
134
|
+
|
135
|
+
=head1 DESCRIPTION
|
136
|
+
|
137
|
+
This module contains definitions required by Image::ExifTool to read
|
138
|
+
metadata from Leica Image File (LIF) images.
|
139
|
+
|
140
|
+
=head1 AUTHOR
|
141
|
+
|
142
|
+
Copyright 2003-2021, Phil Harvey (philharvey66 at gmail.com)
|
143
|
+
|
144
|
+
This library is free software; you can redistribute it and/or modify it
|
145
|
+
under the same terms as Perl itself.
|
146
|
+
|
147
|
+
=head1 SEE ALSO
|
148
|
+
|
149
|
+
L<Image::ExifTool::TagNames/LIF Tags>,
|
150
|
+
L<Image::ExifTool(3pm)|Image::ExifTool>
|
151
|
+
|
152
|
+
=cut
|
153
|
+
|