exiftool_vendored 12.68.0 → 12.70.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 +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