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.
- checksums.yaml +4 -4
- data/bin/Changes +63 -15
- data/bin/META.json +1 -1
- data/bin/META.yml +1 -1
- data/bin/README +2 -2
- data/bin/exiftool +13 -13
- data/bin/lib/Image/ExifTool/CBOR.pm +18 -2
- data/bin/lib/Image/ExifTool/Canon.pm +68 -16
- data/bin/lib/Image/ExifTool/DJI.pm +3 -2
- data/bin/lib/Image/ExifTool/DNG.pm +25 -2
- data/bin/lib/Image/ExifTool/EXE.pm +54 -6
- data/bin/lib/Image/ExifTool/Exif.pm +175 -14
- data/bin/lib/Image/ExifTool/FujiFilm.pm +142 -20
- data/bin/lib/Image/ExifTool/GIF.pm +5 -1
- data/bin/lib/Image/ExifTool/ID3.pm +70 -7
- data/bin/lib/Image/ExifTool/InDesign.pm +1 -1
- data/bin/lib/Image/ExifTool/JPEG.pm +1 -1
- data/bin/lib/Image/ExifTool/Jpeg2000.pm +30 -15
- data/bin/lib/Image/ExifTool/MakerNotes.pm +2 -2
- data/bin/lib/Image/ExifTool/Nikon.pm +58 -18
- data/bin/lib/Image/ExifTool/Olympus.pm +7 -1
- data/bin/lib/Image/ExifTool/PNG.pm +8 -13
- data/bin/lib/Image/ExifTool/Panasonic.pm +15 -2
- data/bin/lib/Image/ExifTool/PhotoMechanic.pm +2 -2
- data/bin/lib/Image/ExifTool/QuickTime.pm +32 -5
- data/bin/lib/Image/ExifTool/README +14 -5
- data/bin/lib/Image/ExifTool/RIFF.pm +60 -10
- data/bin/lib/Image/ExifTool/Sony.pm +95 -34
- data/bin/lib/Image/ExifTool/TagLookup.pm +6937 -6714
- data/bin/lib/Image/ExifTool/TagNames.pod +812 -332
- data/bin/lib/Image/ExifTool/Text.pm +4 -5
- data/bin/lib/Image/ExifTool/Validate.pm +23 -20
- data/bin/lib/Image/ExifTool/WriteCanonRaw.pl +2 -2
- data/bin/lib/Image/ExifTool/WriteExif.pl +14 -4
- data/bin/lib/Image/ExifTool/WriteQuickTime.pl +1 -0
- data/bin/lib/Image/ExifTool/WriteRIFF.pl +31 -6
- data/bin/lib/Image/ExifTool/Writer.pl +40 -14
- data/bin/lib/Image/ExifTool/XMP.pm +67 -2
- data/bin/lib/Image/ExifTool/XMP2.pl +35 -0
- data/bin/lib/Image/ExifTool.pm +79 -40
- data/bin/lib/Image/ExifTool.pod +9 -3
- data/bin/perl-Image-ExifTool.spec +1 -1
- data/lib/exiftool_vendored/version.rb +1 -1
- 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.
|
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
|
-
|
3084
|
-
|
3085
|
-
|
3086
|
-
|
3087
|
-
|
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-
|
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.
|
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->
|
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
|
-
|
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
|
-
|
1674
|
-
|
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
|
-
|
1685
|
-
|
1686
|
-
|
1687
|
-
|
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)
|
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
|
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.
|
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
|