source: gsdl/trunk/perllib/cpan/Image/ExifTool/WriteCanonRaw.pl@ 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: 23.5 KB
Line 
1#------------------------------------------------------------------------------
2# File: WriteCanonRaw.pl
3#
4# Description: Write Canon RAW (CRW and CR2) meta information
5#
6# Revisions: 01/25/2005 - P. Harvey Created
7#------------------------------------------------------------------------------
8package Image::ExifTool::CanonRaw;
9
10use strict;
11use vars qw($VERSION $AUTOLOAD %crwTagFormat);
12use Image::ExifTool::Fixup;
13
14# mappings to from RAW tagID to MakerNotes tagID
15# (Note: upper two bits of RawTagID are zero)
16my %mapRawTag = (
17 # RawTagID => Canon TagID
18 0x080b => 0x07, # CanonFirmwareVersion
19 0x0810 => 0x09, # OwnerName
20 0x0815 => 0x06, # CanonImageType
21 0x1028 => 0x03, # (unknown if no tag name specified)
22 0x1029 => 0x02, # FocalLength
23 0x102a => 0x04, # CanonShotInfo
24 0x102d => 0x01, # CanonCameraSettings
25 0x1033 => 0x0f, # CanonCustomFunctions (only verified for 10D)
26 0x1038 => 0x12, # CanonAFInfo
27 0x1039 => 0x13,
28 0x1093 => 0x93,
29 0x10a8 => 0xa8,
30 0x10a9 => 0xa9, # WhiteBalanceTable
31 0x10aa => 0xaa,
32 0x10ae => 0xae, # ColorTemperature
33 0x10b4 => 0xb4, # ColorSpace
34 0x10b5 => 0xb5,
35 0x10c0 => 0xc0,
36 0x10c1 => 0xc1,
37 0x180b => 0x0c, # SerialNumber
38 0x1817 => 0x08, # FileNumber
39 0x1834 => 0x10,
40 0x183b => 0x15,
41);
42# translation from Rotation to Orientation values
43my %mapRotation = (
44 0 => 1,
45 90 => 6,
46 180 => 3,
47 270 => 8,
48);
49
50
51#------------------------------------------------------------------------------
52# Initialize buffers for building MakerNotes from RAW data
53# Inputs: 0) ExifTool object reference
54sub InitMakerNotes($)
55{
56 my $exifTool = shift;
57 $exifTool->{MAKER_NOTE_INFO} = {
58 Entries => { }, # directory entries keyed by tagID
59 ValBuff => "\0\0\0\0", # value data buffer (start with zero nextIFD pointer)
60 FixupTags => { }, # flags for tags with data in value buffer
61 };
62}
63
64#------------------------------------------------------------------------------
65# Build maker notes from CanonRaw information
66# Inputs: 0) ExifTool object reference, 1) raw tag ID, 2) reference to tagInfo
67# 3) reference to value, 4) format name, 5) count
68# Notes: This will build the directory in the order the tags are found in the CRW
69# file, which isn't sequential (but Canon's version isn't sequential either...)
70sub BuildMakerNotes($$$$$$)
71{
72 my ($exifTool, $rawTag, $tagInfo, $valuePt, $formName, $count) = @_;
73
74 my $tagID = $mapRawTag{$rawTag} || return;
75 $formName or warn(sprintf "No format for tag 0x%x!\n",$rawTag), return;
76 # special case: ignore user comment because it gets saved in EXIF
77 # (and has the same raw tagID as CanonFileDescription)
78 return if $tagInfo and $$tagInfo{Name} eq 'UserComment';
79 my $tagType = ($rawTag >> 8) & 0x38;
80 my $format = $Image::ExifTool::Exif::formatNumber{$formName};
81 my $fsiz = $Image::ExifTool::Exif::formatSize[$format];
82 my $size = length($$valuePt);
83 my $value;
84 if ($count and $size != $count * $fsiz) {
85 if ($size < $count * $fsiz) {
86 warn sprintf("Value too short for raw tag 0x%x\n",$rawTag);
87 return;
88 }
89 # shorten value appropriately
90 $size = $count * $fsiz;
91 $value = substr($$valuePt, 0, $size);
92 } else {
93 $count = $size / $fsiz;
94 $value = $$valuePt;
95 }
96 my $offsetVal;
97 my $makerInfo = $exifTool->{MAKER_NOTE_INFO};
98 if ($size > 4) {
99 my $len = length $makerInfo->{ValBuff};
100 $offsetVal = Set32u($len);
101 $makerInfo->{ValBuff} .= $value;
102 # pad to an even number of bytes
103 $size & 0x01 and $makerInfo->{ValBuff} .= "\0";
104 # set flag indicating that this tag needs a fixup
105 $makerInfo->{FixupTags}->{$tagID} = 1;
106 } else {
107 $offsetVal = $value;
108 $size < 4 and $offsetVal .= "\0" x (4 - $size);
109 }
110 $makerInfo->{Entries}->{$tagID} = Set16u($tagID) . Set16u($format) .
111 Set32u($count) . $offsetVal;
112}
113
114#------------------------------------------------------------------------------
115# Finish building and save MakerNotes
116# Inputs: 0) ExifTool object reference
117sub SaveMakerNotes($)
118{
119 my $exifTool = shift;
120 # save maker notes
121 my $makerInfo = $exifTool->{MAKER_NOTE_INFO};
122 delete $exifTool->{MAKER_NOTE_INFO};
123 my $dirEntries = $makerInfo->{Entries};
124 my $numEntries = scalar(keys %$dirEntries);
125 my $fixup = new Image::ExifTool::Fixup;
126 return unless $numEntries;
127 # build the MakerNotes directory
128 my $makerNotes = Set16u($numEntries);
129 my $tagID;
130 # write the entries in proper tag order (even though Canon doesn't do this...)
131 foreach $tagID (sort { $a <=> $b } keys %$dirEntries) {
132 $makerNotes .= $$dirEntries{$tagID};
133 next unless $makerInfo->{FixupTags}->{$tagID};
134 # add fixup for this pointer
135 $fixup->AddFixup(length($makerNotes) - 4);
136 }
137 # save position of maker notes for pointer fixups
138 $fixup->{Shift} += length($makerNotes);
139 $exifTool->{MAKER_NOTE_FIXUP} = $fixup;
140 $exifTool->{MAKER_NOTE_BYTE_ORDER} = GetByteOrder();
141 # add value data
142 $makerNotes .= $makerInfo->{ValBuff};
143 # get MakerNotes tag info
144 my $tagTablePtr = Image::ExifTool::GetTagTable('Image::ExifTool::Exif::Main');
145 my $tagInfo = $exifTool->GetTagInfo($tagTablePtr, 0x927c);
146 # save the MakerNotes
147 $exifTool->FoundTag($tagInfo, $makerNotes);
148 # save the garbage collection some work later
149 delete $makerInfo->{Entries};
150 delete $makerInfo->{ValBuff};
151 delete $makerInfo->{FixupTags};
152 # also generate Orientation tag since Rotation isn't transferred from RAW info
153 my $rotation = $exifTool->GetValue('Rotation', 'ValueConv');
154 if (defined $rotation and defined $mapRotation{$rotation}) {
155 $tagInfo = $exifTool->GetTagInfo($tagTablePtr, 0x112);
156 $exifTool->FoundTag($tagInfo, $mapRotation{$rotation});
157 }
158}
159
160#------------------------------------------------------------------------------
161# Check CanonRaw information
162# Inputs: 0) ExifTool object reference, 1) tagInfo hash reference,
163# 2) raw value reference
164# Returns: error string or undef (and may change value) on success
165sub CheckCanonRaw($$$)
166{
167 my ($exifTool, $tagInfo, $valPtr) = @_;
168 Image::ExifTool::GenerateTagIDs($$tagInfo{Table});
169 my $tagName = $$tagInfo{Name};
170 if ($tagName eq 'JpgFromRaw' or $tagName eq 'ThumbnailImage') {
171 unless ($$valPtr =~ /^\xff\xd8/ or $exifTool->Options('IgnoreMinorErrors')) {
172 return '[minor] Not a valid image';
173 }
174 } else {
175 my $format = $$tagInfo{Format};
176 my $count = $$tagInfo{Count};
177 unless ($format) {
178 my $tagType = ($$tagInfo{TagID} >> 8) & 0x38;
179 $format = $crwTagFormat{$tagType};
180 }
181 $format and return Image::ExifTool::CheckValue($valPtr, $format, $count);
182 }
183 return undef;
184}
185
186#------------------------------------------------------------------------------
187# Write CR2 file
188# Inputs: 0) ExifTool ref, 1) dirInfo reference (must have read first 16 bytes)
189# 2) tag table reference
190# Returns: true on success
191sub WriteCR2($$$)
192{
193 my ($exifTool, $dirInfo, $tagTablePtr) = @_;
194 my $dataPt = $$dirInfo{DataPt} or return 0;
195 my $raf = $$dirInfo{RAF} or return 0;
196 my $outfile = $$dirInfo{OutFile} or return 0;
197
198 # check CR2 signature
199 return 0 if $$dataPt !~ /^.{8}CR\x02\0/s and
200 $exifTool->Error("Unsupported Canon RAW file. May cause problems if rewritten", 1);
201 # CR2 has a 16-byte header
202 $$dirInfo{NewDataPos} = 16;
203 my $newData = $exifTool->WriteDirectory($dirInfo, $tagTablePtr);
204 return 0 unless defined $newData;
205 unless ($$dirInfo{LastIFD}) {
206 $exifTool->Error("CR2 image IFD may not be deleted");
207 return 0;
208 }
209
210 if (length($newData)) {
211 # build 16 byte header for Canon RAW file
212 my $header = substr($$dataPt, 0, 16);
213 # set IFD0 pointer (may not be 16 if edited by PhotoMechanic)
214 Set32u(16, \$header, 4);
215 # last 4 bytes of header is pointer to last IFD
216 Set32u($$dirInfo{LastIFD}, \$header, 12);
217 Write($outfile, $header, $newData) or return 0;
218 undef $newData; # free memory
219
220 # copy over image data now if necessary
221 if (ref $$dirInfo{ImageData}) {
222 $exifTool->CopyImageData($$dirInfo{ImageData}, $outfile) or return 0;
223 delete $$dirInfo{ImageData};
224 }
225 }
226 return 1;
227}
228
229#------------------------------------------------------------------------------
230# Write CanonRaw (CRW) information
231# Inputs: 0) ExifTool object reference, 1) source dirInfo reference,
232# 2) tag table reference
233# Returns: true on sucess
234# Notes: Increments ExifTool CHANGED flag for each tag changed This routine is
235# different from all of the other write routines because Canon RAW files are
236# designed well! So it isn't necessary to buffer the data in memory before
237# writing it out. Therefore this routine doesn't return the directory data as
238# the rest of the Write routines do. Instead, it writes to the dirInfo
239# Outfile on the fly --> much faster, efficient, and less demanding on memory!
240sub WriteCanonRaw($$$)
241{
242 my ($exifTool, $dirInfo, $tagTablePtr) = @_;
243 $exifTool or return 1; # allow dummy access to autoload this package
244 my $blockStart = $$dirInfo{DirStart};
245 my $blockSize = $$dirInfo{DirLen};
246 my $raf = $$dirInfo{RAF} or return 0;
247 my $outfile = $$dirInfo{OutFile} or return 0;
248 my $outPos = $$dirInfo{OutPos} or return 0;
249 my $outBase = $outPos;
250 my $verbose = $exifTool->Options('Verbose');
251 my $out = $exifTool->Options('TextOut');
252 my ($buff, $tagInfo);
253
254 # 4 bytes at end of block give directory position within block
255 $raf->Seek($blockStart+$blockSize-4, 0) or return 0;
256 $raf->Read($buff, 4) == 4 or return 0;
257 my $dirOffset = Get32u(\$buff,0) + $blockStart;
258 $raf->Seek($dirOffset, 0) or return 0;
259 $raf->Read($buff, 2) == 2 or return 0;
260 my $entries = Get16u(\$buff,0); # get number of entries in directory
261 # read the directory (10 bytes per entry)
262 $raf->Read($buff, 10 * $entries) == 10 * $entries or return 0;
263 my $newDir = '';
264
265 # get hash of new information keyed by tagID
266 my $newTags = $exifTool->GetNewTagInfoHash($tagTablePtr);
267
268 # generate list of tags to add or delete (currently, we only allow JpgFromRaw
269 # and ThumbnailImage, to be added or deleted from the root CanonRaw directory)
270 my (@addTags, %delTag);
271 if ($$dirInfo{Nesting} == 0) {
272 my $tagID;
273 foreach $tagID (keys %$newTags) {
274 my $permanent = $newTags->{$tagID}->{Permanent};
275 push(@addTags, $tagID) if defined($permanent) and not $permanent;
276 }
277 }
278
279 my $index;
280 for ($index=0; ; ++$index) {
281 my ($pt, $tag, $size, $valuePtr, $ptr, $value);
282 if ($index<$entries) {
283 $pt = 10 * $index;
284 $tag = Get16u(\$buff, $pt);
285 $size = Get32u(\$buff, $pt+2);
286 $valuePtr = Get32u(\$buff, $pt+6);
287 $ptr = $valuePtr + $blockStart; # all pointers relative to block start
288 }
289 # add any required new tags
290 # NOTE: can't currently add tags where value is stored in directory
291 if (@addTags and (not defined($tag) or $tag >= $addTags[0])) {
292 my $addTag = shift @addTags;
293 $tagInfo = $$newTags{$addTag};
294 my $newVal = $exifTool->GetNewValues($tagInfo);
295 if (defined $newVal) {
296 # add new directory entry
297 $newDir .= Set16u($addTag) . Set32u(length($newVal)) .
298 Set32u($outPos - $outBase);
299 # write new value data
300 Write($outfile, $newVal) or return 0;
301 $outPos += length($newVal); # update current position
302 $verbose > 1 and print $out " + CanonRaw:$$tagInfo{Name}\n";
303 ++$exifTool->{CHANGED};
304 }
305 # set flag to delete this tag if found later
306 $delTag{$addTag} = 1;
307 }
308 last unless defined $tag; # all done if no more directory entries
309 return 0 if $tag & 0x8000; # top bit should not be set
310 my $tagID = $tag & 0x3fff; # get tag ID
311 my $tagType = ($tag >> 8) & 0x38; # get tag type
312 my $valueInDir = ($tag & 0x4000); # flag for value in directory
313
314 my $tagInfo = $exifTool->GetTagInfo($tagTablePtr,$tagID);
315 my $format = $crwTagFormat{$tagType};
316 my ($count, $subdir);
317 if ($tagInfo) {
318 $subdir = $$tagInfo{SubDirectory};
319 $format = $$tagInfo{Format} if $$tagInfo{Format};
320 $count = $$tagInfo{Count};
321 }
322 if ($valueInDir) {
323 $size = 8;
324 $value = substr($buff, $pt+2, $size);
325 # set count to 1 by default for normal values in directory
326 $count = 1 if not defined $count and $format and
327 $format ne 'string' and not $subdir;
328 } else {
329 if ($tagType==0x28 or $tagType==0x30) {
330 # this type of tag specifies a raw subdirectory
331 my $name;
332 $tagInfo and $name = $$tagInfo{Name};
333 $name or $name = sprintf("CanonRaw_0x%.4x", $tagID);
334 my %subdirInfo = (
335 DirName => $name,
336 DataLen => 0,
337 DirStart => $ptr,
338 DirLen => $size,
339 Nesting => $$dirInfo{Nesting} + 1,
340 RAF => $raf,
341 Parent => $$dirInfo{DirName},
342 OutFile => $outfile,
343 OutPos => $outPos,
344 );
345 my $result = $exifTool->WriteDirectory(\%subdirInfo, $tagTablePtr);
346 return 0 unless $result;
347 # set size and pointer for this new directory
348 $size = $subdirInfo{OutPos} - $outPos;
349 $valuePtr = $outPos - $outBase;
350 $outPos = $subdirInfo{OutPos};
351 } else {
352 # verify that the value data is within this block
353 $valuePtr + $size <= $blockSize or return 0;
354 # read value from file
355 $raf->Seek($ptr, 0) or return 0;
356 $raf->Read($value, $size) == $size or return 0;
357 }
358 }
359 # set count from tagInfo count if necessary
360 if ($format and not $count) {
361 # set count according to format and size
362 my $fnum = $Image::ExifTool::Exif::formatNumber{$format};
363 my $fsiz = $Image::ExifTool::Exif::formatSize[$fnum];
364 $count = int($size / $fsiz);
365 }
366 # edit subdirectory if necessary
367 if ($tagInfo) {
368 if ($subdir and $$subdir{TagTable}) {
369 my $name = $$tagInfo{Name};
370 my $newTagTable = Image::ExifTool::GetTagTable($$subdir{TagTable});
371 return 0 unless $newTagTable;
372 my $subdirStart = 0;
373 #### eval Start ()
374 $subdirStart = eval $$subdir{Start} if $$subdir{Start};
375 my $dirData = \$value;
376 my %subdirInfo = (
377 Name => $name,
378 DataPt => $dirData,
379 DataLen => $size,
380 DirStart => $subdirStart,
381 DirLen => $size - $subdirStart,
382 Nesting => $$dirInfo{Nesting} + 1,
383 RAF => $raf,
384 Parent => $$dirInfo{DirName},
385 );
386 #### eval Validate ($dirData, $subdirStart, $size)
387 if (defined $$subdir{Validate} and not eval $$subdir{Validate}) {
388 $exifTool->Warn("Invalid $name data");
389 } else {
390 $subdir = $exifTool->WriteDirectory(\%subdirInfo, $newTagTable);
391 if (defined $subdir and length $subdir) {
392 if ($subdirStart) {
393 # add header before data directory
394 $value = substr($value, 0, $subdirStart) . $subdir;
395 } else {
396 $value = $subdir;
397 }
398 }
399 }
400 } elsif ($$newTags{$tagID}) {
401 if ($delTag{$tagID}) {
402 $verbose > 1 and print $out " - CanonRaw:$$tagInfo{Name}\n";
403 ++$exifTool->{CHANGED};
404 next; # next since we already added this tag
405 }
406 my $oldVal;
407 if ($format) {
408 $oldVal = ReadValue(\$value, 0, $format, $count, $size);
409 } else {
410 $oldVal = $value;
411 }
412 my $newValueHash = $exifTool->GetNewValueHash($tagInfo);
413 if (Image::ExifTool::IsOverwriting($newValueHash, $oldVal)) {
414 my $newVal = Image::ExifTool::GetNewValues($newValueHash);
415 my $verboseVal;
416 $verboseVal = $newVal if $verbose > 1;
417 # convert to specified format if necessary
418 if (defined $newVal and $format) {
419 $newVal = WriteValue($newVal, $format, $count);
420 }
421 if (defined $newVal) {
422 $value = $newVal;
423 ++$exifTool->{CHANGED};
424 if ($verbose > 1) {
425 my $oldStr = $exifTool->Printable($oldVal);
426 my $newStr = $exifTool->Printable($verboseVal);
427 print $out " - CanonRaw:$$tagInfo{Name} = '$oldStr'\n";
428 print $out " + CanonRaw:$$tagInfo{Name} = '$newStr'\n";
429 }
430 }
431 }
432 }
433 }
434 if ($valueInDir) {
435 my $len = length $value;
436 if ($len < 8) {
437 # pad with original garbage in case it contained something useful
438 $value .= substr($buff, $pt+2+8-$len, 8-$len);
439 } elsif ($len > 8) { # this shouldn't happen
440 warn "Value too long! -- trucated\n";
441 $value = substr($value, 0, 8);
442 }
443 # create new directory entry
444 $newDir .= Set16u($tag) . $value;
445 next; # all done this entry
446 }
447 if (defined $value) {
448 # don't allow value to change length unless Writable is 'resize'
449 my $writable = $$tagInfo{Writable};
450 my $diff = length($value) - $size;
451 if ($diff) {
452 if ($writable and $writable eq 'resize') {
453 $size += $diff; # allow size to change
454 } elsif ($diff > 0) {
455 $value .= ("\0" x $diff);
456 } else {
457 $value = substr($value, 0, $size);
458 }
459 }
460 # pad value if necessary to align on even-byte boundary (as per CIFF spec)
461 $value .= "\0" if $size & 0x01;
462 $valuePtr = $outPos - $outBase;
463 # write out value data
464 Write($outfile, $value) or return 0;
465 $outPos += length($value); # update current position in outfile
466 }
467 # create new directory entry
468 $newDir .= Set16u($tag) . Set32u($size) . Set32u($valuePtr);
469 }
470 # add the directory counts and offset to the directory start,
471 $entries = length($newDir) / 10;
472 $newDir = Set16u($entries) . $newDir . Set32u($outPos - $outBase);
473 # write directory data
474 Write($outfile, $newDir) or return 0;
475
476 # update current output file position in dirInfo
477 $$dirInfo{OutPos} = $outPos + length($newDir);
478 # save outfile directory start (needed for rewriting VRD trailer)
479 $$dirInfo{OutDirStart} = $outPos - $outBase;
480
481 return 1;
482}
483
484#------------------------------------------------------------------------------
485# write Canon RAW (CRW) file
486# Inputs: 0) ExifTool object reference, 1) dirInfo reference
487# Returns: 1 on success, 0 if this wasn't a valid CRW file,
488# or -1 if a write error occurred
489sub WriteCRW($$)
490{
491 my ($exifTool, $dirInfo) = @_;
492 my $outfile = $$dirInfo{OutFile};
493 my $raf = $$dirInfo{RAF};
494 my $rtnVal = 0;
495 my ($buff, $err, $sig);
496
497 $raf->Read($buff,2) == 2 or return 0;
498 SetByteOrder($buff) or return 0;
499 $raf->Read($buff,4) == 4 or return 0;
500 $raf->Read($sig,8) == 8 or return 0; # get file signature
501 $sig =~ /^HEAP(CCDR|JPGM)/ or return 0; # validate signature
502 my $type = $1;
503 my $hlen = Get32u(\$buff, 0); # get header length
504
505 if ($exifTool->{DEL_GROUP}->{MakerNotes}) {
506 if ($type eq 'CCDR') {
507 $exifTool->Error("Can't delete Makernotes group in CRW file");
508 return 0;
509 } else {
510 ++$exifTool->{CHANGED};
511 return 1;
512 }
513 }
514
515 # write header
516 $raf->Seek(0, 0) or return 0;
517 $raf->Read($buff, $hlen) == $hlen or return 0;
518 Write($outfile, $buff) or $err = 1;
519
520 $raf->Seek(0, 2) or return 0; # seek to end of file
521 my $filesize = $raf->Tell() or return 0;
522
523 # build directory information for main raw directory
524 my %dirInfo = (
525 DataLen => 0,
526 DirStart => $hlen,
527 DirLen => $filesize - $hlen,
528 Nesting => 0,
529 RAF => $raf,
530 Parent => 'CRW',
531 OutFile => $outfile,
532 OutPos => $hlen,
533 );
534 # process the raw directory
535 my $tagTablePtr = Image::ExifTool::GetTagTable('Image::ExifTool::CanonRaw::Main');
536 my $success = $exifTool->WriteDirectory(\%dirInfo, $tagTablePtr);
537
538 my $trailPt;
539 while ($success) {
540 # check to see if trailer(s) exist(s)
541 my $trailInfo = Image::ExifTool::IdentifyTrailer($raf) or last;
542 # rewrite the trailer(s)
543 $buff = '';
544 $$trailInfo{OutFile} = \$buff;
545 $success = $exifTool->ProcessTrailers($trailInfo) or last;
546 $trailPt = $$trailInfo{OutFile};
547 # nothing to write if trailers were deleted
548 undef $trailPt if length($$trailPt) < 4;
549 last;
550 }
551 if ($success) {
552 # add CanonVRD trailer if writing as a block
553 $trailPt = $exifTool->AddNewTrailers($trailPt,'CanonVRD');
554 # write trailer
555 if ($trailPt) {
556 # must append DirStart pointer to end of trailer
557 my $newDirStart = Set32u($dirInfo{OutDirStart});
558 my $len = length $$trailPt;
559 my $pad = ($len & 0x01) ? ' ' : ''; # add pad byte if necessary
560 Write($outfile, $pad, substr($$trailPt,0,$len-4), $newDirStart) or $err = 1;
561 }
562 $rtnVal = $err ? -1 : 1;
563 } else {
564 $exifTool->Error('Error rewriting CRW file');
565 }
566 return $rtnVal;
567}
568
5691; # end
570
571__END__
572
573=head1 NAME
574
575Image::ExifTool::WriteCanonRaw.pl - Write Canon RAW (CRW and CR2) information
576
577=head1 SYNOPSIS
578
579These routines are autoloaded by Image::ExifTool::CanonRaw.
580
581=head1 DESCRIPTION
582
583This file contains routines used by ExifTool to write Canon CRW and CR2
584files and metadata.
585
586=head1 NOTES
587
588The CRW format is a pleasure to work with. All pointer offsets are relative
589to the start of the data for each directory. If TIFF/EXIF had implemented
590pointers in this way, it would be MUCH easier to read and write TIFF/JPEG
591files, and would lead to far fewer problems with corrupted metadata.
592
593=head1 AUTHOR
594
595Copyright 2003-2007, Phil Harvey (phil at owl.phy.queensu.ca)
596
597This library is free software; you can redistribute it and/or modify it
598under the same terms as Perl itself.
599
600=head1 SEE ALSO
601
602L<Image::ExifTool::CanonRaw(3pm)|Image::ExifTool::CanonRaw>,
603L<Image::ExifTool(3pm)|Image::ExifTool>,
604L<http://owl.phy.queensu.ca/~phil/exiftool/canon_raw.html>
605
606=cut
Note: See TracBrowser for help on using the repository browser.