1 | #------------------------------------------------------------------------------
|
---|
2 | # File: CanonVRD.pm
|
---|
3 | #
|
---|
4 | # Description: Read/write Canon VRD information
|
---|
5 | #
|
---|
6 | # Revisions: 10/30/2006 - P. Harvey Created
|
---|
7 | #------------------------------------------------------------------------------
|
---|
8 |
|
---|
9 | package Image::ExifTool::CanonVRD;
|
---|
10 |
|
---|
11 | use strict;
|
---|
12 | use vars qw($VERSION);
|
---|
13 | use Image::ExifTool qw(:DataAccess :Utils);
|
---|
14 |
|
---|
15 | $VERSION = '1.05';
|
---|
16 |
|
---|
17 | sub ProcessCanonVRD($$);
|
---|
18 |
|
---|
19 | my $debug; # set this to 1 for offsets relative to binary data start
|
---|
20 |
|
---|
21 | my %noYes = ( 0 => 'No', 1 => 'Yes' );
|
---|
22 |
|
---|
23 | # main tag table IPTC-format records in CanonVRD trailer
|
---|
24 | %Image::ExifTool::CanonVRD::Main = (
|
---|
25 | VARS => { INDEX => 1 },
|
---|
26 | NOTES => q{
|
---|
27 | Canon Digital Photo Professional writes VRD (Recipe Data) information as a
|
---|
28 | trailer record to JPEG, TIFF, CRW and CR2 images, or as a stand-alone VRD
|
---|
29 | file. The tags listed below represent information found in this record. The
|
---|
30 | complete VRD data record may be accessed as a block using the Extra
|
---|
31 | 'CanonVRD' tag, but this tag is not extracted or copied unless specified
|
---|
32 | explicitly.
|
---|
33 | },
|
---|
34 | 0 => {
|
---|
35 | Name => 'VRD1',
|
---|
36 | Size => 0x272, # size of version 1.0 edit information in bytes
|
---|
37 | SubDirectory => { TagTable => 'Image::ExifTool::CanonVRD::Ver1' },
|
---|
38 | },
|
---|
39 | 1 => {
|
---|
40 | Name => 'VRDStampTool',
|
---|
41 | # (size is variable, and obtained from int32u at end of last directory)
|
---|
42 | Size => 0, # size is variable, and obtained from int32u at directory start
|
---|
43 | SubDirectory => { TagTable => 'Image::ExifTool::CanonVRD::StampTool' },
|
---|
44 | },
|
---|
45 | 2 => {
|
---|
46 | Name => 'VRD2',
|
---|
47 | Size => undef, # size is the remaining edit data
|
---|
48 | SubDirectory => { TagTable => 'Image::ExifTool::CanonVRD::Ver2' },
|
---|
49 | },
|
---|
50 | );
|
---|
51 |
|
---|
52 | # VRD version 1 tags
|
---|
53 | %Image::ExifTool::CanonVRD::Ver1 = (
|
---|
54 | PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
|
---|
55 | WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
|
---|
56 | CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
|
---|
57 | WRITABLE => 1,
|
---|
58 | GROUPS => { 2 => 'Image' },
|
---|
59 | #
|
---|
60 | # RAW image adjustment
|
---|
61 | #
|
---|
62 | 0x002 => {
|
---|
63 | Name => 'VRDVersion',
|
---|
64 | Format => 'int16u',
|
---|
65 | Writable => 0,
|
---|
66 | PrintConv => 'sprintf("%.2f", $val / 100)',
|
---|
67 | },
|
---|
68 | # 0x006 related somehow to RGB levels
|
---|
69 | 0x008 => {
|
---|
70 | Name => 'WBAdjRGBLevels',
|
---|
71 | Format => 'int16u[3]',
|
---|
72 | },
|
---|
73 | 0x018 => {
|
---|
74 | Name => 'WhiteBalanceAdj',
|
---|
75 | Format => 'int16u',
|
---|
76 | PrintConv => {
|
---|
77 | 0 => 'Auto',
|
---|
78 | 1 => 'Daylight',
|
---|
79 | 2 => 'Cloudy',
|
---|
80 | 3 => 'Tungsten',
|
---|
81 | 4 => 'Fluorescent',
|
---|
82 | 5 => 'Flash',
|
---|
83 | 8 => 'Shade',
|
---|
84 | 9 => 'Kelvin',
|
---|
85 | 30 => 'Manual (Click)',
|
---|
86 | 31 => 'Shot Settings',
|
---|
87 | },
|
---|
88 | },
|
---|
89 | 0x01a => {
|
---|
90 | Name => 'WBAdjColorTemp',
|
---|
91 | Format => 'int16u',
|
---|
92 | },
|
---|
93 | # 0x01c similar to 0x006
|
---|
94 | # 0x01e similar to 0x008
|
---|
95 | 0x024 => {
|
---|
96 | Name => 'WBFineTuneActive',
|
---|
97 | Format => 'int16u',
|
---|
98 | PrintConv => \%noYes,
|
---|
99 | },
|
---|
100 | 0x028 => {
|
---|
101 | Name => 'WBFineTuneSaturation',
|
---|
102 | Format => 'int16u',
|
---|
103 | },
|
---|
104 | 0x02c => {
|
---|
105 | Name => 'WBFineTuneTone',
|
---|
106 | Format => 'int16u',
|
---|
107 | },
|
---|
108 | 0x02e => {
|
---|
109 | Name => 'RawColorAdj',
|
---|
110 | Format => 'int16u',
|
---|
111 | PrintConv => {
|
---|
112 | 0 => 'Shot Settings',
|
---|
113 | 1 => 'Faithful',
|
---|
114 | 2 => 'Custom',
|
---|
115 | },
|
---|
116 | },
|
---|
117 | 0x030 => {
|
---|
118 | Name => 'RawCustomSaturation',
|
---|
119 | Format => 'int32s',
|
---|
120 | },
|
---|
121 | 0x034 => {
|
---|
122 | Name => 'RawCustomTone',
|
---|
123 | Format => 'int32s',
|
---|
124 | },
|
---|
125 | 0x038 => {
|
---|
126 | Name => 'RawBrightnessAdj',
|
---|
127 | Format => 'int32s',
|
---|
128 | ValueConv => '$val / 6000',
|
---|
129 | ValueConvInv => 'int($val * 6000 + ($val < 0 ? -0.5 : 0.5))',
|
---|
130 | PrintConv => 'sprintf("%.2f",$val)',
|
---|
131 | PrintConvInv => '$val',
|
---|
132 | },
|
---|
133 | 0x03c => {
|
---|
134 | Name => 'ToneCurveProperty',
|
---|
135 | Format => 'int16u',
|
---|
136 | PrintConv => {
|
---|
137 | 0 => 'Shot Settings',
|
---|
138 | 1 => 'Linear',
|
---|
139 | 2 => 'Custom 1',
|
---|
140 | 3 => 'Custom 2',
|
---|
141 | 4 => 'Custom 3',
|
---|
142 | 5 => 'Custom 4',
|
---|
143 | 6 => 'Custom 5',
|
---|
144 | },
|
---|
145 | },
|
---|
146 | # 0x040 usually "10 9 2"
|
---|
147 | 0x07a => {
|
---|
148 | Name => 'DynamicRangeMin',
|
---|
149 | Format => 'int16u',
|
---|
150 | },
|
---|
151 | 0x07c => {
|
---|
152 | Name => 'DynamicRangeMax',
|
---|
153 | Format => 'int16u',
|
---|
154 | },
|
---|
155 | # 0x0c6 usually "10 9 2"
|
---|
156 | #
|
---|
157 | # RGB image adjustment
|
---|
158 | #
|
---|
159 | 0x110 => {
|
---|
160 | Name => 'ToneCurveActive',
|
---|
161 | Format => 'int16u',
|
---|
162 | PrintConv => \%noYes,
|
---|
163 | },
|
---|
164 | 0x114 => {
|
---|
165 | Name => 'BrightnessAdj',
|
---|
166 | Format => 'int8s',
|
---|
167 | },
|
---|
168 | 0x115 => {
|
---|
169 | Name => 'ContrastAdj',
|
---|
170 | Format => 'int8s',
|
---|
171 | },
|
---|
172 | 0x116 => {
|
---|
173 | Name => 'SaturationAdj',
|
---|
174 | Format => 'int16s',
|
---|
175 | },
|
---|
176 | 0x11e => {
|
---|
177 | Name => 'ColorToneAdj',
|
---|
178 | Notes => 'in degrees, so -1 is the same as 359',
|
---|
179 | Format => 'int32s',
|
---|
180 | },
|
---|
181 | 0x160 => {
|
---|
182 | Name => 'RedCurvePoints',
|
---|
183 | Format => 'int16u[21]',
|
---|
184 | PrintConv => 'Image::ExifTool::CanonVRD::ToneCurvePrint($val)',
|
---|
185 | PrintConvInv => 'Image::ExifTool::CanonVRD::ToneCurvePrintInv($val)',
|
---|
186 | },
|
---|
187 | 0x19a => {
|
---|
188 | Name => 'GreenCurvePoints',
|
---|
189 | Format => 'int16u[21]',
|
---|
190 | PrintConv => 'Image::ExifTool::CanonVRD::ToneCurvePrint($val)',
|
---|
191 | PrintConvInv => 'Image::ExifTool::CanonVRD::ToneCurvePrintInv($val)',
|
---|
192 | },
|
---|
193 | 0x1d4 => {
|
---|
194 | Name => 'BlueCurvePoints',
|
---|
195 | Format => 'int16u[21]',
|
---|
196 | PrintConv => 'Image::ExifTool::CanonVRD::ToneCurvePrint($val)',
|
---|
197 | PrintConvInv => 'Image::ExifTool::CanonVRD::ToneCurvePrintInv($val)',
|
---|
198 | },
|
---|
199 | 0x18a => {
|
---|
200 | Name => 'RedCurveLimits',
|
---|
201 | Notes => '4 numbers: input and output highlight and shadow points',
|
---|
202 | Format => 'int16u[4]',
|
---|
203 | },
|
---|
204 | 0x1c4 => {
|
---|
205 | Name => 'GreenCurveLimits',
|
---|
206 | Format => 'int16u[4]',
|
---|
207 | },
|
---|
208 | 0x1fe => {
|
---|
209 | Name => 'BlueCurveLimits',
|
---|
210 | Format => 'int16u[4]',
|
---|
211 | },
|
---|
212 | 0x20e => {
|
---|
213 | Name => 'RGBCurvePoints',
|
---|
214 | Format => 'int16u[21]',
|
---|
215 | PrintConv => 'Image::ExifTool::CanonVRD::ToneCurvePrint($val)',
|
---|
216 | PrintConvInv => 'Image::ExifTool::CanonVRD::ToneCurvePrintInv($val)',
|
---|
217 | },
|
---|
218 | 0x238 => {
|
---|
219 | Name => 'RGBCurveLimits',
|
---|
220 | Format => 'int16u[4]',
|
---|
221 | },
|
---|
222 | 0x244 => {
|
---|
223 | Name => 'CropActive',
|
---|
224 | Format => 'int16u',
|
---|
225 | PrintConv => \%noYes,
|
---|
226 | },
|
---|
227 | 0x246 => {
|
---|
228 | Name => 'CropLeft',
|
---|
229 | Notes => 'crop coordinates in original unrotated image',
|
---|
230 | Format => 'int16u',
|
---|
231 | },
|
---|
232 | 0x248 => {
|
---|
233 | Name => 'CropTop',
|
---|
234 | Format => 'int16u',
|
---|
235 | },
|
---|
236 | 0x24a => {
|
---|
237 | Name => 'CropWidth',
|
---|
238 | Format => 'int16u',
|
---|
239 | },
|
---|
240 | 0x24c => {
|
---|
241 | Name => 'CropHeight',
|
---|
242 | Format => 'int16u',
|
---|
243 | },
|
---|
244 | 0x260 => {
|
---|
245 | Name => 'CropAspectRatio',
|
---|
246 | Format => 'int16u',
|
---|
247 | PrintConv => {
|
---|
248 | 0 => 'Free',
|
---|
249 | 1 => '3:2',
|
---|
250 | 2 => '2:3',
|
---|
251 | 3 => '4:3',
|
---|
252 | 4 => '3:4',
|
---|
253 | 5 => 'A-size Landscape',
|
---|
254 | 6 => 'A-size Portrait',
|
---|
255 | 7 => 'Letter-size Landscape',
|
---|
256 | 8 => 'Letter-size Portrait',
|
---|
257 | 9 => '4:5',
|
---|
258 | 10 => '5:4',
|
---|
259 | 11 => '1:1',
|
---|
260 | },
|
---|
261 | },
|
---|
262 | 0x262 => {
|
---|
263 | Name => 'ConstrainedCropWidth',
|
---|
264 | Format => 'float',
|
---|
265 | PrintConv => 'sprintf("%.7g",$val)',
|
---|
266 | PrintConvInv => '$val',
|
---|
267 | },
|
---|
268 | 0x266 => {
|
---|
269 | Name => 'ConstrainedCropHeight',
|
---|
270 | Format => 'float',
|
---|
271 | PrintConv => 'sprintf("%.7g",$val)',
|
---|
272 | PrintConvInv => '$val',
|
---|
273 | },
|
---|
274 | 0x26a => {
|
---|
275 | Name => 'CheckMark',
|
---|
276 | Format => 'int16u',
|
---|
277 | PrintConv => {
|
---|
278 | 0 => 'Clear',
|
---|
279 | 1 => 1,
|
---|
280 | 2 => 2,
|
---|
281 | 3 => 3,
|
---|
282 | },
|
---|
283 | },
|
---|
284 | 0x26e => {
|
---|
285 | Name => 'Rotation',
|
---|
286 | Format => 'int16u',
|
---|
287 | PrintConv => {
|
---|
288 | 0 => 0,
|
---|
289 | 1 => 90,
|
---|
290 | 2 => 180,
|
---|
291 | 3 => 270,
|
---|
292 | },
|
---|
293 | },
|
---|
294 | 0x270 => {
|
---|
295 | Name => 'WorkColorSpace',
|
---|
296 | Format => 'int16u',
|
---|
297 | PrintConv => {
|
---|
298 | 0 => 'sRGB',
|
---|
299 | 1 => 'Adobe RGB',
|
---|
300 | 2 => 'Wide Gamut RGB',
|
---|
301 | 3 => 'Apple RGB',
|
---|
302 | 4 => 'ColorMatch RGB',
|
---|
303 | },
|
---|
304 | },
|
---|
305 | # (VRD 1.0 edit data ends here -- 0x272 bytes long)
|
---|
306 | );
|
---|
307 |
|
---|
308 | # VRD Stamp Tool tags
|
---|
309 | %Image::ExifTool::CanonVRD::StampTool = (
|
---|
310 | PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
|
---|
311 | GROUPS => { 2 => 'Image' },
|
---|
312 | 0x00 => {
|
---|
313 | Name => 'StampToolCount',
|
---|
314 | Format => 'int32u',
|
---|
315 | },
|
---|
316 | );
|
---|
317 |
|
---|
318 | # VRD version 2 and 3 tags
|
---|
319 | %Image::ExifTool::CanonVRD::Ver2 = (
|
---|
320 | PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
|
---|
321 | WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
|
---|
322 | CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
|
---|
323 | WRITABLE => 1,
|
---|
324 | GROUPS => { 2 => 'Image' },
|
---|
325 | NOTES => 'Tags added in DPP version 2.0 and later.',
|
---|
326 | 0x04 => {
|
---|
327 | Name => 'PictureStyle',
|
---|
328 | Format => 'int16u',
|
---|
329 | PrintConv => {
|
---|
330 | 0 => 'Standard',
|
---|
331 | 1 => 'Portrait',
|
---|
332 | 2 => 'Landscape',
|
---|
333 | 3 => 'Neutral',
|
---|
334 | 4 => 'Faithful',
|
---|
335 | 5 => 'Monochrome',
|
---|
336 | },
|
---|
337 | },
|
---|
338 | 0x1a => {
|
---|
339 | Name => 'RawColorToneAdj',
|
---|
340 | Format => 'int16s',
|
---|
341 | },
|
---|
342 | 0x1c => {
|
---|
343 | Name => 'RawSaturationAdj',
|
---|
344 | Format => 'int16s',
|
---|
345 | },
|
---|
346 | 0x1e => {
|
---|
347 | Name => 'RawContrastAdj',
|
---|
348 | Format => 'int16s',
|
---|
349 | },
|
---|
350 | 0x20 => {
|
---|
351 | Name => 'RawLinear',
|
---|
352 | Format => 'int16u',
|
---|
353 | PrintConv => \%noYes,
|
---|
354 | },
|
---|
355 | 0x22 => {
|
---|
356 | Name => 'RawSharpnessAdj',
|
---|
357 | Format => 'int16s',
|
---|
358 | },
|
---|
359 | 0x24 => {
|
---|
360 | Name => 'RawHighlightPoint',
|
---|
361 | Format => 'int16s',
|
---|
362 | },
|
---|
363 | 0x26 => {
|
---|
364 | Name => 'RawShadowPoint',
|
---|
365 | Format => 'int16s',
|
---|
366 | },
|
---|
367 | 0x74 => {
|
---|
368 | Name => 'MonochromeFilterEffect',
|
---|
369 | Format => 'int16s',
|
---|
370 | PrintConv => {
|
---|
371 | -2 => 'None',
|
---|
372 | -1 => 'Yellow',
|
---|
373 | 0 => 'Orange',
|
---|
374 | 1 => 'Red',
|
---|
375 | 2 => 'Green',
|
---|
376 | },
|
---|
377 | },
|
---|
378 | 0x76 => {
|
---|
379 | Name => 'MonochromeToningEffect',
|
---|
380 | Format => 'int16s',
|
---|
381 | PrintConv => {
|
---|
382 | -2 => 'None',
|
---|
383 | -1 => 'Sepia',
|
---|
384 | 0 => 'Blue',
|
---|
385 | 1 => 'Purple',
|
---|
386 | 2 => 'Green',
|
---|
387 | },
|
---|
388 | },
|
---|
389 | 0x78 => {
|
---|
390 | Name => 'MonochromeContrast',
|
---|
391 | Format => 'int16s',
|
---|
392 | },
|
---|
393 | 0x7a => {
|
---|
394 | Name => 'MonochromeLinear',
|
---|
395 | Format => 'int16u',
|
---|
396 | PrintConv => \%noYes,
|
---|
397 | },
|
---|
398 | 0x7c => {
|
---|
399 | Name => 'MonochromeSharpness',
|
---|
400 | Format => 'int16s',
|
---|
401 | },
|
---|
402 | # (VRD 2.0 edit data ends here -- offset 0xb2)
|
---|
403 | 0xbc => {
|
---|
404 | Name => 'ChrominanceNoiseReduction',
|
---|
405 | Format => 'int16u',
|
---|
406 | PrintConv => {
|
---|
407 | 0 => 'Off',
|
---|
408 | 58 => 'Low',
|
---|
409 | 100 => 'High',
|
---|
410 | },
|
---|
411 | },
|
---|
412 | 0xbe => {
|
---|
413 | Name => 'LuminanceNoiseReduction',
|
---|
414 | Format => 'int16u',
|
---|
415 | PrintConv => {
|
---|
416 | 0 => 'Off',
|
---|
417 | 65 => 'Low',
|
---|
418 | 100 => 'High',
|
---|
419 | },
|
---|
420 | },
|
---|
421 | 0xc0 => {
|
---|
422 | Name => 'ChrominanceNR_TIFF_JPEG',
|
---|
423 | Format => 'int16u',
|
---|
424 | PrintConv => {
|
---|
425 | 0 => 'Off',
|
---|
426 | 33 => 'Low',
|
---|
427 | 100 => 'High',
|
---|
428 | },
|
---|
429 | }
|
---|
430 | # (VRD 3.0 edit data ends here -- offset 0xc4)
|
---|
431 | );
|
---|
432 |
|
---|
433 | #------------------------------------------------------------------------------
|
---|
434 | # Tone curve print conversion
|
---|
435 | sub ToneCurvePrint($)
|
---|
436 | {
|
---|
437 | my $val = shift;
|
---|
438 | my @vals = split ' ', $val;
|
---|
439 | return $val unless @vals == 21;
|
---|
440 | my $n = shift @vals;
|
---|
441 | return $val unless $n >= 2 and $n <= 10;
|
---|
442 | $val = '';
|
---|
443 | while ($n--) {
|
---|
444 | $val and $val .= ' ';
|
---|
445 | $val .= '(' . shift(@vals) . ',' . shift(@vals) . ')';
|
---|
446 | }
|
---|
447 | return $val;
|
---|
448 | }
|
---|
449 |
|
---|
450 | #------------------------------------------------------------------------------
|
---|
451 | # Inverse print conversion for tone curve
|
---|
452 | sub ToneCurvePrintInv($)
|
---|
453 | {
|
---|
454 | my $val = shift;
|
---|
455 | my @vals = ($val =~ /\((\d+),(\d+)\)/g);
|
---|
456 | return undef unless @vals >= 4 and @vals <= 20 and not @vals & 0x01;
|
---|
457 | unshift @vals, scalar(@vals) / 2;
|
---|
458 | while (@vals < 21) { push @vals, 0 }
|
---|
459 | return join(' ',@vals);
|
---|
460 | }
|
---|
461 |
|
---|
462 | #------------------------------------------------------------------------------
|
---|
463 | # Read/write Canon VRD file
|
---|
464 | # Inputs: 0) ExifTool object reference, 1) dirInfo reference
|
---|
465 | # Returns: 1 if this was a Canon VRD file, 0 otherwise, -1 on write error
|
---|
466 | sub ProcessVRD($$)
|
---|
467 | {
|
---|
468 | my ($exifTool, $dirInfo) = @_;
|
---|
469 | my $raf = $$dirInfo{RAF};
|
---|
470 | my $buff;
|
---|
471 | my $num = $raf->Read($buff, 0x1c);
|
---|
472 | if (not $num and $$dirInfo{OutFile}) {
|
---|
473 | # create new VRD file from scratch
|
---|
474 | my $newVal = $exifTool->GetNewValues('CanonVRD');
|
---|
475 | if ($newVal) {
|
---|
476 | $exifTool->VPrint(0, " Writing CanonVRD as a block\n");
|
---|
477 | Write($$dirInfo{OutFile}, $newVal) or return -1;
|
---|
478 | ++$exifTool->{CHANGED};
|
---|
479 | } else {
|
---|
480 | $exifTool->Error('No CanonVRD information to write');
|
---|
481 | }
|
---|
482 | } else {
|
---|
483 | $num == 0x1c or return 0;
|
---|
484 | $buff =~ /^CANON OPTIONAL DATA\0/ or return 0;
|
---|
485 | $exifTool->SetFileType();
|
---|
486 | $$dirInfo{DirName} = 'CanonVRD'; # set directory name for verbose output
|
---|
487 | my $result = ProcessCanonVRD($exifTool, $dirInfo);
|
---|
488 | return $result if $result < 0;
|
---|
489 | $result or $exifTool->Warn('Format error in VRD file');
|
---|
490 | }
|
---|
491 | return 1;
|
---|
492 | }
|
---|
493 |
|
---|
494 | #------------------------------------------------------------------------------
|
---|
495 | # Read/write CanonVRD information
|
---|
496 | # Inputs: 0) ExifTool object reference, 1) dirInfo reference
|
---|
497 | # Returns: 1 on success, 0 not valid VRD, or -1 error writing
|
---|
498 | # - updates DataPos to point to start of CanonVRD information
|
---|
499 | # - updates DirLen to trailer length
|
---|
500 | sub ProcessCanonVRD($$)
|
---|
501 | {
|
---|
502 | my ($exifTool, $dirInfo) = @_;
|
---|
503 | my $raf = $$dirInfo{RAF};
|
---|
504 | my $offset = $$dirInfo{Offset} || 0;
|
---|
505 | my $outfile = $$dirInfo{OutFile};
|
---|
506 | my $verbose = $exifTool->Options('Verbose');
|
---|
507 | my $out = $exifTool->Options('TextOut');
|
---|
508 | my ($buff, $footer, $header, $err);
|
---|
509 | my ($blockLen, $blockType, $recLen, $size);
|
---|
510 |
|
---|
511 | # read and validate the trailer footer
|
---|
512 | $raf->Seek(-64-$offset, 2) or return 0;
|
---|
513 | $raf->Read($footer, 64) == 64 or return 0;
|
---|
514 | $footer =~ /^CANON OPTIONAL DATA\0(.{4})/s or return 0;
|
---|
515 | $size = unpack('N', $1);
|
---|
516 |
|
---|
517 | # read and validate the header too
|
---|
518 | # (header is 0x1c bytes and footer is 0x40 bytes)
|
---|
519 | unless ($size > 12 and ($size & 0x80000000) == 0 and
|
---|
520 | $raf->Seek(-$size-0x5c, 1) and
|
---|
521 | $raf->Read($header, 0x1c) == 0x1c and
|
---|
522 | $header =~ /^CANON OPTIONAL DATA\0/ and
|
---|
523 | $raf->Read($buff, $size) == $size)
|
---|
524 | {
|
---|
525 | $exifTool->Warn('Bad CanonVRD trailer');
|
---|
526 | return 0;
|
---|
527 | }
|
---|
528 | # extract CanonVRD block if Binary option set, or if requested
|
---|
529 | if ($exifTool->{OPTIONS}->{Binary} or $exifTool->{REQ_TAG_LOOKUP}->{canonvrd}) {
|
---|
530 | $exifTool->FoundTag('CanonVRD', $header . $buff . $footer);
|
---|
531 | }
|
---|
532 | # set variables returned in dirInfo hash
|
---|
533 | $$dirInfo{DataPos} = $raf->Tell() - $size - 0x1c;
|
---|
534 | $$dirInfo{DirLen} = $size + 0x5c;
|
---|
535 |
|
---|
536 | if ($outfile) {
|
---|
537 | # delete CanonVRD information if specified
|
---|
538 | if ($exifTool->{DEL_GROUP}->{CanonVRD} or $exifTool->{DEL_GROUP}->{Trailer} or
|
---|
539 | # also delete if writing as a block (will get added back again later)
|
---|
540 | $exifTool->{NEW_VALUE}->{$Image::ExifTool::Extra{CanonVRD}})
|
---|
541 | {
|
---|
542 | if ($exifTool->{FILE_TYPE} eq 'VRD') {
|
---|
543 | my $newVal = $exifTool->GetNewValues('CanonVRD');
|
---|
544 | if ($newVal) {
|
---|
545 | $verbose and printf $out " Writing CanonVRD as a block\n";
|
---|
546 | Write($outfile, $newVal) or return -1;
|
---|
547 | ++$exifTool->{CHANGED};
|
---|
548 | } else {
|
---|
549 | $exifTool->Error("Can't delete all CanonVRD information from a VRD file");
|
---|
550 | }
|
---|
551 | } else {
|
---|
552 | $verbose and printf $out " Deleting CanonVRD trailer\n";
|
---|
553 | ++$exifTool->{CHANGED};
|
---|
554 | }
|
---|
555 | return 1;
|
---|
556 | }
|
---|
557 | # write now and return if CanonVRD was set as a block
|
---|
558 | my $val = $exifTool->GetNewValues('CanonVRD');
|
---|
559 | if ($val) {
|
---|
560 | $verbose and print $out " Writing CanonVRD as a block\n";
|
---|
561 | Write($outfile, $val) or return -1;
|
---|
562 | ++$exifTool->{CHANGED};
|
---|
563 | return 1;
|
---|
564 | }
|
---|
565 | } elsif ($verbose or $exifTool->{HTML_DUMP}) {
|
---|
566 | $exifTool->DumpTrailer($dirInfo);
|
---|
567 | }
|
---|
568 |
|
---|
569 | # validate VRD trailer and get position and length of edit record
|
---|
570 | SetByteOrder('MM');
|
---|
571 | my $pos = 0;
|
---|
572 | my $vrdPos = $$dirInfo{DataPos} + length($header);
|
---|
573 | Block: for (;;) {
|
---|
574 | if ($pos + 8 > $size) {
|
---|
575 | last if $pos == $size;
|
---|
576 | $blockLen = $size; # mark as invalid
|
---|
577 | } else {
|
---|
578 | $blockType = Get32u(\$buff, $pos);
|
---|
579 | $blockLen = Get32u(\$buff, $pos + 4);
|
---|
580 | }
|
---|
581 | $pos += 8; # move to start of block
|
---|
582 | if ($pos + $blockLen > $size) {
|
---|
583 | $exifTool->Warn('Possibly corrupt CanonVRD block');
|
---|
584 | last;
|
---|
585 | }
|
---|
586 | printf $out " CanonVRD block %x ($blockLen bytes at offset 0x%x)\n",
|
---|
587 | $blockType, $pos + $vrdPos if $verbose > 1 and not $outfile;
|
---|
588 | # process edit-data block
|
---|
589 | if ($blockType == 0xffff00f4) {
|
---|
590 | my $blockEnd = $pos + $blockLen;
|
---|
591 | my $recNum;
|
---|
592 | for ($recNum=0;; ++$recNum, $pos+=$recLen) {
|
---|
593 | if ($pos + 4 > $blockEnd) {
|
---|
594 | last Block if $pos == $blockEnd; # all done if we arrived at end
|
---|
595 | $recLen = $blockEnd; # mark as invalid
|
---|
596 | } else {
|
---|
597 | $recLen = Get32u(\$buff, $pos);
|
---|
598 | }
|
---|
599 | $pos += 4; # move to start of record
|
---|
600 | if ($pos + $recLen > $blockEnd) {
|
---|
601 | $exifTool->Warn('Possibly corrupt CanonVRD record');
|
---|
602 | last Block;
|
---|
603 | }
|
---|
604 | printf $out " CanonVRD record ($recLen bytes at offset 0x%x)\n",
|
---|
605 | $pos + $vrdPos if $verbose > 1 and not $outfile;
|
---|
606 |
|
---|
607 | # our edit information is the first record
|
---|
608 | # (don't process if simply deleting VRD)
|
---|
609 | next if $recNum;
|
---|
610 |
|
---|
611 | # process VRD edit information
|
---|
612 | my $tagTablePtr = GetTagTable('Image::ExifTool::CanonVRD::Main');
|
---|
613 | my $index;
|
---|
614 | my $editData = substr($buff, $pos, $recLen);
|
---|
615 | my %subdirInfo = (
|
---|
616 | DataPt => \$editData,
|
---|
617 | DataPos => $debug ? 0 : $vrdPos + $pos,
|
---|
618 | );
|
---|
619 | my $start = 0;
|
---|
620 | for ($index=0; ; ++$index) {
|
---|
621 | my $tagInfo = $$tagTablePtr{$index} or last;
|
---|
622 | my $dirLen;
|
---|
623 | my $maxLen = $recLen - $start;
|
---|
624 | if ($$tagInfo{Size}) {
|
---|
625 | $dirLen = $$tagInfo{Size};
|
---|
626 | } elsif (defined $$tagInfo{Size}) {
|
---|
627 | # get size from int32u at $start
|
---|
628 | last unless $start + 4 <= $recLen;
|
---|
629 | $dirLen = Get32u(\$editData, $start);
|
---|
630 | $start += 4; # skip the length word
|
---|
631 | } else {
|
---|
632 | $dirLen = $maxLen;
|
---|
633 | }
|
---|
634 | $dirLen > $maxLen and $dirLen = $maxLen;
|
---|
635 | if ($dirLen) {
|
---|
636 | my $subTable = GetTagTable($tagInfo->{SubDirectory}->{TagTable});
|
---|
637 | my $subName = $$tagInfo{Name};
|
---|
638 | $subdirInfo{DirStart} = $start;
|
---|
639 | $subdirInfo{DirLen} = $dirLen;
|
---|
640 | $subdirInfo{DirName} = $subName;
|
---|
641 | if ($outfile) {
|
---|
642 | # rewrite CanonVRD information
|
---|
643 | $verbose and print $out " Rewriting Canon $subName\n";
|
---|
644 | my $newVal = $exifTool->WriteDirectory(\%subdirInfo, $subTable);
|
---|
645 | substr($buff, $pos+$start, $dirLen) = $newVal if $newVal;
|
---|
646 | } else {
|
---|
647 | Image::ExifTool::HexDump(\$editData, $dirLen,
|
---|
648 | Start => $start,
|
---|
649 | Addr => 0,
|
---|
650 | Prefix => $$tagInfo{Name}
|
---|
651 | ) if $debug;
|
---|
652 | # extract CanonVRD information
|
---|
653 | $exifTool->ProcessDirectory(\%subdirInfo, $subTable);
|
---|
654 | }
|
---|
655 | }
|
---|
656 | # next directory starts at the end of this one
|
---|
657 | $start += $dirLen;
|
---|
658 | }
|
---|
659 | }
|
---|
660 | } else {
|
---|
661 | $pos += $blockLen; # skip other blocks
|
---|
662 | }
|
---|
663 | }
|
---|
664 | # write CanonVRD trailer
|
---|
665 | Write($outfile, $header, $buff, $footer) or $err = 1 if $outfile;
|
---|
666 |
|
---|
667 | undef $buff;
|
---|
668 | return $err ? -1 : 1;
|
---|
669 | }
|
---|
670 |
|
---|
671 | 1; # end
|
---|
672 |
|
---|
673 | __END__
|
---|
674 |
|
---|
675 | =head1 NAME
|
---|
676 |
|
---|
677 | Image::ExifTool::CanonVRD - Read/write Canon VRD information
|
---|
678 |
|
---|
679 | =head1 SYNOPSIS
|
---|
680 |
|
---|
681 | This module is used by Image::ExifTool
|
---|
682 |
|
---|
683 | =head1 DESCRIPTION
|
---|
684 |
|
---|
685 | This module contains definitions required by Image::ExifTool to read and
|
---|
686 | write VRD Recipe Data information as written by the Canon Digital Photo
|
---|
687 | Professional software. This information is written to VRD files, and as a
|
---|
688 | trailer in JPEG, CRW and CR2 images.
|
---|
689 |
|
---|
690 | =head1 AUTHOR
|
---|
691 |
|
---|
692 | Copyright 2003-2007, Phil Harvey (phil at owl.phy.queensu.ca)
|
---|
693 |
|
---|
694 | This library is free software; you can redistribute it and/or modify it
|
---|
695 | under the same terms as Perl itself.
|
---|
696 |
|
---|
697 | =head1 SEE ALSO
|
---|
698 |
|
---|
699 | L<Image::ExifTool::TagNames/CanonVRD Tags>,
|
---|
700 | L<Image::ExifTool(3pm)|Image::ExifTool>
|
---|
701 |
|
---|
702 | =cut
|
---|
703 |
|
---|