source: gsdl/trunk/perllib/cpan/Image/ExifTool/CanonVRD.pm@ 16842

Last change on this file since 16842 was 16842, checked in by davidb, 16 years ago

ExifTool added to cpan area to support metadata extraction from files such as JPEG. Primarily targetted as Image files (hence the Image folder name decided upon by the ExifTool author) it also can handle video such as flash and audio such as Wav

File size: 21.2 KB
Line 
1#------------------------------------------------------------------------------
2# File: CanonVRD.pm
3#
4# Description: Read/write Canon VRD information
5#
6# Revisions: 10/30/2006 - P. Harvey Created
7#------------------------------------------------------------------------------
8
9package Image::ExifTool::CanonVRD;
10
11use strict;
12use vars qw($VERSION);
13use Image::ExifTool qw(:DataAccess :Utils);
14
15$VERSION = '1.05';
16
17sub ProcessCanonVRD($$);
18
19my $debug; # set this to 1 for offsets relative to binary data start
20
21my %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
435sub 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
452sub 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
466sub 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
500sub 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);
573Block: 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
6711; # end
672
673__END__
674
675=head1 NAME
676
677Image::ExifTool::CanonVRD - Read/write Canon VRD information
678
679=head1 SYNOPSIS
680
681This module is used by Image::ExifTool
682
683=head1 DESCRIPTION
684
685This module contains definitions required by Image::ExifTool to read and
686write VRD Recipe Data information as written by the Canon Digital Photo
687Professional software. This information is written to VRD files, and as a
688trailer in JPEG, CRW and CR2 images.
689
690=head1 AUTHOR
691
692Copyright 2003-2007, Phil Harvey (phil at owl.phy.queensu.ca)
693
694This library is free software; you can redistribute it and/or modify it
695under the same terms as Perl itself.
696
697=head1 SEE ALSO
698
699L<Image::ExifTool::TagNames/CanonVRD Tags>,
700L<Image::ExifTool(3pm)|Image::ExifTool>
701
702=cut
703
Note: See TracBrowser for help on using the repository browser.