exiftool_vendored 12.18.0 → 12.33.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 +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
|
+
|