Ignore:
Timestamp:
2021-02-26T19:39:51+13:00 (3 years ago)
Author:
anupama
Message:

Committing the improvements to EmbeddedMetaPlugin's processing of Keywords vs other metadata fields. Keywords were literally stored as arrays of words rather than phrases in PDFs (at least in Diego's sample PDF), whereas other meta fields like Subjects and Creators stored them as arrays of phrases. To get both to work, Kathy updated EXIF to a newer version, to retrieve the actual EXIF values stored in the PDF. And Kathy and Dr Bainbridge came up with a new option that I added called apply_join_before_split_to_metafields that's a regex which can list the metadata fields to apply the join_before_split to and whcih previously always got applied to all metadata fields. Now it's applied to any *Keywords metafields by default, as that's the metafield we have experience of that behaves differently to the others, as it stores by word instead of phrases. Tested on Diego's sample PDF. Diego has double-checked it to works on his sample PDF too, setting the split char to ; and turning on the join_before_split and leaving apply_join_before_split_to_metafields at its default of .*Keywords. File changes are strings.properties for the tooltip, the plugin introducing the option and working with it and Kathy's EXIF updates affecting cpan/File and cpan/Image.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • main/trunk/greenstone2/perllib/cpan/Image/ExifTool/CanonVRD.pm

    r24107 r34921  
    22# File:         CanonVRD.pm
    33#
    4 # Description:  Read/write Canon VRD information
     4# Description:  Read/write Canon VRD and DR4 information
    55#
    66# Revisions:    2006/10/30 - P. Harvey Created
     
    1010#               2010/06/18 - PH Support variable-length CustomPictureStyle data
    1111#               2010/09/14 - PH Added r/w support for XMP in VRD
     12#               2015/05/16 - PH Added DR4 support (DPP 4.1.50.0)
     13#               2018/03/13 - PH Update to DPP 4.8.20
    1214#
    1315# References:   1) Bogdan private communication (Canon DPP v3.4.1.1)
    14 #               2) Gert Kello private communiation (DPP 3.8)
     16#               2) Gert Kello private communication (DPP 3.8)
    1517#------------------------------------------------------------------------------
    1618
     
    2022use vars qw($VERSION);
    2123use Image::ExifTool qw(:DataAccess :Utils);
    22 
    23 $VERSION = '1.12';
     24use Image::ExifTool::Canon;
     25
     26$VERSION = '1.33';
    2427
    2528sub ProcessCanonVRD($$;$);
     
    2831sub ProcessIHL($$$);
    2932sub ProcessIHLExif($$$);
     33sub ProcessDR4($$;$);
     34sub SortDR4($$);
    3035
    3136# map for adding directories to VRD
     
    3540);
    3641
    37 my %noYes = ( 0 => 'No', 1 => 'Yes' );
     42my %noYes = (
     43    PrintConvColumns => 2,
     44    PrintConv => { 0 => 'No', 1 => 'Yes' },
     45);
     46
     47# DR4 format codes
     48my %vrdFormat = (
     49    1 => 'int32u',
     50    2 => 'string',
     51    8 => 'int32u',
     52    9 => 'int32s',
     53    13 => 'double',
     54    33 => 'int32u', # (array)
     55    38 => 'double', # (array)
     56    # 254 => 'undef', ?
     57    255 => 'undef',
     58);
     59
     60# empty VRD header/footer for creating VRD from scratch
     61my $blankHeader = "CANON OPTIONAL DATA\0\0\x01\0\0\0\0\0\0";
     62my $blankFooter = "CANON OPTIONAL DATA\0" . ("\0" x 42) . "\xff\xd9";
    3863
    3964# main tag table blocks in CanonVRD trailer (ref PH)
     
    4368    NOTES => q{
    4469        Canon Digital Photo Professional writes VRD (Recipe Data) information as a
    45         trailer record to JPEG, TIFF, CRW and CR2 images, or as a stand-alone VRD
    46         file.  The tags listed below represent information found in this record.
    47         The complete VRD data record may be accessed as a block using the Extra
    48         'CanonVRD' tag, but this tag is not extracted or copied unless specified
    49         explicitly.
     70        trailer record to JPEG, TIFF, CRW and CR2 images, or as stand-alone VRD or
     71        DR4 files.  The tags listed below represent information found in these
     72        records.  The complete VRD/DR4 data record may be accessed as a block using
     73        the Extra 'CanonVRD' or 'CanonDR4' tag, but this tag is not extracted or
     74        copied unless specified explicitly.
    5075    },
    5176    0xffff00f4 => {
     
    6590            TagTable => 'Image::ExifTool::XMP::Main',
    6691        },
     92    },
     93    0xffff00f7 => {
     94        Name => 'Edit4Data',
     95        SubDirectory => { TagTable => 'Image::ExifTool::CanonVRD::Edit4' },
    6796    },
    6897);
     
    81110    1 => {
    82111        Name => 'VRDStampTool',
    83         # (size is variable, and obtained from int32u at end of last directory)
    84112        Size => 0,      # size is variable, and obtained from int32u at directory start
    85113        SubDirectory => { TagTable => 'Image::ExifTool::CanonVRD::StampTool' },
     
    89117        Size => undef,  # size is the remaining edit data
    90118        SubDirectory => { TagTable => 'Image::ExifTool::CanonVRD::Ver2' },
     119    },
     120);
     121
     122# Canon DPP version 4 edit information
     123%Image::ExifTool::CanonVRD::Edit4 = (
     124    WRITE_PROC => \&ProcessEditData,
     125    PROCESS_PROC => \&ProcessEditData,
     126    VARS => { ID_LABEL => 'Index' }, # change TagID label in documentation
     127    NOTES => 'Canon DPP version 4 edit information.',
     128    0 => {
     129        Name => 'DR4',
     130        Size => undef,  # size is the remaining edit data
     131        SubDirectory => { TagTable => 'Image::ExifTool::CanonVRD::DR4' },
    91132    },
    92133);
     
    110151            Name => 'IHL_EXIF',
    111152            Notes => q{
    112                 extracted as a block if the Unknown option is used, or processed as the
    113                 first sub-document with the ExtractEmbedded option
     153                extracted as a block if the L<Unknown|../ExifTool.html#Unknown> option is used, or processed as the
     154                first sub-document with the L<ExtractEmbedded|../ExifTool.html#ExtractEmbedded> option
    114155            },
    115156            Binary => 1,
     
    122163        # (same size as the PreviewImage with DPP 3.0.2.6)
    123164        Name => 'ThumbnailImage',
     165        Groups => { 2 => 'Preview' },
    124166        Binary => 1,
    125167    },
    126168    4 => {
    127169        Name => 'PreviewImage',
     170        Groups => { 2 => 'Preview' },
    128171        Binary => 1,
    129172    },
    130173    5 => {
    131174        Name => 'RawCodecVersion',
    132         ValueConv => '$val =~ s/\0.*//; $val',  # truncate string at null
     175        ValueConv => '$val =~ s/\0.*//s; $val',  # truncate string at null
    133176    },
    134177    6 => {
     
    157200        DataMember => 'VRDVersion',
    158201        RawConv => '$$self{VRDVersion} = $val',
    159         PrintConv => 'sprintf("%.2f", $val / 100)',
    160     },
    161     # 0x006 related somehow to RGB levels
    162     0x008 => {
    163         Name => 'WBAdjRGBLevels',
    164         Format => 'int16u[3]',
     202        PrintConv => '$val =~ s/^(\d)(\d*)(\d)$/$1.$2.$3/; $val',
     203    },
     204    0x006 => {
     205        Name => 'WBAdjRGGBLevels',
     206        Format => 'int16u[4]',
    165207    },
    166208    0x018 => {
    167209        Name => 'WhiteBalanceAdj',
    168210        Format => 'int16u',
     211        PrintConvColumns => 2,
    169212        PrintConv => {
    170213            0 => 'Auto',
     
    185228    },
    186229    # 0x01c similar to 0x006
    187     # 0x01e similar to 0x008
    188230    0x024 => {
    189231        Name => 'WBFineTuneActive',
    190232        Format => 'int16u',
    191         PrintConv => \%noYes,
     233        %noYes,
    192234    },
    193235    0x028 => {
     
    227269        Name => 'ToneCurveProperty',
    228270        Format => 'int16u',
     271        PrintConvColumns => 2,
    229272        PrintConv => {
    230273            0 => 'Shot Settings',
     
    253296        Name => 'ToneCurveActive',
    254297        Format => 'int16u',
    255         PrintConv => \%noYes,
     298        %noYes,
    256299    },
    257300    0x113 => {
     
    338381        Name => 'CropActive',
    339382        Format => 'int16u',
    340         PrintConv => \%noYes,
     383        %noYes,
    341384    },
    342385    0x246 => {
     
    377420            10 => '5:4',
    378421            11 => '1:1',
     422            12 => 'Circle',
     423            65535 => 'Custom',
    379424        },
    380425    },
     
    422467        },
    423468    },
    424     # (VRD 1.0 edit data ends here -- 0x272 bytes)
     469    # (VRD 1.0.0 edit data ends here -- 0x272 bytes)
    425470);
    426471
     
    443488    FIRST_ENTRY => 0,
    444489    FORMAT => 'int16s',
    445     DATAMEMBER => [ 0x58 ], # (required for writable var-format tags)
     490    DATAMEMBER => [ 0x58, 0xdc, 0xdf, 0xe0 ], # (required for DataMember and var-format tags)
     491    IS_SUBDIR => [ 0xe0 ],
    446492    GROUPS => { 2 => 'Image' },
    447493    NOTES => 'Tags added in DPP version 2.0 and later.',
    448494    0x02 => {
    449495        Name => 'PictureStyle',
     496        PrintConvColumns => 2,
    450497        PrintConv => {
    451498            0 => 'Standard',
     
    459506        },
    460507    },
    461     0x03 => {
    462         Name => 'IsCustomPictureStyle',
    463         PrintConv => \%noYes,
    464     },
     508    0x03 => { Name => 'IsCustomPictureStyle', %noYes },
     509    # 0x08: 3
     510    # 0x09: 4095
     511    # 0x0a: 0
     512    # 0x0b: 4095
     513    # 0x0c: 0
    465514    0x0d => 'StandardRawColorTone',
    466515    0x0e => 'StandardRawSaturation',
    467516    0x0f => 'StandardRawContrast',
    468     0x10 => {
    469         Name => 'StandardRawLinear',
    470         PrintConv => \%noYes,
    471     },
     517    0x10 => { Name => 'StandardRawLinear', %noYes },
    472518    0x11 => 'StandardRawSharpness',
    473519    0x12 => 'StandardRawHighlightPoint',
     
    478524    0x17 => 'PortraitRawSaturation',
    479525    0x18 => 'PortraitRawContrast',
    480     0x19 => {
    481         Name => 'PortraitRawLinear',
    482         PrintConv => \%noYes,
    483     },
     526    0x19 => { Name => 'PortraitRawLinear', %noYes },
    484527    0x1a => 'PortraitRawSharpness',
    485528    0x1b => 'PortraitRawHighlightPoint',
     
    490533    0x20 => 'LandscapeRawSaturation',
    491534    0x21 => 'LandscapeRawContrast',
    492     0x22 => {
    493         Name => 'LandscapeRawLinear',
    494         PrintConv => \%noYes,
    495     },
     535    0x22 => { Name => 'LandscapeRawLinear', %noYes },
    496536    0x23 => 'LandscapeRawSharpness',
    497537    0x24 => 'LandscapeRawHighlightPoint',
     
    502542    0x29 => 'NeutralRawSaturation',
    503543    0x2a => 'NeutralRawContrast',
    504     0x2b => {
    505         Name => 'NeutralRawLinear',
    506         PrintConv => \%noYes,
    507     },
     544    0x2b => { Name => 'NeutralRawLinear', %noYes },
    508545    0x2c => 'NeutralRawSharpness',
    509546    0x2d => 'NeutralRawHighlightPoint',
     
    514551    0x32 => 'FaithfulRawSaturation',
    515552    0x33 => 'FaithfulRawContrast',
    516     0x34 => {
    517         Name => 'FaithfulRawLinear',
    518         PrintConv => \%noYes,
    519     },
     553    0x34 => { Name => 'FaithfulRawLinear', %noYes },
    520554    0x35 => 'FaithfulRawSharpness',
    521555    0x36 => 'FaithfulRawHighlightPoint',
     
    544578    },
    545579    0x3c => 'MonochromeContrast',
    546     0x3d => {
    547         Name => 'MonochromeLinear',
    548         PrintConv => \%noYes,
    549     },
     580    0x3d => { Name => 'MonochromeLinear', %noYes },
    550581    0x3e => 'MonochromeSharpness',
    551582    0x3f => 'MonochromeRawHighlightPoint',
     
    554585    0x42 => 'MonochromeOutputShadowPoint',
    555586    0x45 => { Name => 'UnknownContrast',            Unknown => 1 },
    556     0x46 => {
    557         Name => 'UnknownLinear',
    558         Unknown => 1,
    559         PrintConv => \%noYes,
    560     },
     587    0x46 => { Name => 'UnknownLinear', %noYes,      Unknown => 1 },
    561588    0x47 => { Name => 'UnknownSharpness',           Unknown => 1 },
    562589    0x48 => { Name => 'UnknownRawHighlightPoint',   Unknown => 1 },
     
    564591    0x4a => { Name => 'UnknownOutputHighlightPoint',Unknown => 1 },
    565592    0x4b => { Name => 'UnknownOutputShadowPoint',   Unknown => 1 },
     593    0x4c => 'CustomColorTone',
     594    0x4d => 'CustomSaturation',
    566595    0x4e => 'CustomContrast',
    567     0x4f => {
    568         Name => 'CustomLinear',
    569         PrintConv => \%noYes,
    570     },
     596    0x4f => { Name => 'CustomLinear', %noYes },
    571597    0x50 => 'CustomSharpness',
    572598    0x51 => 'CustomRawHighlightPoint',
     
    582608        RawConv => 'length($val) == 2 ? undef : $val', # ignore if no data
    583609    },
    584     # (VRD 2.0 edit data ends here: 178 bytes, index 0x59)
     610    # (VRD 2.0.0 edit data ends here: 178 bytes, index 0x59)
    585611    0x5e => [{
    586612        Name => 'ChrominanceNoiseReduction',
    587613        Condition => '$$self{VRDVersion} < 330',
    588         Notes => 'VRDVersion prior to 3.30',
     614        Notes => 'VRDVersion prior to 3.3.0',
    589615        PrintConv => {
    590616            0   => 'Off',
     
    594620    },{ #1
    595621        Name => 'ChrominanceNoiseReduction',
    596         Notes => 'VRDVersion 3.30 or later',
     622        Notes => 'VRDVersion 3.3.0 or later',
    597623        PrintHex => 1,
    598624        PrintConvColumns => 4,
     
    624650        Name => 'LuminanceNoiseReduction',
    625651        Condition => '$$self{VRDVersion} < 330',
    626         Notes => 'VRDVersion prior to 3.30',
     652        Notes => 'VRDVersion prior to 3.3.0',
    627653        PrintConv => {
    628654            0   => 'Off',
     
    632658    },{ #1
    633659        Name => 'LuminanceNoiseReduction',
    634         Notes => 'VRDVersion 3.30 or later',
     660        Notes => 'VRDVersion 3.3.0 or later',
    635661        PrintHex => 1,
    636662        PrintConvColumns => 4,
     
    662688        Name => 'ChrominanceNR_TIFF_JPEG',
    663689        Condition => '$$self{VRDVersion} < 330',
    664         Notes => 'VRDVersion prior to 3.30',
     690        Notes => 'VRDVersion prior to 3.3.0',
    665691        PrintConv => {
    666692            0   => 'Off',
     
    670696    },{ #1
    671697        Name => 'ChrominanceNR_TIFF_JPEG',
    672         Notes => 'VRDVersion 3.30 or later',
     698        Notes => 'VRDVersion 3.3.0 or later',
    673699        PrintHex => 1,
    674700        PrintConvColumns => 4,
     
    697723        },
    698724    }],
    699     # (VRD 3.0 edit data ends here: 196 bytes, index 0x62)
    700     0x62 => {
    701         Name => 'ChromaticAberrationOn',
    702         ValueConv => \%noYes,
    703     },
    704     0x63 => {
    705         Name => 'DistortionCorrectionOn',
    706         ValueConv => \%noYes,
    707     },
    708     0x64 => {
    709         Name => 'PeripheralIlluminationOn',
    710         ValueConv => \%noYes,
    711     },
    712     0x65 => {
    713         Name => 'ColorBlur',
    714         ValueConv => \%noYes,
    715     },
     725    # 0x61: 1
     726    # (VRD 3.0.0 edit data ends here: 196 bytes, index 0x62)
     727    0x62 => { Name => 'ChromaticAberrationOn',      %noYes },
     728    0x63 => { Name => 'DistortionCorrectionOn',     %noYes },
     729    0x64 => { Name => 'PeripheralIlluminationOn',   %noYes },
     730    0x65 => { Name => 'ColorBlur',                  %noYes },
    716731    0x66 => {
    717732        Name => 'ChromaticAberration',
     
    752767        ValueConvInv => 'int($val * 10 + 0.5)',
    753768    },
    754     # (VRD 3.4 edit data ends here: 220 bytes, index 0x6e)
    755     0x6e => {
    756         Name => 'AutoLightingOptimizerOn',
    757         PrintConv => \%noYes,
    758     },
     769    # (VRD 3.4.0 edit data ends here: 220 bytes, index 0x6e)
     770    0x6e => { Name => 'AutoLightingOptimizerOn', %noYes },
    759771    0x6f => {
    760772        Name => 'AutoLightingOptimizer',
     
    766778        },
    767779    },
    768     # (VRD 3.5 edit data ends here: 232 bytes, index 0x74)
     780    # 0x71: 200
     781    # 0x73: 100
     782    # (VRD 3.5.0 edit data ends here: 232 bytes, index 0x74)
    769783    0x75 => {
    770784        Name => 'StandardRawHighlight',
     
    858872        Name => 'CheckMark2',
    859873        Format => 'int16u',
     874        PrintConvColumns => 2,
    860875        PrintConv => {
    861876            0 => 'Clear',
     
    867882        },
    868883    },
    869     # (VRD 3.8 edit data ends here: 286 bytes, index 0x8f)
     884    # (VRD 3.8.0 edit data ends here: 286 bytes, index 0x8f)
    870885    0x90 => {
    871886        Name => 'UnsharpMask',
    872887        PrintConv => { 0 => 'Off', 1 => 'On' },
    873888    },
    874     0x92 => 'UnsharpMaskStrength',
    875     0x94 => 'UnsharpMaskFineness',
    876     0x96 => 'UnsharpMaskThreshold',
    877     # (VRD 3.91 edit data ends here: 392 bytes, index 0xc4)
     889    0x92 => 'StandardUnsharpMaskStrength',
     890    0x94 => 'StandardUnsharpMaskFineness',
     891    0x96 => 'StandardUnsharpMaskThreshold',
     892    0x98 => 'PortraitUnsharpMaskStrength',
     893    0x9a => 'PortraitUnsharpMaskFineness',
     894    0x9c => 'PortraitUnsharpMaskThreshold',
     895    0x9e => 'LandscapeUnsharpMaskStrength',
     896    0xa0 => 'LandscapeUnsharpMaskFineness',
     897    0xa2 => 'LandscapeUnsharpMaskThreshold',
     898    0xa4 => 'NeutraUnsharpMaskStrength',
     899    0xa6 => 'NeutralUnsharpMaskFineness',
     900    0xa8 => 'NeutralUnsharpMaskThreshold',
     901    0xaa => 'FaithfulUnsharpMaskStrength',
     902    0xac => 'FaithfulUnsharpMaskFineness',
     903    0xae => 'FaithfulUnsharpMaskThreshold',
     904    0xb0 => 'MonochromeUnsharpMaskStrength',
     905    0xb2 => 'MonochromeUnsharpMaskFineness',
     906    0xb4 => 'MonochromeUnsharpMaskThreshold',
     907    0xb6 => 'CustomUnsharpMaskStrength',
     908    0xb8 => 'CustomUnsharpMaskFineness',
     909    0xba => 'CustomUnsharpMaskThreshold',
     910    0xbc => 'CustomDefaultUnsharpStrength',
     911    0xbe => 'CustomDefaultUnsharpFineness',
     912    0xc0 => 'CustomDefaultUnsharpThreshold',
     913    # (VRD 3.9.1 edit data ends here: 392 bytes, index 0xc4)
     914    # 0xc9: 3    - some RawSharpness
     915    # 0xca: 4095 - some RawHighlightPoint
     916    # 0xcb: 0    - some RawShadowPoint
     917    # 0xcc: 4095 - some OutputHighlightPoint
     918    # 0xcd: 0    - some OutputShadowPoint
     919    # 0xd1: 3    - some UnsharpMaskStrength
     920    # 0xd3: 7    - some UnsharpMaskFineness
     921    # 0xd5: 3,4  - some UnsharpMaskThreshold
     922    0xd6 => { Name => 'CropCircleActive', %noYes },
     923    0xd7 => 'CropCircleX',
     924    0xd8 => 'CropCircleY',
     925    0xd9 => 'CropCircleRadius',
     926    # 0xda: 0, 1
     927    # 0xdb: 100
     928    0xdc => {
     929        Name => 'DLOOn',
     930        DataMember => 'DLOOn',
     931        RawConv => '$$self{DLOOn} = $val',
     932        %noYes,
     933    },
     934    0xdd => 'DLOSetting',
     935    # (VRD 3.11.0 edit data ends here: 444 bytes, index 0xde)
     936    0xde => {
     937        Name => 'DLOShootingDistance',
     938        Notes => '100% = infinity',
     939        RawConv => '$val == 0x7fff ? undef : $val',
     940        ValueConv => '1 - $val / 0x400',
     941        ValueConvInv => 'int((1 - $val) * 0x400 + 0.5)',
     942        PrintConv => 'sprintf("%.0f%%", $val * 100)',
     943        PrintConvInv => 'ToFloat($val) / 100',
     944    },
     945    0xdf => {
     946        Name => 'DLODataLength',
     947        DataMember => 'DLODataLength',
     948        Format => 'int32u',
     949        Writable => 0,
     950        RawConv => '$$self{DLODataLength} = $val',
     951    },
     952    0xe0 => { # (yes, this overlaps DLODataLength)
     953        Name => 'DLOInfo',
     954        # - have seen DLODataLengths of 65536,64869 when DLO is Off, so must test DLOOn flag
     955        Condition => '$$self{DLOOn}',
     956        SubDirectory => { TagTable => 'Image::ExifTool::CanonVRD::DLOInfo' },
     957        Hook => '$varSize += $$self{DLODataLength} + 0x16',
     958    },
     959    0xe1 => 'CameraRawColorTone',
     960    # (VRD 3.11.2 edit data ends here: 452 bytes, index 0xe2, unless DLO is on)
     961    0xe2 => 'CameraRawSaturation',
     962    0xe3 => 'CameraRawContrast',
     963    0xe4 => { Name => 'CameraRawLinear', %noYes },
     964    0xe5 => 'CameraRawSharpness',
     965    0xe6 => 'CameraRawHighlightPoint',
     966    0xe7 => 'CameraRawShadowPoint',
     967    0xe8 => 'CameraRawOutputHighlightPoint',
     968    0xe9 => 'CameraRawOutputShadowPoint',
    878969);
     970
     971# DLO tags (ref PH)
     972%Image::ExifTool::CanonVRD::DLOInfo = (
     973    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
     974    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
     975    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
     976    WRITABLE => 1,
     977    FIRST_ENTRY => 1,
     978    FORMAT => 'int16s',
     979    GROUPS => { 2 => 'Image' },
     980    NOTES => 'Tags added when DLO (Digital Lens Optimizer) is on.',
     981    # 0x01 - seen 3112,3140
     982    0x04 => 'DLOSettingApplied',
     983    0x05 => {
     984        Name => 'DLOVersion', #(NC)
     985        Format => 'string[10]',
     986    },
     987    0x0a => {
     988        Name => 'DLOData',
     989        LargeTag => 1, # large tag, so avoid storing unnecessarily
     990        Notes => 'variable-length Digital Lens Optimizer data, stored in JPEG-like format',
     991        Format => 'undef[$$self{DLODataLength}]',
     992        Writable => 0,
     993        Binary => 1,
     994    },
     995);
     996
     997# VRD version 4 tags (ref PH)
     998%Image::ExifTool::CanonVRD::DR4 = (
     999    PROCESS_PROC => \&ProcessDR4,
     1000    WRITE_PROC => \&ProcessDR4,
     1001    WRITABLE => 1,
     1002    GROUPS => { 2 => 'Image' },
     1003    VARS => { HEX_ID => 1, SORT_PROC => \&SortDR4 },
     1004    NOTES => q{
     1005        Tags written by Canon DPP version 4 in CanonVRD trailers and DR4 files. Each
     1006        tag has three associated flag words which are stored with the directory
     1007        entry, some of which are extracted as a separate tag, indicated in the table
     1008        below by a decimal appended to the tag ID (.0, .1 or .2).
     1009    },
     1010    header => {
     1011        Name => 'DR4Header',
     1012        SubDirectory => { TagTable => 'Image::ExifTool::CanonVRD::DR4Header' },
     1013    },
     1014    0x10002 => 'Rotation', # left/right rotation 90,180,270
     1015    0x10003 => 'AngleAdj', # crop angle
     1016    # 0x10018 - fmt=8: 0
     1017    # 0x10020 - fmt=2: ''
     1018    0x10021 => 'CustomPictureStyle', # (string)
     1019    0x10101 => {
     1020        Name => 'CheckMark',
     1021        PrintConv => {
     1022            0 => 'Clear',
     1023            1 => 1,
     1024            2 => 2,
     1025            3 => 3,
     1026            4 => 4,
     1027            5 => 5,
     1028        },
     1029    },
     1030    0x10200 => {
     1031        Name => 'WorkColorSpace',
     1032        PrintConv => {
     1033            1 => 'sRGB',
     1034            2 => 'Adobe RGB',
     1035            3 => 'Wide Gamut RGB',
     1036            4 => 'Apple RGB',
     1037            5 => 'ColorMatch RGB',
     1038        },
     1039    },
     1040    # 0x10201 - fmt=9: 0
     1041    # 0x10f20 - fmt=9: 350
     1042    0x20001 => 'RawBrightnessAdj',
     1043    0x20101 => {
     1044        Name => 'WhiteBalanceAdj',
     1045        PrintConvColumns => 2,
     1046        PrintConv => {
     1047            -1 => 'Manual (Click)',
     1048            0 => 'Auto',
     1049            1 => 'Daylight',
     1050            2 => 'Cloudy',
     1051            3 => 'Tungsten',
     1052            4 => 'Fluorescent',
     1053            5 => 'Flash',
     1054            8 => 'Shade',
     1055            9 => 'Kelvin',
     1056            255 => 'Shot Settings',
     1057        },
     1058    },
     1059    0x20102 => 'WBAdjColorTemp',
     1060    0x20105 => 'WBAdjMagentaGreen',
     1061    0x20106 => 'WBAdjBlueAmber',
     1062    0x20125 => {
     1063        Name => 'WBAdjRGGBLevels',
     1064        PrintConv => '$val =~ s/^\d+ //; $val',  # remove first integer (14: what is this for?)
     1065        PrintConvInv => '"14 $val"',
     1066    },
     1067    0x20200 => { Name => 'GammaLinear', %noYes },
     1068    0x20301 => {
     1069        Name => 'PictureStyle',
     1070        PrintHex => 1,
     1071        PrintConv => {
     1072            0x81 => 'Standard',
     1073            0x82 => 'Portrait',
     1074            0x83 => 'Landscape',
     1075            0x84 => 'Neutral',
     1076            0x85 => 'Faithful',
     1077            0x86 => 'Monochrome',
     1078            0x87 => 'Auto',
     1079            0x88 => 'Fine Detail',
     1080            0xf0 => 'Shot Settings',
     1081            0xff => 'Custom',
     1082        },
     1083    },
     1084    # 0x20302 - Gamma curve data
     1085    0x20303 => 'ContrastAdj',
     1086    0x20304 => 'ColorToneAdj',
     1087    0x20305 => 'ColorSaturationAdj',
     1088    0x20306 => {
     1089        Name => 'MonochromeToningEffect',
     1090        PrintConv => {
     1091            0 => 'None',
     1092            1 => 'Sepia',
     1093            2 => 'Blue',
     1094            3 => 'Purple',
     1095            4 => 'Green',
     1096        },
     1097    },
     1098    0x20307 => {
     1099        Name => 'MonochromeFilterEffect',
     1100        PrintConv => {
     1101            0 => 'None',
     1102            1 => 'Yellow',
     1103            2 => 'Orange',
     1104            3 => 'Red',
     1105            4 => 'Green',
     1106        },
     1107    },
     1108    0x20308 => 'UnsharpMaskStrength',
     1109    0x20309 => 'UnsharpMaskFineness',
     1110    0x2030a => 'UnsharpMaskThreshold',
     1111    0x2030b => 'ShadowAdj',
     1112    0x2030c => 'HighlightAdj',
     1113    0x20310 => {
     1114        Name => 'SharpnessAdj',
     1115        PrintConv => {
     1116            0 => 'Sharpness',
     1117            1 => 'Unsharp Mask',
     1118        },
     1119    },
     1120   '0x20310.0' => { Name => 'SharpnessAdjOn', %noYes },
     1121    0x20311 => 'SharpnessStrength',
     1122    0x20400 => {
     1123        Name => 'ToneCurve',
     1124        SubDirectory => { TagTable => 'Image::ExifTool::CanonVRD::ToneCurve' },
     1125    },
     1126   '0x20400.1' => { Name => 'ToneCurveOriginal', %noYes },
     1127    # 0x20401 - fmt=33 (312 bytes)
     1128    0x20410 => 'ToneCurveBrightness',
     1129    0x20411 => 'ToneCurveContrast',
     1130    0x20500 => {
     1131        Name => 'AutoLightingOptimizer',
     1132        PrintConv => {
     1133            0 => 'Low',
     1134            1 => 'Standard',
     1135            2 => 'Strong',
     1136        },
     1137    },
     1138   '0x20500.0' => {
     1139        Name => 'AutoLightingOptimizerOn',
     1140        Notes => 'ignored if gamma is linear',
     1141        %noYes,
     1142    },
     1143    # 0x20501 - fmt=13: 0
     1144    # 0x20502 - fmt=13: 0
     1145    0x20600 => 'LuminanceNoiseReduction',
     1146    0x20601 => 'ChrominanceNoiseReduction',
     1147    # 0x20650 - fmt=9: 0 (JPG images)
     1148    0x20670 => 'ColorMoireReduction',
     1149   '0x20670.0' => { Name => 'ColorMoireReductionOn', %noYes },
     1150    0x20701 => {
     1151        Name => 'ShootingDistance',
     1152        Notes => '100% = infinity',
     1153        ValueConv => '$val / 10',
     1154        ValueConvInv => '$val * 10',
     1155        PrintConv => 'sprintf("%.0f%%", $val * 100)',
     1156        PrintConvInv => 'ToFloat($val) / 100',
     1157    },
     1158    0x20702 => {
     1159        Name => 'PeripheralIllumination',
     1160        PrintConv => 'sprintf "%g", $val',
     1161        PrintConvInv => '$val',
     1162    },
     1163   '0x20702.0' => { Name => 'PeripheralIlluminationOn', %noYes },
     1164    0x20703 => {
     1165        Name => 'ChromaticAberration',
     1166        PrintConv => 'sprintf "%g", $val',
     1167        PrintConvInv => '$val',
     1168    },
     1169   '0x20703.0' => { Name => 'ChromaticAberrationOn', %noYes },
     1170    0x20704 => { Name => 'ColorBlurOn', %noYes },
     1171    0x20705 => {
     1172        Name => 'DistortionCorrection',
     1173        PrintConv => 'sprintf "%g", $val',
     1174        PrintConvInv => '$val',
     1175    },
     1176   '0x20705.0' => { Name => 'DistortionCorrectionOn', %noYes },
     1177    0x20706 => 'DLOSetting',
     1178   '0x20706.0' => { Name => 'DLOOn', %noYes },
     1179    0x20707 => {
     1180        Name => 'ChromaticAberrationRed',
     1181        PrintConv => 'sprintf "%g", $val',
     1182        PrintConvInv => '$val',
     1183    },
     1184    0x20708 => {
     1185        Name => 'ChromaticAberrationBlue',
     1186        PrintConv => 'sprintf "%g", $val',
     1187        PrintConvInv => '$val',
     1188    },
     1189    0x20709 => {
     1190        Name => 'DistortionEffect',
     1191        PrintConv => {
     1192            0 => 'Shot Settings',
     1193            1 => 'Emphasize Linearity',
     1194            2 => 'Emphasize Distance',
     1195            3 => 'Emphasize Periphery',
     1196            4 => 'Emphasize Center',
     1197        },
     1198    },
     1199    # 0x20800 - fmt=1: 0
     1200    # 0x20801 - fmt=1: 0
     1201    0x2070b => { Name => 'DiffractionCorrectionOn', %noYes },
     1202    0x20900 => 'ColorHue',
     1203    0x20901 => 'SaturationAdj',
     1204    0x20910 => 'RedHSL',
     1205    0x20911 => 'OrangeHSL',
     1206    0x20912 => 'YellowHSL',
     1207    0x20913 => 'GreenHSL',
     1208    0x20914 => 'AquaHSL',
     1209    0x20915 => 'BlueHSL',
     1210    0x20916 => 'PurpleHSL',
     1211    0x20917 => 'MagentaHSL',
     1212    0x20a00 => {
     1213        Name => 'GammaInfo',
     1214        SubDirectory => { TagTable => 'Image::ExifTool::CanonVRD::GammaInfo' },
     1215    },
     1216    # 0x20a01 - Auto picture style settings
     1217    # 0x20a02 - Standard picture style settings
     1218    # 0x20a03 - Portrait picture style settings
     1219    # 0x20a04 - Landscape picture style settings
     1220    # 0x20a05 - Neutral picture style settings
     1221    # 0x20a06 - Faithful picture style settings
     1222    # 0x20a07 - Monochrome picture style settings
     1223    # 0x20a08 - (unknown picture style settings)
     1224    # 0x20a09 - Custom picture style settings
     1225    # 0x20a20 - Fine Detail picture style settings
     1226    0x30101 => {
     1227        Name => 'CropAspectRatio',
     1228        PrintConv => {
     1229            0 => 'Free',
     1230            1 => 'Custom',
     1231            2 => '1:1',
     1232            3 => '3:2',
     1233            4 => '2:3',
     1234            5 => '4:3',
     1235            6 => '3:4',
     1236            7 => '5:4',
     1237            8 => '4:5',
     1238            9 => '16:9',
     1239            10 => '9:16',
     1240        },
     1241    },
     1242    0x30102 => 'CropAspectRatioCustom',
     1243    # 0x30103 - fmt=33: "0 0 8"
     1244    0xf0100 => {
     1245        Name => 'CropInfo',
     1246        SubDirectory => { TagTable => 'Image::ExifTool::CanonVRD::CropInfo' },
     1247    },
     1248    0xf0500 => {
     1249        Name => 'CustomPictureStyleData',
     1250        Binary => 1,
     1251    },
     1252    0xf0510 => {
     1253        Name => 'StampInfo',
     1254        SubDirectory => { TagTable => 'Image::ExifTool::CanonVRD::StampInfo' },
     1255    },
     1256    0xf0511 => {
     1257        Name => 'DustInfo',
     1258        SubDirectory => { TagTable => 'Image::ExifTool::CanonVRD::DustInfo' },
     1259    },
     1260    0xf0512 => 'LensFocalLength',
     1261    # 0xf0521 - DLO data
     1262    # 0xf0520 - DLO data
     1263    # 0xf0530 - created when dust delete data applied (4 bytes, all zero)
     1264    # 0xf0600 - fmt=253 (2308 bytes, JPG images)
     1265    # 0xf0601 - fmt=253 (2308 bytes, JPG images)
     1266    # 0x1ff52c - values: 129,130,132 (related to custom picture style somehow)
     1267    # to do:
     1268    # - find 8-15mm CR2 sample and decode linear distortion effect fine-tune
     1269);
     1270
     1271# Version 4 header information (ref PH)
     1272%Image::ExifTool::CanonVRD::DR4Header = (
     1273    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
     1274    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
     1275    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
     1276    WRITABLE => 1,
     1277    FIRST_ENTRY => 0,
     1278    FORMAT => 'int32u',
     1279    GROUPS => { 2 => 'Image' },
     1280    # 0 - value: 'IIII' (presumably byte order)
     1281    # 1 - value: 0x00040004 (currently use this for magic number)
     1282    # 2 - value: 6
     1283    3 => {
     1284        Name => 'DR4CameraModel',
     1285        Writable => 'int32u',
     1286        PrintHex => 1,
     1287        SeparateTable => 'Canon CanonModelID',
     1288        PrintConv => \%Image::ExifTool::Canon::canonModelID,
     1289    },
     1290    # 4 - value: 3
     1291    # 5 - value: 4
     1292    # 6 - value: 5
     1293    # 7 - DR4 directory entry count
     1294);
     1295
     1296# Version 4 RGB tone curve information (ref PH)
     1297%Image::ExifTool::CanonVRD::ToneCurve = (
     1298    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
     1299    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
     1300    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
     1301    WRITABLE => 1,
     1302    FIRST_ENTRY => 0,
     1303    FORMAT => 'int32u',
     1304    GROUPS => { 2 => 'Image' },
     1305    0x00 => {
     1306        Name => 'ToneCurveColorSpace',
     1307        PrintConv => {
     1308            0 => 'RGB',
     1309            1 => 'Luminance',
     1310        },
     1311    },
     1312    0x01 => {
     1313        Name => 'ToneCurveShape',
     1314        PrintConv => {
     1315            0 => 'Curve',
     1316            1 => 'Straight',
     1317        },
     1318    },
     1319    0x03 => { Name => 'ToneCurveInputRange',  Format => 'int32u[2]', Notes => '255 max' },
     1320    0x05 => { Name => 'ToneCurveOutputRange', Format => 'int32u[2]', Notes => '255 max' },
     1321    0x07 => {
     1322        Name => 'RGBCurvePoints',
     1323        Format => 'int32u[21]',
     1324        PrintConv => 'Image::ExifTool::CanonVRD::ToneCurvePrint($val)',
     1325        PrintConvInv => 'Image::ExifTool::CanonVRD::ToneCurvePrintInv($val)',
     1326    },
     1327    0x0a => 'ToneCurveX',
     1328    0x0b => 'ToneCurveY',
     1329    0x2d => {
     1330        Name => 'RedCurvePoints',
     1331        Format => 'int32u[21]',
     1332        PrintConv => 'Image::ExifTool::CanonVRD::ToneCurvePrint($val)',
     1333        PrintConvInv => 'Image::ExifTool::CanonVRD::ToneCurvePrintInv($val)',
     1334    },
     1335    0x53 => {
     1336        Name => 'GreenCurvePoints',
     1337        Format => 'int32u[21]',
     1338        PrintConv => 'Image::ExifTool::CanonVRD::ToneCurvePrint($val)',
     1339        PrintConvInv => 'Image::ExifTool::CanonVRD::ToneCurvePrintInv($val)',
     1340    },
     1341    0x79 => {
     1342        Name => 'BlueCurvePoints',
     1343        Format => 'int32u[21]',
     1344        PrintConv => 'Image::ExifTool::CanonVRD::ToneCurvePrint($val)',
     1345        PrintConvInv => 'Image::ExifTool::CanonVRD::ToneCurvePrintInv($val)',
     1346    },
     1347);
     1348
     1349# Version 4 gamma curve information (ref PH)
     1350%Image::ExifTool::CanonVRD::GammaInfo = (
     1351    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
     1352    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
     1353    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
     1354    WRITABLE => 1,
     1355    FIRST_ENTRY => 0,
     1356    FORMAT => 'double',
     1357    GROUPS => { 2 => 'Image' },
     1358    0x02 => 'GammaContrast',
     1359    0x03 => 'GammaColorTone',
     1360    0x04 => 'GammaSaturation',
     1361    0x05 => 'GammaUnsharpMaskStrength',
     1362    0x06 => 'GammaUnsharpMaskFineness',
     1363    0x07 => 'GammaUnsharpMaskThreshold',
     1364    0x08 => 'GammaSharpnessStrength',
     1365    0x09 => 'GammaShadow',
     1366    0x0a => 'GammaHighlight',
     1367    # 0x0b-0x10 are the same as first 6 doubles of tag DR4_0x20302
     1368    # 0x0b - value: 14
     1369    0x0c => {
     1370        Name => 'GammaBlackPoint',
     1371        ValueConv => q{
     1372            return 0 if $val <= 0;
     1373            $val = log($val / 4.6875) / log(2) + 1;
     1374            return abs($val) > 1e-10 ? $val : 0;
     1375        },
     1376        ValueConvInv => '$val ? exp(($val - 1) * log(2)) * 4.6876 : 0',
     1377        PrintConv => 'sprintf("%+.3f", $val)',
     1378        PrintConvInv => '$val',
     1379    },
     1380    0x0d => {
     1381        Name => 'GammaWhitePoint',
     1382        ValueConv => q{
     1383            return $val if $val <= 0;
     1384            $val = log($val / 4.6875) / log(2) - 11.77109325169954;
     1385            return abs($val) > 1e-10 ? $val : 0;
     1386        },
     1387        ValueConvInv => '$val ? exp(($val + 11.77109325169954) * log(2)) * 4.6875 : 0',
     1388        PrintConv => 'sprintf("%+.3f", $val)',
     1389        PrintConvInv => '$val',
     1390    },
     1391    0x0e => {
     1392        Name => 'GammaMidPoint',
     1393        ValueConv => q{
     1394            return $val if $val <= 0;
     1395            $val = log($val / 4.6875) / log(2) - 8;
     1396            return abs($val) > 1e-10 ? $val : 0;
     1397        },
     1398        ValueConvInv => '$val ? exp(($val + 8) * log(2)) * 4.6876 : 0',
     1399        PrintConv => 'sprintf("%+.3f", $val)',
     1400        PrintConvInv => '$val',
     1401    },
     1402    0x0f => { Name => 'GammaCurveOutputRange', Format => 'double[2]', Notes => '16383 max' },
     1403);
     1404
     1405# Version 4 crop information (ref PH)
     1406%Image::ExifTool::CanonVRD::CropInfo = (
     1407    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
     1408    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
     1409    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
     1410    WRITABLE => 1,
     1411    FIRST_ENTRY => 0,
     1412    FORMAT => 'int32s',
     1413    GROUPS => { 2 => 'Image' },
     1414    0 => { Name => 'CropActive', %noYes },
     1415    1 => 'CropRotatedOriginalWidth',
     1416    2 => 'CropRotatedOriginalHeight',
     1417    3 => 'CropX',
     1418    4 => 'CropY',
     1419    5 => 'CropWidth',
     1420    6 => 'CropHeight',
     1421    8 => {
     1422        Name => 'CropRotation',
     1423        Format => 'double',
     1424        PrintConv => 'sprintf("%.7g",$val)',
     1425        PrintConvInv => '$val',
     1426    },
     1427    0x0a => 'CropOriginalWidth',
     1428    0x0b => 'CropOriginalHeight',
     1429    # 0x0c double - value: 100
     1430);
     1431
     1432# DR4 Stamp Tool tags (ref PH)
     1433%Image::ExifTool::CanonVRD::StampInfo = (
     1434    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
     1435    GROUPS => { 2 => 'Image' },
     1436    FORMAT => 'int32u',
     1437    FIRST_ENTRY => 0,
     1438    0x02 => 'StampToolCount',
     1439);
     1440
     1441# DR4 dust delete information (ref PH)
     1442%Image::ExifTool::CanonVRD::DustInfo = (
     1443    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
     1444    GROUPS => { 2 => 'Image' },
     1445    FORMAT => 'int32u',
     1446    FIRST_ENTRY => 0,
     1447    0x02 => { Name => 'DustDeleteApplied', %noYes },
     1448);
     1449
     1450#------------------------------------------------------------------------------
     1451# sort DR4 tag ID's for the documentation
     1452sub SortDR4($$)
     1453{
     1454    my ($a, $b) = @_;
     1455    my ($aHex, $aDec, $bHex, $bDec);
     1456    ($aHex, $aDec) = ($1, $2) if $a =~ /^(0x[0-9a-f]+)?\.?(\d*?)$/;
     1457    ($bHex, $bDec) = ($1, $2) if $b =~ /^(0x[0-9a-f]+)?\.?(\d*?)$/;
     1458    if ($aHex) {
     1459        return 1 unless defined $bDec;  # $b is 'header';
     1460        return hex($aHex) <=> hex($bHex) || $aDec <=> $bDec if $bHex;
     1461        return hex($aHex) <=> $bDec || 1;
     1462    } elsif ($bHex) {
     1463        return -1 unless defined $aDec;
     1464        return $aDec <=> hex($bHex) || -1;
     1465    } else {
     1466        return 1 unless defined $bDec;
     1467        return -1 unless defined $aDec;
     1468        return $aDec <=> $bDec;
     1469    }
     1470}
    8791471
    8801472#------------------------------------------------------------------------------
     
    9131505sub ProcessEditData($$$)
    9141506{
    915     my ($exifTool, $dirInfo, $tagTablePtr) = @_;
    916     $exifTool or return 1;    # allow dummy access
     1507    my ($et, $dirInfo, $tagTablePtr) = @_;
     1508    $et or return 1;    # allow dummy access
    9171509    my $dataPt = $$dirInfo{DataPt};
    9181510    my $pos = $$dirInfo{DirStart};
     
    9201512    my $outfile = $$dirInfo{OutFile};
    9211513    my $dirLen = $$dirInfo{DirLen};
     1514    my $verbose = $et->Options('Verbose');
     1515    my $out = $et->Options('TextOut');
     1516    my $oldChanged = $$et{CHANGED};
     1517
     1518    $et->VerboseDir('VRD Edit Data', 0, $dirLen) unless $outfile;
     1519
     1520    if ($outfile) {
     1521        # make a copy for editing in place
     1522        my $buff = substr($$dataPt, $pos, $dirLen);
     1523        $dataPt = $$dirInfo{DataPt} = \$buff;
     1524        $dataPos += $pos;
     1525        $pos = $$dirInfo{DirStart} = 0;
     1526    }
    9221527    my $dirEnd = $pos + $dirLen;
    923     my $verbose = $exifTool->Options('Verbose');
    924     my $out = $exifTool->Options('TextOut');
    925     my $oldChanged = $$exifTool{CHANGED};
    926 
    927     $exifTool->VerboseDir('VRD Edit Data', 0, $dirLen);
    9281528
    9291529    # loop through all records in the edit data
     
    9351535        } else {
    9361536            $recLen = Get32u($dataPt, $pos);
     1537            # (DR4 has a null terminator)
     1538            last if $recLen == 0 and $pos + 4 == $dirEnd;
    9371539        }
    9381540        $pos += 4;          # move to start of record
    9391541        if ($pos + $recLen > $dirEnd) {
    940             $exifTool->Warn('Possibly corrupt CanonVRD Edit record');
     1542            $et->Warn('Possibly corrupt CanonVRD Edit record');
    9411543            $err = 1;
    9421544            last;
    9431545        }
     1546        my $saveRecLen = $recLen;
    9441547        if ($verbose > 1 and not $outfile) {
    945             printf $out "$$exifTool{INDENT}CanonVRD Edit record ($recLen bytes at offset 0x%x)\n",
     1548            printf $out "$$et{INDENT}CanonVRD Edit record ($recLen bytes at offset 0x%x)\n",
    9461549                   $pos + $dataPos;
     1550            $et->VerboseDump($dataPt, Len => $recLen, Start => $pos, Addr => $pos + $dataPos) if $recNum;
    9471551        }
    9481552
     
    9511555
    9521556        # process VRD edit information
    953         my $subTablePtr = GetTagTable('Image::ExifTool::CanonVRD::Edit');
     1557        my $subTablePtr = $tagTablePtr;
    9541558        my $index;
    955         my $editData = substr($$dataPt, $pos, $recLen);
    9561559        my %subdirInfo = (
    957             DataPt => \$editData,
    958             DataPos => $dataPos + $pos,
     1560            DataPt   => $dataPt,
     1561            DataPos  => $dataPos,
     1562            DirStart => $pos,
     1563            DirLen   => $recLen,
     1564            OutFile  => $outfile,
    9591565        );
    960         my $start = 0;
     1566        my $subStart = 0;
    9611567        # loop through various sections of the VRD edit data
    9621568        for ($index=0; ; ++$index) {
    9631569            my $tagInfo = $$subTablePtr{$index} or last;
    964             my $dirLen;
    965             my $maxLen = $recLen - $start;
     1570            my $subLen;
     1571            my $maxLen = $recLen - $subStart;
    9661572            if ($$tagInfo{Size}) {
    967                 $dirLen = $$tagInfo{Size};
     1573                $subLen = $$tagInfo{Size};
    9681574            } elsif (defined $$tagInfo{Size}) {
    969                 # get size from int32u at $start
    970                 last unless $start + 4 <= $recLen;
    971                 $dirLen = Get32u(\$editData, $start);
    972                 $start += 4; # skip the length word
     1575                # get size from int32u at $subStart
     1576                last unless $subStart + 4 <= $recLen;
     1577                $subLen = Get32u($dataPt, $subStart + $pos);
     1578                $subStart += 4; # skip the length word
    9731579            } else {
    974                 $dirLen = $maxLen;
     1580                $subLen = $maxLen;
    9751581            }
    976             $dirLen > $maxLen and $dirLen = $maxLen;
    977             if ($dirLen) {
    978                 my $subTable = GetTagTable($tagInfo->{SubDirectory}->{TagTable});
     1582            $subLen > $maxLen and $subLen = $maxLen;
     1583            if ($subLen) {
     1584                my $subTable = GetTagTable($$tagInfo{SubDirectory}{TagTable});
    9791585                my $subName = $$tagInfo{Name};
    980                 $subdirInfo{DirStart} = $start;
    981                 $subdirInfo{DirLen} = $dirLen;
     1586                $subdirInfo{DirStart} = $subStart + $pos;
     1587                $subdirInfo{DirLen} = $subLen;
    9821588                $subdirInfo{DirName} = $subName;
    9831589                if ($outfile) {
    9841590                    # rewrite this section of the VRD edit information
    9851591                    $verbose and print $out "  Rewriting Canon $subName\n";
    986                     my $newVal = $exifTool->WriteDirectory(\%subdirInfo, $subTable);
    987                     substr($$dataPt, $pos+$start, $dirLen) = $newVal if $newVal;
     1592                    my $newVal = $et->WriteDirectory(\%subdirInfo, $subTable);
     1593                    if ($newVal) {
     1594                        my $sizeDiff = length($newVal) - $subLen;
     1595                        substr($$dataPt, $pos+$subStart, $subLen) = $newVal;
     1596                        if ($sizeDiff) {
     1597                            $subLen = length $newVal;
     1598                            $recLen += $sizeDiff;
     1599                            $dirEnd += $sizeDiff;
     1600                            $dirLen += $sizeDiff;
     1601                        }
     1602                    }
    9881603                } else {
    989                     $exifTool->VPrint(0, "$$exifTool{INDENT}$subName (SubDirectory) -->\n");
    990                     $exifTool->VerboseDump(\$editData,
    991                         Start => $start,
    992                         Addr  => $dataPos + $pos + $start,
    993                         Len   => $dirLen,
     1604                    $et->VPrint(0, "$$et{INDENT}$subName (SubDirectory) -->\n");
     1605                    $et->VerboseDump($dataPt,
     1606                        Start => $pos + $subStart,
     1607                        Addr  => $dataPos + $pos + $subStart,
     1608                        Len   => $subLen,
    9941609                    );
    9951610                    # extract tags from this section of the VRD edit information
    996                     $exifTool->ProcessDirectory(\%subdirInfo, $subTable);
     1611                    $et->ProcessDirectory(\%subdirInfo, $subTable);
    9971612                }
    9981613            }
    9991614            # next section starts at the end of this one
    1000             $start += $dirLen;
     1615            $subStart += $subLen;
     1616        }
     1617        if ($outfile and $saveRecLen ne $recLen) {
     1618            # update record length if necessary
     1619            Set32u($recLen, $dataPt, $pos - 4)
    10011620        }
    10021621    }
    10031622    if ($outfile) {
    1004         return undef if $oldChanged == $$exifTool{CHANGED};
     1623        return undef if $oldChanged == $$et{CHANGED};
    10051624        return substr($$dataPt, $$dirInfo{DirStart}, $dirLen);
    10061625    }
     
    10141633sub ProcessIHL($$$)
    10151634{
    1016     my ($exifTool, $dirInfo, $tagTablePtr) = @_;
     1635    my ($et, $dirInfo, $tagTablePtr) = @_;
    10171636    my $dataPt = $$dirInfo{DataPt};
    10181637    my $dataPos = $$dirInfo{DataPos};
     
    10211640    my $dirEnd = $pos + $dirLen;
    10221641
    1023     $exifTool->VerboseDir('VRD IHL', 0, $dirLen);
     1642    $et->VerboseDir('VRD IHL', 0, $dirLen);
    10241643
    10251644    SetByteOrder('II'); # (make up your mind, Canon!)
     
    10271646        my $hdr = substr($$dataPt, $pos, 48);
    10281647        unless ($hdr =~ /^IHL Created Optional Item Data\0\0/) {
    1029             $exifTool->Warn('Possibly corrupted VRD IHL data');
     1648            $et->Warn('Possibly corrupted VRD IHL data');
    10301649            last;
    10311650        }
     
    10341653        my $next = Get32u($dataPt, $pos + 44); # size of complete IHL record
    10351654        if ($size > $next or $pos + 48 + $next > $dirEnd) {
    1036             $exifTool->Warn(sprintf('Bad size for VRD IHL tag 0x%.4x', $tag));
     1655            $et->Warn(sprintf('Bad size for VRD IHL tag 0x%.4x', $tag));
    10371656            last;
    10381657        }
    10391658        $pos += 48;
    1040         $exifTool->HandleTag($tagTablePtr, $tag, substr($$dataPt, $pos, $size),
     1659        $et->HandleTag($tagTablePtr, $tag, substr($$dataPt, $pos, $size),
    10411660            DataPt  => $dataPt,
    10421661            DataPos => $dataPos,
     
    10471666    }
    10481667    return 1;
    1049 }   
    1050    
     1668}
     1669
    10511670#------------------------------------------------------------------------------
    10521671# Process VRD IHL EXIF data
     
    10551674sub ProcessIHLExif($$$)
    10561675{
    1057     my ($exifTool, $dirInfo, $tagTablePtr) = @_;
    1058     $$exifTool{DOC_NUM} = 1;
     1676    my ($et, $dirInfo, $tagTablePtr) = @_;
     1677    $$et{DOC_NUM} = 1;
    10591678    # the IHL-edited maker notes may look messed up, but the offsets should be OK
    1060     my $oldFix = $exifTool->Options(FixBase => 0);
    1061     my $rtnVal = $exifTool->ProcessTIFF($dirInfo, $tagTablePtr);
    1062     $exifTool->Options(FixBase => $oldFix);
    1063     delete $$exifTool{DOC_NUM};
     1679    my $oldFix = $et->Options(FixBase => 0);
     1680    my $rtnVal = $et->ProcessTIFF($dirInfo, $tagTablePtr);
     1681    $et->Options(FixBase => $oldFix);
     1682    delete $$et{DOC_NUM};
    10641683    return $rtnVal;
     1684}
     1685
     1686#------------------------------------------------------------------------------
     1687# Wrap DR4 data with the VRD header/footer and edit record
     1688# Inputs: 0) DR4 record
     1689# Returns: VRD[Edit[DR4]] data
     1690sub WrapDR4($)
     1691{
     1692    my $val = shift;
     1693    my $n = length $val;
     1694    my $oldOrder = GetByteOrder();
     1695    SetByteOrder('MM');
     1696    $val = $blankHeader . "\xff\xff\0\xf7" . Set32u($n+8) . Set32u($n) .
     1697                $val . "\0\0\0\0" . $blankFooter;
     1698    # update the new VRD length in the header/footer
     1699    Set32u($n + 16, \$val, 0x18);  # (extra 16 bytes for the edit record wrapper)
     1700    Set32u($n + 16, \$val, length($val) - 0x2c);
     1701    SetByteOrder($oldOrder);
     1702    return $val;
     1703}
     1704
     1705#------------------------------------------------------------------------------
     1706# Read/Write DPP version 4 edit data or DR4 file
     1707# Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
     1708# Returns:
     1709#   Reading from memory (not RAF and not IsWriting): 1 on success
     1710#   Editing from memory (not RAF and IsWriting): modified edit data, or undef if nothing changed
     1711#   Reading file (RAF and not OutFile): 1 if a valid DR4 file, 0 if not
     1712#   Writing file (RAF and OutFile): 1 if valid DR4 file, 0 if not, -1 on write error
     1713# (serves me right for not having a consistent interface for the various modes of operation)
     1714sub ProcessDR4($$;$)
     1715{
     1716    my ($et, $dirInfo, $tagTablePtr) = @_;
     1717    $et or return 1;    # allow dummy access
     1718    my $dataPt = $$dirInfo{DataPt};
     1719    my $raf = $$dirInfo{RAF};
     1720    my $outfile = $$dirInfo{OutFile};
     1721    my $isWriting = $outfile || $$dirInfo{IsWriting};
     1722    my $dataPos = $$dirInfo{DataPos} || 0;
     1723    my $verbose = $et->Options('Verbose');
     1724    my $unknown = $et->Options('Unknown');
     1725    my ($pos, $dirLen, $numEntries, $err, $newTags);
     1726
     1727    # write CanonDR4 as a block if specified
     1728    if ($isWriting) {
     1729        my $nvHash;
     1730        my $newVal = $et->GetNewValue('CanonDR4', \$nvHash);
     1731        if ($newVal) {
     1732            $et->VPrint(0, "  Writing CanonDR4 as a block\n");
     1733            $$et{DidCanonVRD} = 1;  # set flag so we don't add this twice
     1734            ++$$et{CHANGED};
     1735            if ($outfile) {
     1736                Write($$dirInfo{OutFile}, $newVal) or return -1;
     1737                return 1;
     1738            } else {
     1739                return $newVal;
     1740            }
     1741        } elsif (not $dataPt and ($nvHash or $$et{DEL_GROUP}{CanonVRD})) {
     1742            $et->Error("Can't delete all CanonDR4 information from a DR4 file");
     1743            return 1;
     1744        }
     1745    }
     1746    if ($dataPt) {
     1747        $pos = $$dirInfo{DirStart} || 0;
     1748        $dirLen = $$dirInfo{DirLen} || length($$dataPt) - $pos;
     1749    } else {
     1750        # load DR4 file into memory
     1751        my $buff;
     1752        $raf->Read($buff, 8) == 8 and $buff eq "IIII\x04\0\x04\0" or return 0;
     1753        $et->SetFileType();
     1754        $raf->Seek(0, 2) or return $err = 1;
     1755        $dirLen = $raf->Tell();
     1756        $raf->Seek(0, 0) or return $err = 1;
     1757        $raf->Read($buff, $dirLen) == $dirLen or $err = 1;
     1758        $err and $et->Warn('Error reading DR4 file'), return 1;
     1759        $tagTablePtr = GetTagTable('Image::ExifTool::CanonVRD::DR4');
     1760        $dataPt = \$buff;
     1761        $pos = 0;
     1762    }
     1763    my $dirEnd = $pos + $dirLen;
     1764
     1765    if (($$et{TAGS_FROM_FILE} and
     1766        not $$et{EXCL_TAG_LOOKUP}{canondr4}) or $$et{REQ_TAG_LOOKUP}{canondr4})
     1767    {
     1768        # extract CanonDR4 block if copying tags, or if requested
     1769        $et->FoundTag('CanonDR4', substr($$dataPt, $pos, $dirLen));
     1770    }
     1771
     1772    # version 4 header is 32 bytes (int32u[8])
     1773    if ($dirLen < 32) {
     1774        $err = 1;
     1775    } else {
     1776        SetByteOrder(substr($$dataPt, $pos, 2)) or $err = 1;
     1777        # process the DR4 header
     1778        my %hdrInfo = (
     1779            DataPt   => $dataPt,
     1780            DirStart => $pos,
     1781            DirLen   => 32,
     1782            DirName  => 'DR4Header',
     1783        );
     1784        my $hdrTable = GetTagTable('Image::ExifTool::CanonVRD::DR4Header');
     1785        if ($outfile) {
     1786            my $hdr = $et->WriteDirectory(\%hdrInfo, $hdrTable);
     1787            substr($$dataPt, $pos, 32) = $hdr if $hdr and length $hdr == 32;
     1788        } else {
     1789            $et->VerboseDir('DR4Header', undef, 32);
     1790            $et->ProcessDirectory(\%hdrInfo, $hdrTable);
     1791        }
     1792        # number of entries in the DR4 directory
     1793        $numEntries = Get32u($dataPt, $pos + 28);
     1794        $err = 1 if $dirLen < 36 + 28 * $numEntries;
     1795    }
     1796    $err and $et->Warn('Invalid DR4 directory'), return $outfile ? undef : 0;
     1797
     1798    if ($outfile) {
     1799        $newTags = $et->GetNewTagInfoHash($tagTablePtr);
     1800    } else {
     1801        $et->VerboseDir('DR4', $numEntries, $dirLen);
     1802    }
     1803
     1804    my $index;
     1805    for ($index=0; $index<$numEntries; ++$index) {
     1806        my ($val, @flg, $i);
     1807        my $entry = $pos + 36 + 28 * $index;
     1808        last if $entry + 28 > $dirEnd;
     1809        my $tag = Get32u($dataPt, $entry);
     1810        my $fmt = Get32u($dataPt, $entry + 4);
     1811        $flg[0] = Get32u($dataPt, $entry + 8);
     1812        $flg[1] = Get32u($dataPt, $entry + 12);
     1813        $flg[2] = Get32u($dataPt, $entry + 16);
     1814        my $off = Get32u($dataPt, $entry + 20) + $pos;
     1815        my $len = Get32u($dataPt, $entry + 24);
     1816        next if $off + $len >= $dirEnd;
     1817        my $format = $vrdFormat{$fmt};
     1818        if (not $format) {
     1819            $val = unpack 'H*', substr($$dataPt, $off, $len);
     1820            $format = 'undef';
     1821        } elsif ($format eq 'double' and $len == 8) {
     1822            # avoid teeny weeny values
     1823            $val = ReadValue($dataPt, $off, $format, undef, $len);
     1824            $val = 0 if abs($val) < 1e-100;
     1825        }
     1826        if ($outfile) {
     1827            # write (binary data) subdirectory if it exists
     1828            my $tagInfo = $et->GetTagInfo($tagTablePtr, $tag);
     1829            if ($tagInfo and $$tagInfo{SubDirectory}) {
     1830                my %subdirInfo = (
     1831                    DataPt   => $dataPt,
     1832                    DirStart => $off,
     1833                    DirLen   => $len,
     1834                    DirName  => $$tagInfo{Name},
     1835                );
     1836                my $subTablePtr = GetTagTable($$tagInfo{SubDirectory}{TagTable});
     1837                my $saveChanged = $$et{CHANGED};
     1838                my $dat = $et->WriteDirectory(\%subdirInfo, $subTablePtr);
     1839                if (defined $dat and length($dat) == $len) {
     1840                    substr($$dataPt, $off, $len) = $dat;
     1841                } else {
     1842                    $$et{CHANGED} = $saveChanged;   # didn't change anything after all
     1843                }
     1844            } else {
     1845                # loop through main tag and flags (don't yet consider flag 2)
     1846                for ($i=-1; $i<2; ++$i) {
     1847                    $tagInfo = $$newTags{$i>=0 ? sprintf('0x%x.%d',$tag,$i) : $tag};
     1848                    next unless $tagInfo;
     1849                    if ($i >= 0) {
     1850                        $off = $entry + 8 + 4 * $i;
     1851                        $format = 'int32u';
     1852                        $len = 4;
     1853                        undef $val;
     1854                    }
     1855                    $val = ReadValue($dataPt, $off, $format, undef, $len) unless defined $val;
     1856                    my $nvHash;
     1857                    my $newVal = $et->GetNewValue($tagInfo, \$nvHash);
     1858                    if ($et->IsOverwriting($nvHash, $val) and defined $newVal) {
     1859                        my $count = int($len / Image::ExifTool::FormatSize($format));
     1860                        my $rtnVal = WriteValue($newVal, $format, $count, $dataPt, $off);
     1861                        if (defined $rtnVal) {
     1862                            $et->VerboseValue("- CanonVRD:$$tagInfo{Name}", $val);
     1863                            $et->VerboseValue("+ CanonVRD:$$tagInfo{Name}", $newVal);
     1864                            ++$$et{CHANGED};
     1865                        }
     1866                    }
     1867                }
     1868            }
     1869            next;
     1870        }
     1871        $et->HandleTag($tagTablePtr, $tag, $val,
     1872            DataPt  => $dataPt,
     1873            DataPos => $dataPos,
     1874            Start   => $off,
     1875            Size    => $len,
     1876            Index   => $index,
     1877            Format  => $format,
     1878            # $flg[0] is on/off flag
     1879            # $flg[1] "is default" flag?
     1880            # $flg[2] changed to 0 when some unsharp mask settings were changed
     1881            Extra   => ", fmt=$fmt flags=" . join(',', @flg),
     1882        );
     1883        foreach $i (0..2) {
     1884            my $flagID = sprintf('0x%x.%d', $tag, $i);
     1885            $et->HandleTag($tagTablePtr, $flagID, $flg[$i]) if $$tagTablePtr{$flagID};
     1886        }
     1887    }
     1888    return 1 unless $isWriting;
     1889    return substr($$dataPt, $pos, $dirLen) unless $raf;
     1890    return 1 if Write($outfile, substr($$dataPt, $pos, $dirLen));
     1891    return -1;
    10651892}
    10661893
     
    10711898sub ProcessVRD($$)
    10721899{
    1073     my ($exifTool, $dirInfo) = @_;
     1900    my ($et, $dirInfo) = @_;
    10741901    my $raf = $$dirInfo{RAF};
    10751902    my $buff;
     
    10771904
    10781905    # initialize write directories if necessary
    1079     $exifTool->InitWriteDirs(\%vrdMap, 'XMP') if $$dirInfo{OutFile};
     1906    $et->InitWriteDirs(\%vrdMap, 'XMP') if $$dirInfo{OutFile};
    10801907
    10811908    if (not $num and $$dirInfo{OutFile}) {
    10821909        # create new VRD file from scratch
    1083         my $newVal = $exifTool->GetNewValues('CanonVRD');
     1910        my $newVal = $et->GetNewValue('CanonVRD');
    10841911        if ($newVal) {
    1085             $exifTool->VPrint(0, "  Writing CanonVRD as a block\n");
     1912            $et->VPrint(0, "  Writing CanonVRD as a block\n");
    10861913            Write($$dirInfo{OutFile}, $newVal) or return -1;
    1087             ++$exifTool->{CHANGED};
     1914            $$et{DidCanonVRD} = 1;
     1915            ++$$et{CHANGED};
    10881916        } else {
    10891917            # allow VRD to be created from individual tags
    1090             if ($$exifTool{ADD_DIRS}{CanonVRD}) {
     1918            if ($$et{ADD_DIRS}{CanonVRD}) {
    10911919                my $newVal = '';
    1092                 if (ProcessCanonVRD($exifTool, { OutFile => \$newVal }) > 0) {
     1920                if (ProcessCanonVRD($et, { OutFile => \$newVal }) > 0) {
    10931921                    Write($$dirInfo{OutFile}, $newVal) or return -1;
    1094                     ++$exifTool->{CHANGED};
     1922                    ++$$et{CHANGED};
    10951923                    return 1;
    10961924                }
    10971925            }
    1098             $exifTool->Error('No CanonVRD information to write');
     1926            $et->Error('No CanonVRD information to write');
    10991927        }
    11001928    } else {
    11011929        $num == 0x1c or return 0;
    11021930        $buff =~ /^CANON OPTIONAL DATA\0/ or return 0;
    1103         $exifTool->SetFileType();
     1931        $et->SetFileType();
    11041932        $$dirInfo{DirName} = 'CanonVRD';    # set directory name for verbose output
    1105         my $result = ProcessCanonVRD($exifTool, $dirInfo);
     1933        my $result = ProcessCanonVRD($et, $dirInfo);
    11061934        return $result if $result < 0;
    1107         $result or $exifTool->Warn('Format error in VRD file');
     1935        $result or $et->Warn('Format error in VRD file');
    11081936    }
    11091937    return 1;
     
    11171945sub WriteCanonVRD($$;$)
    11181946{
    1119     my ($exifTool, $dirInfo, $tagTablePtr) = @_;
    1120     $exifTool or return 1;    # allow dummy access
    1121     my $nvHash = $exifTool->GetNewValueHash($Image::ExifTool::Extra{CanonVRD});
    1122     return undef unless Image::ExifTool::IsOverwriting($nvHash);
    1123     my $val = Image::ExifTool::GetNewValues($nvHash);
     1947    my ($et, $dirInfo, $tagTablePtr) = @_;
     1948    $et or return 1;    # allow dummy access
     1949    my $nvHash = $et->GetNewValueHash($Image::ExifTool::Extra{CanonVRD});
     1950    my $val = $et->GetNewValue($nvHash);
    11241951    $val = '' unless defined $val;
    1125     ++$exifTool->{CHANGED};
     1952    return undef unless $et->IsOverwriting($nvHash, $val);
     1953    ++$$et{CHANGED};
    11261954    return $val;
    11271955}
    11281956
    11291957#------------------------------------------------------------------------------
    1130 # Read/write CanonVRD information
     1958# Write DR4-type CanonVRD edit record
     1959# Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
     1960# Returns: VRD data block (may be empty if deleted, of undef on error)
     1961sub WriteCanonDR4($$;$)
     1962{
     1963    my ($et, $dirInfo, $tagTablePtr) = @_;
     1964    $et or return 1;    # allow dummy access
     1965    my $nvHash = $et->GetNewValueHash($Image::ExifTool::Extra{CanonDR4});
     1966    my $val = $et->GetNewValue($nvHash);
     1967    if (defined $val) {
     1968        return undef unless $et->IsOverwriting($nvHash, $val);
     1969        $et->VPrint(0, "  Writing CanonDR4 as a block\n");
     1970        ++$$et{CHANGED};
     1971        return WrapDR4($val);
     1972    }
     1973    my $buff = '';
     1974    $$dirInfo{OutFile} = \$buff;
     1975    return $buff if ProcessCanonVRD($et, $dirInfo, $tagTablePtr) > 0;
     1976    return undef;
     1977}
     1978
     1979#------------------------------------------------------------------------------
     1980# Read/write CanonVRD information (from VRD file or VRD trailer)
    11311981# Inputs: 0) ExifTool object reference, 1) dirInfo reference
    11321982# Returns: 1 on success, 0 not valid VRD, or -1 error writing
    11331983# - updates DataPos to point to start of CanonVRD information
    1134 # - updates DirLen to trailer length
     1984# - updates DirLen to existing trailer length
    11351985sub ProcessCanonVRD($$;$)
    11361986{
    1137     my ($exifTool, $dirInfo, $tagTablePtr) = @_;
     1987    my ($et, $dirInfo, $tagTablePtr) = @_;
    11381988    my $raf = $$dirInfo{RAF};
    11391989    my $offset = $$dirInfo{Offset} || 0;
    11401990    my $outfile = $$dirInfo{OutFile};
    1141     my $verbose = $exifTool->Options('Verbose');
    1142     my $out = $exifTool->Options('TextOut');
    1143     my ($buff, $footer, $header, $created, $err);
    1144     my ($blockLen, $blockType, $size, %didDir);
    1145 
    1146     if (not $raf) {
    1147         my $vrdPt = $$dirInfo{DataPt};
    1148         unless ($vrdPt) {
     1991    my $dataPt = $$dirInfo{DataPt};
     1992    my $verbose = $et->Options('Verbose');
     1993    my $out = $et->Options('TextOut');
     1994    my ($buff, $created, $err, $blockLen, $blockType, %didDir, $fromFile);
     1995#
     1996# The CanonVRD trailer has a 0x1c-byte header and a 0x40-byte footer,
     1997# each beginning with "CANON OPTIONAL DATA\0" and containing an int32u
     1998# giving the size of the contained data (at byte 0x18 and 0x14 respectively)
     1999#
     2000    if ($raf) {
     2001        $fromFile = 1;
     2002    } else {
     2003        unless ($dataPt) {
    11492004            return 1 unless $outfile;
    11502005            # create blank VRD data from scratch
    1151             my $blank = "CANON OPTIONAL DATA\0\0\x01\0\0\0\0\0\0" .
    1152                         "CANON OPTIONAL DATA\0" . ("\0" x 42) . "\xff\xd9";
    1153             $vrdPt = \$blank;
    1154             $verbose and printf $out "  Creating CanonVRD trailer\n";
     2006            my $blank = $blankHeader . $blankFooter;
     2007            $dataPt = \$blank;
     2008            $verbose and print $out "  Creating CanonVRD trailer\n";
    11552009            $created = 1;
    11562010        }
    1157         $raf = new File::RandomAccess($vrdPt);
     2011        $raf = new File::RandomAccess($dataPt);
    11582012    }
    1159     # read and validate the trailer footer
    1160     $raf->Seek(-64-$offset, 2)    or return 0;
    1161     $raf->Read($footer, 64) == 64 or return 0;
    1162     $footer =~ /^CANON OPTIONAL DATA\0(.{4})/s or return 0;
    1163     $size = unpack('N', $1);
    1164 
    1165     # read and validate the header too
    1166     # (header is 0x1c bytes and footer is 0x40 bytes)
    1167     unless (($size & 0x80000000) == 0 and
    1168             $raf->Seek(-$size-0x5c, 1) and
    1169             $raf->Read($header, 0x1c) == 0x1c and
    1170             $header =~ /^CANON OPTIONAL DATA\0/ and
    1171             $raf->Read($buff, $size) == $size)
     2013    # read and validate the footer
     2014    $raf->Seek(-0x40-$offset, 2)    or return 0;
     2015    $raf->Read($buff, 0x40) == 0x40 or return 0;
     2016    $buff =~ /^CANON OPTIONAL DATA\0(.{4})/s or return 0;
     2017    my $dirLen = unpack('N', $1) + 0x5c;  # size including header+footer
     2018
     2019    # read and validate the header
     2020    unless ($dirLen < 0x80000000 and
     2021            $raf->Seek(-$dirLen, 1) and
     2022            $raf->Read($buff, 0x1c) == 0x1c and
     2023            $buff =~ /^CANON OPTIONAL DATA\0/ and
     2024            $raf->Seek(-0x1c, 1))
    11722025    {
    1173         $exifTool->Warn('Bad CanonVRD trailer');
     2026        $et->Warn('Bad CanonVRD trailer');
    11742027        return 0;
    11752028    }
    1176     # extract CanonVRD block if Binary option set, or if requested
    1177     if ($exifTool->{OPTIONS}->{Binary} or $exifTool->{REQ_TAG_LOOKUP}->{canonvrd}) {
    1178         $exifTool->FoundTag('CanonVRD', $header . $buff . $footer);
     2029    # set variables returned in dirInfo hash
     2030    $$dirInfo{DataPos} = $raf->Tell();
     2031    $$dirInfo{DirLen} = $dirLen;
     2032
     2033    if ($outfile and ref $outfile eq 'SCALAR' and not length $$outfile) {
     2034        # write directly to outfile to avoid duplicating data in memory
     2035        $$outfile = $$dataPt unless $fromFile;
     2036        # TRICKY! -- copy to outfile memory buffer and edit in place
     2037        # (so we must disable all Write() calls for this case)
     2038        $dataPt = $outfile;
    11792039    }
    1180     # set variables returned in dirInfo hash
    1181     $$dirInfo{DataPos} = $raf->Tell() - $size - 0x1c;
    1182     $$dirInfo{DirLen} = $size + 0x5c;
     2040    if ($fromFile or $$dirInfo{DirStart}) {
     2041        $dataPt = \$buff unless $dataPt;
     2042        # read VRD data into memory if necessary
     2043        unless ($raf->Read($$dataPt, $dirLen) == $dirLen) {
     2044            $$dataPt = '' if $outfile and $outfile eq $dataPt;
     2045            $et->Warn('Error reading CanonVRD data');
     2046            return 0;
     2047        }
     2048    }
     2049    # exit quickly if writing and no CanonVRD tags are being edited
     2050    if ($outfile and not exists $$et{EDIT_DIRS}{CanonVRD}) {
     2051        print $out "$$et{INDENT}  [nothing changed]\n" if $verbose;
     2052        return 1 if $outfile eq $dataPt;
     2053        return Write($outfile, $$dataPt) ? 1 : -1;
     2054    }
     2055
     2056    my $vrdType = 'VRD';
    11832057
    11842058    if ($outfile) {
    1185         $verbose and not $created and printf $out "  Rewriting CanonVRD trailer\n";
     2059        $verbose and not $created and print $out "  Rewriting CanonVRD trailer\n";
    11862060        # delete CanonVRD information if specified
    1187         if ($exifTool->{DEL_GROUP}->{CanonVRD} or $exifTool->{DEL_GROUP}->{Trailer} or
    1188             # also delete if writing as a block (will get added back again later)
    1189             $exifTool->{NEW_VALUE}->{$Image::ExifTool::Extra{CanonVRD}})
    1190         {
    1191             if ($exifTool->{FILE_TYPE} eq 'VRD') {
    1192                 my $newVal = $exifTool->GetNewValues('CanonVRD');
     2061        my $doDel = $$et{DEL_GROUP}{CanonVRD};
     2062        unless ($doDel) {
     2063            $doDel = 1 if $$et{DEL_GROUP}{Trailer} and $$et{FILE_TYPE} ne 'VRD';
     2064            unless ($doDel) {
     2065                # also delete if writing as a block (will get added back again later)
     2066                if ($$et{NEW_VALUE}{$Image::ExifTool::Extra{CanonVRD}}) {
     2067                    # delete if this isn't version 4
     2068                    $doDel = 1 unless $$dataPt =~ /^.{28}\xff\xff\0\xf7/s;
     2069                }
     2070                if ($$et{NEW_VALUE}{$Image::ExifTool::Extra{CanonDR4}} and not $doDel) {
     2071                    # delete if this is version 4
     2072                    $doDel = 1 if $$dataPt =~ /^.{28}\xff\xff\0\xf7/s;
     2073                }
     2074            }
     2075        }
     2076        if ($doDel) {
     2077            if ($$et{FILE_TYPE} eq 'VRD') {
     2078                my $newVal = $et->GetNewValue('CanonVRD');
    11932079                if ($newVal) {
    1194                     $verbose and printf $out "  Writing CanonVRD as a block\n";
    1195                     Write($outfile, $newVal) or return -1;
    1196                     ++$exifTool->{CHANGED};
     2080                    $verbose and print $out "  Writing CanonVRD as a block\n";
     2081                    if ($outfile eq $dataPt) {
     2082                        $$outfile = $newVal;
     2083                    } else {
     2084                        Write($outfile, $newVal) or return -1;
     2085                    }
     2086                    $$et{DidCanonVRD} = 1;
     2087                    ++$$et{CHANGED};
    11972088                } else {
    1198                     $exifTool->Error("Can't delete all CanonVRD information from a VRD file");
     2089                    $et->Error("Can't delete all CanonVRD information from a VRD file");
    11992090                }
    12002091            } else {
    1201                 $verbose and printf $out "  Deleting CanonVRD trailer\n";
    1202                 ++$exifTool->{CHANGED};
     2092                $verbose and print $out "  Deleting CanonVRD trailer\n";
     2093                $$outfile = '' if $outfile eq $dataPt;
     2094                ++$$et{CHANGED};
    12032095            }
    12042096            return 1;
    12052097        }
    12062098        # write now and return if CanonVRD was set as a block
    1207         my $val = $exifTool->GetNewValues('CanonVRD');
     2099        my $val = $et->GetNewValue('CanonVRD');
     2100        unless ($val) {
     2101            $val = $et->GetNewValue('CanonDR4');
     2102            $vrdType = 'DR4' if $val;
     2103        }
    12082104        if ($val) {
    1209             $verbose and print $out "  Writing CanonVRD as a block\n";
    1210             Write($outfile, $val) or return -1;
    1211             ++$exifTool->{CHANGED};
     2105            $verbose and print $out "  Writing Canon$vrdType as a block\n";
     2106            # must wrap DR4 data with the VRD header/footer and edit record
     2107            $val = WrapDR4($val) if $vrdType eq 'DR4';
     2108            if ($outfile eq $dataPt) {
     2109                $$outfile = $val;
     2110            } else {
     2111                Write($outfile, $val) or return -1;
     2112            }
     2113            $$et{DidCanonVRD} = 1;
     2114            ++$$et{CHANGED};
    12122115            return 1;
    12132116        }
    1214     } elsif ($verbose or $exifTool->{HTML_DUMP}) {
    1215         $exifTool->DumpTrailer($dirInfo) if $$dirInfo{RAF};
     2117    } elsif ($verbose or $$et{HTML_DUMP}) {
     2118        $et->DumpTrailer($dirInfo) if $$dirInfo{RAF};
    12162119    }
    12172120
     
    12192122
    12202123    # validate VRD trailer and get position and length of edit record
    1221     SetByteOrder('MM');
    1222     my $pos = 0;
    1223     my $vrdPos = $$dirInfo{DataPos} + length($header);
    1224     my $dataPt = \$buff;
     2124    SetByteOrder('MM'); # VRD header/footer is big-endian
     2125    my $pos = 0x1c;     # start at end of header
    12252126
    12262127    # loop through the VRD blocks
    12272128    for (;;) {
    1228         if ($pos + 8 > $size) {
    1229             last if $pos == $size;
    1230             $blockLen = $size;  # mark as invalid
     2129        my $end = $dirLen - 0x40;   # end of last VRD block (and start of footer)
     2130        if ($pos + 8 > $end) {
     2131            last if $pos == $end;
     2132            $blockLen = $end;       # mark as invalid
    12312133        } else {
    12322134            $blockType = Get32u($dataPt, $pos);
    12332135            $blockLen = Get32u($dataPt, $pos + 4);
    12342136        }
    1235         $pos += 8;          # move to start of block
    1236         if ($pos + $blockLen > $size) {
    1237             $exifTool->Warn('Possibly corrupt CanonVRD block');
     2137        $vrdType = 'DR4' if $blockType == 0xffff00f7;
     2138        $pos += 8;  # move to start of block
     2139        if ($pos + $blockLen > $end) {
     2140            $et->Warn('Possibly corrupt CanonVRD block');
    12382141            last;
    12392142        }
    12402143        if ($verbose > 1 and not $outfile) {
    12412144            printf $out "  CanonVRD block 0x%.8x ($blockLen bytes at offset 0x%x)\n",
    1242                 $blockType, $pos + $vrdPos;
    1243             if ($verbose > 2) {
    1244                 my %parms = (
    1245                     Start  => $pos,
    1246                     Addr   => $pos + $vrdPos,
    1247                     Out    => $out,
    1248                     Prefix => $$exifTool{INDENT},
    1249                 );
    1250                 $parms{MaxLen} = $verbose == 3 ? 96 : 2048 if $verbose < 5;
    1251                 Image::ExifTool::HexDump($dataPt, $blockLen, %parms);
    1252             }
     2145                $blockType, $pos + $$dirInfo{DataPos};
     2146            $et->VerboseDump($dataPt, Len => $blockLen, Start => $pos, Addr => $pos + $$dirInfo{DataPos});
    12532147        }
    12542148        my $tagInfo = $$tagTablePtr{$blockType};
    12552149        unless ($tagInfo) {
    1256             unless ($exifTool->Options('Unknown')) {
     2150            unless ($et->Options('Unknown')) {
    12572151                $pos += $blockLen;  # step to next block
    12582152                next;
     
    12662160                Binary      => 1,
    12672161            };
    1268             Image::ExifTool::AddTagToTable($tagTablePtr, $blockType, $tagInfo);
     2162            AddTagToTable($tagTablePtr, $blockType, $tagInfo);
    12692163        }
    12702164        if ($$tagInfo{SubDirectory}) {
     
    12732167                DataPt   => $dataPt,
    12742168                DataLen  => length $$dataPt,
    1275                 DataPos  => $vrdPos,
     2169                DataPos  => $$dirInfo{DataPos},
    12762170                DirStart => $pos,
    12772171                DirLen   => $blockLen,
     
    12832177                # set flag indicating we did this directory
    12842178                $didDir{$$tagInfo{Name}} = 1;
    1285                 my $dat;
    1286                 if ($$exifTool{NEW_VALUE}{$tagInfo}) {
     2179                my ($dat, $diff);
     2180                if ($$et{NEW_VALUE}{$tagInfo}) {
    12872181                    # write as a block
    1288                     $exifTool->VPrint(0, "Writing $$tagInfo{Name} as a block\n");
    1289                     $dat = $exifTool->GetNewValues($tagInfo);
     2182                    $et->VPrint(0, "Writing $$tagInfo{Name} as a block\n");
     2183                    $dat = $et->GetNewValue($tagInfo);
    12902184                    $dat = '' unless defined $dat;
    1291                     ++$$exifTool{CHANGED};
     2185                    ++$$et{CHANGED};
    12922186                } else {
    1293                     $dat = $exifTool->WriteDirectory(\%subdirInfo, $subTablePtr);
     2187                    $dat = $et->WriteDirectory(\%subdirInfo, $subTablePtr);
    12942188                }
    12952189                # update data with new directory
    12962190                if (defined $dat) {
    1297                     my $buf2;
    1298                     if (length $dat or $$exifTool{FILE_TYPE} !~ /^(CRW|VRD)$/) {
     2191                    if (length $dat or $$et{FILE_TYPE} !~ /^(CRW|VRD)$/) {
    12992192                        # replace with new block (updating the block length word)
    1300                         $buf2 = substr($$dataPt, 0, $pos - 4) . Set32u(length $dat) . $dat;
     2193                        substr($$dataPt, $pos-4, $blockLen+4) = Set32u(length $dat) . $dat;
    13012194                    } else {
    13022195                        # remove block totally (CRW/VRD files only)
    1303                         $buf2 = substr($$dataPt, 0, $pos - 8);
     2196                        substr($$dataPt, $pos-8, $blockLen+8) = '';
    13042197                    }
    1305                     my $oldBlockEnd = $pos + $blockLen;
    1306                     $pos = length $$dataPt; # set to start of next block
    1307                     $buf2 .= substr($$dataPt, $oldBlockEnd);
    1308                     undef $$dataPt;         # free the memory
    1309                     $dataPt = \$buf2;
    1310                     # update the new VRD length in the header/footer
    1311                     Set32u(length($buf2), \$header, 0x18);
    1312                     Set32u(length($buf2), \$footer, 0x14);
    1313                     next;
     2198                    # make necessary adjustments if block changes length
     2199                    if (($diff = length($$dataPt) - $dirLen) != 0) {
     2200                        $pos += $diff;
     2201                        $dirLen += $diff;
     2202                        # update the new VRD length in the header/footer
     2203                        Set32u($dirLen - 0x5c, $dataPt, 0x18);
     2204                        Set32u($dirLen - 0x5c, $dataPt, $dirLen - 0x2c);
     2205                    }
    13142206                }
    13152207            } else {
    13162208                # extract as a block if requested
    1317                 $exifTool->ProcessDirectory(\%subdirInfo, $subTablePtr);
     2209                $et->ProcessDirectory(\%subdirInfo, $subTablePtr);
    13182210            }
    13192211        } else {
    1320             $exifTool->HandleTag($tagTablePtr, $blockType, substr($$dataPt, $pos, $blockLen));
     2212            $et->HandleTag($tagTablePtr, $blockType, substr($$dataPt, $pos, $blockLen));
    13212213        }
    13222214        $pos += $blockLen;  # step to next block
     
    13242216    if ($outfile) {
    13252217        # create XMP block if necessary (CRW/VRD files only)
    1326         if ($$exifTool{ADD_DIRS}{CanonVRD} and not $didDir{XMP}) {
     2218        if ($$et{ADD_DIRS}{CanonVRD} and not $didDir{XMP}) {
    13272219            my $subTablePtr = GetTagTable('Image::ExifTool::XMP::Main');
    1328             my $dat = $exifTool->WriteDirectory({ Parent => 'CanonVRD' }, $subTablePtr);
     2220            my $dat = $et->WriteDirectory({ Parent => 'CanonVRD' }, $subTablePtr);
    13292221            if ($dat) {
    1330                 $$dataPt .= Set32u(0xffff00f6) . Set32u(length $dat) . $dat;
     2222                my $blockLen = length $dat;
     2223                substr($$dataPt, -0x40, 0) = Set32u(0xffff00f6) . Set32u(length $dat) . $dat;
     2224                $dirLen = length $$dataPt;
    13312225                # update the new VRD length in the header/footer
    1332                 Set32u(length($$dataPt), \$header, 0x18);
    1333                 Set32u(length($$dataPt), \$footer, 0x14);
     2226                Set32u($dirLen - 0x5c, $dataPt, 0x18);
     2227                Set32u($dirLen - 0x5c, $dataPt, $dirLen - 0x2c);
    13342228            }
    13352229        }
    13362230        # write CanonVRD trailer unless it is empty
    13372231        if (length $$dataPt) {
    1338             Write($outfile, $header, $$dataPt, $footer) or $err = 1;
     2232            Write($outfile, $$dataPt) or $err = 1 unless $outfile eq $dataPt;
    13392233        } else {
    1340             $verbose and printf $out "  Deleting CanonVRD trailer\n";
    1341         }
     2234            $verbose and print $out "  Deleting CanonVRD trailer\n";
     2235        }
     2236    } elsif ($vrdType eq 'VRD' and (($$et{TAGS_FROM_FILE} and
     2237        not $$et{EXCL_TAG_LOOKUP}{canonvrd}) or $$et{REQ_TAG_LOOKUP}{canonvrd}))
     2238    {
     2239        # extract CanonVRD block if copying tags, or if requested (and not DR4 info)
     2240        $et->FoundTag('CanonVRD', $buff);
    13422241    }
    1343     undef $$dataPt;
     2242    undef $buff;
    13442243    return $err ? -1 : 1;
    13452244}
     
    13512250=head1 NAME
    13522251
    1353 Image::ExifTool::CanonVRD - Read/write Canon VRD information
     2252Image::ExifTool::CanonVRD - Read/write Canon VRD and DR4 information
    13542253
    13552254=head1 SYNOPSIS
     
    13602259
    13612260This module contains definitions required by Image::ExifTool to read and
    1362 write VRD Recipe Data information as written by the Canon Digital Photo
    1363 Professional software.  This information is written to VRD files, and as a
    1364 trailer in JPEG, CRW, CR2 and TIFF images.
     2261write VRD and DR4 Recipe Data information as written by the Canon Digital
     2262Photo Professional software.  This information is written to VRD and DR4
     2263files, and as a trailer in JPEG, CRW, CR2 and TIFF images.
    13652264
    13662265=head1 AUTHOR
    13672266
    1368 Copyright 2003-2011, Phil Harvey (phil at owl.phy.queensu.ca)
     2267Copyright 2003-2021, Phil Harvey (philharvey66 at gmail.com)
    13692268
    13702269This library is free software; you can redistribute it and/or modify it
Note: See TracChangeset for help on using the changeset viewer.