File Coverage

blib/lib/Image/ExifTool/WriteCanonRaw.pl
Criterion Covered Total %
statement 277 303 91.4
branch 135 212 63.6
condition 43 59 72.8
subroutine 10 10 100.0
pod 0 7 0.0
total 465 591 78.6


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