exiftool_vendored 12.22.0 → 12.25.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/bin/Changes +50 -0
  3. data/bin/MANIFEST +11 -0
  4. data/bin/META.json +1 -1
  5. data/bin/META.yml +1 -1
  6. data/bin/README +44 -43
  7. data/bin/config_files/acdsee.config +193 -6
  8. data/bin/config_files/cuepointlist.config +70 -0
  9. data/bin/exiftool +45 -44
  10. data/bin/lib/Image/ExifTool.pm +85 -26
  11. data/bin/lib/Image/ExifTool.pod +61 -50
  12. data/bin/lib/Image/ExifTool/BuildTagLookup.pm +1 -1
  13. data/bin/lib/Image/ExifTool/Canon.pm +3 -2
  14. data/bin/lib/Image/ExifTool/DjVu.pm +6 -5
  15. data/bin/lib/Image/ExifTool/Exif.pm +16 -7
  16. data/bin/lib/Image/ExifTool/FujiFilm.pm +18 -8
  17. data/bin/lib/Image/ExifTool/JPEG.pm +6 -2
  18. data/bin/lib/Image/ExifTool/JSON.pm +24 -3
  19. data/bin/lib/Image/ExifTool/Jpeg2000.pm +361 -16
  20. data/bin/lib/Image/ExifTool/MRC.pm +341 -0
  21. data/bin/lib/Image/ExifTool/MWG.pm +3 -3
  22. data/bin/lib/Image/ExifTool/MXF.pm +1 -1
  23. data/bin/lib/Image/ExifTool/MacOS.pm +1 -1
  24. data/bin/lib/Image/ExifTool/Microsoft.pm +5 -3
  25. data/bin/lib/Image/ExifTool/Nikon.pm +3 -2
  26. data/bin/lib/Image/ExifTool/NikonSettings.pm +10 -1
  27. data/bin/lib/Image/ExifTool/PNG.pm +2 -2
  28. data/bin/lib/Image/ExifTool/Panasonic.pm +14 -1
  29. data/bin/lib/Image/ExifTool/PhaseOne.pm +4 -3
  30. data/bin/lib/Image/ExifTool/QuickTime.pm +102 -44
  31. data/bin/lib/Image/ExifTool/QuickTimeStream.pl +88 -29
  32. data/bin/lib/Image/ExifTool/RIFF.pm +83 -11
  33. data/bin/lib/Image/ExifTool/Samsung.pm +2 -1
  34. data/bin/lib/Image/ExifTool/Sony.pm +98 -24
  35. data/bin/lib/Image/ExifTool/TagLookup.pm +56 -15
  36. data/bin/lib/Image/ExifTool/TagNames.pod +388 -173
  37. data/bin/lib/Image/ExifTool/WritePostScript.pl +1 -0
  38. data/bin/lib/Image/ExifTool/WriteQuickTime.pl +37 -12
  39. data/bin/lib/Image/ExifTool/WriteXMP.pl +6 -2
  40. data/bin/lib/Image/ExifTool/Writer.pl +1 -0
  41. data/bin/lib/Image/ExifTool/XMP.pm +20 -7
  42. data/bin/perl-Image-ExifTool.spec +42 -42
  43. data/lib/exiftool_vendored/version.rb +1 -1
  44. metadata +6 -8
@@ -65,47 +65,47 @@ supported by ExifTool (r = read, w = write, c = create):
65
65
 
66
66
  File Types
67
67
  ------------+-------------+-------------+-------------+------------
68
- 360 r/w | DPX r | ITC r | ODP r | RIFF r
69
- 3FR r | DR4 r/w/c | J2C r | ODS r | RSRC r
70
- 3G2 r/w | DSS r | JNG r/w | ODT r | RTF r
71
- 3GP r/w | DV r | JP2 r/w | OFR r | RW2 r/w
72
- A r | DVB r/w | JPEG r/w | OGG r | RWL r/w
73
- AA r | DVR-MS r | JSON r | OGV r | RWZ r
74
- AAE r | DYLIB r | K25 r | ONP r | RM r
75
- AAX r/w | EIP r | KDC r | OPUS r | SEQ r
76
- ACR r | EPS r/w | KEY r | ORF r/w | SKETCH r
77
- AFM r | EPUB r | LA r | OTF r | SO r
78
- AI r/w | ERF r/w | LFP r | PAC r | SR2 r/w
79
- AIFF r | EXE r | LNK r | PAGES r | SRF r
80
- APE r | EXIF r/w/c | LRV r/w | PBM r/w | SRW r/w
81
- ARQ r/w | EXR r | M2TS r | PCD r | SVG r
82
- ARW r/w | EXV r/w/c | M4A/V r/w | PCX r | SWF r
83
- ASF r | F4A/V r/w | MACOS r | PDB r | THM r/w
84
- AVI r | FFF r/w | MAX r | PDF r/w | TIFF r/w
85
- AVIF r/w | FITS r | MEF r/w | PEF r/w | TORRENT r
86
- AZW r | FLA r | MIE r/w/c | PFA r | TTC r
87
- BMP r | FLAC r | MIFF r | PFB r | TTF r
88
- BPG r | FLIF r/w | MKA r | PFM r | TXT r
89
- BTF r | FLV r | MKS r | PGF r | VCF r
90
- CHM r | FPF r | MKV r | PGM r/w | VRD r/w/c
91
- COS r | FPX r | MNG r/w | PLIST r | VSD r
92
- CR2 r/w | GIF r/w | MOBI r | PICT r | WAV r
93
- CR3 r/w | GPR r/w | MODD r | PMP r | WDP r/w
94
- CRM r/w | GZ r | MOI r | PNG r/w | WEBP r
95
- CRW r/w | HDP r/w | MOS r/w | PPM r/w | WEBM r
96
- CS1 r/w | HDR r | MOV r/w | PPT r | WMA r
97
- CSV r | HEIC r/w | MP3 r | PPTX r | WMV r
98
- CZI r | HEIF r/w | MP4 r/w | PS r/w | WTV r
99
- DCM r | HTML r | MPC r | PSB r/w | WV r
100
- DCP r/w | ICC r/w/c | MPG r | PSD r/w | X3F r/w
101
- DCR r | ICS r | MPO r/w | PSP r | XCF r
102
- DFONT r | IDML r | MQV r/w | QTIF r/w | XLS r
103
- DIVX r | IIQ r/w | MRW r/w | R3D r | XLSX r
104
- DJVU r | IND r/w | MXF r | RA r | XMP r/w/c
105
- DLL r | INSP r/w | NEF r/w | RAF r/w | ZIP r
106
- DNG r/w | INSV r | NRW r/w | RAM r |
107
- DOC r | INX r | NUMBERS r | RAR r |
108
- DOCX r | ISO r | O r | RAW r/w |
68
+ 360 r/w | DPX r | ITC r | NUMBERS r | RAM r
69
+ 3FR r | DR4 r/w/c | J2C r | O r | RAR r
70
+ 3G2 r/w | DSS r | JNG r/w | ODP r | RAW r/w
71
+ 3GP r/w | DV r | JP2 r/w | ODS r | RIFF r
72
+ A r | DVB r/w | JPEG r/w | ODT r | RSRC r
73
+ AA r | DVR-MS r | JSON r | OFR r | RTF r
74
+ AAE r | DYLIB r | JXL r | OGG r | RW2 r/w
75
+ AAX r/w | EIP r | K25 r | OGV r | RWL r/w
76
+ ACR r | EPS r/w | KDC r | ONP r | RWZ r
77
+ AFM r | EPUB r | KEY r | OPUS r | RM r
78
+ AI r/w | ERF r/w | LA r | ORF r/w | SEQ r
79
+ AIFF r | EXE r | LFP r | ORI r/w | SKETCH r
80
+ APE r | EXIF r/w/c | LNK r | OTF r | SO r
81
+ ARQ r/w | EXR r | LRV r/w | PAC r | SR2 r/w
82
+ ARW r/w | EXV r/w/c | M2TS r | PAGES r | SRF r
83
+ ASF r | F4A/V r/w | M4A/V r/w | PBM r/w | SRW r/w
84
+ AVI r | FFF r/w | MACOS r | PCD r | SVG r
85
+ AVIF r/w | FITS r | MAX r | PCX r | SWF r
86
+ AZW r | FLA r | MEF r/w | PDB r | THM r/w
87
+ BMP r | FLAC r | MIE r/w/c | PDF r/w | TIFF r/w
88
+ BPG r | FLIF r/w | MIFF r | PEF r/w | TORRENT r
89
+ BTF r | FLV r | MKA r | PFA r | TTC r
90
+ CHM r | FPF r | MKS r | PFB r | TTF r
91
+ COS r | FPX r | MKV r | PFM r | TXT r
92
+ CR2 r/w | GIF r/w | MNG r/w | PGF r | VCF r
93
+ CR3 r/w | GPR r/w | MOBI r | PGM r/w | VRD r/w/c
94
+ CRM r/w | GZ r | MODD r | PLIST r | VSD r
95
+ CRW r/w | HDP r/w | MOI r | PICT r | WAV r
96
+ CS1 r/w | HDR r | MOS r/w | PMP r | WDP r/w
97
+ CSV r | HEIC r/w | MOV r/w | PNG r/w | WEBP r
98
+ CZI r | HEIF r/w | MP3 r | PPM r/w | WEBM r
99
+ DCM r | HTML r | MP4 r/w | PPT r | WMA r
100
+ DCP r/w | ICC r/w/c | MPC r | PPTX r | WMV r
101
+ DCR r | ICS r | MPG r | PS r/w | WTV r
102
+ DFONT r | IDML r | MPO r/w | PSB r/w | WV r
103
+ DIVX r | IIQ r/w | MQV r/w | PSD r/w | X3F r/w
104
+ DJVU r | IND r/w | MRC r | PSP r | XCF r
105
+ DLL r | INSP r/w | MRW r/w | QTIF r/w | XLS r
106
+ DNG r/w | INSV r | MXF r | R3D r | XLSX r
107
+ DOC r | INX r | NEF r/w | RA r | XMP r/w/c
108
+ DOCX r | ISO r | NRW r/w | RAF r/w | ZIP r
109
109
 
110
110
  Meta Information
111
111
  ----------------------+----------------------+---------------------
@@ -176,6 +176,9 @@ Creates a new ExifTool object.
176
176
 
177
177
  $exifTool = new Image::ExifTool;
178
178
 
179
+ One ExifTool object may be used to process many files, so creating multiple
180
+ ExifTool objects usually is not necessary.
181
+
179
182
  Note that ExifTool uses AUTOLOAD to load non-member methods, so any class
180
183
  using Image::ExifTool as a base class must define an AUTOLOAD which calls
181
184
  Image::ExifTool::DoAutoLoad(). eg)
@@ -386,7 +389,8 @@ to some functions. Option names are not case sensitive.
386
389
 
387
390
  The default option values may be changed by defining a
388
391
  %Image::ExifTool::UserDefined::Options hash. See the ExifTool_config file
389
- in the full ExifTool distribution for examples.
392
+ in the full ExifTool distribution for examples. A default of undef has the
393
+ same behaviour as a value of 0 for numerical options.
390
394
 
391
395
  # exclude the 'OwnerName' tag from returned information
392
396
  $exifTool->Options(Exclude => 'OwnerName');
@@ -438,6 +442,13 @@ large binary data blocks (eg. ThumbnailImage) are not necessarily extracted
438
442
  unless this option is set or the tag is specifically requested by name.
439
443
  Default is undef.
440
444
 
445
+ =item BlockExtract
446
+
447
+ Flag to extract some directories (mentioned in the
448
+ L<ExifTool tag name documentation|Image::ExifTool::TagNames>) as a block.
449
+ Setting this to a value of 2 also prevents parsing the block to extract tags
450
+ contained within.
451
+
441
452
  =item ByteOrder
442
453
 
443
454
  The byte order for newly created EXIF segments when writing. Note that if
@@ -2277,12 +2288,12 @@ AFCP, AIFF, APE, APP0, APP1, APP11, APP12, APP13, APP14, APP15, APP4, APP5,
2277
2288
  APP6, APP8, ASF, Audible, CanonVRD, Composite, DICOM, DNG, DV, DjVu, Ducky,
2278
2289
  EXE, EXIF, ExifTool, FITS, FLAC, FLIR, File, Flash, FlashPix, Font,
2279
2290
  FotoStation, GIF, GIMP, GeoTiff, GoPro, H264, HTML, ICC_Profile, ID3, IPTC,
2280
- ISO, ITC, JFIF, JPEG, JSON, Jpeg2000, LNK, Leaf, Lytro, M2TS, MIE, MIFF,
2281
- MNG, MOI, MPC, MPEG, MPF, MXF, MakerNotes, Matroska, Meta, Ogg, OpenEXR,
2282
- Opus, PDF, PICT, PLIST, PNG, PSP, Palm, Parrot, PanasonicRaw, PhotoCD,
2283
- PhotoMechanic, Photoshop, PostScript, PrintIM, QuickTime, RAF, RIFF, RSRC,
2284
- RTF, Radiance, Rawzor, Real, Red, SVG, SigmaRaw, Stim, Theora, Torrent,
2285
- Trailer, UserParam, VCard, Vorbis, WTV, XML, XMP, ZIP
2291
+ ISO, ITC, JFIF, JPEG, JSON, JUMBF, Jpeg2000, LNK, Leaf, Lytro, M2TS, MIE,
2292
+ MIFF, MNG, MOI, MPC, MPEG, MPF, MXF, MakerNotes, Matroska, Meta, Ogg,
2293
+ OpenEXR, Opus, PDF, PICT, PLIST, PNG, PSP, Palm, Parrot, PanasonicRaw,
2294
+ PhotoCD, PhotoMechanic, Photoshop, PostScript, PrintIM, QuickTime, RAF,
2295
+ RIFF, RSRC, RTF, Radiance, Rawzor, Real, Red, SVG, SigmaRaw, Stim, Theora,
2296
+ Torrent, Trailer, UserParam, VCard, Vorbis, WTV, XML, XMP, ZIP
2286
2297
 
2287
2298
  =item Family 1 (Specific Location):
2288
2299
 
@@ -2295,8 +2306,8 @@ GoPro, GraphConv, H264, HP, HTC, HTML, HTML-dc, HTML-ncc, HTML-office,
2295
2306
  HTML-prod, HTML-vw96, HTTP-equiv, ICC-chrm, ICC-clrt, ICC-header, ICC-meas,
2296
2307
  ICC-meta, ICC-view, ICC_Profile, ICC_Profile#, ID3, ID3v1, ID3v1_Enh,
2297
2308
  ID3v2_2, ID3v2_3, ID3v2_4, IFD0, IFD1, IPTC, IPTC#, ISO, ITC, Insta360,
2298
- InteropIFD, ItemList, JFIF, JFXX, JPEG, JPEG-HDR, JSON, JVC, Jpeg2000,
2299
- KDC_IFD, Keys, Kodak, KodakBordersIFD, KodakEffectsIFD, KodakIFD,
2309
+ InteropIFD, ItemList, JFIF, JFXX, JPEG, JPEG-HDR, JSON, JUMBF, JVC,
2310
+ Jpeg2000, KDC_IFD, Keys, Kodak, KodakBordersIFD, KodakEffectsIFD, KodakIFD,
2300
2311
  KyoceraRaw, LNK, Leaf, LeafSubIFD, Leica, Lyrics3, Lytro, M2TS, MAC,
2301
2312
  MIE-Audio, MIE-Camera, MIE-Canon, MIE-Doc, MIE-Extender, MIE-Flash, MIE-GPS,
2302
2313
  MIE-Geo, MIE-Image, MIE-Lens, MIE-Main, MIE-MakerNotes, MIE-Meta,
@@ -639,7 +639,7 @@ overlapping EXIF, IPTC and XMP tags to be reconciled when reading, and
639
639
  synchronized when writing. The MWG Composite tags below are designed to aid
640
640
  in the implementation of these recommendations. As well, the MWG defines
641
641
  new XMP tags which are listed in the subsequent tables below. See
642
- L<https://web.archive.org/web/20181006115950/http://www.metadataworkinggroup.org/specs/>
642
+ L<https://web.archive.org/web/20180919181934/http://www.metadataworkinggroup.org/pdf/mwg_guidance.pdf>
643
643
  for the official MWG specification.
644
644
  },
645
645
  MacOS => q{
@@ -88,7 +88,7 @@ sub ProcessCTMD($$$);
88
88
  sub ProcessExifInfo($$$);
89
89
  sub SwapWords($);
90
90
 
91
- $VERSION = '4.46';
91
+ $VERSION = '4.47';
92
92
 
93
93
  # Note: Removed 'USM' from 'L' lenses since it is redundant - PH
94
94
  # (or is it? Ref 32 shows 5 non-USM L-type lenses)
@@ -473,7 +473,8 @@ $VERSION = '4.46';
473
473
  253 => 'Canon EF 70-200mm f/2.8L IS II USM + 2x', #PH (NC)
474
474
  253.1 => 'Canon EF 70-200mm f/2.8L IS III USM + 2x', #PH (NC)
475
475
  # 253.2 => 'Tamron SP 70-200mm f/2.8 Di VC USD G2 (A025) + 2x', #forum9367
476
- 254 => 'Canon EF 100mm f/2.8L Macro IS USM', #42
476
+ 254 => 'Canon EF 100mm f/2.8L Macro IS USM or Tamron Lens', #42
477
+ 254.1 => 'Tamron SP 90mm f/2.8 Di VC USD 1:1 Macro (F017)', #PH
477
478
  255 => 'Sigma 24-105mm f/4 DG OS HSM | A or Other Lens', #50
478
479
  255.1 => 'Sigma 180mm f/2.8 EX DG OS HSM APO Macro', #50
479
480
  255.2 => 'Tamron SP 70-200mm f/2.8 Di VC USD', #exiv issue 1202 (A009)
@@ -18,7 +18,7 @@ use strict;
18
18
  use vars qw($VERSION);
19
19
  use Image::ExifTool qw(:DataAccess :Utils);
20
20
 
21
- $VERSION = '1.06';
21
+ $VERSION = '1.07';
22
22
 
23
23
  sub ParseAnt($);
24
24
  sub ProcessAnt($$$);
@@ -227,10 +227,11 @@ Tok: for (;;) {
227
227
  last unless $tok =~ /(\\+)$/ and length($1) & 0x01;
228
228
  $tok .= '"'; # quote is part of the string
229
229
  }
230
- # must protect unescaped "$" and "@" symbols, and "\" at end of string
231
- $tok =~ s{\\(.)|([\$\@]|\\$)}{'\\'.($2 || $1)}sge;
232
- # convert C escape sequences (allowed in quoted text)
233
- $tok = eval qq{"$tok"};
230
+ # convert C escape sequences, allowed in quoted text
231
+ # (note: this only converts a few of them!)
232
+ my %esc = ( a => "\a", b => "\b", f => "\f", n => "\n",
233
+ r => "\r", t => "\t", '"' => '"', '\\' => '\\' );
234
+ $tok =~ s/\\(.)/$esc{$1}||'\\'.$1/egs;
234
235
  } else { # key name
235
236
  pos($$dataPt) = pos($$dataPt) - 1;
236
237
  # allow anything in key but whitespace, braces and double quotes
@@ -56,7 +56,7 @@ use vars qw($VERSION $AUTOLOAD @formatSize @formatName %formatNumber %intFormat
56
56
  use Image::ExifTool qw(:DataAccess :Utils);
57
57
  use Image::ExifTool::MakerNotes;
58
58
 
59
- $VERSION = '4.33';
59
+ $VERSION = '4.34';
60
60
 
61
61
  sub ProcessExif($$$);
62
62
  sub WriteExif($$$);
@@ -321,6 +321,7 @@ my %utf8StringConv = (
321
321
  my %longBin = (
322
322
  ValueConv => 'length($val) > 64 ? \$val : $val',
323
323
  ValueConvInv => '$val',
324
+ LongBinary => 1, # flag to avoid decoding values of a large array
324
325
  );
325
326
 
326
327
  # PrintConv for SampleFormat (0x153)
@@ -3618,11 +3619,11 @@ my %opcodeInfo = (
3618
3619
  },
3619
3620
  0xc6fc => {
3620
3621
  Name => 'ProfileToneCurve',
3622
+ %longBin,
3621
3623
  Writable => 'float',
3622
3624
  WriteGroup => 'IFD0',
3623
3625
  Count => -1,
3624
3626
  Protected => 1,
3625
- Binary => 1,
3626
3627
  },
3627
3628
  0xc6fd => {
3628
3629
  Name => 'ProfileEmbedPolicy',
@@ -3747,11 +3748,11 @@ my %opcodeInfo = (
3747
3748
  },
3748
3749
  0xc726 => {
3749
3750
  Name => 'ProfileLookTableData',
3751
+ %longBin,
3750
3752
  Writable => 'float',
3751
3753
  WriteGroup => 'IFD0',
3752
3754
  Count => -1,
3753
3755
  Protected => 1,
3754
- Binary => 1,
3755
3756
  },
3756
3757
  0xc740 => { Name => 'OpcodeList1', %opcodeInfo }, # DNG 1.3
3757
3758
  0xc741 => { Name => 'OpcodeList2', %opcodeInfo }, # DNG 1.3
@@ -6172,15 +6173,23 @@ sub ProcessExif($$$)
6172
6173
  # (avoids long delays when processing some corrupted files)
6173
6174
  if ($count > 100000 and $formatStr !~ /^(undef|string|binary)$/) {
6174
6175
  my $tagName = $tagInfo ? $$tagInfo{Name} : sprintf('tag 0x%.4x', $tagID);
6176
+ # (count of 196608 is typical for ColorMap)
6175
6177
  if ($tagName ne 'TransferFunction' or $count != 196608) {
6176
6178
  my $minor = $count > 2000000 ? 0 : 2;
6177
6179
  next if $et->Warn("Ignoring $dirName $tagName with excessive count", $minor);
6178
6180
  }
6179
6181
  }
6180
- # convert according to specified format
6181
- $val = ReadValue($valueDataPt,$valuePtr,$formatStr,$count,$readSize,\$rational);
6182
- # re-code if necessary
6183
- $val = $et->Decode($val, $strEnc) if $strEnc and $formatStr eq 'string' and defined $val;
6182
+ if ($count > 500 and $formatStr !~ /^(undef|string|binary)$/ and
6183
+ (not $tagInfo or $$tagInfo{LongBinary}) and not $$et{OPTIONS}{IgnoreMinorErrors})
6184
+ {
6185
+ $et->WarnOnce('Not decoding some large array(s). Ignore minor errors to decode', 2);
6186
+ $val = "(large array of $count $formatStr values)";
6187
+ } else {
6188
+ # convert according to specified format
6189
+ $val = ReadValue($valueDataPt,$valuePtr,$formatStr,$count,$readSize,\$rational);
6190
+ # re-code if necessary
6191
+ $val = $et->Decode($val, $strEnc) if $strEnc and $formatStr eq 'string' and defined $val;
6192
+ }
6184
6193
  }
6185
6194
 
6186
6195
  if ($verbose) {
@@ -31,7 +31,7 @@ use vars qw($VERSION);
31
31
  use Image::ExifTool qw(:DataAccess :Utils);
32
32
  use Image::ExifTool::Exif;
33
33
 
34
- $VERSION = '1.79';
34
+ $VERSION = '1.80';
35
35
 
36
36
  sub ProcessFujiDir($$$);
37
37
  sub ProcessFaceRec($$$);
@@ -507,6 +507,7 @@ my %faceCategories = (
507
507
  0 => 'Off',
508
508
  1 => 'On',
509
509
  2 => 'No flash & flash', #3
510
+ 6 => 'Pixel Shift', #IB (GFX100S)
510
511
  },
511
512
  }],
512
513
  0x1101 => {
@@ -517,6 +518,8 @@ my %faceCategories = (
517
518
  Name => 'DriveSettings',
518
519
  SubDirectory => { TagTable => 'Image::ExifTool::FujiFilm::DriveSettings' },
519
520
  },
521
+ 0x1105 => { Name => 'PixelShiftShots', Writable => 'int16u' }, #IB
522
+ 0x1106 => { Name => 'PixelShiftOffset', Writable => 'rational64s', Count => 2 }, #IB
520
523
  # (0x1150-0x1152 exist only for Pro Low-light and Pro Focus PictureModes)
521
524
  # 0x1150 - Pro Low-light - val=1; Pro Focus - val=2 (ref 7); HDR - val=128 (forum10799)
522
525
  # 0x1151 - Pro Low-light - val=4 (number of pictures taken?); Pro Focus - val=2,3 (ref 7); HDR - val=3 (forum10799)
@@ -915,15 +918,22 @@ my %faceCategories = (
915
918
  WRITABLE => 1,
916
919
  0.1 => {
917
920
  Name => 'FocusMode2',
918
- Mask => 0x000000ff,
921
+ Mask => 0x0000000f,
919
922
  PrintConv => {
920
- 0x00 => 'AF-M',
921
- 0x01 => 'AF-S',
922
- 0x02 => 'AF-C',
923
- 0x11 => 'AF-S (Auto)',
923
+ 0x0 => 'AF-M',
924
+ 0x1 => 'AF-S',
925
+ 0x2 => 'AF-C',
924
926
  },
925
927
  },
926
928
  0.2 => {
929
+ Name => 'PreAF',
930
+ Mask => 0x00f0,
931
+ PrintConv => {
932
+ 0 => 'Off',
933
+ 1 => 'On',
934
+ },
935
+ },
936
+ 0.3 => {
927
937
  Name => 'AFAreaMode',
928
938
  Mask => 0x0f00,
929
939
  PrintConv => {
@@ -932,7 +942,7 @@ my %faceCategories = (
932
942
  2 => 'Wide/Tracking',
933
943
  },
934
944
  },
935
- 0.3 => {
945
+ 0.4 => {
936
946
  Name => 'AFAreaPointSize',
937
947
  Mask => 0xf000,
938
948
  PrintConv => {
@@ -940,7 +950,7 @@ my %faceCategories = (
940
950
  OTHER => sub { return $_[0] },
941
951
  },
942
952
  },
943
- 0.4 => {
953
+ 0.5 => {
944
954
  Name => 'AFAreaZoneSize',
945
955
  Mask => 0xf0000,
946
956
  PrintConv => {
@@ -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.02';
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.27';
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, however ExifTool
111
- currently writes only EXIF, IPTC and XMP tags in these images.
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 => 'ContiguousCodestream',
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. This is a List-type tag because multiple XML blocks may
217
- exist
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 "\x00\x00\x00\x0cjP \x0d\x0a\x87\x0a" or # (ref 1)
821
- $hdr eq "\x00\x00\x00\x0cjP\x1a\x1a\x0d\x0a\x87\x0a") # (ref 2)
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
- $et->InitWriteDirs(\%jp2Map);
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__