exiftool_vendored 12.68.0 → 12.70.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.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/bin/Changes +63 -15
  3. data/bin/META.json +1 -1
  4. data/bin/META.yml +1 -1
  5. data/bin/README +2 -2
  6. data/bin/exiftool +13 -13
  7. data/bin/lib/Image/ExifTool/CBOR.pm +18 -2
  8. data/bin/lib/Image/ExifTool/Canon.pm +68 -16
  9. data/bin/lib/Image/ExifTool/DJI.pm +3 -2
  10. data/bin/lib/Image/ExifTool/DNG.pm +25 -2
  11. data/bin/lib/Image/ExifTool/EXE.pm +54 -6
  12. data/bin/lib/Image/ExifTool/Exif.pm +175 -14
  13. data/bin/lib/Image/ExifTool/FujiFilm.pm +142 -20
  14. data/bin/lib/Image/ExifTool/GIF.pm +5 -1
  15. data/bin/lib/Image/ExifTool/ID3.pm +70 -7
  16. data/bin/lib/Image/ExifTool/InDesign.pm +1 -1
  17. data/bin/lib/Image/ExifTool/JPEG.pm +1 -1
  18. data/bin/lib/Image/ExifTool/Jpeg2000.pm +30 -15
  19. data/bin/lib/Image/ExifTool/MakerNotes.pm +2 -2
  20. data/bin/lib/Image/ExifTool/Nikon.pm +58 -18
  21. data/bin/lib/Image/ExifTool/Olympus.pm +7 -1
  22. data/bin/lib/Image/ExifTool/PNG.pm +8 -13
  23. data/bin/lib/Image/ExifTool/Panasonic.pm +15 -2
  24. data/bin/lib/Image/ExifTool/PhotoMechanic.pm +2 -2
  25. data/bin/lib/Image/ExifTool/QuickTime.pm +32 -5
  26. data/bin/lib/Image/ExifTool/README +14 -5
  27. data/bin/lib/Image/ExifTool/RIFF.pm +60 -10
  28. data/bin/lib/Image/ExifTool/Sony.pm +95 -34
  29. data/bin/lib/Image/ExifTool/TagLookup.pm +6937 -6714
  30. data/bin/lib/Image/ExifTool/TagNames.pod +812 -332
  31. data/bin/lib/Image/ExifTool/Text.pm +4 -5
  32. data/bin/lib/Image/ExifTool/Validate.pm +23 -20
  33. data/bin/lib/Image/ExifTool/WriteCanonRaw.pl +2 -2
  34. data/bin/lib/Image/ExifTool/WriteExif.pl +14 -4
  35. data/bin/lib/Image/ExifTool/WriteQuickTime.pl +1 -0
  36. data/bin/lib/Image/ExifTool/WriteRIFF.pl +31 -6
  37. data/bin/lib/Image/ExifTool/Writer.pl +40 -14
  38. data/bin/lib/Image/ExifTool/XMP.pm +67 -2
  39. data/bin/lib/Image/ExifTool/XMP2.pl +35 -0
  40. data/bin/lib/Image/ExifTool.pm +79 -40
  41. data/bin/lib/Image/ExifTool.pod +9 -3
  42. data/bin/perl-Image-ExifTool.spec +1 -1
  43. data/lib/exiftool_vendored/version.rb +1 -1
  44. metadata +2 -2
@@ -57,7 +57,7 @@ use vars qw($VERSION $AUTOLOAD @formatSize @formatName %formatNumber %intFormat
57
57
  use Image::ExifTool qw(:DataAccess :Utils);
58
58
  use Image::ExifTool::MakerNotes;
59
59
 
60
- $VERSION = '4.44';
60
+ $VERSION = '4.46';
61
61
 
62
62
  sub ProcessExif($$$);
63
63
  sub WriteExif($$$);
@@ -250,6 +250,7 @@ $formatName[129] = 'utf8'; # (Exif 3.0)
250
250
  34927 => 'WebP', #LibTiff
251
251
  34933 => 'PNG', # (TIFF mail list)
252
252
  34934 => 'JPEG XR', # (TIFF mail list)
253
+ 52546 => 'JPEG XL', # (DNG 1.7)
253
254
  65000 => 'Kodak DCR Compressed', #PH
254
255
  65535 => 'Pentax PEF Compressed', #Jens
255
256
  );
@@ -1004,7 +1005,7 @@ my %opcodeInfo = (
1004
1005
  },
1005
1006
  0x14d => 'InkNames', #3
1006
1007
  0x14e => 'NumberofInks', #3
1007
- 0x150 => 'DotRange',
1008
+ 0x150 => 'DotRange', # (int8u or int16u)
1008
1009
  0x151 => {
1009
1010
  Name => 'TargetPrinter',
1010
1011
  Writable => 'string',
@@ -1491,6 +1492,75 @@ my %opcodeInfo = (
1491
1492
  WriteGroup => 'IFD0',
1492
1493
  Avoid => 1,
1493
1494
  },
1495
+ # tags 0x5XXX are obscure tags defined by Microsoft:
1496
+ # ref https://learn.microsoft.com/en-us/previous-versions/windows/embedded/ms932271(v=msdn.10)
1497
+ # ref https://learn.microsoft.com/en-us/windows/win32/gdiplus/-gdiplus-constant-property-item-descriptions
1498
+ 0x5001 => { Name => 'ResolutionXUnit', Notes => "ID's from 0x5001 to 0x5113 are obscure tags defined by Microsoft" }, # (int16u)
1499
+ 0x5002 => 'ResolutionYUnit', # (int16u)
1500
+ 0x5003 => 'ResolutionXLengthUnit', # (int16u)
1501
+ 0x5004 => 'ResolutionYLengthUnit', # (int16u)
1502
+ 0x5005 => 'PrintFlags', # (string)
1503
+ 0x5006 => 'PrintFlagsVersion', # (int16u)
1504
+ 0x5007 => 'PrintFlagsCrop', # (int8u)
1505
+ 0x5008 => 'PrintFlagsBleedWidth', # (int32u)
1506
+ 0x5009 => 'PrintFlagsBleedWidthScale', # (int16u)
1507
+ 0x500a => 'HalftoneLPI', # (rational64u)
1508
+ 0x500b => 'HalftoneLPIUnit', # (int16u, 1=inch, 2=cm)
1509
+ 0x500c => 'HalftoneDegree', # (rational64u)
1510
+ 0x500d => 'HalftoneShape', # (int16u,0=round,1=Ellipse,2=Line,3=Square,4=Cross,5=Diamond)
1511
+ 0x500e => 'HalftoneMisc', # (int32u)
1512
+ 0x500f => 'HalftoneScreen', # (int8u)
1513
+ 0x5010 => 'JPEGQuality', # (int32u[N])
1514
+ 0x5011 => { Name => 'GridSize', Binary => 1 }, # (undef)
1515
+ 0x5012 => 'ThumbnailFormat', # (int32u,1=raw RGB,2=JPEG)
1516
+ 0x5013 => 'ThumbnailWidth', # (int32u)
1517
+ 0x5014 => 'ThumbnailHeight', # (int32u)
1518
+ 0x5015 => 'ThumbnailColorDepth', # (int16u)
1519
+ 0x5016 => 'ThumbnailPlanes', # (int16u)
1520
+ 0x5017 => 'ThumbnailRawBytes', # (int32u)
1521
+ 0x5018 => 'ThumbnailLength', # (int32u)
1522
+ 0x5019 => 'ThumbnailCompressedSize', # (int32u)
1523
+ 0x501a => { Name => 'ColorTransferFunction', Binary => 1 }, # (undef)
1524
+ 0x501b => { Name => 'ThumbnailData', Binary => 1, Format => 'undef' }, # (int8u)
1525
+ 0x5020 => 'ThumbnailImageWidth', # (int16u or int32u)
1526
+ 0x5021 => 'ThumbnailImageHeight', # (int16u or int32u)
1527
+ 0x5022 => 'ThumbnailBitsPerSample', # (int16u[N])
1528
+ 0x5023 => 'ThumbnailCompression', # (int16u)
1529
+ 0x5024 => 'ThumbnailPhotometricInterp', # (int16u)
1530
+ 0x5025 => 'ThumbnailDescription', # (string)
1531
+ 0x5026 => 'ThumbnailEquipMake', # (string)
1532
+ 0x5027 => 'ThumbnailEquipModel', # (string)
1533
+ 0x5028 => 'ThumbnailStripOffsets', # (int16u or int32u)
1534
+ 0x5029 => 'ThumbnailOrientation', # (int16u)
1535
+ 0x502a => 'ThumbnailSamplesPerPixel', # (int16u)
1536
+ 0x502b => 'ThumbnailRowsPerStrip', # (int16u or int32u)
1537
+ 0x502c => 'ThumbnailStripByteCounts', # (int16u or int32u)
1538
+ 0x502d => 'ThumbnailResolutionX',
1539
+ 0x502e => 'ThumbnailResolutionY',
1540
+ 0x502f => 'ThumbnailPlanarConfig', # (int16u)
1541
+ 0x5030 => 'ThumbnailResolutionUnit', # (int16u)
1542
+ 0x5031 => 'ThumbnailTransferFunction', # (int16u[N])
1543
+ 0x5032 => 'ThumbnailSoftware', # (string)
1544
+ 0x5033 => { Name => 'ThumbnailDateTime', Groups => { 2 => 'Time' } }, # (string)
1545
+ 0x5034 => 'ThumbnailArtist', # (string)
1546
+ 0x5035 => 'ThumbnailWhitePoint', # (rational64u[2])
1547
+ 0x5036 => 'ThumbnailPrimaryChromaticities', # (rational64u[6])
1548
+ 0x5037 => 'ThumbnailYCbCrCoefficients', # (rational64u[3])
1549
+ 0x5038 => 'ThumbnailYCbCrSubsampling', # (int16u)
1550
+ 0x5039 => 'ThumbnailYCbCrPositioning', # (int16u)
1551
+ 0x503a => 'ThumbnailRefBlackWhite', # (rational64u[6])
1552
+ 0x503b => 'ThumbnailCopyright', # (string)
1553
+ 0x5090 => 'LuminanceTable', # (int16u[64])
1554
+ 0x5091 => 'ChrominanceTable', # (int16u[64])
1555
+ 0x5100 => 'FrameDelay', # (int32u[N])
1556
+ 0x5101 => 'LoopCount', # (int16u)
1557
+ 0x5102 => 'GlobalPalette', # (int8u[N])
1558
+ 0x5103 => 'IndexBackground', # (int8u)
1559
+ 0x5104 => 'IndexTransparent', # (int8u)
1560
+ 0x5110 => 'PixelUnits', # (int8u)
1561
+ 0x5111 => 'PixelsPerUnitX', # (int32u)
1562
+ 0x5112 => 'PixelsPerUnitY', # (int32u)
1563
+ 0x5113 => 'PaletteHistogram', # (int8u[N])
1494
1564
  0x7000 => { #JR
1495
1565
  Name => 'SonyRawFileType',
1496
1566
  # (only valid if Sony:FileFormat >= ARW 2.0, ref IB)
@@ -3077,14 +3147,31 @@ my %opcodeInfo = (
3077
3147
  },
3078
3148
  PrintConvInv => '$val =~ /^PrintIM/ ? $val : undef', # quick validation
3079
3149
  },
3150
+ 0xc519 => { # (Hasselblad X2D)
3151
+ Name => 'HasselbladXML',
3152
+ Format => 'undef',
3153
+ TruncateOK => 1, # (incorrect size written by X2D)
3154
+ SubDirectory => {
3155
+ DirName => 'XML',
3156
+ TagTable => 'Image::ExifTool::PLIST::Main',
3157
+ Start => '$valuePtr + 4',
3158
+ },
3159
+ },
3080
3160
  0xc51b => { # (Hasselblad H3D)
3081
3161
  Name => 'HasselbladExif',
3082
3162
  Format => 'undef',
3083
- RawConv => q{
3084
- $$self{DOC_NUM} = ++$$self{DOC_COUNT};
3085
- $self->ExtractInfo(\$val, { ReEntry => 1 });
3086
- $$self{DOC_NUM} = 0;
3087
- return undef;
3163
+ SubDirectory => {
3164
+ Start => '$valuePtr',
3165
+ Base => '$start',
3166
+ TagTable => 'Image::ExifTool::Exif::Main',
3167
+ ProcessProc => \&Image::ExifTool::ProcessSubTIFF,
3168
+ # Writing this is problematic due to the braindead Hasselblad programmers.
3169
+ # One problem is that some values run outside the HasselbladExif data so they
3170
+ # will be lost if we do a simple copy (which is what we are currently doing
3171
+ # by returning undef from the WriteProc), but we can't rebuild this directory
3172
+ # by writing it properly because there is an erroneous StripByteCounts value
3173
+ # written by the X2D 100C that renders the data unreadable
3174
+ WriteProc => sub { return undef },
3088
3175
  },
3089
3176
  },
3090
3177
  0xc573 => { #PH
@@ -3123,7 +3210,7 @@ my %opcodeInfo = (
3123
3210
  0xc612 => {
3124
3211
  Name => 'DNGVersion',
3125
3212
  Notes => q{
3126
- tags 0xc612-0xcd3b are defined by the DNG specification unless otherwise
3213
+ tags 0xc612-0xcd48 are defined by the DNG specification unless otherwise
3127
3214
  noted. See L<https://helpx.adobe.com/photoshop/digital-negative.html> for
3128
3215
  the specification
3129
3216
  },
@@ -3609,6 +3696,11 @@ my %opcodeInfo = (
3609
3696
  Writable => 'int16u',
3610
3697
  WriteGroup => 'IFD0',
3611
3698
  Protected => 1,
3699
+ PrintConv => {
3700
+ 0 => 'Scene-referred',
3701
+ 1 => 'Output-referred (ICC Profile Dynamic Range)',
3702
+ 2 => 'Output-referred (High Dyanmic Range)', # DNG 1.7
3703
+ },
3612
3704
  },
3613
3705
  0xc6c5 => { Name => 'SRawType', Description => 'SRaw Type', WriteGroup => 'IFD0' }, #exifprobe (CR2 proprietary)
3614
3706
  0xc6d2 => { #JD (Panasonic DMC-TZ5)
@@ -4133,7 +4225,7 @@ my %opcodeInfo = (
4133
4225
  0xcd2d => { # DNG 1.6
4134
4226
  Name => 'ProfileGainTableMap',
4135
4227
  Writable => 'undef',
4136
- WriteGroup => 'SubIFD',
4228
+ WriteGroup => 'SubIFD', # (according to DNG 1.7 docs, this was an error and it should have been IFD0)
4137
4229
  Protected => 1,
4138
4230
  Binary => 1,
4139
4231
  },
@@ -4221,6 +4313,61 @@ my %opcodeInfo = (
4221
4313
  WriteGroup => 'IFD0',
4222
4314
  Protected => 1,
4223
4315
  },
4316
+ 0xcd40 => { # DNG 1.7
4317
+ Name => 'ProfileGainTableMap2',
4318
+ Writable => 'undef',
4319
+ WriteGroup => 'IFD0',
4320
+ Protected => 1,
4321
+ Binary => 1,
4322
+ },
4323
+ 0xcd41 => {
4324
+ Name => 'JUMBF',
4325
+ # (set Deletable flag so we can delete this because
4326
+ # Jpeg2000 directories are otherwise permanent)
4327
+ Deletable => 1,
4328
+ SubDirectory => {
4329
+ TagTable => 'Image::ExifTool::Jpeg2000::Main',
4330
+ ByteOrder => 'BigEndian',
4331
+ },
4332
+ },
4333
+ 0xcd43 => { # DNG 1.7
4334
+ Name => 'ColumnInterleaveFactor',
4335
+ Writable => 'int32u',
4336
+ WriteGroup => 'SubIFD',
4337
+ Protected => 1,
4338
+ },
4339
+ 0xcd44 => { # DNG 1.7
4340
+ Name => 'ImageSequenceInfo',
4341
+ Writable => 'undef',
4342
+ WriteGroup => 'IFD0',
4343
+ SubDirectory => {
4344
+ TagTable => 'Image::ExifTool::DNG::ImageSeq',
4345
+ ByteOrder => 'BigEndian',
4346
+ },
4347
+ },
4348
+ 0xcd46 => { # DNG 1.7
4349
+ Name => 'ImageStats',
4350
+ Writable => 'undef',
4351
+ WriteGroup => 'IFD0',
4352
+ Binary => 1,
4353
+ Protected => 1,
4354
+ },
4355
+ 0xcd47 => { # DNG 1.7
4356
+ Name => 'ProfileDynamicRange',
4357
+ Writable => 'undef',
4358
+ WriteGroup => 'IFD0',
4359
+ SubDirectory => {
4360
+ TagTable => 'Image::ExifTool::DNG::ProfileDynamicRange',
4361
+ ByteOrder => 'BigEndian', # (not indicated in spec)
4362
+ },
4363
+ },
4364
+ 0xcd48 => { # DNG 1.7
4365
+ Name => 'ProfileGroupName',
4366
+ Writable => 'string',
4367
+ Format => 'string',
4368
+ WriteGroup => 'IFD0',
4369
+ Protected => 1,
4370
+ },
4224
4371
  0xea1c => { #13
4225
4372
  Name => 'Padding',
4226
4373
  Binary => 1,
@@ -5944,6 +6091,12 @@ sub ProcessExif($$$)
5944
6091
  my $inMakerNotes = $$tagTablePtr{GROUPS}{0} eq 'MakerNotes';
5945
6092
  my $isExif = ($tagTablePtr eq \%Image::ExifTool::Exif::Main);
5946
6093
 
6094
+ # warn for incorrect maker notes in CR3 files
6095
+ if ($$dirInfo{DirName} eq 'MakerNotes' and $$et{FileType} eq 'CR3' and
6096
+ $$dirInfo{Parent} and $$dirInfo{Parent} eq 'ExifIFD')
6097
+ {
6098
+ $et->WarnOnce("MakerNotes shouldn't exist ExifIFD of CR3 image", 1);
6099
+ }
5947
6100
  # set flag to calculate image data hash if requested
5948
6101
  $doHash = 1 if $$et{ImageDataHash} and (($$et{FILE_TYPE} eq 'TIFF' and not $base and not $inMakerNotes) or
5949
6102
  ($$et{FILE_TYPE} eq 'RAF' and $dirName eq 'FujiIFD'));
@@ -6219,7 +6372,7 @@ sub ProcessExif($$$)
6219
6372
  }
6220
6373
  # read from file if necessary
6221
6374
  unless (defined $buff) {
6222
- my $wrn;
6375
+ my ($wrn, $truncOK);
6223
6376
  my $readFromRAF = ($tagInfo and $$tagInfo{ReadFromRAF});
6224
6377
  if (not $raf->Seek($base + $valuePtr + $dataPos, 0)) {
6225
6378
  $wrn = "Invalid offset for $dir entry $index";
@@ -6229,18 +6382,22 @@ sub ProcessExif($$$)
6229
6382
  $buff = "$$tagInfo{Name} data $size bytes";
6230
6383
  $readSize = length $buff;
6231
6384
  } elsif ($raf->Read($buff,$size) != $size) {
6232
- $wrn = "Error reading value for $dir entry $index";
6385
+ $wrn = sprintf("Error reading value for $dir entry $index, ID 0x%.4x", $tagID);
6386
+ if ($tagInfo and not $$tagInfo{Unknown}) {
6387
+ $wrn .= " $$tagInfo{Name}";
6388
+ $truncOK = $$tagInfo{TruncateOK};
6389
+ }
6233
6390
  } elsif ($readFromRAF) {
6234
6391
  # seek back to the start of the value
6235
6392
  $raf->Seek($base + $valuePtr + $dataPos, 0);
6236
6393
  }
6237
6394
  if ($wrn) {
6238
- $et->Warn($wrn, $inMakerNotes);
6239
- return 0 unless $inMakerNotes or $htmlDump;
6395
+ $et->Warn($wrn, $inMakerNotes || $truncOK);
6396
+ return 0 unless $inMakerNotes or $htmlDump or $truncOK;
6240
6397
  ++$warnCount;
6241
6398
  $buff = '' unless defined $buff;
6242
6399
  $readSize = length $buff;
6243
- $bad = 1;
6400
+ $bad = 1 unless $truncOK;
6244
6401
  }
6245
6402
  }
6246
6403
  $valueDataLen = length $buff;
@@ -6813,6 +6970,10 @@ sub ProcessExif($$$)
6813
6970
  # save original components of rational numbers (used when copying)
6814
6971
  $$et{RATIONAL}{$tagKey} = $rational if defined $rational;
6815
6972
  $$et{TAG_EXTRA}{$tagKey}{G6} = $saveFormat if $saveFormat;
6973
+ if ($$et{MAKER_NOTE_FIXUP}) {
6974
+ $$et{TAG_EXTRA}{$tagKey}{Fixup} = $$et{MAKER_NOTE_FIXUP};
6975
+ delete $$et{MAKER_NOTE_FIXUP};
6976
+ }
6816
6977
  }
6817
6978
  }
6818
6979
 
@@ -31,10 +31,11 @@ use vars qw($VERSION);
31
31
  use Image::ExifTool qw(:DataAccess :Utils);
32
32
  use Image::ExifTool::Exif;
33
33
 
34
- $VERSION = '1.90';
34
+ $VERSION = '1.91';
35
35
 
36
36
  sub ProcessFujiDir($$$);
37
37
  sub ProcessFaceRec($$$);
38
+ sub ProcessMRAW($$$);
38
39
 
39
40
  # the following RAF version numbers have been tested for writing:
40
41
  # (as of ExifTool 11.70, this lookup is no longer used if the version number is numerical)
@@ -42,11 +43,12 @@ my %testedRAF = (
42
43
  '0100' => 'E550, E900, F770, S5600, S6000fd, S6500fd, HS10/HS11, HS30, S200EXR, X100, XF1, X-Pro1, X-S1, XQ2 Ver1.00, X-T100, GFX 50R, XF10',
43
44
  '0101' => 'X-E1, X20 Ver1.01, X-T3',
44
45
  '0102' => 'S100FS, X10 Ver1.02',
45
- '0103' => 'IS Pro Ver1.03',
46
+ '0103' => 'IS Pro and X-T5 Ver1.03',
46
47
  '0104' => 'S5Pro Ver1.04',
47
48
  '0106' => 'S5Pro Ver1.06',
48
49
  '0111' => 'S5Pro Ver1.11',
49
50
  '0114' => 'S9600 Ver1.00',
51
+ '0120' => 'X-T4 Ver1.20',
50
52
  '0132' => 'X-T2 Ver1.32',
51
53
  '0144' => 'X100T Ver1.44',
52
54
  '0159' => 'S2Pro Ver1.00',
@@ -1454,6 +1456,27 @@ my %faceCategories = (
1454
1456
  },
1455
1457
  );
1456
1458
 
1459
+ # tags in RAF M-RAW header (ref PH)
1460
+ %Image::ExifTool::FujiFilm::MRAW = (
1461
+ PROCESS_PROC => \&ProcessMRAW,
1462
+ GROUPS => { 0 => 'RAF', 1 => 'M-RAW', 2 => 'Image' },
1463
+ FORMAT => 'int32u',
1464
+ TAG_PREFIX => 'MRAW',
1465
+ NOTES => q{
1466
+ Tags extracted from the M-RAW header of multi-image RAF files. The family 1
1467
+ group name for these tags is "M-RAW".
1468
+ },
1469
+ 1 => { Name => 'RawImageNumber', Format => 'int32u' },
1470
+ # 3 - seen "0 100", "-300 100" and "300 100" for a sequence of 3 images
1471
+ 3 => { Name => 'MRAW_0x0003', Format => 'rational32s', Unknown => 1, Hidden => 1, PrintConv => 'sprintf("%+.2f",$val)' },
1472
+ # 4 - (same value as 3 in all my samples)
1473
+ 4 => { Name => 'MRAW_0x0004', Format => 'rational32s', Unknown => 1, Hidden => 1, PrintConv => 'sprintf("%+.2f",$val)' },
1474
+ # 5 - seen "10 1600", "10 6800", "10 200", "10 35000" etc
1475
+ # 6 - seen "450 100", "400 100" (all images in RAF have same value)
1476
+ # 7 - seen 200, 125, 250, 2000
1477
+ # 8 - seen 0
1478
+ );
1479
+
1457
1480
  #------------------------------------------------------------------------------
1458
1481
  # decode information from FujiFilm face recognition information
1459
1482
  # Inputs: 0) ExifTool object reference, 1) dirInfo reference, 2) tag table ref
@@ -1525,7 +1548,7 @@ sub ProcessFujiDir($$$)
1525
1548
  $raf->Read($buff, 4) or return 0;
1526
1549
  my $entries = unpack 'N', $buff;
1527
1550
  $entries < 256 or return 0;
1528
- $et->Options('Verbose') and $et->VerboseDir('Fuji', $entries);
1551
+ $et->VerboseDir('Fuji', $entries);
1529
1552
  SetByteOrder('MM');
1530
1553
  my $pos = $offset + 4;
1531
1554
  for ($index=0; $index<$entries; ++$index) {
@@ -1557,6 +1580,51 @@ sub ProcessFujiDir($$$)
1557
1580
  return 1;
1558
1581
  }
1559
1582
 
1583
+ #------------------------------------------------------------------------------
1584
+ # get information from FujiFilm M-RAW header
1585
+ # Inputs: 0) ExifTool ref, 1) dirInfo ref, 2) tag table ref
1586
+ # Returns: 1 if this was a valid M-RAW headerx
1587
+ sub ProcessMRAW($$$)
1588
+ {
1589
+ my ($et, $dirInfo, $tagTablePtr) = @_;
1590
+ my $dataPt = $$dirInfo{DataPt};
1591
+ my $dataPos = $$dirInfo{DataPos};
1592
+ my $dataLen = length $$dataPt;
1593
+ $dataLen < 44 and $et->Warn('Short M-RAW header'), return 0;
1594
+ $$dataPt =~ /^FUJIFILMM-RAW / or $et->Warn('Bad M-RAW header'), return 0;
1595
+ my $ver = substr($$dataPt, 16, 4);
1596
+ $et->VerboseDir("M-RAW $ver", undef, $dataLen);
1597
+ SetByteOrder('MM');
1598
+ my $size = Get16u($dataPt, 40); # (these are just a guess - PH)
1599
+ my $num = Get16u($dataPt, 42);
1600
+ my $pos = 44;
1601
+ my ($i, $n);
1602
+ for ($n=0; ; ++$n) {
1603
+ $pos += 16; # skip offset/size fields
1604
+ my $end = $pos + $size;
1605
+ last if $end > $dataLen;
1606
+ $$et{DOC_NUM} = ++$$et{DOC_COUNT} if $pos > 60;
1607
+ $et->VPrint(0, "$$et{INDENT}(Raw image $n parameters: $size bytes, $num entries)\n");
1608
+ for ($i=0; $i<$num; ++$i) {
1609
+ last if $pos + 4 > $end;
1610
+ # (don't know what the byte at the current $pos is for, value = 0x20)
1611
+ my $tag = Get8u($dataPt, $pos+1);
1612
+ my $size = Get16u($dataPt, $pos+2);
1613
+ $pos += 4;
1614
+ last if $pos + $size > $end;
1615
+ $et->HandleTag($tagTablePtr, $tag, undef,
1616
+ DataPt => $dataPt,
1617
+ DataPos => $dataPos,
1618
+ Start => $pos,
1619
+ Size => $size,
1620
+ );
1621
+ $pos += $size;
1622
+ }
1623
+ }
1624
+ delete $$et{DOC_NUM};
1625
+ return 1;
1626
+ }
1627
+
1560
1628
  #------------------------------------------------------------------------------
1561
1629
  # write information to FujiFilm RAW file (RAF)
1562
1630
  # Inputs: 0) ExifTool object reference, 1) dirInfo reference
@@ -1572,10 +1640,12 @@ sub WriteRAF($$)
1572
1640
  my $ver = substr($hdr, 0x3c, 4);
1573
1641
  $ver =~ /^\d{4}$/ or $testedRAF{$ver} or return 0;
1574
1642
 
1643
+ # get position and size of M-RAW header
1644
+ my ($mpos, $mlen) = unpack('x72NN', $hdr);
1575
1645
  # get the position and size of embedded JPEG
1576
1646
  my ($jpos, $jlen) = unpack('x84NN', $hdr);
1577
1647
  # check to be sure the JPEG starts in the expected location
1578
- if ($jpos > 0x94 or $jpos < 0x68 or $jpos & 0x03) {
1648
+ if (($mpos > 0x94 or $jpos > 0x94 + $mlen) or $jpos < 0x68 or $jpos & 0x03) {
1579
1649
  $et->Error("Unsupported or corrupted RAF image (version $ver)");
1580
1650
  return 1;
1581
1651
  }
@@ -1589,6 +1659,27 @@ sub WriteRAF($$)
1589
1659
  $et->Error('Error reading RAF meta information');
1590
1660
  return 1;
1591
1661
  }
1662
+ if ($mpos) {
1663
+ if ($mlen != 0x11c) {
1664
+ $et->Error('Unsupported M-RAW header (please submit sample for testing)');
1665
+ return 1;
1666
+ }
1667
+ # read M-RAW header and add to file header
1668
+ my $mraw;
1669
+ unless ($raf->Seek($mpos, 0) and $raf->Read($mraw, $mlen) == $mlen) {
1670
+ $et->Error('Error reading M-RAW header');
1671
+ return 1;
1672
+ }
1673
+ $hdr .= $mraw;
1674
+ # verify that the 1st raw image offset is zero, and that the 1st raw image
1675
+ # length is the same as the 2nd raw image offset
1676
+ unless (substr($hdr, 0xc0, 8) eq "\0\0\0\0\0\0\0\0" and
1677
+ substr($hdr, 0xc8, 8) eq substr($hdr, 0x110, 8))
1678
+ {
1679
+ $et->Error('Unexpected layout of M-RAW header');
1680
+ return 1;
1681
+ }
1682
+ }
1592
1683
  # use same write directories as JPEG
1593
1684
  $et->InitWriteDirs('JPEG');
1594
1685
  # rewrite the embedded JPEG in memory
@@ -1633,12 +1724,28 @@ sub WriteRAF($$)
1633
1724
  }
1634
1725
  # calculate offset difference due to change in JPEG size
1635
1726
  my $ptrDiff = length($outJpeg) + length($pad) - ($jlen + $oldPadLen);
1636
- # update necessary pointers in header
1637
- foreach $offset (0x5c, 0x64, 0x78, 0x80) {
1727
+ # update necessary pointers in header (0xcc and higher in M-RAW header)
1728
+ foreach $offset (0x5c, 0x64, 0x78, 0x80, 0xcc, 0x114, 0x164) {
1638
1729
  last if $offset >= $jpos; # some versions have a short header
1639
1730
  my $oldPtr = Get32u(\$hdr, $offset);
1640
1731
  next unless $oldPtr; # don't update if pointer is zero
1641
- Set32u($oldPtr + $ptrDiff, \$hdr, $offset);
1732
+ my $newPtr = $oldPtr + $ptrDiff;
1733
+ if ($newPtr < 0 or $newPtr > 0xffffffff) {
1734
+ $offset < 0xcc and $et->Error('Invalid offset in RAF header'), return 1;
1735
+ # assume values at 0xcc and greater are 8-byte integers (NC)
1736
+ # and adjust high word if necessary
1737
+ my $high = Get32u(\$hdr, $offset-4);
1738
+ if ($newPtr < 0) {
1739
+ $high -= 1;
1740
+ $newPtr += 0xffffffff + 1;
1741
+ $high < 0 and $et->Error('RAF header offset error'), return 1;
1742
+ } else {
1743
+ $high += 1;
1744
+ $newPtr -= 0xffffffff + 1;
1745
+ }
1746
+ Set32u($high, \$hdr, $offset-4);
1747
+ }
1748
+ Set32u($newPtr, \$hdr, $offset);
1642
1749
  }
1643
1750
  # write the new header
1644
1751
  my $outfile = $$dirInfo{OutFile};
@@ -1668,11 +1775,14 @@ sub ProcessRAF($$)
1668
1775
  my $raf = $$dirInfo{RAF};
1669
1776
  $raf->Read($buff,0x5c) == 0x5c or return 0;
1670
1777
  $buff =~ /^FUJIFILM/ or return 0;
1778
+ # get position and size of M-RAW header and jpeg preview
1779
+ my ($mpos, $mlen) = unpack('x72NN', $buff);
1671
1780
  my ($jpos, $jlen) = unpack('x84NN', $buff);
1672
1781
  $jpos & 0x8000 and return 0;
1673
- $raf->Seek($jpos, 0) or return 0;
1674
- $raf->Read($jpeg, $jlen) == $jlen or return 0;
1675
-
1782
+ if ($jpos) {
1783
+ $raf->Seek($jpos, 0) or return 0;
1784
+ $raf->Read($jpeg, $jlen) == $jlen or return 0;
1785
+ }
1676
1786
  $et->SetFileType();
1677
1787
  $et->FoundTag('RAFVersion', substr($buff, 0x3c, 4));
1678
1788
 
@@ -1681,15 +1791,16 @@ sub ProcessRAF($$)
1681
1791
  Parent => 'RAF',
1682
1792
  RAF => new File::RandomAccess(\$jpeg),
1683
1793
  );
1684
- $$et{BASE} += $jpos;
1685
- my $rtnVal = $et->ProcessJPEG(\%dirInfo);
1686
- $$et{BASE} -= $jpos;
1687
- $et->FoundTag('PreviewImage', \$jpeg) if $rtnVal;
1688
-
1794
+ if ($jpos) {
1795
+ $$et{BASE} += $jpos;
1796
+ my $ok = $et->ProcessJPEG(\%dirInfo);
1797
+ $$et{BASE} -= $jpos;
1798
+ $et->FoundTag('PreviewImage', \$jpeg) if $ok;
1799
+ }
1689
1800
  # extract information from Fuji RAF and TIFF directories
1690
1801
  my ($rafNum, $ifdNum) = ('','');
1691
- foreach $offset (0x5c, 0x64, 0x78, 0x80) {
1692
- last if $offset >= $jpos;
1802
+ foreach $offset (0x48, 0x5c, 0x64, 0x78, 0x80) {
1803
+ last if $jpos and $offset >= $jpos;
1693
1804
  unless ($raf->Seek($offset, 0) and $raf->Read($buff, 8)) {
1694
1805
  $warn = 1;
1695
1806
  last;
@@ -1711,6 +1822,14 @@ sub ProcessRAF($$)
1711
1822
  }
1712
1823
  delete $$et{SET_GROUP1};
1713
1824
  $ifdNum = ($ifdNum || 1) + 1;
1825
+ } elsif ($offset == 0x48) {
1826
+ $$et{VALUE}{FileType} .= ' (M-RAW)';
1827
+ if ($raf->Seek($start, 0) and $raf->Read($buff, $mlen) == $mlen) {
1828
+ my $tbl = GetTagTable('Image::ExifTool::FujiFilm::MRAW');
1829
+ $et->ProcessDirectory({ DataPt => \$buff, DataPos => $start, DirName => 'M-RAW' }, $tbl);
1830
+ } else {
1831
+ $et->Warn('Error reading M-RAW header');
1832
+ }
1714
1833
  } else {
1715
1834
  # parse RAF directory
1716
1835
  %dirInfo = (
@@ -1719,14 +1838,17 @@ sub ProcessRAF($$)
1719
1838
  );
1720
1839
  $$et{SET_GROUP1} = "RAF$rafNum";
1721
1840
  my $tagTablePtr = GetTagTable('Image::ExifTool::FujiFilm::RAF');
1722
- $et->ProcessDirectory(\%dirInfo, $tagTablePtr) or $warn = 1;
1841
+ if ($et->ProcessDirectory(\%dirInfo, $tagTablePtr)) {
1842
+ $rafNum = ($rafNum || 1) + 1;
1843
+ } else {
1844
+ $warn = 1;
1845
+ }
1723
1846
  delete $$et{SET_GROUP1};
1724
- $rafNum = ($rafNum || 1) + 1;
1725
1847
  }
1726
1848
  }
1727
1849
  $warn and $et->Warn('Possibly corrupt RAF information');
1728
1850
 
1729
- return $rtnVal;
1851
+ return 1;
1730
1852
  }
1731
1853
 
1732
1854
  1; # end
@@ -20,7 +20,7 @@ use strict;
20
20
  use vars qw($VERSION);
21
21
  use Image::ExifTool qw(:DataAccess :Utils);
22
22
 
23
- $VERSION = '1.19';
23
+ $VERSION = '1.20';
24
24
 
25
25
  # road map of directory locations in GIF images
26
26
  my %gifMap = (
@@ -85,6 +85,10 @@ my %gifMap = (
85
85
  Groups => { 2 => 'Audio' },
86
86
  Binary => 1,
87
87
  },
88
+ 'C2PA_GIF/' => { #https://c2pa.org/specifications/ (NC) (authentication code is 0x010000 binary, so removed from tag ID)
89
+ Name => 'JUMBF',
90
+ SubDirectory => { TagTable => 'Image::ExifTool::Jpeg2000::Main' },
91
+ },
88
92
  );
89
93
 
90
94
  # GIF locical screen descriptor