source: main/trunk/greenstone2/perllib/cpan/Image/ExifTool/SigmaRaw.pm@ 34921

Last change on this file since 34921 was 34921, checked in by anupama, 3 years ago

Committing the improvements to EmbeddedMetaPlugin's processing of Keywords vs other metadata fields. Keywords were literally stored as arrays of words rather than phrases in PDFs (at least in Diego's sample PDF), whereas other meta fields like Subjects and Creators stored them as arrays of phrases. To get both to work, Kathy updated EXIF to a newer version, to retrieve the actual EXIF values stored in the PDF. And Kathy and Dr Bainbridge came up with a new option that I added called apply_join_before_split_to_metafields that's a regex which can list the metadata fields to apply the join_before_split to and whcih previously always got applied to all metadata fields. Now it's applied to any *Keywords metafields by default, as that's the metafield we have experience of that behaves differently to the others, as it stores by word instead of phrases. Tested on Diego's sample PDF. Diego has double-checked it to works on his sample PDF too, setting the split char to ; and turning on the join_before_split and leaving apply_join_before_split_to_metafields at its default of .*Keywords. File changes are strings.properties for the tooltip, the plugin introducing the option and working with it and Kathy's EXIF updates affecting cpan/File and cpan/Image.

File size: 23.4 KB
Line 
1#------------------------------------------------------------------------------
2# File: SigmaRaw.pm
3#
4# Description: Read Sigma/Foveon RAW (X3F) meta information
5#
6# Revisions: 2005/10/16 - P. Harvey Created
7# 2009/11/30 - P. Harvey Support X3F v2.3 written by Sigma DP2
8#
9# References: 1) http://www.x3f.info/technotes/FileDocs/X3F_Format.pdf
10#------------------------------------------------------------------------------
11
12package Image::ExifTool::SigmaRaw;
13
14use strict;
15use vars qw($VERSION);
16use Image::ExifTool qw(:DataAccess :Utils);
17use Image::ExifTool::Sigma;
18
19$VERSION = '1.27';
20
21sub ProcessX3FHeader($$$);
22sub ProcessX3FDirectory($$$);
23sub ProcessX3FProperties($$$);
24
25# main X3F sections (plus header stuff)
26%Image::ExifTool::SigmaRaw::Main = (
27 PROCESS_PROC => \&ProcessX3FDirectory,
28 NOTES => q{
29 These tags are used in Sigma and Foveon RAW (.X3F) images. Metadata is also
30 extracted from the JpgFromRaw image if it exists (all models but the SD9 and
31 SD10). Currently, metadata may only be written to the embedded JpgFromRaw.
32 },
33 Header => {
34 SubDirectory => { TagTable => 'Image::ExifTool::SigmaRaw::Header' },
35 },
36 Header4 => {
37 SubDirectory => { TagTable => 'Image::ExifTool::SigmaRaw::Header4' },
38 },
39 HeaderExt => {
40 SubDirectory => { TagTable => 'Image::ExifTool::SigmaRaw::HeaderExt' },
41 },
42 PROP => {
43 Name => 'Properties',
44 SubDirectory => { TagTable => 'Image::ExifTool::SigmaRaw::Properties' },
45 },
46 IMAG => {
47 Name => 'PreviewImage',
48 Groups => { 2 => 'Preview' },
49 Binary => 1,
50 },
51 IMA2 => [
52 {
53 Name => 'PreviewImage',
54 Condition => 'not $$self{IsJpgFromRaw}',
55 Groups => { 2 => 'Preview' },
56 Binary => 1,
57 },
58 {
59 Name => 'JpgFromRaw',
60 Groups => { 2 => 'Preview' },
61 Binary => 1,
62 },
63 ]
64);
65
66# common X3F header structure
67%Image::ExifTool::SigmaRaw::Header = (
68 PROCESS_PROC => \&ProcessX3FHeader,
69 FORMAT => 'int32u',
70 NOTES => 'Information extracted from the header of an X3F file.',
71 1 => {
72 Name => 'FileVersion',
73 ValueConv => '($val >> 16) . "." . ($val & 0xffff)',
74 },
75 2 => {
76 Name => 'ImageUniqueID',
77 # the serial number (with an extra leading "0") makes up
78 # the first 8 digits of this UID,
79 Format => 'undef[16]',
80 ValueConv => 'unpack("H*", $val)',
81 },
82 6 => {
83 Name => 'MarkBits',
84 PrintConv => { BITMASK => { } },
85 },
86 7 => 'ImageWidth',
87 8 => 'ImageHeight',
88 9 => 'Rotation',
89 10 => {
90 Name => 'WhiteBalance',
91 Format => 'string[32]',
92 },
93 18 => { #PH (DP2, FileVersion 2.3)
94 Name => 'SceneCaptureType',
95 Format => 'string[32]',
96 },
97);
98
99# X3F version 4 header structure (ref PH)
100%Image::ExifTool::SigmaRaw::Header4 = (
101 PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
102 FORMAT => 'int32u',
103 NOTES => 'Header information for version 4.0 or greater X3F.',
104 1 => {
105 Name => 'FileVersion',
106 ValueConv => '($val >> 16) . "." . ($val & 0xffff)',
107 },
108 # 8 - undef[4]: 4 random ASCII characters
109 10 => 'ImageWidth',
110 11 => 'ImageHeight',
111 12 => 'Rotation',
112 # don't know what the rest of the header contains, but none of
113 # these values change in any of my samples...
114);
115
116# extended header tags
117%Image::ExifTool::SigmaRaw::HeaderExt = (
118 GROUPS => { 2 => 'Camera' },
119 FORMAT => 'float',
120 NOTES => 'Extended header data found in version 2.1 and 2.2 files',
121 0 => 'Unused',
122 1 => { Name => 'ExposureAdjust',PrintConv => 'sprintf("%.1f",$val)' },
123 2 => { Name => 'Contrast', PrintConv => 'sprintf("%.1f",$val)' },
124 3 => { Name => 'Shadow', PrintConv => 'sprintf("%.1f",$val)' },
125 4 => { Name => 'Highlight', PrintConv => 'sprintf("%.1f",$val)' },
126 5 => { Name => 'Saturation', PrintConv => 'sprintf("%.1f",$val)' },
127 6 => { Name => 'Sharpness', PrintConv => 'sprintf("%.1f",$val)' },
128 7 => { Name => 'RedAdjust', PrintConv => 'sprintf("%.1f",$val)' },
129 8 => { Name => 'GreenAdjust', PrintConv => 'sprintf("%.1f",$val)' },
130 9 => { Name => 'BlueAdjust', PrintConv => 'sprintf("%.1f",$val)' },
131 10 => { Name => 'X3FillLight', PrintConv => 'sprintf("%.1f",$val)' },
132);
133
134# PROP tags
135%Image::ExifTool::SigmaRaw::Properties = (
136 PROCESS_PROC => \&ProcessX3FProperties,
137 GROUPS => { 2 => 'Camera' },
138 PRIORITY => 0, # (because these aren't writable like the EXIF ones)
139 AEMODE => {
140 Name => 'MeteringMode',
141 PrintConv => {
142 8 => '8-segment',
143 C => 'Center-weighted average',
144 A => 'Average',
145 },
146 },
147 AFAREA => 'AFArea', # observed: CENTER_V
148 AFINFOCUS => 'AFInFocus', # observed: H
149 AFMODE => 'FocusMode',
150 AP_DESC => 'ApertureDisplayed',
151 APERTURE => {
152 Name => 'FNumber',
153 Groups => { 2 => 'Image' },
154 PrintConv => 'sprintf("%.1f",$val)',
155 },
156 BRACKET => 'BracketShot',
157 BURST => 'BurstShot',
158 CAMMANUF => 'Make',
159 CAMMODEL => 'Model',
160 CAMNAME => 'CameraName',
161 CAMSERIAL => 'SerialNumber',
162 CM_DESC => 'SceneCaptureType', #PH (DP2)
163 COLORSPACE => 'ColorSpace', # observed: sRGB
164 DRIVE => {
165 Name => 'DriveMode',
166 PrintConv => {
167 SINGLE => 'Single Shot',
168 MULTI => 'Multi Shot',
169 '2S' => '2 s Timer',
170 '10S' => '10 s Timer',
171 UP => 'Mirror Up',
172 AB => 'Auto Bracket',
173 OFF => 'Off',
174 },
175 },
176 EVAL_STATE => 'EvalState', # observed: POST-EXPOSURE
177 EXPCOMP => {
178 Name => 'ExposureCompensation',
179 Groups => { 2 => 'Image' },
180 PrintConv => 'Image::ExifTool::Exif::PrintFraction($val)',
181 },
182 EXPNET => {
183 Name => 'NetExposureCompensation',
184 Groups => { 2 => 'Image' },
185 PrintConv => 'Image::ExifTool::Exif::PrintFraction($val)',
186 },
187 EXPTIME => {
188 Name => 'IntegrationTime',
189 Groups => { 2 => 'Image' },
190 ValueConv => '$val * 1e-6', # convert from usec
191 PrintConv => 'Image::ExifTool::Exif::PrintExposureTime($val)',
192 },
193 FIRMVERS => 'FirmwareVersion',
194 FLASH => {
195 Name => 'FlashMode',
196 PrintConv => 'ucfirst(lc($val))',
197 },
198 FLASHEXPCOMP=> 'FlashExpComp',
199 FLASHPOWER => 'FlashPower',
200 FLASHTTLMODE=> 'FlashTTLMode', # observed: ON
201 FLASHTYPE => 'FlashType', # observed: NONE
202 FLENGTH => {
203 Name => 'FocalLength',
204 PrintConv => 'sprintf("%.1f mm",$val)',
205 },
206 FLEQ35MM => {
207 Name => 'FocalLengthIn35mmFormat',
208 PrintConv => 'sprintf("%.1f mm",$val)',
209 },
210 FOCUS => {
211 Name => 'Focus',
212 PrintConv => {
213 AF => 'Auto-focus Locked',
214 'NO LOCK' => "Auto-focus Didn't Lock",
215 M => 'Manual',
216 },
217 },
218 IMAGERBOARDID => 'ImagerBoardID',
219 IMAGERTEMP => {
220 Name => 'SensorTemperature',
221 PrintConv => '"$val C"',
222 },
223 IMAGEBOARDID=> 'ImageBoardID', #PH (DP2)
224 ISO => 'ISO',
225 LENSARANGE => 'LensApertureRange',
226 LENSFRANGE => 'LensFocalRange',
227 LENSMODEL => {
228 Name => 'LensType',
229 ValueConv => '$val =~ /^[0-9a-f]+$/i ? hex($val) : $val',
230 ValueConvInv => '$val=~s/\.\d+$//; IsInt($val) ? sprintf("%x",$val) : $val', # (truncate decimal part)
231 SeparateTable => 'Sigma LensType',
232 PrintHex => 1,
233 PrintConv => \%Image::ExifTool::Sigma::sigmaLensTypes,
234 },
235 PMODE => {
236 Name => 'ExposureProgram',
237 PrintConv => {
238 P => 'Program',
239 A => 'Aperture Priority',
240 S => 'Shutter Priority',
241 M => 'Manual',
242 },
243 },
244 RESOLUTION => {
245 Name => 'Quality',
246 PrintConv => {
247 LOW => 'Low',
248 MED => 'Medium',
249 HI => 'High',
250 },
251 },
252 SENSORID => 'SensorID',
253 SH_DESC => 'ShutterSpeedDisplayed',
254 SHUTTER => {
255 Name => 'ExposureTime',
256 Groups => { 2 => 'Image' },
257 PrintConv => 'Image::ExifTool::Exif::PrintExposureTime($val)',
258 },
259 TIME => {
260 Name => 'DateTimeOriginal',
261 Groups => { 2 => 'Time' },
262 Description => 'Date/Time Original',
263 ValueConv => 'ConvertUnixTime($val)',
264 PrintConv => '$self->ConvertDateTime($val)',
265 },
266 WB_DESC => 'WhiteBalance',
267 VERSION_BF => 'VersionBF',
268);
269
270#------------------------------------------------------------------------------
271# Extract null-terminated unicode string from list of characters
272# Inputs: 0) ExifTool ref, 1) list ref, 2) position in list
273# Returns: Converted string
274sub ExtractUnicodeString($$$)
275{
276 my ($et, $chars, $pos) = @_;
277 my $i;
278 for ($i=$pos; $i<@$chars; ++$i) {
279 last unless $$chars[$i];
280 }
281 my $buff = pack('v*', @$chars[$pos..$i-1]);
282 return $et->Decode($buff, 'UCS2', 'II');
283}
284
285#------------------------------------------------------------------------------
286# Process an X3F header
287# Inputs: 0) ExifTool ref, 1) DirInfo ref, 2) tag table ref
288# Returns: 1 on success
289sub ProcessX3FHeader($$$)
290{
291 my ($et, $dirInfo, $tagTablePtr) = @_;
292 my $dataPt = $$dirInfo{DataPt};
293 my $hdrLen = $$dirInfo{DirLen};
294
295 # process the static header structure first
296 $et->ProcessBinaryData($dirInfo, $tagTablePtr);
297
298 # process extended data if available
299 if (length($$dataPt) - $hdrLen >= 160) {
300 my $verbose = $et->Options('Verbose');
301 if ($verbose) {
302 $et->VerboseDir('X3F HeaderExt', 32);
303 $et->VerboseDump($dataPt, Start => $hdrLen);
304 }
305 $tagTablePtr = GetTagTable('Image::ExifTool::SigmaRaw::HeaderExt');
306 my @tags = unpack("x${hdrLen}C32", $$dataPt);
307 my $i;
308 my $unused = 0;
309 for ($i=0; $i<32; ++$i) {
310 $tags[$i] or ++$unused, next;
311 $et->HandleTag($tagTablePtr, $tags[$i], undef,
312 Index => $i,
313 DataPt => $dataPt,
314 Start => $hdrLen + 32 + $i * 4,
315 Size => 4,
316 );
317 }
318 $et->VPrint(0, "$$et{INDENT}($unused entries unused)\n");
319 }
320 return 1;
321}
322
323#------------------------------------------------------------------------------
324# Process an X3F properties
325# Inputs: 0) ExifTool ref, 1) DirInfo ref, 2) tag table ref
326# Returns: 1 on success
327sub ProcessX3FProperties($$$)
328{
329 my ($et, $dirInfo, $tagTablePtr) = @_;
330 my $dataPt = $$dirInfo{DataPt};
331 my $size = length($$dataPt);
332 my $verbose = $et->Options('Verbose');
333 my $unknown = $et->Options('Unknown');
334
335 unless ($size >= 24 and $$dataPt =~ /^SECp/) {
336 $et->Warn('Bad properties header');
337 return 0;
338 }
339 my ($entries, $fmt, $len) = unpack('x8V2x4V', $$dataPt);
340 unless ($size >= 24 + 8 * $entries + $len) {
341 $et->Warn('Truncated Property directory');
342 return 0;
343 }
344 $verbose and $et->VerboseDir('Properties', $entries);
345 $fmt == 0 or $et->Warn("Unsupported character format $fmt"), return 0;
346 my $charPos = 24 + 8 * $entries;
347 my @chars = unpack('v*',substr($$dataPt, $charPos, $len * 2));
348 my $index;
349 for ($index=0; $index<$entries; ++$index) {
350 my ($namePos, $valPos) = unpack('V2',substr($$dataPt, $index*8 + 24, 8));
351 if ($namePos >= @chars or $valPos >= @chars) {
352 $et->Warn('Bad Property pointer');
353 return 0;
354 }
355 my $tag = ExtractUnicodeString($et, \@chars, $namePos);
356 my $val = ExtractUnicodeString($et, \@chars, $valPos);
357 if (not $$tagTablePtr{$tag} and $unknown and $tag =~ /^\w+$/) {
358 my $tagInfo = {
359 Name => "SigmaRaw_$tag",
360 Description => Image::ExifTool::MakeDescription('SigmaRaw', $tag),
361 Unknown => 1,
362 Writable => 0, # can't write unknown tags
363 };
364 # add tag information to table
365 AddTagToTable($tagTablePtr, $tag, $tagInfo);
366 }
367
368 $et->HandleTag($tagTablePtr, $tag, $val,
369 Index => $index,
370 DataPt => $dataPt,
371 Start => $charPos + 2 * $valPos,
372 Size => 2 * (length($val) + 1),
373 );
374 }
375 return 1;
376}
377
378#------------------------------------------------------------------------------
379# Write an X3F file
380# Inputs: 0) ExifTool ref, 1) DirInfo ref (DirStart = directory offset)
381# Returns: error string, undef on success, or -1 on write error
382# Notes: Writes metadata to embedded JpgFromRaw image
383sub WriteX3F($$)
384{
385 my ($et, $dirInfo) = @_;
386 my $raf = $$dirInfo{RAF};
387 my $outfile = $$dirInfo{OutFile};
388 my ($outDir, $buff, $ver, $entries, $dir, $outPos, $index, $didContain);
389
390 $raf->Seek($$dirInfo{DirStart}, 0) or return 'Error seeking to directory start';
391
392 # read the X3F directory header (will be copied directly to output)
393 $raf->Read($outDir, 12) == 12 or return 'Truncated X3F image';
394 $outDir =~ /^SECd/ or return 'Bad section header';
395 ($ver, $entries) = unpack('x4V2', $outDir);
396
397 # do sanity check on number of entries in directory
398 return 'Invalid X3F directory count' unless $entries > 2 and $entries < 20;
399 # read the directory entries
400 unless ($raf->Read($dir, $entries * 12) == $entries * 12) {
401 return 'Truncated X3F directory';
402 }
403 # do a quick scan to determine the offset of the first data subsection
404 for ($index=0; $index<$entries; ++$index) {
405 my $pos = $index * 12;
406 my ($offset, $len, $tag) = unpack("x${pos}V2a4", $dir);
407 # remember position of first data subsection
408 $outPos = $offset if not defined $outPos or $outPos > $offset;
409 }
410 # copy the file header up to the start of the first data subsection
411 unless ($raf->Seek(0,0) and $raf->Read($buff, $outPos) == $outPos) {
412 return 'Error reading X3F header';
413 }
414 Write($outfile, $buff) or return -1;
415
416 # loop through directory, rewriting each section
417 for ($index=0; $index<$entries; ++$index) {
418
419 my $pos = $index * 12;
420 my ($offset, $len, $tag) = unpack("x${pos}V2a4", $dir);
421 $raf->Seek($offset, 0) or return 'Bad data offset';
422
423 if ($tag eq 'IMA2' and $len > 28) {
424 # check subsection header (28 bytes) to see if this is a JPEG preview image
425 $raf->Read($buff, 28) == 28 or return 'Error reading PreviewImage header';
426 Write($outfile, $buff) or return -1;
427 $len -= 28;
428
429 # only rewrite full-sized JpgFromRaw (version 2.0, type 2, format 18)
430 if ($buff =~ /^SECi\0\0\x02\0\x02\0\0\0\x12\0\0\0/) {
431 $raf->Read($buff, $len) == $len or return 'Error reading JpgFromRaw';
432 if ($buff =~ /^\xff\xd8\xff\xe1/) { # does this preview contain EXIF?
433 # use same write directories as JPEG
434 $et->InitWriteDirs('JPEG');
435 # make sure we don't add APP0 JFIF because it would mess up our preview identification
436 delete $$et{ADD_DIRS}{APP0};
437 delete $$et{ADD_DIRS}{JFIF};
438 # rewrite the embedded JPEG in memory
439 my $newData;
440 my %jpegInfo = (
441 Parent => 'X3F',
442 RAF => new File::RandomAccess(\$buff),
443 OutFile => \$newData,
444 );
445 $$et{FILE_TYPE} = 'JPEG';
446 my $success = $et->WriteJPEG(\%jpegInfo);
447 $$et{FILE_TYPE} = 'X3F';
448 SetByteOrder('II');
449 return 'Error writing X3F JpgFromRaw' unless $success and $newData;
450 return -1 if $success < 0;
451 # (this shouldn't happen unless someone tries to delete the EXIF...)
452 return 'EXIF segment must come first in X3F JpgFromRaw' unless $newData =~ /^\xff\xd8\xff\xe1/;
453 # write new data if anything changed, otherwise copy old image
454 my $outPt = $$et{CHANGED} ? \$newData : \$buff;
455 Write($outfile, $$outPt) or return -1;
456 # set $len to the total subsection data length
457 $len = length($$outPt);
458 $didContain = 1;
459 } else {
460 Write($outfile, $buff) or return -1;
461 }
462 } else {
463 # copy original image data
464 Image::ExifTool::CopyBlock($raf, $outfile, $len) or return 'Corrupted X3F image';
465 }
466 $len += 28; # add back header length
467 } else {
468 # copy data for this subsection
469 Image::ExifTool::CopyBlock($raf, $outfile, $len) or return 'Corrupted X3F directory';
470 }
471 # add directory entry and update output file position
472 $outDir .= pack('V2a4', $outPos, $len, $tag);
473 $outPos += $len;
474 # pad data to an even 4-byte boundary
475 if ($len & 0x03) {
476 my $pad = 4 - ($len & 0x03);
477 Write($outfile, "\0" x $pad) or return -1;
478 $outPos += $pad;
479 }
480 }
481 # warn if we couldn't add metadata to this image (should only be SD9 or SD10)
482 $didContain or $et->Warn("Can't yet write SD9 or SD10 X3F images");
483 # write out the directory and the directory pointer, and we are done
484 Write($outfile, $outDir, pack('V', $outPos)) or return -1;
485 return undef;
486}
487
488#------------------------------------------------------------------------------
489# Process an X3F directory
490# Inputs: 0) ExifTool ref, 1) DirInfo ref, 2) tag table ref
491# Returns: error string or undef on success
492sub ProcessX3FDirectory($$$)
493{
494 my ($et, $dirInfo, $tagTablePtr) = @_;
495 my $raf = $$dirInfo{RAF};
496 my $verbose = $et->Options('Verbose');
497
498 $raf->Seek($$dirInfo{DirStart}, 0) or return 'Error seeking to directory start';
499
500 # parse the X3F directory structure
501 my ($buff, $ver, $entries, $index, $dir);
502 $raf->Read($buff, 12) == 12 or return 'Truncated X3F image';
503 $buff =~ /^SECd/ or return 'Bad section header';
504 ($ver, $entries) = unpack('x4V2', $buff);
505 $verbose and $et->VerboseDir('X3F Subsection', $entries);
506 $raf->Read($dir, $entries * 12) == $entries * 12 or return 'Truncated X3F directory';
507 for ($index=0; $index<$entries; ++$index) {
508 my $pos = $index * 12;
509 my ($offset, $len, $tag) = unpack("x${pos}V2a4", $dir);
510 my $tagInfo = $et->GetTagInfo($tagTablePtr, $tag);
511 if ($verbose) {
512 $et->VPrint(0, "$$et{INDENT}$index) $tag Subsection ($len bytes):\n");
513 if ($verbose > 2) {
514 $raf->Seek($offset, 0) or return 'Error seeking';
515 $raf->Read($buff, $len) == $len or return 'Truncated image';
516 $et->VerboseDump(\$buff);
517 }
518 }
519 next unless $tagInfo;
520 $raf->Seek($offset, 0) or return "Error seeking for $$tagInfo{Name}";
521 if ($$tagInfo{Name} eq 'PreviewImage') {
522 # check image header to see if this is a JPEG preview image
523 $raf->Read($buff, 28) == 28 or return 'Error reading PreviewImage header';
524 # ignore all image data but JPEG compressed (version 2.0, type 2, format 18)
525 next unless $buff =~ /^SECi\0\0\x02\0\x02\0\0\0\x12\0\0\0/;
526 $offset += 28;
527 $len -= 28;
528 $raf->Read($buff, $len) == $len or return "Error reading PreviewImage data";
529 # check fore EXIF segment, and extract this image as the JpgFromRaw
530 if ($buff =~ /^\xff\xd8\xff\xe1/) {
531 $$et{IsJpgFromRaw} = 1;
532 $tagInfo = $et->GetTagInfo($tagTablePtr, $tag);
533 delete $$et{IsJpgFromRaw};
534 }
535 } else {
536 $raf->Read($buff, $len) == $len or return "Error reading $$tagInfo{Name} data";
537 }
538 my $subdir = $$tagInfo{SubDirectory};
539 if ($subdir) {
540 my %dirInfo = ( DataPt => \$buff );
541 my $subTable = GetTagTable($$subdir{TagTable});
542 $et->ProcessDirectory(\%dirInfo, $subTable);
543 } else {
544 # extract metadata from JpgFromRaw
545 if ($$tagInfo{Name} eq 'JpgFromRaw') {
546 my %dirInfo = (
547 Parent => 'X3F',
548 RAF => new File::RandomAccess(\$buff),
549 );
550 $$et{BASE} += $offset;
551 $et->ProcessJPEG(\%dirInfo);
552 $$et{BASE} -= $offset;
553 SetByteOrder('II');
554 }
555 $et->FoundTag($tagInfo, $buff);
556 }
557 }
558 return undef;
559}
560
561#------------------------------------------------------------------------------
562# Read/write information from a Sigma raw (X3F) image
563# Inputs: 0) ExifTool ref, 1) DirInfo ref
564# Returns: 1 on success, 0 if this wasn't a valid X3F image, or -1 on write error
565sub ProcessX3F($$)
566{
567 my ($et, $dirInfo) = @_;
568 my $outfile = $$dirInfo{OutFile};
569 my $raf = $$dirInfo{RAF};
570 my $warn = $outfile ? \&Image::ExifTool::Error : \&Image::ExifTool::Warn;
571 my ($buff, $err, $hdrLen);
572
573 return 0 unless $raf->Read($buff, 40) == 40;
574 return 0 unless $buff =~ /^FOVb/;
575
576 SetByteOrder('II');
577 $et->SetFileType();
578
579 # check version number
580 my $ver = unpack('x4V',$buff);
581 $ver = ($ver >> 16) . '.' . ($ver & 0xffff);
582 if ($ver > 5) {
583 &$warn($et, "Untested X3F version ($ver). Please submit sample for testing", 1);
584 }
585 # read version 2.1/2.2/2.3 extended header
586 if ($ver > 2) {
587 my ($extra, $buf2);
588 if ($ver >= 4) {
589 $hdrLen = 0x300;
590 $extra = 0;
591 } else {
592 $hdrLen = $ver > 2.2 ? 104 : 72; # SceneCaptureType string added in 2.3
593 $extra = 160; # (extended header is 160 bytes)
594 }
595 my $more = $hdrLen - length($buff) + $extra;
596 unless ($raf->Read($buf2, $more) == $more) {
597 &$warn($et, 'Error reading X3F header');
598 return 1;
599 }
600 $buff .= $buf2;
601 }
602 my ($widPos, $hdrType) = $ver < 4 ? (28, 'Header') : (40, 'Header4');
603 # process header information
604 my $tagTablePtr = GetTagTable('Image::ExifTool::SigmaRaw::Main');
605 unless ($outfile) {
606 $et->HandleTag($tagTablePtr, $hdrType, $buff,
607 DataPt => \$buff,
608 Size => $hdrLen,
609 );
610 }
611 # read the directory pointer
612 $raf->Seek(-4, 2) or &$warn($et, 'Seek error'), return 1;
613 unless ($raf->Read($buff, 4) == 4) {
614 &$warn($et, 'Error reading X3F dir pointer');
615 return 1;
616 }
617 my $offset = unpack('V', $buff);
618 my %dirInfo = (
619 RAF => $raf,
620 DirStart => $offset,
621 );
622 if ($outfile) {
623 $dirInfo{OutFile} = $outfile;
624 $err = WriteX3F($et, \%dirInfo);
625 return -1 if $err and $err eq '-1';
626 } else {
627 # process the X3F subsections
628 $err = $et->ProcessDirectory(\%dirInfo, $tagTablePtr);
629 }
630 $err and &$warn($et, $err);
631 return 1;
632}
633
6341; # end
635
636__END__
637
638=head1 NAME
639
640Image::ExifTool::SigmaRaw - Read Sigma/Foveon RAW (X3F) meta information
641
642=head1 SYNOPSIS
643
644This module is loaded automatically by Image::ExifTool when required.
645
646=head1 DESCRIPTION
647
648This module contains definitions required by Image::ExifTool to read
649Sigma and Foveon X3F images.
650
651=head1 AUTHOR
652
653Copyright 2003-2021, Phil Harvey (philharvey66 at gmail.com)
654
655This library is free software; you can redistribute it and/or modify it
656under the same terms as Perl itself.
657
658=head1 REFERENCES
659
660=over 4
661
662=item L<http://www.x3f.info/technotes/FileDocs/X3F_Format.pdf>
663
664=back
665
666=head1 SEE ALSO
667
668L<Image::ExifTool::TagNames/SigmaRaw Tags>,
669L<Image::ExifTool::Sigma(3pm)|Image::ExifTool::Sigma>,
670L<Image::ExifTool(3pm)|Image::ExifTool>
671
672=cut
Note: See TracBrowser for help on using the repository browser.