1 | #------------------------------------------------------------------------------
|
---|
2 | # File: DNG.pm
|
---|
3 | #
|
---|
4 | # Description: Read DNG-specific information
|
---|
5 | #
|
---|
6 | # Revisions: 01/09/2006 - P. Harvey Created
|
---|
7 | #
|
---|
8 | # References: 1) http://www.adobe.com/products/dng/
|
---|
9 | #------------------------------------------------------------------------------
|
---|
10 |
|
---|
11 | package Image::ExifTool::DNG;
|
---|
12 |
|
---|
13 | use strict;
|
---|
14 | use vars qw($VERSION);
|
---|
15 | use Image::ExifTool qw(:DataAccess :Utils);
|
---|
16 | use Image::ExifTool::Exif;
|
---|
17 | use Image::ExifTool::MakerNotes;
|
---|
18 | use Image::ExifTool::CanonRaw;
|
---|
19 |
|
---|
20 | $VERSION = '1.15';
|
---|
21 |
|
---|
22 | sub ProcessOriginalRaw($$$);
|
---|
23 | sub ProcessAdobeData($$$);
|
---|
24 | sub ProcessAdobeMakN($$$);
|
---|
25 | sub ProcessAdobeCRW($$$);
|
---|
26 | sub ProcessAdobeRAF($$$);
|
---|
27 | sub ProcessAdobeMRW($$$);
|
---|
28 | sub ProcessAdobeSR2($$$);
|
---|
29 | sub ProcessAdobeIFD($$$);
|
---|
30 | sub WriteAdobeStuff($$$);
|
---|
31 |
|
---|
32 | # data in OriginalRawFileData
|
---|
33 | %Image::ExifTool::DNG::OriginalRaw = (
|
---|
34 | GROUPS => { 2 => 'Image' },
|
---|
35 | PROCESS_PROC => \&ProcessOriginalRaw,
|
---|
36 | NOTES => q{
|
---|
37 | This table defines tags extracted from the DNG OriginalRawFileData
|
---|
38 | information.
|
---|
39 | },
|
---|
40 | 0 => { Name => 'OriginalRawImage', Binary => 1 },
|
---|
41 | 1 => { Name => 'OriginalRawResource', Binary => 1 },
|
---|
42 | 2 => 'OriginalRawFileType',
|
---|
43 | 3 => 'OriginalRawCreator',
|
---|
44 | 4 => { Name => 'OriginalTHMImage', Binary => 1 },
|
---|
45 | 5 => { Name => 'OriginalTHMResource', Binary => 1 },
|
---|
46 | 6 => 'OriginalTHMFileType',
|
---|
47 | 7 => 'OriginalTHMCreator',
|
---|
48 | );
|
---|
49 |
|
---|
50 | %Image::ExifTool::DNG::AdobeData = ( #PH
|
---|
51 | GROUPS => { 0 => 'MakerNotes', 1 => 'Adobe', 2 => 'Image' },
|
---|
52 | PROCESS_PROC => \&ProcessAdobeData,
|
---|
53 | WRITE_PROC => \&WriteAdobeStuff,
|
---|
54 | NOTES => q{
|
---|
55 | This information is found in the "Adobe" DNGPrivateData.
|
---|
56 |
|
---|
57 | The maker notes ('MakN') are processed by ExifTool, but some information may
|
---|
58 | have been lost by the Adobe DNG Converter. This is because the Adobe DNG
|
---|
59 | Converter (as of version 6.3) doesn't properly handle information referenced
|
---|
60 | from inside the maker notes that lies outside the original maker notes
|
---|
61 | block. This information is lost when only the maker note block is copied to
|
---|
62 | the DNG image. While this doesn't effect all makes of cameras, it is a
|
---|
63 | problem for some major brands such as Olympus and Sony.
|
---|
64 |
|
---|
65 | Other entries in this table represent proprietary information that is
|
---|
66 | extracted from the original RAW image and restructured to a different (but
|
---|
67 | still proprietary) Adobe format.
|
---|
68 | },
|
---|
69 | MakN => [ ], # (filled in later)
|
---|
70 | 'CRW ' => {
|
---|
71 | Name => 'AdobeCRW',
|
---|
72 | SubDirectory => {
|
---|
73 | TagTable => 'Image::ExifTool::CanonRaw::Main',
|
---|
74 | ProcessProc => \&ProcessAdobeCRW,
|
---|
75 | WriteProc => \&WriteAdobeStuff,
|
---|
76 | },
|
---|
77 | },
|
---|
78 | 'MRW ' => {
|
---|
79 | Name => 'AdobeMRW',
|
---|
80 | SubDirectory => {
|
---|
81 | TagTable => 'Image::ExifTool::MinoltaRaw::Main',
|
---|
82 | ProcessProc => \&ProcessAdobeMRW,
|
---|
83 | WriteProc => \&WriteAdobeStuff,
|
---|
84 | },
|
---|
85 | },
|
---|
86 | 'SR2 ' => {
|
---|
87 | Name => 'AdobeSR2',
|
---|
88 | SubDirectory => {
|
---|
89 | TagTable => 'Image::ExifTool::Sony::SR2Private',
|
---|
90 | ProcessProc => \&ProcessAdobeSR2,
|
---|
91 | },
|
---|
92 | },
|
---|
93 | 'RAF ' => {
|
---|
94 | Name => 'AdobeRAF',
|
---|
95 | SubDirectory => {
|
---|
96 | TagTable => 'Image::ExifTool::FujiFilm::RAF',
|
---|
97 | ProcessProc => \&ProcessAdobeRAF,
|
---|
98 | },
|
---|
99 | },
|
---|
100 | 'Pano' => {
|
---|
101 | Name => 'AdobePano',
|
---|
102 | SubDirectory => {
|
---|
103 | TagTable => 'Image::ExifTool::PanasonicRaw::Main',
|
---|
104 | ProcessProc => \&ProcessAdobeIFD,
|
---|
105 | },
|
---|
106 | },
|
---|
107 | 'Koda' => {
|
---|
108 | Name => 'AdobeKoda',
|
---|
109 | SubDirectory => {
|
---|
110 | TagTable => 'Image::ExifTool::Kodak::IFD',
|
---|
111 | ProcessProc => \&ProcessAdobeIFD,
|
---|
112 | },
|
---|
113 | },
|
---|
114 | 'Leaf' => {
|
---|
115 | Name => 'AdobeLeaf',
|
---|
116 | SubDirectory => {
|
---|
117 | TagTable => 'Image::ExifTool::Leaf::SubIFD',
|
---|
118 | ProcessProc => \&ProcessAdobeIFD,
|
---|
119 | },
|
---|
120 | },
|
---|
121 | );
|
---|
122 |
|
---|
123 | # fill in maker notes
|
---|
124 | {
|
---|
125 | my $tagInfo;
|
---|
126 | my $list = $Image::ExifTool::DNG::AdobeData{MakN};
|
---|
127 | foreach $tagInfo (@Image::ExifTool::MakerNotes::Main) {
|
---|
128 | unless (ref $tagInfo eq 'HASH') {
|
---|
129 | push @$list, $tagInfo;
|
---|
130 | next;
|
---|
131 | }
|
---|
132 | my %copy = %$tagInfo;
|
---|
133 | delete $copy{Groups};
|
---|
134 | delete $copy{GotGroups};
|
---|
135 | delete $copy{Table};
|
---|
136 | push @$list, \%copy;
|
---|
137 | }
|
---|
138 | }
|
---|
139 |
|
---|
140 | #------------------------------------------------------------------------------
|
---|
141 | # Process DNG OriginalRawFileData information
|
---|
142 | # Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
|
---|
143 | # Returns: 1 on success, otherwise returns 0 and sets a Warning
|
---|
144 | sub ProcessOriginalRaw($$$)
|
---|
145 | {
|
---|
146 | my ($exifTool, $dirInfo, $tagTablePtr) = @_;
|
---|
147 | my $dataPt = $$dirInfo{DataPt};
|
---|
148 | my $start = $$dirInfo{DirStart};
|
---|
149 | my $end = $start + $$dirInfo{DirLen};
|
---|
150 | my $pos = $start;
|
---|
151 | my ($index, $err);
|
---|
152 |
|
---|
153 | SetByteOrder('MM'); # pointers are always big-endian in this structure
|
---|
154 | for ($index=0; $index<8; ++$index) {
|
---|
155 | last if $pos + 4 > $end;
|
---|
156 | my $val = Get32u($dataPt, $pos);
|
---|
157 | $val or $pos += 4, next; # ignore zero values
|
---|
158 | my $tagInfo = $exifTool->GetTagInfo($tagTablePtr, $index);
|
---|
159 | $tagInfo or $err = "Missing DNG tag $index", last;
|
---|
160 | if ($index & 0x02) {
|
---|
161 | # extract a simple file type (tags 2, 3, 6 and 7)
|
---|
162 | $val = substr($$dataPt, $pos, 4);
|
---|
163 | $pos += 4;
|
---|
164 | } else {
|
---|
165 | # extract a compressed data block (tags 0, 1, 4 and 5)
|
---|
166 | my $n = int(($val + 65535) / 65536);
|
---|
167 | my $hdrLen = 4 * ($n + 2);
|
---|
168 | $pos + $hdrLen > $end and $err = '', last;
|
---|
169 | my $tag = $$tagInfo{Name};
|
---|
170 | # only extract this information if requested (because it takes time)
|
---|
171 | if ($exifTool->{OPTIONS}->{Binary} or
|
---|
172 | $exifTool->{REQ_TAG_LOOKUP}->{lc($tag)})
|
---|
173 | {
|
---|
174 | unless (eval 'require Compress::Zlib') {
|
---|
175 | $err = 'Install Compress::Zlib to extract compressed images';
|
---|
176 | last;
|
---|
177 | }
|
---|
178 | my $i;
|
---|
179 | $val = '';
|
---|
180 | my $p2 = $pos + Get32u($dataPt, $pos + 4);
|
---|
181 | for ($i=0; $i<$n; ++$i) {
|
---|
182 | # inflate this compressed block
|
---|
183 | my $p1 = $p2;
|
---|
184 | $p2 = $pos + Get32u($dataPt, $pos + ($i + 2) * 4);
|
---|
185 | if ($p1 >= $p2 or $p2 > $end) {
|
---|
186 | $err = 'Bad compressed RAW image';
|
---|
187 | last;
|
---|
188 | }
|
---|
189 | my $buff = substr($$dataPt, $p1, $p2 - $p1);
|
---|
190 | my ($v2, $stat);
|
---|
191 | my $inflate = Compress::Zlib::inflateInit();
|
---|
192 | $inflate and ($v2, $stat) = $inflate->inflate($buff);
|
---|
193 | if ($inflate and $stat == Compress::Zlib::Z_STREAM_END()) {
|
---|
194 | $val .= $v2;
|
---|
195 | } else {
|
---|
196 | $err = 'Error inflating compressed RAW image';
|
---|
197 | last;
|
---|
198 | }
|
---|
199 | }
|
---|
200 | $pos = $p2;
|
---|
201 | } else {
|
---|
202 | $pos + $hdrLen > $end and $err = '', last;
|
---|
203 | my $len = Get32u($dataPt, $pos + $hdrLen - 4);
|
---|
204 | $pos + $len > $end and $err = '', last;
|
---|
205 | $val = substr($$dataPt, $pos + $hdrLen, $len - $hdrLen);
|
---|
206 | $val = "Binary data $len bytes";
|
---|
207 | $pos += $len; # skip over this block
|
---|
208 | }
|
---|
209 | }
|
---|
210 | $exifTool->FoundTag($tagInfo, $val);
|
---|
211 | }
|
---|
212 | $exifTool->Warn($err || 'Bad OriginalRawFileData') if defined $err;
|
---|
213 | return 1;
|
---|
214 | }
|
---|
215 |
|
---|
216 | #------------------------------------------------------------------------------
|
---|
217 | # Process Adobe DNGPrivateData directory
|
---|
218 | # Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
|
---|
219 | # Returns: 1 on success
|
---|
220 | sub ProcessAdobeData($$$)
|
---|
221 | {
|
---|
222 | my ($exifTool, $dirInfo, $tagTablePtr) = @_;
|
---|
223 | my $dataPt = $$dirInfo{DataPt};
|
---|
224 | my $dataPos = $$dirInfo{DataPos};
|
---|
225 | my $pos = $$dirInfo{DirStart};
|
---|
226 | my $end = $$dirInfo{DirLen} + $pos;
|
---|
227 | my $outfile = $$dirInfo{OutFile};
|
---|
228 | my $verbose = $exifTool->Options('Verbose');
|
---|
229 | my $htmlDump = $exifTool->Options('HtmlDump');
|
---|
230 |
|
---|
231 | return 0 unless $$dataPt =~ /^Adobe\0/;
|
---|
232 | unless ($outfile) {
|
---|
233 | $exifTool->VerboseDir($dirInfo);
|
---|
234 | # don't parse makernotes if FastScan > 1
|
---|
235 | my $fast = $exifTool->Options('FastScan');
|
---|
236 | return 1 if $fast and $fast > 1;
|
---|
237 | }
|
---|
238 | $htmlDump and $exifTool->HDump($dataPos, 6, 'Adobe DNGPrivateData header');
|
---|
239 | SetByteOrder('MM'); # always big endian
|
---|
240 | $pos += 6;
|
---|
241 | while ($pos + 8 <= $end) {
|
---|
242 | my ($tag, $size) = unpack("x${pos}a4N", $$dataPt);
|
---|
243 | $pos += 8;
|
---|
244 | last if $pos + $size > $end;
|
---|
245 | my $tagInfo = $$tagTablePtr{$tag};
|
---|
246 | if ($htmlDump) {
|
---|
247 | my $name = "Adobe$tag";
|
---|
248 | $name =~ tr/ //d;
|
---|
249 | $exifTool->HDump($dataPos + $pos - 8, 8, "$name header", "Data Size: $size bytes");
|
---|
250 | # dump non-EXIF format data
|
---|
251 | unless ($tag =~ /^(MakN|SR2 )$/) {
|
---|
252 | $exifTool->HDump($dataPos + $pos, $size, "$name data");
|
---|
253 | }
|
---|
254 | }
|
---|
255 | if ($verbose and not $outfile) {
|
---|
256 | $tagInfo or $exifTool->VPrint(0, "$$exifTool{INDENT}Unsupported DNGAdobeData record: ($tag)\n");
|
---|
257 | $exifTool->VerboseInfo($tag,
|
---|
258 | ref $tagInfo eq 'HASH' ? $tagInfo : undef,
|
---|
259 | DataPt => $dataPt,
|
---|
260 | DataPos => $dataPos,
|
---|
261 | Start => $pos,
|
---|
262 | Size => $size,
|
---|
263 | );
|
---|
264 | }
|
---|
265 | my $value;
|
---|
266 | while ($tagInfo) {
|
---|
267 | my ($subTable, $subName, $processProc);
|
---|
268 | if (ref $tagInfo eq 'HASH') {
|
---|
269 | unless ($$tagInfo{SubDirectory}) {
|
---|
270 | if ($outfile) {
|
---|
271 | # copy value across to outfile
|
---|
272 | $value = substr($$dataPt, $pos, $size);
|
---|
273 | } else {
|
---|
274 | $exifTool->HandleTag($tagTablePtr, $tag, substr($$dataPt, $pos, $size));
|
---|
275 | }
|
---|
276 | last;
|
---|
277 | }
|
---|
278 | $subTable = GetTagTable($tagInfo->{SubDirectory}->{TagTable});
|
---|
279 | $subName = $$tagInfo{Name};
|
---|
280 | $processProc = $tagInfo->{SubDirectory}->{ProcessProc};
|
---|
281 | } else {
|
---|
282 | $subTable = $tagTablePtr;
|
---|
283 | $subName = 'AdobeMakN';
|
---|
284 | $processProc = \&ProcessAdobeMakN;
|
---|
285 | }
|
---|
286 | my %dirInfo = (
|
---|
287 | Base => $$dirInfo{Base},
|
---|
288 | DataPt => $dataPt,
|
---|
289 | DataPos => $dataPos,
|
---|
290 | DataLen => $$dirInfo{DataLen},
|
---|
291 | DirStart => $pos,
|
---|
292 | DirLen => $size,
|
---|
293 | DirName => $subName,
|
---|
294 | );
|
---|
295 | if ($outfile) {
|
---|
296 | $dirInfo{Proc} = $processProc; # WriteAdobeStuff() calls this to do the actual writing
|
---|
297 | $value = $exifTool->WriteDirectory(\%dirInfo, $subTable, \&WriteAdobeStuff);
|
---|
298 | # use old directory if an error occurred
|
---|
299 | defined $value or $value = substr($$dataPt, $pos, $size);
|
---|
300 | } else {
|
---|
301 | # override process proc for MakN
|
---|
302 | $exifTool->ProcessDirectory(\%dirInfo, $subTable, $processProc);
|
---|
303 | }
|
---|
304 | last;
|
---|
305 | }
|
---|
306 | if (defined $value and length $value) {
|
---|
307 | # add "Adobe" header if necessary
|
---|
308 | $$outfile = "Adobe\0" unless $$outfile and length $$outfile;
|
---|
309 | $$outfile .= $tag . pack('N', length $value) . $value;
|
---|
310 | $$outfile .= "\0" if length($value) & 0x01; # pad if necessary
|
---|
311 | }
|
---|
312 | $pos += $size;
|
---|
313 | ++$pos if $size & 0x01; # (darn padding)
|
---|
314 | }
|
---|
315 | $pos == $end or $exifTool->Warn("$pos $end Adobe private data is corrupt");
|
---|
316 | return 1;
|
---|
317 | }
|
---|
318 |
|
---|
319 | #------------------------------------------------------------------------------
|
---|
320 | # Process Adobe CRW directory
|
---|
321 | # Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
|
---|
322 | # Returns: 1 on success, otherwise returns 0 and sets a Warning
|
---|
323 | # Notes: data has 4 byte header (2 for byte order and 2 for entry count)
|
---|
324 | # - this routine would be as simple as ProcessAdobeMRW() below if Adobe hadn't
|
---|
325 | # pulled the bonehead move of reformatting the CRW information
|
---|
326 | sub ProcessAdobeCRW($$$)
|
---|
327 | {
|
---|
328 | my ($exifTool, $dirInfo, $tagTablePtr) = @_;
|
---|
329 | my $dataPt = $$dirInfo{DataPt};
|
---|
330 | my $start = $$dirInfo{DirStart};
|
---|
331 | my $end = $start + $$dirInfo{DirLen};
|
---|
332 | my $verbose = $exifTool->Options('Verbose');
|
---|
333 | my $buildMakerNotes = $exifTool->Options('MakerNotes');
|
---|
334 | my $outfile = $$dirInfo{OutFile};
|
---|
335 | my ($newTags, $oldChanged);
|
---|
336 |
|
---|
337 | SetByteOrder('MM'); # always big endian
|
---|
338 | return 0 if $$dirInfo{DirLen} < 4;
|
---|
339 | my $byteOrder = substr($$dataPt, $start, 2);
|
---|
340 | return 0 unless $byteOrder =~ /^(II|MM)$/;
|
---|
341 |
|
---|
342 | # initialize maker note data if building maker notes
|
---|
343 | $buildMakerNotes and Image::ExifTool::CanonRaw::InitMakerNotes($exifTool);
|
---|
344 |
|
---|
345 | my $entries = Get16u($dataPt, $start + 2);
|
---|
346 | my $pos = $start + 4;
|
---|
347 | $exifTool->VerboseDir($dirInfo, $entries) unless $outfile;
|
---|
348 | if ($outfile) {
|
---|
349 | # get hash of new tags
|
---|
350 | $newTags = $exifTool->GetNewTagInfoHash($tagTablePtr);
|
---|
351 | $$outfile = substr($$dataPt, $start, 4);
|
---|
352 | $oldChanged = $exifTool->{CHANGED};
|
---|
353 | }
|
---|
354 | # loop through entries in Adobe CRW information
|
---|
355 | my $index;
|
---|
356 | for ($index=0; $index<$entries; ++$index) {
|
---|
357 | last if $pos + 6 > $end;
|
---|
358 | my $tag = Get16u($dataPt, $pos);
|
---|
359 | my $size = Get32u($dataPt, $pos + 2);
|
---|
360 | $pos += 6;
|
---|
361 | last if $pos + $size > $end;
|
---|
362 | my $value = substr($$dataPt, $pos, $size);
|
---|
363 | my $tagID = $tag & 0x3fff;
|
---|
364 | my $tagType = ($tag >> 8) & 0x38; # get tag type
|
---|
365 | my $format = $Image::ExifTool::CanonRaw::crwTagFormat{$tagType};
|
---|
366 | my $count;
|
---|
367 | my $tagInfo = $exifTool->GetTagInfo($tagTablePtr, $tagID, \$value);
|
---|
368 | if ($tagInfo) {
|
---|
369 | $format = $$tagInfo{Format} if $$tagInfo{Format};
|
---|
370 | $count = $$tagInfo{Count};
|
---|
371 | }
|
---|
372 | # set count to 1 by default for values that were in the directory entry
|
---|
373 | if (not defined $count and $tag & 0x4000 and $format and $format ne 'string') {
|
---|
374 | $count = 1;
|
---|
375 | }
|
---|
376 | # set count from tagInfo count if necessary
|
---|
377 | if ($format and not $count) {
|
---|
378 | # set count according to format and size
|
---|
379 | my $fnum = $Image::ExifTool::Exif::formatNumber{$format};
|
---|
380 | my $fsiz = $Image::ExifTool::Exif::formatSize[$fnum];
|
---|
381 | $count = int($size / $fsiz);
|
---|
382 | }
|
---|
383 | $format or $format = 'undef';
|
---|
384 | SetByteOrder($byteOrder);
|
---|
385 | my $val = ReadValue(\$value, 0, $format, $count, $size);
|
---|
386 | if ($outfile) {
|
---|
387 | if ($tagInfo) {
|
---|
388 | my $subdir = $$tagInfo{SubDirectory};
|
---|
389 | if ($subdir and $$subdir{TagTable}) {
|
---|
390 | my $name = $$tagInfo{Name};
|
---|
391 | my $newTagTable = GetTagTable($$subdir{TagTable});
|
---|
392 | return 0 unless $newTagTable;
|
---|
393 | my $subdirStart = 0;
|
---|
394 | #### eval Start ()
|
---|
395 | $subdirStart = eval $$subdir{Start} if $$subdir{Start};
|
---|
396 | my $dirData = \$value;
|
---|
397 | my %subdirInfo = (
|
---|
398 | Name => $name,
|
---|
399 | DataPt => $dirData,
|
---|
400 | DataLen => $size,
|
---|
401 | DirStart => $subdirStart,
|
---|
402 | DirLen => $size - $subdirStart,
|
---|
403 | Parent => $$dirInfo{DirName},
|
---|
404 | );
|
---|
405 | #### eval Validate ($dirData, $subdirStart, $size)
|
---|
406 | if (defined $$subdir{Validate} and not eval $$subdir{Validate}) {
|
---|
407 | $exifTool->Warn("Invalid $name data");
|
---|
408 | } else {
|
---|
409 | $subdir = $exifTool->WriteDirectory(\%subdirInfo, $newTagTable);
|
---|
410 | if (defined $subdir and length $subdir) {
|
---|
411 | if ($subdirStart) {
|
---|
412 | # add header before data directory
|
---|
413 | $value = substr($value, 0, $subdirStart) . $subdir;
|
---|
414 | } else {
|
---|
415 | $value = $subdir;
|
---|
416 | }
|
---|
417 | }
|
---|
418 | }
|
---|
419 | } elsif ($$newTags{$tagID}) {
|
---|
420 | my $nvHash = $exifTool->GetNewValueHash($tagInfo);
|
---|
421 | if (Image::ExifTool::IsOverwriting($nvHash, $val)) {
|
---|
422 | my $newVal = Image::ExifTool::GetNewValues($nvHash);
|
---|
423 | my $verboseVal;
|
---|
424 | $verboseVal = $newVal if $verbose > 1;
|
---|
425 | # convert to specified format if necessary
|
---|
426 | if (defined $newVal and $format) {
|
---|
427 | $newVal = WriteValue($newVal, $format, $count);
|
---|
428 | }
|
---|
429 | if (defined $newVal) {
|
---|
430 | $exifTool->VerboseValue("- CanonRaw:$$tagInfo{Name}", $value);
|
---|
431 | $exifTool->VerboseValue("+ CanonRaw:$$tagInfo{Name}", $verboseVal);
|
---|
432 | $value = $newVal;
|
---|
433 | ++$exifTool->{CHANGED};
|
---|
434 | }
|
---|
435 | }
|
---|
436 | }
|
---|
437 | }
|
---|
438 | # write out new value (always big-endian)
|
---|
439 | SetByteOrder('MM');
|
---|
440 | # (verified that there is no padding here)
|
---|
441 | $$outfile .= Set16u($tag) . Set32u(length($value)) . $value;
|
---|
442 | } else {
|
---|
443 | $exifTool->HandleTag($tagTablePtr, $tagID, $val,
|
---|
444 | Index => $index,
|
---|
445 | DataPt => $dataPt,
|
---|
446 | DataPos => $$dirInfo{DataPos},
|
---|
447 | Start => $pos,
|
---|
448 | Size => $size,
|
---|
449 | TagInfo => $tagInfo,
|
---|
450 | );
|
---|
451 | if ($buildMakerNotes) {
|
---|
452 | # build maker notes information if requested
|
---|
453 | Image::ExifTool::CanonRaw::BuildMakerNotes($exifTool, $tagID, $tagInfo,
|
---|
454 | \$value, $format, $count);
|
---|
455 | }
|
---|
456 | }
|
---|
457 | # (we lost the directory structure, but the second tag 0x0805
|
---|
458 | # should be in the ImageDescription directory)
|
---|
459 | $exifTool->{DIR_NAME} = 'ImageDescription' if $tagID == 0x0805;
|
---|
460 | SetByteOrder('MM');
|
---|
461 | $pos += $size;
|
---|
462 | }
|
---|
463 | if ($outfile and (not defined $$outfile or $index != $entries or
|
---|
464 | $exifTool->{CHANGED} == $oldChanged))
|
---|
465 | {
|
---|
466 | $exifTool->{CHANGED} = $oldChanged; # nothing changed
|
---|
467 | undef $$outfile; # rewrite old directory
|
---|
468 | }
|
---|
469 | if ($index != $entries) {
|
---|
470 | $exifTool->Warn('Truncated CRW notes');
|
---|
471 | } elsif ($pos < $end) {
|
---|
472 | $exifTool->Warn($end-$pos . ' extra bytes at end of CRW notes');
|
---|
473 | }
|
---|
474 | # finish building maker notes if necessary
|
---|
475 | if ($buildMakerNotes) {
|
---|
476 | SetByteOrder($byteOrder);
|
---|
477 | Image::ExifTool::CanonRaw::SaveMakerNotes($exifTool);
|
---|
478 | }
|
---|
479 | return 1;
|
---|
480 | }
|
---|
481 |
|
---|
482 | #------------------------------------------------------------------------------
|
---|
483 | # Process Adobe MRW directory
|
---|
484 | # Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
|
---|
485 | # Returns: 1 on success, otherwise returns 0 and sets a Warning
|
---|
486 | # Notes: data has 4 byte header (2 for byte order and 2 for entry count)
|
---|
487 | sub ProcessAdobeMRW($$$)
|
---|
488 | {
|
---|
489 | my ($exifTool, $dirInfo, $tagTablePtr) = @_;
|
---|
490 | my $dataPt = $$dirInfo{DataPt};
|
---|
491 | my $dirLen = $$dirInfo{DirLen};
|
---|
492 | my $dirStart = $$dirInfo{DirStart};
|
---|
493 | my $outfile = $$dirInfo{OutFile};
|
---|
494 |
|
---|
495 | # construct fake MRW file
|
---|
496 | my $buff = "\0MRM" . pack('N', $dirLen - 4);
|
---|
497 | # ignore leading byte order and directory count words
|
---|
498 | $buff .= substr($$dataPt, $dirStart + 4, $dirLen - 4);
|
---|
499 | my $raf = new File::RandomAccess(\$buff);
|
---|
500 | my %dirInfo = ( RAF => $raf, OutFile => $outfile );
|
---|
501 | my $rtnVal = Image::ExifTool::MinoltaRaw::ProcessMRW($exifTool, \%dirInfo);
|
---|
502 | if ($outfile and defined $$outfile and length $$outfile) {
|
---|
503 | # remove MRW header and add Adobe header
|
---|
504 | $$outfile = substr($$dataPt, $dirStart, 4) . substr($$outfile, 8);
|
---|
505 | }
|
---|
506 | return $rtnVal;
|
---|
507 | }
|
---|
508 |
|
---|
509 | #------------------------------------------------------------------------------
|
---|
510 | # Process Adobe RAF directory
|
---|
511 | # Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
|
---|
512 | # Returns: 1 on success, otherwise returns 0 and sets a Warning
|
---|
513 | sub ProcessAdobeRAF($$$)
|
---|
514 | {
|
---|
515 | my ($exifTool, $dirInfo, $tagTablePtr) = @_;
|
---|
516 | return 0 if $$dirInfo{OutFile}; # (can't write this yet)
|
---|
517 | my $dataPt = $$dirInfo{DataPt};
|
---|
518 | my $pos = $$dirInfo{DirStart};
|
---|
519 | my $dirEnd = $$dirInfo{DirLen} + $pos;
|
---|
520 | my ($readIt, $warn);
|
---|
521 |
|
---|
522 | # set byte order according to first 2 bytes of Adobe RAF data
|
---|
523 | if ($pos + 2 <= $dirEnd and SetByteOrder(substr($$dataPt, $pos, 2))) {
|
---|
524 | $pos += 2;
|
---|
525 | } else {
|
---|
526 | $exifTool->Warn('Invalid DNG RAF data');
|
---|
527 | return 0;
|
---|
528 | }
|
---|
529 | $exifTool->VerboseDir($dirInfo);
|
---|
530 | # make fake RAF object for processing (same acronym, different meaning)
|
---|
531 | my $raf = new File::RandomAccess($dataPt);
|
---|
532 | my $num = '';
|
---|
533 | # loop through all records in Adobe RAF data:
|
---|
534 | # 0 - RAF table (not processed)
|
---|
535 | # 1 - first RAF directory
|
---|
536 | # 2 - second RAF directory (if available)
|
---|
537 | for (;;) {
|
---|
538 | last if $pos + 4 > $dirEnd;
|
---|
539 | my $len = Get32u($dataPt, $pos);
|
---|
540 | $pos += 4 + $len; # step to next entry in Adobe RAF record
|
---|
541 | $len or last; # ends with an empty entry
|
---|
542 | $readIt or $readIt = 1, next; # ignore first entry (RAF table)
|
---|
543 | my %dirInfo = (
|
---|
544 | RAF => $raf,
|
---|
545 | DirStart => $pos - $len,
|
---|
546 | );
|
---|
547 | $$exifTool{SET_GROUP1} = "RAF$num";
|
---|
548 | $exifTool->ProcessDirectory(\%dirInfo, $tagTablePtr) or $warn = 1;
|
---|
549 | delete $$exifTool{SET_GROUP1};
|
---|
550 | $num = ($num || 1) + 1;
|
---|
551 | }
|
---|
552 | $warn and $exifTool->Warn('Possibly corrupt RAF information');
|
---|
553 | return 1;
|
---|
554 | }
|
---|
555 |
|
---|
556 | #------------------------------------------------------------------------------
|
---|
557 | # Process Adobe SR2 directory
|
---|
558 | # Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
|
---|
559 | # Returns: 1 on success, otherwise returns 0 and sets a Warning
|
---|
560 | # Notes: data has 6 byte header (2 for byte order and 4 for original offset)
|
---|
561 | sub ProcessAdobeSR2($$$)
|
---|
562 | {
|
---|
563 | my ($exifTool, $dirInfo, $tagTablePtr) = @_;
|
---|
564 | return 0 if $$dirInfo{OutFile}; # (can't write this yet)
|
---|
565 | my $dataPt = $$dirInfo{DataPt};
|
---|
566 | my $start = $$dirInfo{DirStart};
|
---|
567 | my $len = $$dirInfo{DirLen};
|
---|
568 |
|
---|
569 | return 0 if $len < 6;
|
---|
570 | SetByteOrder('MM');
|
---|
571 | my $originalPos = Get32u($dataPt, $start + 2);
|
---|
572 | return 0 unless SetByteOrder(substr($$dataPt, $start, 2));
|
---|
573 |
|
---|
574 | $exifTool->VerboseDir($dirInfo);
|
---|
575 | my $dataPos = $$dirInfo{DataPos};
|
---|
576 | my $dirStart = $start + 6; # pointer to maker note directory
|
---|
577 | my $dirLen = $len - 6;
|
---|
578 |
|
---|
579 | # initialize subdirectory information
|
---|
580 | my $fix = $dataPos + $dirStart - $originalPos;
|
---|
581 | my %subdirInfo = (
|
---|
582 | DirName => 'AdobeSR2',
|
---|
583 | Base => $$dirInfo{Base} + $fix,
|
---|
584 | DataPt => $dataPt,
|
---|
585 | DataPos => $dataPos - $fix,
|
---|
586 | DataLen => $$dirInfo{DataLen},
|
---|
587 | DirStart => $dirStart,
|
---|
588 | DirLen => $dirLen,
|
---|
589 | Parent => $$dirInfo{DirName},
|
---|
590 | );
|
---|
591 | if ($exifTool->Options('HtmlDump')) {
|
---|
592 | $exifTool->HDump($dataPos + $start, 6, 'Adobe SR2 data');
|
---|
593 | }
|
---|
594 | # parse the SR2 directory
|
---|
595 | $exifTool->ProcessDirectory(\%subdirInfo, $tagTablePtr);
|
---|
596 | return 1;
|
---|
597 | }
|
---|
598 |
|
---|
599 | #------------------------------------------------------------------------------
|
---|
600 | # Process Adobe-mutilated IFD directory
|
---|
601 | # Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
|
---|
602 | # Returns: 1 on success, otherwise returns 0 and sets a Warning
|
---|
603 | # Notes: data has 2 byte header (byte order of the data)
|
---|
604 | sub ProcessAdobeIFD($$$)
|
---|
605 | {
|
---|
606 | my ($exifTool, $dirInfo, $tagTablePtr) = @_;
|
---|
607 | return 0 if $$dirInfo{OutFile}; # (can't write this yet)
|
---|
608 | my $dataPt = $$dirInfo{DataPt};
|
---|
609 | my $pos = $$dirInfo{DirStart};
|
---|
610 | my $dataPos = $$dirInfo{DataPos};
|
---|
611 |
|
---|
612 | return 0 if $$dirInfo{DirLen} < 4;
|
---|
613 | my $dataOrder = substr($$dataPt, $pos, 2);
|
---|
614 | return 0 unless SetByteOrder($dataOrder); # validate byte order of data
|
---|
615 |
|
---|
616 | # parse the mutilated IFD. This is similar to a TIFF IFD, except:
|
---|
617 | # - data follows directly after Count entry in IFD
|
---|
618 | # - byte order of IFD entires is always big-endian, but byte order of data changes
|
---|
619 | SetByteOrder('MM'); # IFD structure is always big-endian
|
---|
620 | my $entries = Get16u($dataPt, $pos + 2);
|
---|
621 | $exifTool->VerboseDir($dirInfo, $entries);
|
---|
622 | $pos += 4;
|
---|
623 |
|
---|
624 | my $end = $pos + $$dirInfo{DirLen};
|
---|
625 | my $index;
|
---|
626 | for ($index=0; $index<$entries; ++$index) {
|
---|
627 | last if $pos + 8 > $end;
|
---|
628 | SetByteOrder('MM'); # directory entries always big-endian (doh!)
|
---|
629 | my $tagID = Get16u($dataPt, $pos);
|
---|
630 | my $format = Get16u($dataPt, $pos+2);
|
---|
631 | my $count = Get32u($dataPt, $pos+4);
|
---|
632 | if ($format < 1 or $format > 13) {
|
---|
633 | # warn unless the IFD was just padded with zeros
|
---|
634 | $format and $exifTool->Warn(
|
---|
635 | sprintf("Unknown format ($format) for $$dirInfo{DirName} tag 0x%x",$tagID));
|
---|
636 | return 0; # must be corrupted
|
---|
637 | }
|
---|
638 | my $size = $Image::ExifTool::Exif::formatSize[$format] * $count;
|
---|
639 | last if $pos + 8 + $size > $end;
|
---|
640 | my $formatStr = $Image::ExifTool::Exif::formatName[$format];
|
---|
641 | SetByteOrder($dataOrder); # data stored in native order
|
---|
642 | my $val = ReadValue($dataPt, $pos + 8, $formatStr, $count, $size);
|
---|
643 | $exifTool->HandleTag($tagTablePtr, $tagID, $val,
|
---|
644 | Index => $index,
|
---|
645 | DataPt => $dataPt,
|
---|
646 | DataPos => $dataPos,
|
---|
647 | Start => $pos + 8,
|
---|
648 | Size => $size
|
---|
649 | );
|
---|
650 | $pos += 8 + $size;
|
---|
651 | }
|
---|
652 | if ($index < $entries) {
|
---|
653 | $exifTool->Warn("Truncated $$dirInfo{DirName} directory");
|
---|
654 | return 0;
|
---|
655 | }
|
---|
656 | return 1;
|
---|
657 | }
|
---|
658 |
|
---|
659 | #------------------------------------------------------------------------------
|
---|
660 | # Process Adobe MakerNotes directory
|
---|
661 | # Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
|
---|
662 | # Returns: 1 on success, otherwise returns 0 and sets a Warning
|
---|
663 | # Notes: data has 6 byte header (2 for byte order and 4 for original offset)
|
---|
664 | sub ProcessAdobeMakN($$$)
|
---|
665 | {
|
---|
666 | my ($exifTool, $dirInfo, $tagTablePtr) = @_;
|
---|
667 | my $dataPt = $$dirInfo{DataPt};
|
---|
668 | my $start = $$dirInfo{DirStart};
|
---|
669 | my $len = $$dirInfo{DirLen};
|
---|
670 | my $outfile = $$dirInfo{OutFile};
|
---|
671 |
|
---|
672 | return 0 if $len < 6;
|
---|
673 | SetByteOrder('MM');
|
---|
674 | my $originalPos = Get32u($dataPt, $start + 2);
|
---|
675 | return 0 unless SetByteOrder(substr($$dataPt, $start, 2));
|
---|
676 |
|
---|
677 | $exifTool->VerboseDir($dirInfo) unless $outfile;
|
---|
678 | my $dataPos = $$dirInfo{DataPos};
|
---|
679 | my $dirStart = $start + 6; # pointer to maker note directory
|
---|
680 | my $dirLen = $len - 6;
|
---|
681 |
|
---|
682 | my $hdr = substr($$dataPt, $dirStart, $dirLen < 48 ? $dirLen : 48);
|
---|
683 | my $tagInfo = $exifTool->GetTagInfo($tagTablePtr, 'MakN', \$hdr);
|
---|
684 | return 0 unless $tagInfo and $$tagInfo{SubDirectory};
|
---|
685 | my $subdir = $$tagInfo{SubDirectory};
|
---|
686 | my $subTable = GetTagTable($$subdir{TagTable});
|
---|
687 | # initialize subdirectory information
|
---|
688 | my %subdirInfo = (
|
---|
689 | DirName => 'MakerNotes',
|
---|
690 | Name => $$tagInfo{Name}, # needed for maker notes verbose dump
|
---|
691 | Base => $$dirInfo{Base},
|
---|
692 | DataPt => $dataPt,
|
---|
693 | DataPos => $dataPos,
|
---|
694 | DataLen => $$dirInfo{DataLen},
|
---|
695 | DirStart => $dirStart,
|
---|
696 | DirLen => $dirLen,
|
---|
697 | TagInfo => $tagInfo,
|
---|
698 | FixBase => $$subdir{FixBase},
|
---|
699 | EntryBased=> $$subdir{EntryBased},
|
---|
700 | Parent => $$dirInfo{DirName},
|
---|
701 | );
|
---|
702 | # look for start of maker notes IFD
|
---|
703 | my $loc = Image::ExifTool::MakerNotes::LocateIFD($exifTool,\%subdirInfo);
|
---|
704 | unless (defined $loc) {
|
---|
705 | $exifTool->Warn('Maker notes could not be parsed');
|
---|
706 | return 0;
|
---|
707 | }
|
---|
708 | if ($exifTool->Options('HtmlDump')) {
|
---|
709 | $exifTool->HDump($dataPos + $start, 6, 'Adobe MakN data');
|
---|
710 | $exifTool->HDump($dataPos + $dirStart, $loc, "$$tagInfo{Name} header") if $loc;
|
---|
711 | }
|
---|
712 |
|
---|
713 | my $fix = 0;
|
---|
714 | unless ($$subdir{Base}) {
|
---|
715 | # adjust base offset for current maker note position
|
---|
716 | $fix = $dataPos + $dirStart - $originalPos;
|
---|
717 | $subdirInfo{Base} += $fix;
|
---|
718 | $subdirInfo{DataPos} -= $fix;
|
---|
719 | }
|
---|
720 | if ($outfile) {
|
---|
721 | # rewrite the maker notes directory
|
---|
722 | my $fixup = $subdirInfo{Fixup} = new Image::ExifTool::Fixup;
|
---|
723 | my $oldChanged = $$exifTool{CHANGED};
|
---|
724 | my $buff = $exifTool->WriteDirectory(\%subdirInfo, $subTable);
|
---|
725 | # nothing to do if error writing directory or nothing changed
|
---|
726 | unless (defined $buff and $exifTool->{CHANGED} != $oldChanged) {
|
---|
727 | $exifTool->{CHANGED} = $oldChanged;
|
---|
728 | return 1;
|
---|
729 | }
|
---|
730 | # deleting maker notes if directory is empty
|
---|
731 | unless (length $buff) {
|
---|
732 | $$outfile = '';
|
---|
733 | return 1;
|
---|
734 | }
|
---|
735 | # apply a one-time fixup to offsets
|
---|
736 | if ($subdirInfo{Relative}) {
|
---|
737 | # shift all offsets to be relative to new base
|
---|
738 | my $baseShift = $dataPos + $dirStart + $$dirInfo{Base} - $subdirInfo{Base};
|
---|
739 | $fixup->{Shift} += $baseShift;
|
---|
740 | } else {
|
---|
741 | # shift offsets to position of original maker notes
|
---|
742 | $fixup->{Shift} += $originalPos;
|
---|
743 | }
|
---|
744 | # if we wrote the directory as a block the header is already included
|
---|
745 | $loc = 0 if $subdirInfo{BlockWrite};
|
---|
746 | $fixup->{Shift} += $loc; # adjust for makernotes header
|
---|
747 | $fixup->ApplyFixup(\$buff); # fix up pointer offsets
|
---|
748 | # get copy of original Adobe header (6) and makernotes header ($loc)
|
---|
749 | my $header = substr($$dataPt, $start, 6 + $loc);
|
---|
750 | # add Adobe and makernotes headers to new directory
|
---|
751 | $$outfile = $header . $buff;
|
---|
752 | } else {
|
---|
753 | # parse the maker notes directory
|
---|
754 | $exifTool->ProcessDirectory(\%subdirInfo, $subTable, $$subdir{ProcessProc});
|
---|
755 | # extract maker notes as a block if specified
|
---|
756 | if ($exifTool->Options('MakerNotes') or
|
---|
757 | $exifTool->{REQ_TAG_LOOKUP}->{lc($$tagInfo{Name})})
|
---|
758 | {
|
---|
759 | my $val;
|
---|
760 | if ($$tagInfo{MakerNotes}) {
|
---|
761 | $subdirInfo{Base} = $$dirInfo{Base} + $fix;
|
---|
762 | $subdirInfo{DataPos} = $dataPos - $fix;
|
---|
763 | $subdirInfo{DirStart} = $dirStart;
|
---|
764 | $subdirInfo{DirLen} = $dirLen;
|
---|
765 | # rebuild the maker notes to identify all offsets that require fixing up
|
---|
766 | $val = Image::ExifTool::Exif::RebuildMakerNotes($exifTool, $subTable, \%subdirInfo);
|
---|
767 | defined $val or $exifTool->Warn('Error rebuilding maker notes (may be corrupt)');
|
---|
768 | } else {
|
---|
769 | # extract this directory as a block if specified
|
---|
770 | return 1 unless $$tagInfo{Writable};
|
---|
771 | }
|
---|
772 | $val = substr($$dataPt, 20) unless defined $val;
|
---|
773 | $exifTool->FoundTag($tagInfo, $val);
|
---|
774 | }
|
---|
775 | }
|
---|
776 | return 1;
|
---|
777 | }
|
---|
778 |
|
---|
779 | #------------------------------------------------------------------------------
|
---|
780 | # Write Adobe information (calls appropriate ProcessProc to do the actual work)
|
---|
781 | # Inputs: 0) ExifTool object ref, 1) source dirInfo ref, 2) tag table ref
|
---|
782 | # Returns: new data block (may be empty if directory is deleted) or undef on error
|
---|
783 | sub WriteAdobeStuff($$$)
|
---|
784 | {
|
---|
785 | my ($exifTool, $dirInfo, $tagTablePtr) = @_;
|
---|
786 | $exifTool or return 1; # allow dummy access
|
---|
787 | my $proc = $$dirInfo{Proc} || \&ProcessAdobeData;
|
---|
788 | my $buff;
|
---|
789 | $$dirInfo{OutFile} = \$buff;
|
---|
790 | &$proc($exifTool, $dirInfo, $tagTablePtr) or undef $buff;
|
---|
791 | return $buff;
|
---|
792 | }
|
---|
793 |
|
---|
794 | 1; # end
|
---|
795 |
|
---|
796 | __END__
|
---|
797 |
|
---|
798 | =head1 NAME
|
---|
799 |
|
---|
800 | Image::ExifTool::DNG.pm - Read DNG-specific information
|
---|
801 |
|
---|
802 | =head1 SYNOPSIS
|
---|
803 |
|
---|
804 | This module is used by Image::ExifTool
|
---|
805 |
|
---|
806 | =head1 DESCRIPTION
|
---|
807 |
|
---|
808 | This module contains routines required by Image::ExifTool to process
|
---|
809 | information in DNG (Digital Negative) images.
|
---|
810 |
|
---|
811 | =head1 AUTHOR
|
---|
812 |
|
---|
813 | Copyright 2003-2011, Phil Harvey (phil at owl.phy.queensu.ca)
|
---|
814 |
|
---|
815 | This library is free software; you can redistribute it and/or modify it
|
---|
816 | under the same terms as Perl itself.
|
---|
817 |
|
---|
818 | =head1 REFERENCES
|
---|
819 |
|
---|
820 | =over 4
|
---|
821 |
|
---|
822 | =item L<http://www.adobe.com/products/dng/>
|
---|
823 |
|
---|
824 | =back
|
---|
825 |
|
---|
826 | =head1 SEE ALSO
|
---|
827 |
|
---|
828 | L<Image::ExifTool::TagNames/DNG Tags>,
|
---|
829 | L<Image::ExifTool::TagNames/EXIF Tags>,
|
---|
830 | L<Image::ExifTool(3pm)|Image::ExifTool>
|
---|
831 |
|
---|
832 | =cut
|
---|