tikiwiki/packages/tiki-pkg-mediaalchemyst/phpexiftool/exiftool/lib/Image/ExifTool/CanonVRD.pm

2274 lines
75 KiB
Perl
Raw Permalink Normal View History

2023-11-20 21:52:04 +01:00
#------------------------------------------------------------------------------
# File: CanonVRD.pm
#
# Description: Read/write Canon VRD and DR4 information
#
# Revisions: 2006/10/30 - P. Harvey Created
# 2007/10/23 - PH Added new VRD 3.0 tags
# 2008/08/29 - PH Added new VRD 3.4 tags
# 2008/12/02 - PH Added new VRD 3.5 tags
# 2010/06/18 - PH Support variable-length CustomPictureStyle data
# 2010/09/14 - PH Added r/w support for XMP in VRD
# 2015/05/16 - PH Added DR4 support (DPP 4.1.50.0)
#
# References: 1) Bogdan private communication (Canon DPP v3.4.1.1)
# 2) Gert Kello private communiation (DPP 3.8)
#------------------------------------------------------------------------------
package Image::ExifTool::CanonVRD;
use strict;
use vars qw($VERSION);
use Image::ExifTool qw(:DataAccess :Utils);
use Image::ExifTool::Canon;
$VERSION = '1.28';
sub ProcessCanonVRD($$;$);
sub WriteCanonVRD($$;$);
sub ProcessEditData($$$);
sub ProcessIHL($$$);
sub ProcessIHLExif($$$);
sub ProcessDR4($$;$);
sub SortDR4($$);
# map for adding directories to VRD
my %vrdMap = (
XMP => 'CanonVRD',
CanonVRD => 'VRD',
);
my %noYes = (
PrintConvColumns => 2,
PrintConv => { 0 => 'No', 1 => 'Yes' },
);
# DR4 format codes
my %vrdFormat = (
1 => 'int32u',
2 => 'string',
8 => 'int32u',
9 => 'int32s',
13 => 'double',
33 => 'int32u', # (array)
38 => 'double', # (array)
# 254 => 'undef', ?
255 => 'undef',
);
# empty VRD header/footer for creating VRD from scratch
my $blankHeader = "CANON OPTIONAL DATA\0\0\x01\0\0\0\0\0\0";
my $blankFooter = "CANON OPTIONAL DATA\0" . ("\0" x 42) . "\xff\xd9";
# main tag table blocks in CanonVRD trailer (ref PH)
%Image::ExifTool::CanonVRD::Main = (
WRITE_PROC => \&WriteCanonVRD,
PROCESS_PROC => \&ProcessCanonVRD,
NOTES => q{
Canon Digital Photo Professional writes VRD (Recipe Data) information as a
trailer record to JPEG, TIFF, CRW and CR2 images, or as stand-alone VRD or
DR4 files. The tags listed below represent information found in these
records. The complete VRD/DR4 data record may be accessed as a block using
the Extra 'CanonVRD' or 'CanonDR4' tag, but this tag is not extracted or
copied unless specified explicitly.
},
0xffff00f4 => {
Name => 'EditData',
SubDirectory => { TagTable => 'Image::ExifTool::CanonVRD::Edit' },
},
0xffff00f5 => {
Name => 'IHLData',
SubDirectory => { TagTable => 'Image::ExifTool::CanonVRD::IHL' },
},
0xffff00f6 => {
Name => 'XMP',
Flags => [ 'Binary', 'Protected' ],
Writable => 'undef', # allow writing/deleting as a block
SubDirectory => {
DirName => 'XMP',
TagTable => 'Image::ExifTool::XMP::Main',
},
},
0xffff00f7 => {
Name => 'Edit4Data',
SubDirectory => { TagTable => 'Image::ExifTool::CanonVRD::Edit4' },
},
);
# the VRD edit information is divided into sections
%Image::ExifTool::CanonVRD::Edit = (
WRITE_PROC => \&ProcessEditData,
PROCESS_PROC => \&ProcessEditData,
VARS => { ID_LABEL => 'Index' }, # change TagID label in documentation
NOTES => 'Canon VRD edit information.',
0 => {
Name => 'VRD1',
Size => 0x272, # size of version 1.0 edit information in bytes
SubDirectory => { TagTable => 'Image::ExifTool::CanonVRD::Ver1' },
},
1 => {
Name => 'VRDStampTool',
Size => 0, # size is variable, and obtained from int32u at directory start
SubDirectory => { TagTable => 'Image::ExifTool::CanonVRD::StampTool' },
},
2 => {
Name => 'VRD2',
Size => undef, # size is the remaining edit data
SubDirectory => { TagTable => 'Image::ExifTool::CanonVRD::Ver2' },
},
);
# Canon DPP version 4 edit information
%Image::ExifTool::CanonVRD::Edit4 = (
WRITE_PROC => \&ProcessEditData,
PROCESS_PROC => \&ProcessEditData,
VARS => { ID_LABEL => 'Index' }, # change TagID label in documentation
NOTES => 'Canon DPP version 4 edit information.',
0 => {
Name => 'DR4',
Size => undef, # size is the remaining edit data
SubDirectory => { TagTable => 'Image::ExifTool::CanonVRD::DR4' },
},
);
# "IHL Created Optional Item Data" tags (not yet writable)
%Image::ExifTool::CanonVRD::IHL = (
PROCESS_PROC => \&ProcessIHL,
TAG_PREFIX => 'VRD_IHL',
GROUPS => { 2 => 'Image' },
1 => [
# this contains edited TIFF-format data, with an original IFD at 0x0008
# and an edited IFD with offset given in the TIFF header.
{
Name => 'IHL_EXIF',
Condition => '$self->Options("ExtractEmbedded")',
SubDirectory => {
TagTable => 'Image::ExifTool::Exif::Main',
ProcessProc => \&ProcessIHLExif,
},
},{
Name => 'IHL_EXIF',
Notes => q{
extracted as a block if the Unknown option is used, or processed as the
first sub-document with the ExtractEmbedded option
},
Binary => 1,
Unknown => 1,
},
],
# 2 - written by DPP 3.0.2.6, and it looks something like edit data,
# but I haven't decoded it yet - PH
3 => {
# (same size as the PreviewImage with DPP 3.0.2.6)
Name => 'ThumbnailImage',
Groups => { 2 => 'Preview' },
Binary => 1,
},
4 => {
Name => 'PreviewImage',
Groups => { 2 => 'Preview' },
Binary => 1,
},
5 => {
Name => 'RawCodecVersion',
ValueConv => '$val =~ s/\0.*//s; $val', # truncate string at null
},
6 => {
Name => 'CRCDevelParams',
Binary => 1,
Unknown => 1,
},
);
# VRD version 1 tags (ref PH)
%Image::ExifTool::CanonVRD::Ver1 = (
PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
WRITABLE => 1,
FIRST_ENTRY => 0,
GROUPS => { 2 => 'Image' },
DATAMEMBER => [ 0x002 ], # necessary for writing
#
# RAW image adjustment
#
0x002 => {
Name => 'VRDVersion',
Format => 'int16u',
Writable => 0,
DataMember => 'VRDVersion',
RawConv => '$$self{VRDVersion} = $val',
PrintConv => '$val =~ s/^(\d)(\d*)(\d)$/$1.$2.$3/; $val',
},
0x006 => {
Name => 'WBAdjRGGBLevels',
Format => 'int16u[4]',
},
0x018 => {
Name => 'WhiteBalanceAdj',
Format => 'int16u',
PrintConvColumns => 2,
PrintConv => {
0 => 'Auto',
1 => 'Daylight',
2 => 'Cloudy',
3 => 'Tungsten',
4 => 'Fluorescent',
5 => 'Flash',
8 => 'Shade',
9 => 'Kelvin',
30 => 'Manual (Click)',
31 => 'Shot Settings',
},
},
0x01a => {
Name => 'WBAdjColorTemp',
Format => 'int16u',
},
# 0x01c similar to 0x006
0x024 => {
Name => 'WBFineTuneActive',
Format => 'int16u',
%noYes,
},
0x028 => {
Name => 'WBFineTuneSaturation',
Format => 'int16u',
},
0x02c => {
Name => 'WBFineTuneTone',
Format => 'int16u',
},
0x02e => {
Name => 'RawColorAdj',
Format => 'int16u',
PrintConv => {
0 => 'Shot Settings',
1 => 'Faithful',
2 => 'Custom',
},
},
0x030 => {
Name => 'RawCustomSaturation',
Format => 'int32s',
},
0x034 => {
Name => 'RawCustomTone',
Format => 'int32s',
},
0x038 => {
Name => 'RawBrightnessAdj',
Format => 'int32s',
ValueConv => '$val / 6000',
ValueConvInv => 'int($val * 6000 + ($val < 0 ? -0.5 : 0.5))',
PrintConv => 'sprintf("%.2f",$val)',
PrintConvInv => '$val',
},
0x03c => {
Name => 'ToneCurveProperty',
Format => 'int16u',
PrintConvColumns => 2,
PrintConv => {
0 => 'Shot Settings',
1 => 'Linear',
2 => 'Custom 1',
3 => 'Custom 2',
4 => 'Custom 3',
5 => 'Custom 4',
6 => 'Custom 5',
},
},
# 0x040 usually "10 9 2"
0x07a => {
Name => 'DynamicRangeMin',
Format => 'int16u',
},
0x07c => {
Name => 'DynamicRangeMax',
Format => 'int16u',
},
# 0x0c6 usually "10 9 2"
#
# RGB image adjustment
#
0x110 => {
Name => 'ToneCurveActive',
Format => 'int16u',
%noYes,
},
0x113 => {
Name => 'ToneCurveMode',
PrintConv => { 0 => 'RGB', 1 => 'Luminance' },
},
0x114 => {
Name => 'BrightnessAdj',
Format => 'int8s',
},
0x115 => {
Name => 'ContrastAdj',
Format => 'int8s',
},
0x116 => {
Name => 'SaturationAdj',
Format => 'int16s',
},
0x11e => {
Name => 'ColorToneAdj',
Notes => 'in degrees, so -1 is the same as 359',
Format => 'int32s',
},
0x126 => {
Name => 'LuminanceCurvePoints',
Format => 'int16u[21]',
PrintConv => 'Image::ExifTool::CanonVRD::ToneCurvePrint($val)',
PrintConvInv => 'Image::ExifTool::CanonVRD::ToneCurvePrintInv($val)',
},
0x150 => {
Name => 'LuminanceCurveLimits',
Notes => '4 numbers: input and output highlight and shadow points',
Format => 'int16u[4]',
},
0x159 => {
Name => 'ToneCurveInterpolation',
PrintConv => { 0 => 'Curve', 1 => 'Straight' },
},
0x160 => {
Name => 'RedCurvePoints',
Format => 'int16u[21]',
PrintConv => 'Image::ExifTool::CanonVRD::ToneCurvePrint($val)',
PrintConvInv => 'Image::ExifTool::CanonVRD::ToneCurvePrintInv($val)',
},
# 0x193 same as 0x159
0x19a => {
Name => 'GreenCurvePoints',
Format => 'int16u[21]',
PrintConv => 'Image::ExifTool::CanonVRD::ToneCurvePrint($val)',
PrintConvInv => 'Image::ExifTool::CanonVRD::ToneCurvePrintInv($val)',
},
# 0x1cd same as 0x159
0x1d4 => {
Name => 'BlueCurvePoints',
Format => 'int16u[21]',
PrintConv => 'Image::ExifTool::CanonVRD::ToneCurvePrint($val)',
PrintConvInv => 'Image::ExifTool::CanonVRD::ToneCurvePrintInv($val)',
},
0x18a => {
Name => 'RedCurveLimits',
Format => 'int16u[4]',
},
0x1c4 => {
Name => 'GreenCurveLimits',
Format => 'int16u[4]',
},
0x1fe => {
Name => 'BlueCurveLimits',
Format => 'int16u[4]',
},
# 0x207 same as 0x159
0x20e => {
Name => 'RGBCurvePoints',
Format => 'int16u[21]',
PrintConv => 'Image::ExifTool::CanonVRD::ToneCurvePrint($val)',
PrintConvInv => 'Image::ExifTool::CanonVRD::ToneCurvePrintInv($val)',
},
0x238 => {
Name => 'RGBCurveLimits',
Format => 'int16u[4]',
},
# 0x241 same as 0x159
0x244 => {
Name => 'CropActive',
Format => 'int16u',
%noYes,
},
0x246 => {
Name => 'CropLeft',
Notes => 'crop coordinates in original unrotated image',
Format => 'int16u',
},
0x248 => {
Name => 'CropTop',
Format => 'int16u',
},
0x24a => {
Name => 'CropWidth',
Format => 'int16u',
},
0x24c => {
Name => 'CropHeight',
Format => 'int16u',
},
0x25a => {
Name => 'SharpnessAdj',
Format => 'int16u',
},
0x260 => {
Name => 'CropAspectRatio',
Format => 'int16u',
PrintConv => {
0 => 'Free',
1 => '3:2',
2 => '2:3',
3 => '4:3',
4 => '3:4',
5 => 'A-size Landscape',
6 => 'A-size Portrait',
7 => 'Letter-size Landscape',
8 => 'Letter-size Portrait',
9 => '4:5',
10 => '5:4',
11 => '1:1',
12 => 'Circle',
65535 => 'Custom',
},
},
0x262 => {
Name => 'ConstrainedCropWidth',
Format => 'float',
PrintConv => 'sprintf("%.7g",$val)',
PrintConvInv => '$val',
},
0x266 => {
Name => 'ConstrainedCropHeight',
Format => 'float',
PrintConv => 'sprintf("%.7g",$val)',
PrintConvInv => '$val',
},
0x26a => {
Name => 'CheckMark',
Format => 'int16u',
PrintConv => {
0 => 'Clear',
1 => 1,
2 => 2,
3 => 3,
},
},
0x26e => {
Name => 'Rotation',
Format => 'int16u',
PrintConv => {
0 => 0,
1 => 90,
2 => 180,
3 => 270,
},
},
0x270 => {
Name => 'WorkColorSpace',
Format => 'int16u',
PrintConv => {
0 => 'sRGB',
1 => 'Adobe RGB',
2 => 'Wide Gamut RGB',
3 => 'Apple RGB',
4 => 'ColorMatch RGB',
},
},
# (VRD 1.0.0 edit data ends here -- 0x272 bytes)
);
# VRD Stamp Tool tags (ref PH)
%Image::ExifTool::CanonVRD::StampTool = (
PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
GROUPS => { 2 => 'Image' },
0x00 => {
Name => 'StampToolCount',
Format => 'int32u',
},
);
# VRD version 2 and 3 tags (ref PH)
%Image::ExifTool::CanonVRD::Ver2 = (
PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
WRITABLE => 1,
FIRST_ENTRY => 0,
FORMAT => 'int16s',
DATAMEMBER => [ 0x58, 0xdc, 0xdf, 0xe0 ], # (required for DataMember and var-format tags)
IS_SUBDIR => [ 0xe0 ],
GROUPS => { 2 => 'Image' },
NOTES => 'Tags added in DPP version 2.0 and later.',
0x02 => {
Name => 'PictureStyle',
PrintConvColumns => 2,
PrintConv => {
0 => 'Standard',
1 => 'Portrait',
2 => 'Landscape',
3 => 'Neutral',
4 => 'Faithful',
5 => 'Monochrome',
6 => 'Unknown?', # PH (maybe in-camera custom picture style?)
7 => 'Custom',
},
},
0x03 => { Name => 'IsCustomPictureStyle', %noYes },
# 0x08: 3
# 0x09: 4095
# 0x0a: 0
# 0x0b: 4095
# 0x0c: 0
0x0d => 'StandardRawColorTone',
0x0e => 'StandardRawSaturation',
0x0f => 'StandardRawContrast',
0x10 => { Name => 'StandardRawLinear', %noYes },
0x11 => 'StandardRawSharpness',
0x12 => 'StandardRawHighlightPoint',
0x13 => 'StandardRawShadowPoint',
0x14 => 'StandardOutputHighlightPoint', #2
0x15 => 'StandardOutputShadowPoint', #2
0x16 => 'PortraitRawColorTone',
0x17 => 'PortraitRawSaturation',
0x18 => 'PortraitRawContrast',
0x19 => { Name => 'PortraitRawLinear', %noYes },
0x1a => 'PortraitRawSharpness',
0x1b => 'PortraitRawHighlightPoint',
0x1c => 'PortraitRawShadowPoint',
0x1d => 'PortraitOutputHighlightPoint',
0x1e => 'PortraitOutputShadowPoint',
0x1f => 'LandscapeRawColorTone',
0x20 => 'LandscapeRawSaturation',
0x21 => 'LandscapeRawContrast',
0x22 => { Name => 'LandscapeRawLinear', %noYes },
0x23 => 'LandscapeRawSharpness',
0x24 => 'LandscapeRawHighlightPoint',
0x25 => 'LandscapeRawShadowPoint',
0x26 => 'LandscapeOutputHighlightPoint',
0x27 => 'LandscapeOutputShadowPoint',
0x28 => 'NeutralRawColorTone',
0x29 => 'NeutralRawSaturation',
0x2a => 'NeutralRawContrast',
0x2b => { Name => 'NeutralRawLinear', %noYes },
0x2c => 'NeutralRawSharpness',
0x2d => 'NeutralRawHighlightPoint',
0x2e => 'NeutralRawShadowPoint',
0x2f => 'NeutralOutputHighlightPoint',
0x30 => 'NeutralOutputShadowPoint',
0x31 => 'FaithfulRawColorTone',
0x32 => 'FaithfulRawSaturation',
0x33 => 'FaithfulRawContrast',
0x34 => { Name => 'FaithfulRawLinear', %noYes },
0x35 => 'FaithfulRawSharpness',
0x36 => 'FaithfulRawHighlightPoint',
0x37 => 'FaithfulRawShadowPoint',
0x38 => 'FaithfulOutputHighlightPoint',
0x39 => 'FaithfulOutputShadowPoint',
0x3a => {
Name => 'MonochromeFilterEffect',
PrintConv => {
-2 => 'None',
-1 => 'Yellow',
0 => 'Orange',
1 => 'Red',
2 => 'Green',
},
},
0x3b => {
Name => 'MonochromeToningEffect',
PrintConv => {
-2 => 'None',
-1 => 'Sepia',
0 => 'Blue',
1 => 'Purple',
2 => 'Green',
},
},
0x3c => 'MonochromeContrast',
0x3d => { Name => 'MonochromeLinear', %noYes },
0x3e => 'MonochromeSharpness',
0x3f => 'MonochromeRawHighlightPoint',
0x40 => 'MonochromeRawShadowPoint',
0x41 => 'MonochromeOutputHighlightPoint',
0x42 => 'MonochromeOutputShadowPoint',
0x45 => { Name => 'UnknownContrast', Unknown => 1 },
0x46 => { Name => 'UnknownLinear', %noYes, Unknown => 1 },
0x47 => { Name => 'UnknownSharpness', Unknown => 1 },
0x48 => { Name => 'UnknownRawHighlightPoint', Unknown => 1 },
0x49 => { Name => 'UnknownRawShadowPoint', Unknown => 1 },
0x4a => { Name => 'UnknownOutputHighlightPoint',Unknown => 1 },
0x4b => { Name => 'UnknownOutputShadowPoint', Unknown => 1 },
0x4c => 'CustomColorTone',
0x4d => 'CustomSaturation',
0x4e => 'CustomContrast',
0x4f => { Name => 'CustomLinear', %noYes },
0x50 => 'CustomSharpness',
0x51 => 'CustomRawHighlightPoint',
0x52 => 'CustomRawShadowPoint',
0x53 => 'CustomOutputHighlightPoint',
0x54 => 'CustomOutputShadowPoint',
0x58 => {
Name => 'CustomPictureStyleData',
Format => 'var_int16u',
Binary => 1,
Notes => 'variable-length data structure',
Writable => 0,
RawConv => 'length($val) == 2 ? undef : $val', # ignore if no data
},
# (VRD 2.0.0 edit data ends here: 178 bytes, index 0x59)
0x5e => [{
Name => 'ChrominanceNoiseReduction',
Condition => '$$self{VRDVersion} < 330',
Notes => 'VRDVersion prior to 3.3.0',
PrintConv => {
0 => 'Off',
58 => 'Low',
100 => 'High',
},
},{ #1
Name => 'ChrominanceNoiseReduction',
Notes => 'VRDVersion 3.3.0 or later',
PrintHex => 1,
PrintConvColumns => 4,
PrintConv => {
0x00 => 0,
0x10 => 1,
0x21 => 2,
0x32 => 3,
0x42 => 4,
0x53 => 5,
0x64 => 6,
0x74 => 7,
0x85 => 8,
0x96 => 9,
0xa6 => 10,
0xa7 => 11,
0xa8 => 12,
0xa9 => 13,
0xaa => 14,
0xab => 15,
0xac => 16,
0xad => 17,
0xae => 18,
0xaf => 19,
0xb0 => 20,
},
}],
0x5f => [{
Name => 'LuminanceNoiseReduction',
Condition => '$$self{VRDVersion} < 330',
Notes => 'VRDVersion prior to 3.3.0',
PrintConv => {
0 => 'Off',
65 => 'Low',
100 => 'High',
},
},{ #1
Name => 'LuminanceNoiseReduction',
Notes => 'VRDVersion 3.3.0 or later',
PrintHex => 1,
PrintConvColumns => 4,
PrintConv => {
0x00 => 0,
0x41 => 1,
0x64 => 2,
0x6e => 3,
0x78 => 4,
0x82 => 5,
0x8c => 6,
0x96 => 7,
0xa0 => 8,
0xaa => 9,
0xb4 => 10,
0xb5 => 11,
0xb6 => 12,
0xb7 => 13,
0xb8 => 14,
0xb9 => 15,
0xba => 16,
0xbb => 17,
0xbc => 18,
0xbd => 19,
0xbe => 20,
},
}],
0x60 => [{
Name => 'ChrominanceNR_TIFF_JPEG',
Condition => '$$self{VRDVersion} < 330',
Notes => 'VRDVersion prior to 3.3.0',
PrintConv => {
0 => 'Off',
33 => 'Low',
100 => 'High',
},
},{ #1
Name => 'ChrominanceNR_TIFF_JPEG',
Notes => 'VRDVersion 3.3.0 or later',
PrintHex => 1,
PrintConvColumns => 4,
PrintConv => {
0x00 => 0,
0x10 => 1,
0x21 => 2,
0x32 => 3,
0x42 => 4,
0x53 => 5,
0x64 => 6,
0x74 => 7,
0x85 => 8,
0x96 => 9,
0xa6 => 10,
0xa7 => 11,
0xa8 => 12,
0xa9 => 13,
0xaa => 14,
0xab => 15,
0xac => 16,
0xad => 17,
0xae => 18,
0xaf => 19,
0xb0 => 20,
},
}],
# 0x61: 1
# (VRD 3.0.0 edit data ends here: 196 bytes, index 0x62)
0x62 => { Name => 'ChromaticAberrationOn', %noYes },
0x63 => { Name => 'DistortionCorrectionOn', %noYes },
0x64 => { Name => 'PeripheralIlluminationOn', %noYes },
0x65 => { Name => 'ColorBlur', %noYes },
0x66 => {
Name => 'ChromaticAberration',
ValueConv => '$val / 0x400',
ValueConvInv => 'int($val * 0x400 + 0.5)',
PrintConv => 'sprintf("%.0f%%", $val * 100)',
PrintConvInv => 'ToFloat($val) / 100',
},
0x67 => {
Name => 'DistortionCorrection',
ValueConv => '$val / 0x400',
ValueConvInv => 'int($val * 0x400 + 0.5)',
PrintConv => 'sprintf("%.0f%%", $val * 100)',
PrintConvInv => 'ToFloat($val) / 100',
},
0x68 => {
Name => 'PeripheralIllumination',
ValueConv => '$val / 0x400',
ValueConvInv => 'int($val * 0x400 + 0.5)',
PrintConv => 'sprintf("%.0f%%", $val * 100)',
PrintConvInv => 'ToFloat($val) / 100',
},
0x69 => {
Name => 'AberrationCorrectionDistance',
Notes => '100% = infinity',
RawConv => '$val == 0x7fff ? undef : $val',
ValueConv => '1 - $val / 0x400',
ValueConvInv => 'int((1 - $val) * 0x400 + 0.5)',
PrintConv => 'sprintf("%.0f%%", $val * 100)',
PrintConvInv => 'ToFloat($val) / 100',
},
0x6a => 'ChromaticAberrationRed',
0x6b => 'ChromaticAberrationBlue',
0x6d => { #1
Name => 'LuminanceNR_TIFF_JPEG',
Notes => 'val = raw / 10',
ValueConv => '$val / 10',
ValueConvInv => 'int($val * 10 + 0.5)',
},
# (VRD 3.4.0 edit data ends here: 220 bytes, index 0x6e)
0x6e => { Name => 'AutoLightingOptimizerOn', %noYes },
0x6f => {
Name => 'AutoLightingOptimizer',
PrintConv => {
100 => 'Low',
200 => 'Standard',
300 => 'Strong',
0x7fff => 'n/a', #1
},
},
# 0x71: 200
# 0x73: 100
# (VRD 3.5.0 edit data ends here: 232 bytes, index 0x74)
0x75 => {
Name => 'StandardRawHighlight',
ValueConv => '$val / 10',
ValueConvInv => '$val * 10',
},
0x76 => {
Name => 'PortraitRawHighlight',
ValueConv => '$val / 10',
ValueConvInv => '$val * 10',
},
0x77 => {
Name => 'LandscapeRawHighlight',
ValueConv => '$val / 10',
ValueConvInv => '$val * 10',
},
0x78 => {
Name => 'NeutralRawHighlight',
ValueConv => '$val / 10',
ValueConvInv => '$val * 10',
},
0x79 => {
Name => 'FaithfulRawHighlight',
ValueConv => '$val / 10',
ValueConvInv => '$val * 10',
},
0x7a => {
Name => 'MonochromeRawHighlight',
ValueConv => '$val / 10',
ValueConvInv => '$val * 10',
},
0x7b => {
Name => 'UnknownRawHighlight',
Unknown => 1,
ValueConv => '$val / 10',
ValueConvInv => '$val * 10',
},
0x7c => {
Name => 'CustomRawHighlight',
ValueConv => '$val / 10',
ValueConvInv => '$val * 10',
},
0x7e => {
Name => 'StandardRawShadow',
ValueConv => '$val / 10',
ValueConvInv => '$val * 10',
},
0x7f => {
Name => 'PortraitRawShadow',
ValueConv => '$val / 10',
ValueConvInv => '$val * 10',
},
0x80 => {
Name => 'LandscapeRawShadow',
ValueConv => '$val / 10',
ValueConvInv => '$val * 10',
},
0x81 => {
Name => 'NeutralRawShadow',
ValueConv => '$val / 10',
ValueConvInv => '$val * 10',
},
0x82 => {
Name => 'FaithfulRawShadow',
ValueConv => '$val / 10',
ValueConvInv => '$val * 10',
},
0x83 => {
Name => 'MonochromeRawShadow',
ValueConv => '$val / 10',
ValueConvInv => '$val * 10',
},
0x84 => {
Name => 'UnknownRawShadow',
Unknown => 1,
ValueConv => '$val / 10',
ValueConvInv => '$val * 10',
},
0x85 => {
Name => 'CustomRawShadow',
ValueConv => '$val / 10',
ValueConvInv => '$val * 10',
},
0x8b => { #2
Name => 'AngleAdj',
Format => 'int32s',
ValueConv => '$val / 100',
ValueConvInv => '$val * 100',
},
0x8e => {
Name => 'CheckMark2',
Format => 'int16u',
PrintConvColumns => 2,
PrintConv => {
0 => 'Clear',
1 => 1,
2 => 2,
3 => 3,
4 => 4,
5 => 5,
},
},
# (VRD 3.8.0 edit data ends here: 286 bytes, index 0x8f)
0x90 => {
Name => 'UnsharpMask',
PrintConv => { 0 => 'Off', 1 => 'On' },
},
0x92 => 'StandardUnsharpMaskStrength',
0x94 => 'StandardUnsharpMaskFineness',
0x96 => 'StandardUnsharpMaskThreshold',
0x98 => 'PortraitUnsharpMaskStrength',
0x9a => 'PortraitUnsharpMaskFineness',
0x9c => 'PortraitUnsharpMaskThreshold',
0x9e => 'LandscapeUnsharpMaskStrength',
0xa0 => 'LandscapeUnsharpMaskFineness',
0xa2 => 'LandscapeUnsharpMaskThreshold',
0xa4 => 'NeutraUnsharpMaskStrength',
0xa6 => 'NeutralUnsharpMaskFineness',
0xa8 => 'NeutralUnsharpMaskThreshold',
0xaa => 'FaithfulUnsharpMaskStrength',
0xac => 'FaithfulUnsharpMaskFineness',
0xae => 'FaithfulUnsharpMaskThreshold',
0xb0 => 'MonochromeUnsharpMaskStrength',
0xb2 => 'MonochromeUnsharpMaskFineness',
0xb4 => 'MonochromeUnsharpMaskThreshold',
0xb6 => 'CustomUnsharpMaskStrength',
0xb8 => 'CustomUnsharpMaskFineness',
0xba => 'CustomUnsharpMaskThreshold',
0xbc => 'CustomDefaultUnsharpStrength',
0xbe => 'CustomDefaultUnsharpFineness',
0xc0 => 'CustomDefaultUnsharpThreshold',
# (VRD 3.9.1 edit data ends here: 392 bytes, index 0xc4)
# 0xc9: 3 - some RawSharpness
# 0xca: 4095 - some RawHighlightPoint
# 0xcb: 0 - some RawShadowPoint
# 0xcc: 4095 - some OutputHighlightPoint
# 0xcd: 0 - some OutputShadowPoint
# 0xd1: 3 - some UnsharpMaskStrength
# 0xd3: 7 - some UnsharpMaskFineness
# 0xd5: 3,4 - some UnsharpMaskThreshold
0xd6 => { Name => 'CropCircleActive', %noYes },
0xd7 => 'CropCircleX',
0xd8 => 'CropCircleY',
0xd9 => 'CropCircleRadius',
# 0xda: 0, 1
# 0xdb: 100
0xdc => {
Name => 'DLOOn',
DataMember => 'DLOOn',
RawConv => '$$self{DLOOn} = $val',
%noYes,
},
0xdd => 'DLOSetting',
# (VRD 3.11.0 edit data ends here: 444 bytes, index 0xde)
0xde => {
Name => 'DLOShootingDistance',
Notes => '100% = infinity',
RawConv => '$val == 0x7fff ? undef : $val',
ValueConv => '1 - $val / 0x400',
ValueConvInv => 'int((1 - $val) * 0x400 + 0.5)',
PrintConv => 'sprintf("%.0f%%", $val * 100)',
PrintConvInv => 'ToFloat($val) / 100',
},
0xdf => {
Name => 'DLODataLength',
DataMember => 'DLODataLength',
Format => 'int32u',
Writable => 0,
RawConv => '$$self{DLODataLength} = $val',
},
0xe0 => { # (yes, this overlaps DLODataLength)
Name => 'DLOInfo',
# - have seen DLODataLengths of 65536,64869 when DLO is Off, so must test DLOOn flag
Condition => '$$self{DLOOn}',
SubDirectory => { TagTable => 'Image::ExifTool::CanonVRD::DLOInfo' },
Hook => '$varSize += $$self{DLODataLength} + 0x16',
},
0xe1 => 'CameraRawColorTone',
# (VRD 3.11.2 edit data ends here: 452 bytes, index 0xe2, unless DLO is on)
0xe2 => 'CameraRawSaturation',
0xe3 => 'CameraRawContrast',
0xe4 => { Name => 'CameraRawLinear', %noYes },
0xe5 => 'CameraRawSharpness',
0xe6 => 'CameraRawHighlightPoint',
0xe7 => 'CameraRawShadowPoint',
0xe8 => 'CameraRawOutputHighlightPoint',
0xe9 => 'CameraRawOutputShadowPoint',
);
# DLO tags (ref PH)
%Image::ExifTool::CanonVRD::DLOInfo = (
PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
WRITABLE => 1,
FIRST_ENTRY => 1,
FORMAT => 'int16s',
GROUPS => { 2 => 'Image' },
NOTES => 'Tags added when DLO (Digital Lens Optimizer) is on.',
# 0x01 - seen 3112,3140
0x04 => 'DLOSettingApplied',
0x05 => {
Name => 'DLOVersion', #(NC)
Format => 'string[10]',
},
0x0a => {
Name => 'DLOData',
LargeTag => 1, # large tag, so avoid storing unnecessarily
Notes => 'variable-length Digital Lens Optimizer data, stored in JPEG-like format',
Format => 'undef[$$self{DLODataLength}]',
Writable => 0,
Binary => 1,
},
);
# VRD version 4 tags (ref PH)
%Image::ExifTool::CanonVRD::DR4 = (
PROCESS_PROC => \&ProcessDR4,
WRITE_PROC => \&ProcessDR4,
WRITABLE => 1,
GROUPS => { 2 => 'Image' },
VARS => { HEX_ID => 1, SORT_PROC => \&SortDR4 },
NOTES => q{
Tags written by Canon DPP version 4 in CanonVRD trailers and DR4 files. Each
tag has three associated flag words which are stored with the directory
entry, some of which are extracted as a separate tag, indicated in the table
below by a decimal appended to the tag ID (.0, .1 or .2).
},
header => {
Name => 'DR4Header',
SubDirectory => { TagTable => 'Image::ExifTool::CanonVRD::DR4Header' },
},
0x10002 => 'Rotation', # left/right rotation 90,180,270
0x10003 => 'AngleAdj', # crop angle
# 0x10018 - fmt=8: 0
# 0x10020 - fmt=2: ''
0x10021 => 'CustomPictureStyle', # (string)
0x10101 => {
Name => 'CheckMark',
PrintConv => {
0 => 'Clear',
1 => 1,
2 => 2,
3 => 3,
4 => 4,
5 => 5,
},
},
0x10200 => {
Name => 'WorkColorSpace',
PrintConv => {
1 => 'sRGB',
2 => 'Adobe RGB',
3 => 'Wide Gamut RGB',
4 => 'Apple RGB',
5 => 'ColorMatch RGB',
},
},
# 0x10201 - fmt=9: 0
# 0x10f20 - fmt=9: 350
0x20001 => 'RawBrightnessAdj',
0x20101 => {
Name => 'WhiteBalanceAdj',
PrintConvColumns => 2,
PrintConv => {
-1 => 'Manual (Click)',
0 => 'Auto',
1 => 'Daylight',
2 => 'Cloudy',
3 => 'Tungsten',
4 => 'Fluorescent',
5 => 'Flash',
8 => 'Shade',
9 => 'Kelvin',
255 => 'Shot Settings',
},
},
0x20102 => 'WBAdjColorTemp',
0x20105 => 'WBAdjMagentaGreen',
0x20106 => 'WBAdjBlueAmber',
0x20125 => {
Name => 'WBAdjRGGBLevels',
PrintConv => '$val =~ s/^\d+ //; $val', # remove first integer (14: what is this for?)
PrintConvInv => '"14 $val"',
},
0x20200 => { Name => 'GammaLinear', %noYes },
0x20301 => {
Name => 'PictureStyle',
PrintHex => 1,
PrintConv => {
0x81 => 'Standard',
0x82 => 'Portrait',
0x83 => 'Landscape',
0x84 => 'Neutral',
0x85 => 'Faithful',
0x86 => 'Monochrome',
0x87 => 'Auto',
0x88 => 'Fine Detail',
0xf0 => 'Shot Settings',
0xff => 'Custom',
},
},
# 0x20302 - Gamma curve data
0x20303 => 'ContrastAdj',
0x20304 => 'ColorToneAdj',
0x20305 => 'ColorSaturationAdj',
0x20306 => {
Name => 'MonochromeToningEffect',
PrintConv => {
0 => 'None',
1 => 'Sepia',
2 => 'Blue',
3 => 'Purple',
4 => 'Green',
},
},
0x20307 => {
Name => 'MonochromeFilterEffect',
PrintConv => {
0 => 'None',
1 => 'Yellow',
2 => 'Orange',
3 => 'Red',
4 => 'Green',
},
},
0x20308 => 'UnsharpMaskStrength',
0x20309 => 'UnsharpMaskFineness',
0x2030a => 'UnsharpMaskThreshold',
0x2030b => 'ShadowAdj',
0x2030c => 'HighlightAdj',
0x20310 => {
Name => 'SharpnessAdj',
PrintConv => {
0 => 'Sharpness',
1 => 'Unsharp Mask',
},
},
'0x20310.0' => { Name => 'SharpnessAdjOn', %noYes },
0x20311 => 'SharpnessStrength',
0x20400 => {
Name => 'ToneCurve',
SubDirectory => { TagTable => 'Image::ExifTool::CanonVRD::ToneCurve' },
},
'0x20400.1' => { Name => 'ToneCurveOriginal', %noYes },
# 0x20401 - fmt=33 (312 bytes)
0x20410 => 'ToneCurveBrightness',
0x20411 => 'ToneCurveContrast',
0x20500 => {
Name => 'AutoLightingOptimizer',
PrintConv => {
0 => 'Low',
1 => 'Standard',
2 => 'Strong',
},
},
'0x20500.0' => {
Name => 'AutoLightingOptimizerOn',
Notes => 'ignored if gamma is linear',
%noYes,
},
# 0x20501 - fmt=13: 0
# 0x20502 - fmt=13: 0
0x20600 => 'LuminanceNoiseReduction',
0x20601 => 'ChrominanceNoiseReduction',
# 0x20650 - fmt=9: 0 (JPG images)
0x20701 => {
Name => 'ShootingDistance',
Notes => '100% = infinity',
ValueConv => '$val / 10',
ValueConvInv => '$val * 10',
PrintConv => 'sprintf("%.0f%%", $val * 100)',
PrintConvInv => 'ToFloat($val) / 100',
},
0x20702 => {
Name => 'PeripheralIllumination',
PrintConv => 'sprintf "%g", $val',
PrintConvInv => '$val',
},
'0x20702.0' => { Name => 'PeripheralIlluminationOn', %noYes },
0x20703 => {
Name => 'ChromaticAberration',
PrintConv => 'sprintf "%g", $val',
PrintConvInv => '$val',
},
'0x20703.0' => { Name => 'ChromaticAberrationOn', %noYes },
0x20704 => { Name => 'ColorBlurOn', %noYes },
0x20705 => {
Name => 'DistortionCorrection',
PrintConv => 'sprintf "%g", $val',
PrintConvInv => '$val',
},
'0x20705.0' => { Name => 'DistortionCorrectionOn', %noYes },
0x20706 => 'DLOSetting',
'0x20706.0' => { Name => 'DLOOn', %noYes },
0x20707 => {
Name => 'ChromaticAberrationRed',
PrintConv => 'sprintf "%g", $val',
PrintConvInv => '$val',
},
0x20708 => {
Name => 'ChromaticAberrationBlue',
PrintConv => 'sprintf "%g", $val',
PrintConvInv => '$val',
},
0x20709 => {
Name => 'DistortionEffect',
PrintConv => {
0 => 'Shot Settings',
1 => 'Emphasize Linearity',
2 => 'Emphasize Distance',
3 => 'Emphasize Periphery',
4 => 'Emphasize Center',
},
},
# 0x20800 - fmt=1: 0
# 0x20801 - fmt=1: 0
0x20900 => 'ColorHue',
0x20901 => 'SaturationAdj',
0x20910 => 'RedHSL',
0x20911 => 'OrangeHSL',
0x20912 => 'GreenHSL',
0x20913 => 'AquaHSL',
0x20914 => 'BlueHSL',
0x20915 => 'BlueHSL',
0x20916 => 'PurpleHSL',
0x20917 => 'MagentaHSL',
0x20a00 => {
Name => 'GammaInfo',
SubDirectory => { TagTable => 'Image::ExifTool::CanonVRD::GammaInfo' },
},
# 0x20a01 - Auto picture style settings
# 0x20a02 - Standard picture style settings
# 0x20a03 - Portrait picture style settings
# 0x20a04 - Landscape picture style settings
# 0x20a05 - Neutral picture style settings
# 0x20a06 - Faithful picture style settings
# 0x20a07 - Monochrome picture style settings
# 0x20a08 - (unknown picture style settings)
# 0x20a09 - Custom picture style settings
# 0x20a20 - Fine Detail picture style settings
0x30101 => {
Name => 'CropAspectRatio',
PrintConv => {
0 => 'Free',
1 => 'Custom',
2 => '1:1',
3 => '3:2',
4 => '2:3',
5 => '4:3',
6 => '3:4',
7 => '5:4',
8 => '4:5',
9 => '16:9',
10 => '9:16',
},
},
0x30102 => 'CropAspectRatioCustom',
# 0x30103 - fmt=33: "0 0 8"
0xf0100 => {
Name => 'CropInfo',
SubDirectory => { TagTable => 'Image::ExifTool::CanonVRD::CropInfo' },
},
0xf0500 => {
Name => 'CustomPictureStyleData',
Binary => 1,
},
0xf0510 => {
Name => 'StampInfo',
SubDirectory => { TagTable => 'Image::ExifTool::CanonVRD::StampInfo' },
},
0xf0511 => {
Name => 'DustInfo',
SubDirectory => { TagTable => 'Image::ExifTool::CanonVRD::DustInfo' },
},
0xf0512 => 'LensFocalLength',
# 0xf0521 - DLO data
# 0xf0520 - DLO data
# 0xf0530 - created when dust delete data applied (4 bytes, all zero)
# 0xf0600 - fmt=253 (2308 bytes, JPG images)
# 0xf0601 - fmt=253 (2308 bytes, JPG images)
# 0x1ff52c - values: 129,130,132 (related to custom picture style somehow)
# to do:
# - find 8-15mm CR2 sample and decode linear distortion effect fine-tune
);
# Version 4 header information (ref PH)
%Image::ExifTool::CanonVRD::DR4Header = (
PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
WRITABLE => 1,
FIRST_ENTRY => 0,
FORMAT => 'int32u',
GROUPS => { 2 => 'Image' },
# 0 - value: 'IIII' (presumably byte order)
# 1 - value: 0x00040004 (currently use this for magic number)
# 2 - value: 6
3 => {
Name => 'DR4CameraModel',
Writable => 'int32u',
PrintHex => 1,
SeparateTable => 'Canon CameraModelID',
PrintConv => \%Image::ExifTool::Canon::canonModelID,
},
# 4 - value: 3
# 5 - value: 4
# 6 - value: 5
# 7 - DR4 directory entry count
);
# Version 4 RGB tone curve information (ref PH)
%Image::ExifTool::CanonVRD::ToneCurve = (
PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
WRITABLE => 1,
FIRST_ENTRY => 0,
FORMAT => 'int32u',
GROUPS => { 2 => 'Image' },
0x00 => {
Name => 'ToneCurveColorSpace',
PrintConv => {
0 => 'RGB',
1 => 'Luminance',
},
},
0x01 => {
Name => 'ToneCurveShape',
PrintConv => {
0 => 'Curve',
1 => 'Straight',
},
},
0x03 => { Name => 'ToneCurveInputRange', Format => 'int32u[2]', Notes => '255 max' },
0x05 => { Name => 'ToneCurveOutputRange', Format => 'int32u[2]', Notes => '255 max' },
0x07 => {
Name => 'RGBCurvePoints',
Format => 'int32u[21]',
PrintConv => 'Image::ExifTool::CanonVRD::ToneCurvePrint($val)',
PrintConvInv => 'Image::ExifTool::CanonVRD::ToneCurvePrintInv($val)',
},
0x0a => 'ToneCurveX',
0x0b => 'ToneCurveY',
0x2d => {
Name => 'RedCurvePoints',
Format => 'int32u[21]',
PrintConv => 'Image::ExifTool::CanonVRD::ToneCurvePrint($val)',
PrintConvInv => 'Image::ExifTool::CanonVRD::ToneCurvePrintInv($val)',
},
0x53 => {
Name => 'GreenCurvePoints',
Format => 'int32u[21]',
PrintConv => 'Image::ExifTool::CanonVRD::ToneCurvePrint($val)',
PrintConvInv => 'Image::ExifTool::CanonVRD::ToneCurvePrintInv($val)',
},
0x79 => {
Name => 'BlueCurvePoints',
Format => 'int32u[21]',
PrintConv => 'Image::ExifTool::CanonVRD::ToneCurvePrint($val)',
PrintConvInv => 'Image::ExifTool::CanonVRD::ToneCurvePrintInv($val)',
},
);
# Version 4 gamma curve information (ref PH)
%Image::ExifTool::CanonVRD::GammaInfo = (
PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
WRITABLE => 1,
FIRST_ENTRY => 0,
FORMAT => 'double',
GROUPS => { 2 => 'Image' },
0x02 => 'GammaContrast',
0x03 => 'GammaColorTone',
0x04 => 'GammaSaturation',
0x05 => 'GammaUnsharpMaskStrength',
0x06 => 'GammaUnsharpMaskFineness',
0x07 => 'GammaUnsharpMaskThreshold',
0x08 => 'GammaSharpnessStrength',
0x09 => 'GammaShadow',
0x0a => 'GammaHighlight',
# 0x0b-0x10 are the same as first 6 doubles of tag DR4_0x20302
# 0x0b - value: 14
0x0c => {
Name => 'GammaBlackPoint',
ValueConv => q{
return 0 if $val <= 0;
$val = log($val / 4.6875) / log(2) + 1;
return abs($val) > 1e-10 ? $val : 0;
},
ValueConvInv => '$val ? exp(($val - 1) * log(2)) * 4.6876 : 0',
PrintConv => 'sprintf("%+.3f", $val)',
PrintConvInv => '$val',
},
0x0d => {
Name => 'GammaWhitePoint',
ValueConv => q{
return $val if $val <= 0;
$val = log($val / 4.6875) / log(2) - 11.77109325169954;
return abs($val) > 1e-10 ? $val : 0;
},
ValueConvInv => '$val ? exp(($val + 11.77109325169954) * log(2)) * 4.6875 : 0',
PrintConv => 'sprintf("%+.3f", $val)',
PrintConvInv => '$val',
},
0x0e => {
Name => 'GammaMidPoint',
ValueConv => q{
return $val if $val <= 0;
$val = log($val / 4.6875) / log(2) - 8;
return abs($val) > 1e-10 ? $val : 0;
},
ValueConvInv => '$val ? exp(($val + 8) * log(2)) * 4.6876 : 0',
PrintConv => 'sprintf("%+.3f", $val)',
PrintConvInv => '$val',
},
0x0f => { Name => 'GammaCurveOutputRange', Format => 'double[2]', Notes => '16383 max' },
);
# Version 4 crop information (ref PH)
%Image::ExifTool::CanonVRD::CropInfo = (
PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
WRITABLE => 1,
FIRST_ENTRY => 0,
FORMAT => 'int32s',
GROUPS => { 2 => 'Image' },
0 => { Name => 'CropActive', %noYes },
1 => 'CropRotatedOriginalWidth',
2 => 'CropRotatedOriginalHeight',
3 => 'CropX',
4 => 'CropY',
5 => 'CropWidth',
6 => 'CropHeight',
8 => {
Name => 'CropRotation',
Format => 'double',
PrintConv => 'sprintf("%.7g",$val)',
PrintConvInv => '$val',
},
0x0a => 'CropOriginalWidth',
0x0b => 'CropOriginalHeight',
# 0x0c double - value: 100
);
# DR4 Stamp Tool tags (ref PH)
%Image::ExifTool::CanonVRD::StampInfo = (
PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
GROUPS => { 2 => 'Image' },
FORMAT => 'int32u',
FIRST_ENTRY => 0,
0x02 => 'StampToolCount',
);
# DR4 dust delete information (ref PH)
%Image::ExifTool::CanonVRD::DustInfo = (
PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
GROUPS => { 2 => 'Image' },
FORMAT => 'int32u',
FIRST_ENTRY => 0,
0x02 => { Name => 'DustDeleteApplied', %noYes },
);
#------------------------------------------------------------------------------
# sort DR4 tag ID's for the documentation
sub SortDR4($$)
{
my ($a, $b) = @_;
my ($aHex, $aDec, $bHex, $bDec);
($aHex, $aDec) = ($1, $2) if $a =~ /^(0x[0-9a-f]+)?\.?(\d*?)$/;
($bHex, $bDec) = ($1, $2) if $b =~ /^(0x[0-9a-f]+)?\.?(\d*?)$/;
if ($aHex) {
return 1 unless defined $bDec; # $b is 'header';
return hex($aHex) <=> hex($bHex) || $aDec <=> $bDec if $bHex;
return hex($aHex) <=> $bDec || 1;
} elsif ($bHex) {
return -1 unless defined $aDec;
return $aDec <=> hex($bHex) || -1;
} else {
return 1 unless defined $bDec;
return -1 unless defined $aDec;
return $aDec <=> $bDec;
}
}
#------------------------------------------------------------------------------
# Tone curve print conversion
sub ToneCurvePrint($)
{
my $val = shift;
my @vals = split ' ', $val;
return $val unless @vals == 21;
my $n = shift @vals;
return $val unless $n >= 2 and $n <= 10;
$val = '';
while ($n--) {
$val and $val .= ' ';
$val .= '(' . shift(@vals) . ',' . shift(@vals) . ')';
}
return $val;
}
#------------------------------------------------------------------------------
# Inverse print conversion for tone curve
sub ToneCurvePrintInv($)
{
my $val = shift;
my @vals = ($val =~ /\((\d+),(\d+)\)/g);
return undef unless @vals >= 4 and @vals <= 20 and not @vals & 0x01;
unshift @vals, scalar(@vals) / 2;
while (@vals < 21) { push @vals, 0 }
return join(' ',@vals);
}
#------------------------------------------------------------------------------
# Read/Write VRD edit data
# Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
# Returns: Reading: 1 on success; Writing: modified edit data, or undef if nothing changed
sub ProcessEditData($$$)
{
my ($et, $dirInfo, $tagTablePtr) = @_;
$et or return 1; # allow dummy access
my $dataPt = $$dirInfo{DataPt};
my $pos = $$dirInfo{DirStart};
my $dataPos = $$dirInfo{DataPos};
my $outfile = $$dirInfo{OutFile};
my $dirLen = $$dirInfo{DirLen};
my $verbose = $et->Options('Verbose');
my $out = $et->Options('TextOut');
my $oldChanged = $$et{CHANGED};
$et->VerboseDir('VRD Edit Data', 0, $dirLen) unless $outfile;
if ($outfile) {
# make a copy for editing in place
my $buff = substr($$dataPt, $pos, $dirLen);
$dataPt = $$dirInfo{DataPt} = \$buff;
$pos = $$dirInfo{DirStart} = 0;
}
my $dirEnd = $pos + $dirLen;
# loop through all records in the edit data
my ($recNum, $recLen, $err);
for ($recNum=0;; ++$recNum, $pos+=$recLen) {
if ($pos + 4 > $dirEnd) {
last if $pos == $dirEnd; # all done if we arrived at end
$recLen = 0; # just reset record size (will exit loop on test below)
} else {
$recLen = Get32u($dataPt, $pos);
# (DR4 has a null terminator)
last if $recLen == 0 and $pos + 4 == $dirEnd;
}
$pos += 4; # move to start of record
if ($pos + $recLen > $dirEnd) {
$et->Warn('Possibly corrupt CanonVRD Edit record');
$err = 1;
last;
}
my $saveRecLen = $recLen;
if ($verbose > 1 and not $outfile) {
printf $out "$$et{INDENT}CanonVRD Edit record ($recLen bytes at offset 0x%x)\n",
$pos + $dataPos;
if ($recNum and $verbose > 2) {
my %parms = (
Start => $pos,
Addr => $pos + $dataPos,
Out => $out,
Prefix => $$et{INDENT},
);
$parms{MaxLen} = $verbose == 3 ? 96 : 2048 if $verbose < 5;
HexDump($dataPt, $recLen, %parms);
}
}
# our edit information is the 0th record, so don't process the others
next if $recNum;
# process VRD edit information
my $subTablePtr = $tagTablePtr;
my $index;
my %subdirInfo = (
DataPt => $dataPt,
DataPos => $dataPos,
DirStart => $pos,
DirLen => $recLen,
OutFile => $outfile,
);
my $subStart = 0;
# loop through various sections of the VRD edit data
for ($index=0; ; ++$index) {
my $tagInfo = $$subTablePtr{$index} or last;
my $subLen;
my $maxLen = $recLen - $subStart;
if ($$tagInfo{Size}) {
$subLen = $$tagInfo{Size};
} elsif (defined $$tagInfo{Size}) {
# get size from int32u at $subStart
last unless $subStart + 4 <= $recLen;
$subLen = Get32u($dataPt, $subStart + $pos);
$subStart += 4; # skip the length word
} else {
$subLen = $maxLen;
}
$subLen > $maxLen and $subLen = $maxLen;
if ($subLen) {
my $subTable = GetTagTable($$tagInfo{SubDirectory}{TagTable});
my $subName = $$tagInfo{Name};
$subdirInfo{DirStart} = $subStart + $pos;
$subdirInfo{DirLen} = $subLen;
$subdirInfo{DirName} = $subName;
if ($outfile) {
# rewrite this section of the VRD edit information
$verbose and print $out " Rewriting Canon $subName\n";
my $newVal = $et->WriteDirectory(\%subdirInfo, $subTable);
if ($newVal) {
my $sizeDiff = length($newVal) - $subLen;
substr($$dataPt, $pos+$subStart, $subLen) = $newVal;
if ($sizeDiff) {
$subLen = length $newVal;
$recLen += $sizeDiff;
$dirEnd += $sizeDiff;
$dirLen += $sizeDiff;
}
}
} else {
$et->VPrint(0, "$$et{INDENT}$subName (SubDirectory) -->\n");
$et->VerboseDump($dataPt,
Start => $pos + $subStart,
Addr => $dataPos + $pos + $subStart,
Len => $subLen,
);
# extract tags from this section of the VRD edit information
$et->ProcessDirectory(\%subdirInfo, $subTable);
}
}
# next section starts at the end of this one
$subStart += $subLen;
}
if ($outfile and $saveRecLen ne $recLen) {
# update record length if necessary
Set32u($recLen, $dataPt, $pos - 4)
}
}
if ($outfile) {
return undef if $oldChanged == $$et{CHANGED};
return substr($$dataPt, $$dirInfo{DirStart}, $dirLen);
}
return $err ? 0 : 1;
}
#------------------------------------------------------------------------------
# Process VRD IHL data
# Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
# Returns: 1 on success
sub ProcessIHL($$$)
{
my ($et, $dirInfo, $tagTablePtr) = @_;
my $dataPt = $$dirInfo{DataPt};
my $dataPos = $$dirInfo{DataPos};
my $pos = $$dirInfo{DirStart};
my $dirLen = $$dirInfo{DirLen};
my $dirEnd = $pos + $dirLen;
$et->VerboseDir('VRD IHL', 0, $dirLen);
SetByteOrder('II'); # (make up your mind, Canon!)
while ($pos + 48 <= $dirEnd) {
my $hdr = substr($$dataPt, $pos, 48);
unless ($hdr =~ /^IHL Created Optional Item Data\0\0/) {
$et->Warn('Possibly corrupted VRD IHL data');
last;
}
my $tag = Get32u($dataPt, $pos + 36);
my $size = Get32u($dataPt, $pos + 40); # size of data in IHL record
my $next = Get32u($dataPt, $pos + 44); # size of complete IHL record
if ($size > $next or $pos + 48 + $next > $dirEnd) {
$et->Warn(sprintf('Bad size for VRD IHL tag 0x%.4x', $tag));
last;
}
$pos += 48;
$et->HandleTag($tagTablePtr, $tag, substr($$dataPt, $pos, $size),
DataPt => $dataPt,
DataPos => $dataPos,
Start => $pos,
Size => $size
);
$pos += $next;
}
return 1;
}
#------------------------------------------------------------------------------
# Process VRD IHL EXIF data
# Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
# Returns: 1 on success
sub ProcessIHLExif($$$)
{
my ($et, $dirInfo, $tagTablePtr) = @_;
$$et{DOC_NUM} = 1;
# the IHL-edited maker notes may look messed up, but the offsets should be OK
my $oldFix = $et->Options(FixBase => 0);
my $rtnVal = $et->ProcessTIFF($dirInfo, $tagTablePtr);
$et->Options(FixBase => $oldFix);
delete $$et{DOC_NUM};
return $rtnVal;
}
#------------------------------------------------------------------------------
# Wrap DR4 data with the VRD header/footer and edit record
# Inputs: 0) DR4 record
# Returns: VRD[Edit[DR4]] data
sub WrapDR4($)
{
my $val = shift;
my $n = length $val;
my $oldOrder = GetByteOrder();
SetByteOrder('MM');
$val = $blankHeader . "\xff\xff\0\xf7" . Set32u($n+8) . Set32u($n) .
$val . "\0\0\0\0" . $blankFooter;
# update the new VRD length in the header/footer
Set32u($n + 16, \$val, 0x18); # (extra 16 bytes for the edit record wrapper)
Set32u($n + 16, \$val, length($val) - 0x2c);
SetByteOrder($oldOrder);
return $val;
}
#------------------------------------------------------------------------------
# Read/Write DPP version 4 edit data or DR4 file
# Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
# Returns:
# Reading from memory (not RAF and not IsWriting): 1 on success
# Editing from memory (not RAF and IsWriting): modified edit data, or undef if nothing changed
# Reading file (RAF and not OutFile): 1 if a valid DR4 file, 0 if not
# Writing file (RAF and OutFile): 1 if valid DR4 file, 0 if not, -1 on write error
# (serves me right for not having a consistent interface for the various modes of operation)
sub ProcessDR4($$;$)
{
my ($et, $dirInfo, $tagTablePtr) = @_;
$et or return 1; # allow dummy access
my $dataPt = $$dirInfo{DataPt};
my $raf = $$dirInfo{RAF};
my $outfile = $$dirInfo{OutFile};
my $isWriting = $outfile || $$dirInfo{IsWriting};
my $dataPos = $$dirInfo{DataPos} || 0;
my $verbose = $et->Options('Verbose');
my $unknown = $et->Options('Unknown');
my ($pos, $dirLen, $numEntries, $err, $newTags);
# write CanonDR4 as a block if specified
if ($isWriting) {
my $nvHash;
my $newVal = $et->GetNewValue('CanonDR4', \$nvHash);
if ($newVal) {
$et->VPrint(0, " Writing CanonDR4 as a block\n");
$$et{DidCanonVRD} = 1; # set flag so we don't add this twice
++$$et{CHANGED};
if ($outfile) {
Write($$dirInfo{OutFile}, $newVal) or return -1;
return 1;
} else {
return $newVal;
}
} elsif (not $dataPt and ($nvHash or $$et{DEL_GROUP}{CanonVRD})) {
$et->Error("Can't delete all CanonDR4 information from a DR4 file");
return 1;
}
}
if ($dataPt) {
$pos = $$dirInfo{DirStart} || 0;
$dirLen = $$dirInfo{DirLen} || length($$dataPt) - $pos;
} else {
# load DR4 file into memory
my $buff;
$raf->Read($buff, 8) == 8 and $buff eq "IIII\x04\0\x04\0" or return 0;
$et->SetFileType();
$raf->Seek(0, 2) or return $err = 1;
$dirLen = $raf->Tell();
$raf->Seek(0, 0) or return $err = 1;
$raf->Read($buff, $dirLen) == $dirLen or $err = 1;
$err and $et->Warn('Error reading DR4 file'), return 1;
$tagTablePtr = GetTagTable('Image::ExifTool::CanonVRD::DR4');
$dataPt = \$buff;
$pos = 0;
}
my $dirEnd = $pos + $dirLen;
if (($$et{TAGS_FROM_FILE} and
not $$et{EXCL_TAG_LOOKUP}{canondr4}) or $$et{REQ_TAG_LOOKUP}{canondr4})
{
# extract CanonDR4 block if copying tags, or if requested
$et->FoundTag('CanonDR4', substr($$dataPt, $pos, $dirLen));
}
# version 4 header is 32 bytes (int32u[8])
if ($dirLen < 32) {
$err = 1;
} else {
SetByteOrder(substr($$dataPt, $pos, 2)) or $err = 1;
# process the DR4 header
my %hdrInfo = (
DataPt => $dataPt,
DirStart => $pos,
DirLen => 32,
DirName => 'DR4Header',
);
my $hdrTable = GetTagTable('Image::ExifTool::CanonVRD::DR4Header');
if ($outfile) {
my $hdr = $et->WriteDirectory(\%hdrInfo, $hdrTable);
substr($$dataPt, $pos, 32) = $hdr if $hdr and length $hdr == 32;
} else {
$et->VerboseDir('DR4Header', undef, 32);
$et->ProcessDirectory(\%hdrInfo, $hdrTable);
}
# number of entries in the DR4 directory
$numEntries = Get32u($dataPt, $pos + 28);
$err = 1 if $dirLen < 36 + 28 * $numEntries;
}
$err and $et->Warn('Invalid DR4 directory'), return $outfile ? undef : 0;
if ($outfile) {
$newTags = $et->GetNewTagInfoHash($tagTablePtr);
} else {
$et->VerboseDir('DR4', $numEntries, $dirLen);
}
my $index;
for ($index=0; $index<$numEntries; ++$index) {
my ($val, @flg, $i);
my $entry = $pos + 36 + 28 * $index;
last if $entry + 28 > $dirEnd;
my $tag = Get32u($dataPt, $entry);
my $fmt = Get32u($dataPt, $entry + 4);
$flg[0] = Get32u($dataPt, $entry + 8);
$flg[1] = Get32u($dataPt, $entry + 12);
$flg[2] = Get32u($dataPt, $entry + 16);
my $off = Get32u($dataPt, $entry + 20) + $pos;
my $len = Get32u($dataPt, $entry + 24);
next if $off + $len >= $dirEnd;
my $format = $vrdFormat{$fmt};
if (not $format) {
$val = unpack 'H*', substr($$dataPt, $off, $len);
$format = 'undef';
} elsif ($format eq 'double' and $len eq 8) {
# avoid teeny weeny values
$val = ReadValue($dataPt, $off, $format, undef, $len);
$val = 0 if abs($val) < 1e-100;
}
if ($outfile) {
# write (binary data) subdirectory if it exists
my $tagInfo = $et->GetTagInfo($tagTablePtr, $tag);
if ($tagInfo and $$tagInfo{SubDirectory}) {
my %subdirInfo = (
DataPt => $dataPt,
DirStart => $off,
DirLen => $len,
DirName => $$tagInfo{Name},
);
my $subTablePtr = GetTagTable($$tagInfo{SubDirectory}{TagTable});
my $saveChanged = $$et{CHANGED};
my $dat = $et->WriteDirectory(\%subdirInfo, $subTablePtr);
if (defined $dat and length($dat) == $len) {
substr($$dataPt, $off, $len) = $dat;
} else {
$$et{CHANGED} = $saveChanged; # didn't change anything after all
}
} else {
# loop through main tag and flags (don't yet consider flag 2)
for ($i=-1; $i<2; ++$i) {
$tagInfo = $$newTags{$i>=0 ? sprintf('0x%x.%d',$tag,$i) : $tag};
next unless $tagInfo;
if ($i >= 0) {
$off = $entry + 8 + 4 * $i;
$format = 'int32u';
$len = 4;
undef $val;
}
$val = ReadValue($dataPt, $off, $format, undef, $len) unless defined $val;
my $nvHash;
my $newVal = $et->GetNewValue($tagInfo, \$nvHash);
if ($et->IsOverwriting($nvHash, $val) and defined $newVal) {
my $count = int($len / Image::ExifTool::FormatSize($format));
my $rtnVal = WriteValue($newVal, $format, $count, $dataPt, $off);
if (defined $rtnVal) {
$et->VerboseValue("- CanonVRD:$$tagInfo{Name}", $val);
$et->VerboseValue("+ CanonVRD:$$tagInfo{Name}", $newVal);
++$$et{CHANGED};
}
}
}
}
next;
}
$et->HandleTag($tagTablePtr, $tag, $val,
DataPt => $dataPt,
DataPos => $dataPos,
Start => $off,
Size => $len,
Index => $index,
Format => $format,
# $flg[0] is on/off flag
# $flg[1] "is default" flag?
# $flg[2] changed to 0 when some unsharp mask settings were changed
Extra => ", fmt=$fmt flags=" . join(',', @flg),
);
foreach $i (0..2) {
my $flagID = sprintf('0x%x.%d', $tag, $i);
$et->HandleTag($tagTablePtr, $flagID, $flg[$i]) if $$tagTablePtr{$flagID};
}
}
return 1 unless $isWriting;
return substr($$dataPt, $pos, $dirLen) unless $raf;
return 1 if Write($outfile, substr($$dataPt, $pos, $dirLen));
return -1;
}
#------------------------------------------------------------------------------
# Read/write Canon VRD file
# Inputs: 0) ExifTool object reference, 1) dirInfo reference
# Returns: 1 if this was a Canon VRD file, 0 otherwise, -1 on write error
sub ProcessVRD($$)
{
my ($et, $dirInfo) = @_;
my $raf = $$dirInfo{RAF};
my $buff;
my $num = $raf->Read($buff, 0x1c);
# initialize write directories if necessary
$et->InitWriteDirs(\%vrdMap, 'XMP') if $$dirInfo{OutFile};
if (not $num and $$dirInfo{OutFile}) {
# create new VRD file from scratch
my $newVal = $et->GetNewValue('CanonVRD');
if ($newVal) {
$et->VPrint(0, " Writing CanonVRD as a block\n");
Write($$dirInfo{OutFile}, $newVal) or return -1;
$$et{DidCanonVRD} = 1;
++$$et{CHANGED};
} else {
# allow VRD to be created from individual tags
if ($$et{ADD_DIRS}{CanonVRD}) {
my $newVal = '';
if (ProcessCanonVRD($et, { OutFile => \$newVal }) > 0) {
Write($$dirInfo{OutFile}, $newVal) or return -1;
++$$et{CHANGED};
return 1;
}
}
$et->Error('No CanonVRD information to write');
}
} else {
$num == 0x1c or return 0;
$buff =~ /^CANON OPTIONAL DATA\0/ or return 0;
$et->SetFileType();
$$dirInfo{DirName} = 'CanonVRD'; # set directory name for verbose output
my $result = ProcessCanonVRD($et, $dirInfo);
return $result if $result < 0;
$result or $et->Warn('Format error in VRD file');
}
return 1;
}
#------------------------------------------------------------------------------
# Write VRD data record as a block
# Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
# Returns: VRD data block (may be empty if no VRD data)
# Notes: Increments ExifTool CHANGED flag if changed
sub WriteCanonVRD($$;$)
{
my ($et, $dirInfo, $tagTablePtr) = @_;
$et or return 1; # allow dummy access
my $nvHash = $et->GetNewValueHash($Image::ExifTool::Extra{CanonVRD});
my $val = $et->GetNewValue($nvHash);
$val = '' unless defined $val;
return undef unless $et->IsOverwriting($nvHash, $val);
++$$et{CHANGED};
return $val;
}
#------------------------------------------------------------------------------
# Read/write CanonVRD information (from VRD file or VRD trailer)
# Inputs: 0) ExifTool object reference, 1) dirInfo reference
# Returns: 1 on success, 0 not valid VRD, or -1 error writing
# - updates DataPos to point to start of CanonVRD information
# - updates DirLen to existing trailer length
sub ProcessCanonVRD($$;$)
{
my ($et, $dirInfo, $tagTablePtr) = @_;
my $raf = $$dirInfo{RAF};
my $offset = $$dirInfo{Offset} || 0;
my $outfile = $$dirInfo{OutFile};
my $dataPt = $$dirInfo{DataPt};
my $verbose = $et->Options('Verbose');
my $out = $et->Options('TextOut');
my ($buff, $created, $err, $blockLen, $blockType, %didDir, $fromFile);
#
# The CanonVRD trailer has a 0x1c-byte header and a 0x40-byte footer,
# each beginning with "CANON OPTIONAL DATA\0" and containing an int32u
# giving the size of the contained data (at byte 0x18 and 0x14 respectively)
#
if ($raf) {
$fromFile = 1;
} else {
unless ($dataPt) {
return 1 unless $outfile;
# create blank VRD data from scratch
my $blank = $blankHeader . $blankFooter;
$dataPt = \$blank;
$verbose and print $out " Creating CanonVRD trailer\n";
$created = 1;
}
$raf = new File::RandomAccess($dataPt);
}
# read and validate the footer
$raf->Seek(-0x40-$offset, 2) or return 0;
$raf->Read($buff, 0x40) == 0x40 or return 0;
$buff =~ /^CANON OPTIONAL DATA\0(.{4})/s or return 0;
my $dirLen = unpack('N', $1) + 0x5c; # size including header+footer
# read and validate the header
unless ($dirLen < 0x80000000 and
$raf->Seek(-$dirLen, 1) and
$raf->Read($buff, 0x1c) == 0x1c and
$buff =~ /^CANON OPTIONAL DATA\0/ and
$raf->Seek(-0x1c, 1))
{
$et->Warn('Bad CanonVRD trailer');
return 0;
}
# set variables returned in dirInfo hash
$$dirInfo{DataPos} = $raf->Tell();
$$dirInfo{DirLen} = $dirLen;
if ($outfile and ref $outfile eq 'SCALAR' and not length $$outfile) {
# write directly to outfile to avoid duplicating data in memory
$$outfile = $$dataPt unless $fromFile;
# TRICKY! -- copy to outfile memory buffer and edit in place
# (so we must disable all Write() calls for this case)
$dataPt = $outfile;
}
if ($fromFile) {
$dataPt = \$buff unless $dataPt;
# read VRD data into memory if necessary
unless ($raf->Read($$dataPt, $dirLen) == $dirLen) {
$$dataPt = '' if $outfile and $outfile eq $dataPt;
$et->Warn('Error reading CanonVRD data');
return 0;
}
}
# exit quickly if writing and no CanonVRD tags are being edited
if ($outfile and not exists $$et{EDIT_DIRS}{CanonVRD}) {
print $out "$$et{INDENT} [nothing changed]\n" if $verbose;
return 1 if $outfile eq $dataPt;
return Write($outfile, $$dataPt) ? 1 : -1;
}
my $vrdType = 'VRD';
if ($outfile) {
$verbose and not $created and print $out " Rewriting CanonVRD trailer\n";
# delete CanonVRD information if specified
my $doDel = $$et{DEL_GROUP}{CanonVRD};
unless ($doDel) {
$doDel = 1 if $$et{DEL_GROUP}{Trailer} and $$et{FILE_TYPE} ne 'VRD';
unless ($doDel) {
# also delete if writing as a block (will get added back again later)
if ($$et{NEW_VALUE}{$Image::ExifTool::Extra{CanonVRD}}) {
# delete if this isn't version 4
$doDel = 1 unless $$dataPt =~ /^.{28}\xff\xff\0\xf7/s;
}
if ($$et{NEW_VALUE}{$Image::ExifTool::Extra{CanonDR4}} and not $doDel) {
# delete if this is version 4
$doDel = 1 if $$dataPt =~ /^.{28}\xff\xff\0\xf7/s;
}
}
}
if ($doDel) {
if ($$et{FILE_TYPE} eq 'VRD') {
my $newVal = $et->GetNewValue('CanonVRD');
if ($newVal) {
$verbose and print $out " Writing CanonVRD as a block\n";
if ($outfile eq $dataPt) {
$$outfile = $newVal;
} else {
Write($outfile, $newVal) or return -1;
}
$$et{DidCanonVRD} = 1;
++$$et{CHANGED};
} else {
$et->Error("Can't delete all CanonVRD information from a VRD file");
}
} else {
$verbose and print $out " Deleting CanonVRD trailer\n";
$$outfile = '' if $outfile eq $dataPt;
++$$et{CHANGED};
}
return 1;
}
# write now and return if CanonVRD was set as a block
my $val = $et->GetNewValue('CanonVRD');
unless ($val) {
$val = $et->GetNewValue('CanonDR4');
$vrdType = 'DR4' if $val;
}
if ($val) {
$verbose and print $out " Writing Canon$vrdType as a block\n";
# must wrap DR4 data with the VRD header/footer and edit record
$val = WrapDR4($val) if $vrdType eq 'DR4';
if ($outfile eq $dataPt) {
$$outfile = $val;
} else {
Write($outfile, $val) or return -1;
}
$$et{DidCanonVRD} = 1;
++$$et{CHANGED};
return 1;
}
} elsif ($verbose or $$et{HTML_DUMP}) {
$et->DumpTrailer($dirInfo) if $$dirInfo{RAF};
}
$tagTablePtr = GetTagTable('Image::ExifTool::CanonVRD::Main');
# validate VRD trailer and get position and length of edit record
SetByteOrder('MM'); # VRD header/footer is big-endian
my $pos = 0x1c; # start at end of header
# loop through the VRD blocks
for (;;) {
my $end = $dirLen - 0x40; # end of last VRD block (and start of footer)
if ($pos + 8 > $end) {
last if $pos == $end;
$blockLen = $end; # mark as invalid
} else {
$blockType = Get32u($dataPt, $pos);
$blockLen = Get32u($dataPt, $pos + 4);
}
$vrdType = 'DR4' if $blockType eq 0xffff00f7;
$pos += 8; # move to start of block
if ($pos + $blockLen > $end) {
$et->Warn('Possibly corrupt CanonVRD block');
last;
}
if ($verbose > 1 and not $outfile) {
printf $out " CanonVRD block 0x%.8x ($blockLen bytes at offset 0x%x)\n",
$blockType, $pos + $$dirInfo{DataPos};
if ($verbose > 2) {
my %parms = (
Start => $pos,
Addr => $pos + $$dirInfo{DataPos},
Out => $out,
Prefix => $$et{INDENT},
);
$parms{MaxLen} = $verbose == 3 ? 96 : 2048 if $verbose < 5;
HexDump($dataPt, $blockLen, %parms);
}
}
my $tagInfo = $$tagTablePtr{$blockType};
unless ($tagInfo) {
unless ($et->Options('Unknown')) {
$pos += $blockLen; # step to next block
next;
}
my $name = sprintf('CanonVRD_0x%.8x', $blockType);
my $desc = $name;
$desc =~ tr/_/ /;
$tagInfo = {
Name => $name,
Description => $desc,
Binary => 1,
};
AddTagToTable($tagTablePtr, $blockType, $tagInfo);
}
if ($$tagInfo{SubDirectory}) {
my $subTablePtr = GetTagTable($$tagInfo{SubDirectory}{TagTable});
my %subdirInfo = (
DataPt => $dataPt,
DataLen => length $$dataPt,
DataPos => $$dirInfo{DataPos},
DirStart => $pos,
DirLen => $blockLen,
DirName => $$tagInfo{Name},
Parent => 'CanonVRD',
OutFile => $outfile,
);
if ($outfile) {
# set flag indicating we did this directory
$didDir{$$tagInfo{Name}} = 1;
my ($dat, $diff);
if ($$et{NEW_VALUE}{$tagInfo}) {
# write as a block
$et->VPrint(0, "Writing $$tagInfo{Name} as a block\n");
$dat = $et->GetNewValue($tagInfo);
$dat = '' unless defined $dat;
++$$et{CHANGED};
} else {
$dat = $et->WriteDirectory(\%subdirInfo, $subTablePtr);
}
# update data with new directory
if (defined $dat) {
if (length $dat or $$et{FILE_TYPE} !~ /^(CRW|VRD)$/) {
# replace with new block (updating the block length word)
substr($$dataPt, $pos-4, $blockLen+4) = Set32u(length $dat) . $dat;
} else {
# remove block totally (CRW/VRD files only)
substr($$dataPt, $pos-8, $blockLen+8) = '';
}
# make necessary adjustments if block changes length
if (($diff = length($$dataPt) - $dirLen) != 0) {
$pos += $diff;
$dirLen += $diff;
# update the new VRD length in the header/footer
Set32u($dirLen - 0x5c, $dataPt, 0x18);
Set32u($dirLen - 0x5c, $dataPt, $dirLen - 0x2c);
}
}
} else {
# extract as a block if requested
$et->ProcessDirectory(\%subdirInfo, $subTablePtr);
}
} else {
$et->HandleTag($tagTablePtr, $blockType, substr($$dataPt, $pos, $blockLen));
}
$pos += $blockLen; # step to next block
}
if ($outfile) {
# create XMP block if necessary (CRW/VRD files only)
if ($$et{ADD_DIRS}{CanonVRD} and not $didDir{XMP}) {
my $subTablePtr = GetTagTable('Image::ExifTool::XMP::Main');
my $dat = $et->WriteDirectory({ Parent => 'CanonVRD' }, $subTablePtr);
if ($dat) {
my $blockLen = length $dat;
substr($$dataPt, -0x40, 0) = Set32u(0xffff00f6) . Set32u(length $dat) . $dat;
$dirLen = length $$dataPt;
# update the new VRD length in the header/footer
Set32u($dirLen - 0x5c, $dataPt, 0x18);
Set32u($dirLen - 0x5c, $dataPt, $dirLen - 0x2c);
}
}
# write CanonVRD trailer unless it is empty
if (length $$dataPt) {
Write($outfile, $$dataPt) or $err = 1 unless $outfile eq $dataPt;
} else {
$verbose and print $out " Deleting CanonVRD trailer\n";
}
} elsif ($vrdType eq 'VRD' and (($$et{TAGS_FROM_FILE} and
not $$et{EXCL_TAG_LOOKUP}{canonvrd}) or $$et{REQ_TAG_LOOKUP}{canonvrd}))
{
# extract CanonVRD block if copying tags, or if requested (and not DR4 info)
$et->FoundTag('CanonVRD', $buff);
}
undef $buff;
return $err ? -1 : 1;
}
1; # end
__END__
=head1 NAME
Image::ExifTool::CanonVRD - Read/write Canon VRD and DR4 information
=head1 SYNOPSIS
This module is used by Image::ExifTool
=head1 DESCRIPTION
This module contains definitions required by Image::ExifTool to read and
write VRD and DR4 Recipe Data information as written by the Canon Digital
Photo Professional software. This information is written to VRD and DR4
files, and as a trailer in JPEG, CRW, CR2 and TIFF images.
=head1 AUTHOR
Copyright 2003-2016, Phil Harvey (phil at owl.phy.queensu.ca)
This library is free software; you can redistribute it and/or modify it
under the same terms as Perl itself.
=head1 ACKNOWLEDGEMENTS
Thanks to Bogdan and Gert Kello for decoding some tags.
=head1 SEE ALSO
L<Image::ExifTool::TagNames/CanonVRD Tags>,
L<Image::ExifTool(3pm)|Image::ExifTool>
=cut