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 | #------------------------------------------------------------------------------
|
---|
8 | package Image::ExifTool::CanonRaw;
|
---|
9 |
|
---|
10 | use strict;
|
---|
11 | use vars qw($VERSION $AUTOLOAD %crwTagFormat);
|
---|
12 | use Image::ExifTool::Fixup;
|
---|
13 |
|
---|
14 | # mappings to from RAW tagID to MakerNotes tagID
|
---|
15 | # (Note: upper two bits of RawTagID are zero)
|
---|
16 | my %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
|
---|
43 | my %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
|
---|
54 | sub 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...)
|
---|
70 | sub 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
|
---|
117 | sub 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
|
---|
165 | sub 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
|
---|
191 | sub 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!
|
---|
240 | sub 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
|
---|
489 | sub 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 |
|
---|
569 | 1; # end
|
---|
570 |
|
---|
571 | __END__
|
---|
572 |
|
---|
573 | =head1 NAME
|
---|
574 |
|
---|
575 | Image::ExifTool::WriteCanonRaw.pl - Write Canon RAW (CRW and CR2) information
|
---|
576 |
|
---|
577 | =head1 SYNOPSIS
|
---|
578 |
|
---|
579 | These routines are autoloaded by Image::ExifTool::CanonRaw.
|
---|
580 |
|
---|
581 | =head1 DESCRIPTION
|
---|
582 |
|
---|
583 | This file contains routines used by ExifTool to write Canon CRW and CR2
|
---|
584 | files and metadata.
|
---|
585 |
|
---|
586 | =head1 NOTES
|
---|
587 |
|
---|
588 | The CRW format is a pleasure to work with. All pointer offsets are relative
|
---|
589 | to the start of the data for each directory. If TIFF/EXIF had implemented
|
---|
590 | pointers in this way, it would be MUCH easier to read and write TIFF/JPEG
|
---|
591 | files, and would lead to far fewer problems with corrupted metadata.
|
---|
592 |
|
---|
593 | =head1 AUTHOR
|
---|
594 |
|
---|
595 | Copyright 2003-2007, Phil Harvey (phil at owl.phy.queensu.ca)
|
---|
596 |
|
---|
597 | This library is free software; you can redistribute it and/or modify it
|
---|
598 | under the same terms as Perl itself.
|
---|
599 |
|
---|
600 | =head1 SEE ALSO
|
---|
601 |
|
---|
602 | L<Image::ExifTool::CanonRaw(3pm)|Image::ExifTool::CanonRaw>,
|
---|
603 | L<Image::ExifTool(3pm)|Image::ExifTool>,
|
---|
604 | L<http://owl.phy.queensu.ca/~phil/exiftool/canon_raw.html>
|
---|
605 |
|
---|
606 | =cut
|
---|