source: gs2-extensions/parallel-building/trunk/src/perllib/cpan/Image/ExifTool/FujiFilm.pm@ 24626

Last change on this file since 24626 was 24626, checked in by jmt12, 13 years ago

An (almost) complete copy of the perllib directory from a (circa SEP2011) head checkout from Greenstone 2 trunk - in order to try and make merging in this extension a little easier later on (as there have been some major changes to buildcol.pl commited in the main trunk but not in the x64 branch)

File size: 27.1 KB
Line 
1#------------------------------------------------------------------------------
2# File: FujiFilm.pm
3#
4# Description: Read/write FujiFilm maker notes and RAF images
5#
6# Revisions: 11/25/2003 - P. Harvey Created
7# 11/14/2007 - PH Added abilty to write RAF images
8#
9# References: 1) http://park2.wakwak.com/~tsuruzoh/Computer/Digicams/exif-e.html
10# 2) http://homepage3.nifty.com/kamisaka/makernote/makernote_fuji.htm (2007/09/11)
11# 3) Michael Meissner private communication
12# 4) Paul Samuelson private communication (S5)
13# 5) http://www.cybercom.net/~dcoffin/dcraw/
14# 6) http://forums.dpreview.com/forums/readflat.asp?forum=1012&thread=31350384
15# and http://forum.photome.de/viewtopic.php?f=2&t=353&p=742#p740
16# JD) Jens Duttke private communication
17#------------------------------------------------------------------------------
18
19package Image::ExifTool::FujiFilm;
20
21use strict;
22use vars qw($VERSION);
23use Image::ExifTool qw(:DataAccess :Utils);
24use Image::ExifTool::Exif;
25
26$VERSION = '1.31';
27
28sub ProcessFujiDir($$$);
29sub ProcessFaceRec($$$);
30
31# the following RAF version numbers have been tested for writing:
32my %testedRAF = (
33 '0100' => 'E550, E900, S5600, S6000fd, S6500fd, HS10/HS11, S200EXR, X100 (all Ver1.00)',
34 '0102' => 'S100FS Ver1.02',
35 '0104' => 'S5Pro Ver1.04',
36 '0106' => 'S5Pro Ver1.06',
37 '0111' => 'S5Pro Ver1.11',
38 '0114' => 'S9600 Ver1.00',
39 '0159' => 'S2Pro Ver1.00',
40 '0212' => 'S3Pro Ver2.12',
41 '0216' => 'S3Pro Ver2.16', # (NC)
42 '0218' => 'S3Pro Ver2.18',
43 '0264' => 'F700 Ver2.00',
44 '0266' => 'S9500 Ver1.01',
45 '0269' => 'S9500 Ver1.02',
46 '0271' => 'S3Pro Ver2.71', # UV/IR model?
47 '0712' => 'S5000 Ver3.00',
48 '0716' => 'S5000 Ver3.00', # (yes, 2 RAF versions with the same firmware version)
49);
50
51my %faceCategories = (
52 Format => 'int8u',
53 PrintConv => { BITMASK => {
54 1 => 'Partner',
55 2 => 'Family',
56 3 => 'Friend',
57 }},
58);
59
60# FujiFilm MakerNotes tags
61%Image::ExifTool::FujiFilm::Main = (
62 WRITE_PROC => \&Image::ExifTool::Exif::WriteExif,
63 CHECK_PROC => \&Image::ExifTool::Exif::CheckExif,
64 WRITABLE => 1,
65 GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
66 0x0 => {
67 Name => 'Version',
68 Writable => 'undef',
69 },
70 0x0010 => { #PH (how does this compare to actual serial number?)
71 Name => 'InternalSerialNumber',
72 Writable => 'string',
73 Notes => q{
74 this number is unique, and contains the date of manufacture, but doesn't
75 necessarily correspond to the camera body number -- this needs to be checked
76 },
77 # ie) "FPX20017035 592D31313034060427796060110384"
78 # "FPX 20495643 592D313335310701318AD010110047" (F40fd)
79 # yymmdd
80 PrintConv => q{
81 return $val unless $val=~/^(.*)(\d{2})(\d{2})(\d{2})(.{12})$/;
82 my $yr = $2 + ($2 < 70 ? 2000 : 1900);
83 return "$1 $yr:$3:$4 $5";
84 },
85 PrintConvInv => '$_=$val; s/ (19|20)(\d{2}):(\d{2}):(\d{2}) /$2$3$4/; $_',
86 },
87 0x1000 => {
88 Name => 'Quality',
89 Writable => 'string',
90 },
91 0x1001 => {
92 Name => 'Sharpness',
93 Flags => 'PrintHex',
94 Writable => 'int16u',
95 PrintConv => {
96 0x01 => 'Soft',
97 0x02 => 'Soft2',
98 0x03 => 'Normal',
99 0x04 => 'Hard',
100 0x05 => 'Hard2',
101 0x82 => 'Medium Soft', #2
102 0x84 => 'Medium Hard', #2
103 0x8000 => 'Film Simulation', #2
104 0xffff => 'n/a', #2
105 },
106 },
107 0x1002 => {
108 Name => 'WhiteBalance',
109 Flags => 'PrintHex',
110 Writable => 'int16u',
111 PrintConv => {
112 0x0 => 'Auto',
113 0x100 => 'Daylight',
114 0x200 => 'Cloudy',
115 0x300 => 'Daylight Fluorescent',
116 0x301 => 'Day White Fluorescent',
117 0x302 => 'White Fluorescent',
118 0x303 => 'Warm White Fluorescent', #2/PH (S5)
119 0x304 => 'Living Room Warm White Fluorescent', #2/PH (S5)
120 0x400 => 'Incandescent',
121 0x500 => 'Flash', #4
122 0xf00 => 'Custom',
123 0xf01 => 'Custom2', #2
124 0xf02 => 'Custom3', #2
125 0xf03 => 'Custom4', #2
126 0xf04 => 'Custom5', #2
127 # 0xfe0 => 'Gray Point?', #2
128 0xff0 => 'Kelvin', #4
129 },
130 },
131 0x1003 => {
132 Name => 'Saturation',
133 Flags => 'PrintHex',
134 Writable => 'int16u',
135 PrintConv => {
136 0x0 => 'Normal',
137 0x080 => 'Medium High', #2
138 0x100 => 'High',
139 0x180 => 'Medium Low', #2
140 0x200 => 'Low',
141 0x300 => 'None (B&W)', #2
142 0x301 => 'B&W Green Filter', #PH (X100)
143 0x302 => 'B&W Yellow Filter', #PH (X100)
144 0x303 => 'B&W Blue Filter', #PH (X100)
145 0x310 => 'B&W Sepia', #PH (X100)
146 0x8000 => 'Film Simulation', #2
147 },
148 },
149 0x1004 => {
150 Name => 'Contrast',
151 Flags => 'PrintHex',
152 Writable => 'int16u',
153 PrintConv => {
154 0x0 => 'Normal',
155 0x080 => 'Medium High', #2
156 0x100 => 'High',
157 0x180 => 'Medium Low', #2
158 0x200 => 'Low',
159 0x8000 => 'Film Simulation', #2
160 },
161 },
162 0x1005 => { #4
163 Name => 'ColorTemperature',
164 Writable => 'int16u',
165 },
166 0x1006 => { #JD
167 Name => 'Contrast',
168 Flags => 'PrintHex',
169 Writable => 'int16u',
170 PrintConv => {
171 0x0 => 'Normal',
172 0x100 => 'High',
173 0x300 => 'Low',
174 },
175 },
176 0x100a => { #2
177 Name => 'WhiteBalanceFineTune',
178 Writable => 'int32s',
179 Count => 2,
180 PrintConv => 'sprintf("Red %+d, Blue %+d", split(" ", $val))',
181 PrintConvInv => 'my @v=($val=~/-?\d+/g);"@v"',
182 },
183 0x100b => { #2
184 Name => 'NoiseReduction',
185 Flags => 'PrintHex',
186 Writable => 'int16u',
187 PrintConv => {
188 0x40 => 'Low',
189 0x80 => 'Normal',
190 0x100 => 'n/a', #PH (NC) (all X100 samples)
191 },
192 },
193 0x100e => { #PH (X100)
194 Name => 'HighISONoiseReduction',
195 Flags => 'PrintHex',
196 Writable => 'int16u',
197 PrintConv => {
198 0x000 => 'Normal',
199 0x100 => 'Strong',
200 0x200 => 'Weak',
201 },
202 },
203 0x1010 => {
204 Name => 'FujiFlashMode',
205 Writable => 'int16u',
206 PrintConv => {
207 0 => 'Auto',
208 1 => 'On',
209 2 => 'Off',
210 3 => 'Red-eye reduction',
211 4 => 'External', #JD
212 },
213 },
214 0x1011 => {
215 Name => 'FlashExposureComp', #JD
216 Writable => 'rational64s',
217 },
218 0x1020 => {
219 Name => 'Macro',
220 Writable => 'int16u',
221 PrintConv => {
222 0 => 'Off',
223 1 => 'On',
224 },
225 },
226 0x1021 => {
227 Name => 'FocusMode',
228 Writable => 'int16u',
229 PrintConv => {
230 0 => 'Auto',
231 1 => 'Manual',
232 },
233 },
234 0x1023 => { #2
235 Name => 'FocusPixel',
236 Writable => 'int16u',
237 Count => 2,
238 },
239 0x1030 => {
240 Name => 'SlowSync',
241 Writable => 'int16u',
242 PrintConv => {
243 0 => 'Off',
244 1 => 'On',
245 },
246 },
247 0x1031 => {
248 Name => 'PictureMode',
249 Flags => 'PrintHex',
250 Writable => 'int16u',
251 PrintConv => {
252 0x0 => 'Auto',
253 0x1 => 'Portrait',
254 0x2 => 'Landscape',
255 0x3 => 'Macro', #JD
256 0x4 => 'Sports',
257 0x5 => 'Night Scene',
258 0x6 => 'Program AE',
259 0x7 => 'Natural Light', #3
260 0x8 => 'Anti-blur', #3
261 0x9 => 'Beach & Snow', #JD
262 0xa => 'Sunset', #3
263 0xb => 'Museum', #3
264 0xc => 'Party', #3
265 0xd => 'Flower', #3
266 0xe => 'Text', #3
267 0xf => 'Natural Light & Flash', #3
268 0x10 => 'Beach', #3
269 0x11 => 'Snow', #3
270 0x12 => 'Fireworks', #3
271 0x13 => 'Underwater', #3
272 0x16 => 'Panorama', #PH (X100)
273 0x100 => 'Aperture-priority AE',
274 0x200 => 'Shutter speed priority AE',
275 0x300 => 'Manual',
276 },
277 },
278# this usually has a value of 1
279# 0x1032 => { #2
280# Name => 'ShutterCount',
281# Writable => 'int16u',
282# },
283 0x1033 => { #6
284 Name => 'EXRAuto',
285 Writable => 'int16u',
286 PrintConv => {
287 0 => 'Auto',
288 1 => 'Manual',
289 },
290 },
291 0x1034 => { #6
292 Name => 'EXRMode',
293 Writable => 'int16u',
294 PrintHex => 1,
295 PrintConv => {
296 0x100 => 'HR (High Resolution)',
297 0x200 => 'SN (Signal to Noise priority)',
298 0x300 => 'DR (Dynamic Range priority)',
299 },
300 },
301 0x1100 => {
302 Name => 'AutoBracketing',
303 Writable => 'int16u',
304 PrintConv => {
305 0 => 'Off',
306 1 => 'On',
307 2 => 'No flash & flash', #3
308 },
309 },
310 0x1101 => {
311 Name => 'SequenceNumber',
312 Writable => 'int16u',
313 },
314 0x1210 => { #2
315 Name => 'ColorMode',
316 Writable => 'int16u',
317 PrintHex => 1,
318 PrintConv => {
319 0x00 => 'Standard',
320 0x10 => 'Chrome',
321 0x30 => 'B & W',
322 },
323 },
324 0x1300 => {
325 Name => 'BlurWarning',
326 Writable => 'int16u',
327 PrintConv => {
328 0 => 'None',
329 1 => 'Blur Warning',
330 },
331 },
332 0x1301 => {
333 Name => 'FocusWarning',
334 Writable => 'int16u',
335 PrintConv => {
336 0 => 'Good',
337 1 => 'Out of focus',
338 },
339 },
340 0x1302 => {
341 Name => 'ExposureWarning',
342 Writable => 'int16u',
343 PrintConv => {
344 0 => 'Good',
345 1 => 'Bad exposure',
346 },
347 },
348 0x1304 => { #PH
349 Name => 'GEImageSize',
350 Condition => '$$self{Make} =~ /^GENERAL IMAGING/',
351 Format => 'string',
352 Notes => 'GE models only',
353 },
354 0x1400 => { #2
355 Name => 'DynamicRange',
356 Writable => 'int16u',
357 PrintConv => {
358 1 => 'Standard',
359 3 => 'Wide',
360 # the S5Pro has 100%(STD),130%,170%,230%(W1),300%,400%(W2) - PH
361 },
362 },
363 0x1401 => { #2 (this doesn't seem to work for the X100 - PH)
364 Name => 'FilmMode',
365 Writable => 'int16u',
366 PrintHex => 1,
367 PrintConv => {
368 0x000 => 'F0/Standard',
369 0x100 => 'F1/Studio Portrait',
370 0x110 => 'F1a/Studio Portrait Enhanced Saturation',
371 0x120 => 'F1b/Studio Portrait Smooth Skin Tone',
372 0x130 => 'F1c/Studio Portrait Increased Sharpness',
373 0x200 => 'F2/Fujichrome',
374 0x300 => 'F3/Studio Portrait Ex',
375 0x400 => 'F4/Velvia',
376 },
377 },
378 0x1402 => { #2
379 Name => 'DynamicRangeSetting',
380 Writable => 'int16u',
381 PrintHex => 1,
382 PrintConv => {
383 0x000 => 'Auto (100-400%)',
384 0x001 => 'Manual', #(ref http://forum.photome.de/viewtopic.php?f=2&t=353)
385 0x100 => 'Standard (100%)',
386 0x200 => 'Wide1 (230%)',
387 0x201 => 'Wide2 (400%)',
388 0x8000 => 'Film Simulation',
389 },
390 },
391 0x1403 => { #2 (only valid for manual DR, ref 6)
392 Name => 'DevelopmentDynamicRange',
393 Writable => 'int16u',
394 },
395 0x1404 => { #2
396 Name => 'MinFocalLength',
397 Writable => 'rational64s',
398 },
399 0x1405 => { #2
400 Name => 'MaxFocalLength',
401 Writable => 'rational64s',
402 },
403 0x1406 => { #2
404 Name => 'MaxApertureAtMinFocal',
405 Writable => 'rational64s',
406 },
407 0x1407 => { #2
408 Name => 'MaxApertureAtMaxFocal',
409 Writable => 'rational64s',
410 },
411 # 0x1408 - values: '0100', 'S100', 'VQ10'
412 # 0x1409 - values: same as 0x1408
413 # 0x140a - values: 0, 1, 3, 5, 7
414 0x140b => { #6
415 Name => 'AutoDynamicRange',
416 Writable => 'int16u',
417 PrintConv => '"$val%"',
418 PrintConvInv => '$val=~s/\s*\%$//; $val',
419 },
420 # 0x140b - DR value for AutoDR???? (ref 6) - values: 100
421 # 0x3820 - int16u video frame rate? - PH (HS20EXR)
422 # 0x3821 - int16u video frame width? - PH (HS20EXR)
423 # 0x3822 - int16u video frame height? - PH (HS20EXR)
424 0x4100 => { #PH
425 Name => 'FacesDetected',
426 Writable => 'int16u',
427 },
428 0x4103 => { #PH
429 Name => 'FacePositions',
430 Writable => 'int16u',
431 Count => -1,
432 Notes => q{
433 left, top, right and bottom coordinates in full-sized image for each face
434 detected
435 },
436 },
437 # 0x4104 - also related to face detection (same number of entries as FacePositions)
438 # 0x4203 - same as 0x4103
439 # 0x4204 - same as 0x4104
440 0x4282 => { #PH
441 Name => 'FaceRecInfo',
442 SubDirectory => { TagTable => 'Image::ExifTool::FujiFilm::FaceRecInfo' },
443 },
444 0x8000 => { #2
445 Name => 'FileSource',
446 Writable => 'string',
447 },
448 0x8002 => { #2
449 Name => 'OrderNumber',
450 Writable => 'int32u',
451 },
452 0x8003 => { #2
453 Name => 'FrameNumber',
454 Writable => 'int16u',
455 },
456 0xb211 => { #PH
457 Name => 'Parallax',
458 # (value set in camera is -0.5 times this value in MPImage2... why?)
459 Writable => 'rational64s',
460 Notes => 'only found in MPImage2 of .MPO images',
461 },
462 # 0xb212 - also found in MPIMage2 images - PH
463);
464
465# Face recognition information from FinePix F550EXR (ref PH)
466%Image::ExifTool::FujiFilm::FaceRecInfo = (
467 PROCESS_PROC => \&ProcessFaceRec,
468 GROUPS => { 0 => 'MakerNotes', 2 => 'Image' },
469 VARS => { NO_ID => 1 },
470 NOTES => 'Face recognition information.',
471 Face1Name => { },
472 Face2Name => { },
473 Face3Name => { },
474 Face4Name => { },
475 Face5Name => { },
476 Face6Name => { },
477 Face7Name => { },
478 Face8Name => { },
479 Face1Category => { %faceCategories },
480 Face2Category => { %faceCategories },
481 Face3Category => { %faceCategories },
482 Face4Category => { %faceCategories },
483 Face5Category => { %faceCategories },
484 Face6Category => { %faceCategories },
485 Face7Category => { %faceCategories },
486 Face8Category => { %faceCategories },
487 Face1Birthday => { },
488 Face2Birthday => { },
489 Face3Birthday => { },
490 Face4Birthday => { },
491 Face5Birthday => { },
492 Face6Birthday => { },
493 Face7Birthday => { },
494 Face8Birthday => { },
495);
496
497# tags in RAF images (ref 5)
498%Image::ExifTool::FujiFilm::RAF = (
499 PROCESS_PROC => \&ProcessFujiDir,
500 GROUPS => { 0 => 'RAF', 1 => 'RAF', 2 => 'Image' },
501 PRIORITY => 0, # so the first RAF directory takes precedence
502 NOTES => q{
503 FujiFilm RAF images contain meta information stored in a proprietary
504 FujiFilm RAF format, as well as EXIF information stored inside an embedded
505 JPEG preview image. The table below lists tags currently decoded from the
506 RAF-format information.
507 },
508 0x100 => {
509 Name => 'RawImageFullSize',
510 Format => 'int16u',
511 Groups => { 1 => 'RAF2' }, # (so RAF2 shows up in family 1 list)
512 Count => 2,
513 Notes => 'including borders',
514 ValueConv => 'my @v=reverse split(" ",$val);"@v"',
515 PrintConv => '$val=~tr/ /x/; $val',
516 },
517 0x121 => [
518 {
519 Name => 'RawImageSize',
520 Condition => '$$self{Model} eq "FinePixS2Pro"',
521 Format => 'int16u',
522 Count => 2,
523 ValueConv => q{
524 my @v=split(" ",$val);
525 $v[0]*=2, $v[1]/=2;
526 return "@v";
527 },
528 PrintConv => '$val=~tr/ /x/; $val',
529 },
530 {
531 Name => 'RawImageSize',
532 Format => 'int16u',
533 Count => 2,
534 # values are height then width, adjusted for the layout
535 ValueConv => q{
536 my @v=reverse split(" ",$val);
537 $$self{FujiLayout} and $v[0]/=2, $v[1]*=2;
538 return "@v";
539 },
540 PrintConv => '$val=~tr/ /x/; $val',
541 },
542 ],
543 0x130 => {
544 Name => 'FujiLayout',
545 Format => 'int8u',
546 RawConv => q{
547 my ($v) = split ' ', $val;
548 $$self{FujiLayout} = $v & 0x80 ? 1 : 0;
549 return $val;
550 },
551 },
552 0x2ff0 => {
553 Name => 'WB_GRGBLevels',
554 Format => 'int16u',
555 Count => 4,
556 },
557);
558
559# information found in FFMV atom of MOV videos
560%Image::ExifTool::FujiFilm::FFMV = (
561 PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
562 GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
563 FIRST_ENTRY => 0,
564 NOTES => 'Information found in the FFMV atom of MOV videos.',
565 0 => {
566 Name => 'MovieStreamName',
567 Format => 'string[34]',
568 },
569);
570
571#------------------------------------------------------------------------------
572# decode information from FujiFilm face recognition information
573# Inputs: 0) ExifTool object reference, 1) dirInfo reference, 2) tag table ref
574# Returns: 1
575sub ProcessFaceRec($$$)
576{
577 my ($exifTool, $dirInfo, $tagTablePtr) = @_;
578 my $dataPt = $$dirInfo{DataPt};
579 my $dataPos = $$dirInfo{DataPos} + ($$dirInfo{Base} || 0);
580 my $dirStart = $$dirInfo{DirStart};
581 my $dirLen = $$dirInfo{DirLen};
582 my $pos = $dirStart;
583 my $end = $dirStart + $dirLen;
584 my ($i, $n, $p, $val);
585 $exifTool->VerboseDir('FaceRecInfo');
586 for ($i=1; ; ++$i) {
587 last if $pos + 8 > $end;
588 my $off = Get32u($dataPt, $pos) + $dirStart;
589 my $len = Get32u($dataPt, $pos + 4);
590 last if $len==0 or $off>$end or $off+$len>$end or $len < 62;
591 # values observed for each offset (always zero if not listed):
592 # 0=5; 3=1; 4=4; 6=1; 10-13=numbers(constant for a given registered face)
593 # 15=16; 16=3; 18=1; 22=nameLen; 26=1; 27=16; 28=7; 30-33=nameLen(int32u)
594 # 34-37=nameOffset(int32u); 38=32; 39=16; 40=4; 42=1; 46=0,2,4,8(category)
595 # 50=33; 51=16; 52=7; 54-57=dateLen(int32u); 58-61=dateOffset(int32u)
596 $n = Get32u($dataPt, $off + 30);
597 $p = Get32u($dataPt, $off + 34) + $dirStart;
598 last if $p < $dirStart or $p + $n > $end;
599 $val = substr($$dataPt, $p, $n);
600 $exifTool->HandleTag($tagTablePtr, "Face${i}Name", $val,
601 DataPt => $dataPt,
602 DataPos => $dataPos,
603 Start => $p,
604 Size => $n,
605 );
606 $n = Get32u($dataPt, $off + 54);
607 $p = Get32u($dataPt, $off + 58) + $dirStart;
608 last if $p < $dirStart or $p + $n > $end;
609 $val = substr($$dataPt, $p, $n);
610 $val =~ s/(\d{4})(\d{2})(\d{2})/$1:$2:$2/;
611 $exifTool->HandleTag($tagTablePtr, "Face${i}Birthday", $val,
612 DataPt => $dataPt,
613 DataPos => $dataPos,
614 Start => $p,
615 Size => $n,
616 );
617 $exifTool->HandleTag($tagTablePtr, "Face${i}Category", undef,
618 DataPt => $dataPt,
619 DataPos => $dataPos,
620 Start => $off + 46,
621 Size => 1,
622 );
623 $pos += 8;
624 }
625 return 1;
626}
627
628#------------------------------------------------------------------------------
629# get information from FujiFilm RAF directory
630# Inputs: 0) ExifTool object reference, 1) dirInfo reference, 2) tag table ref
631# Returns: 1 if this was a valid FujiFilm directory
632sub ProcessFujiDir($$$)
633{
634 my ($exifTool, $dirInfo, $tagTablePtr) = @_;
635 my $raf = $$dirInfo{RAF};
636 my $offset = $$dirInfo{DirStart};
637 $raf->Seek($offset, 0) or return 0;
638 my ($buff, $index);
639 $raf->Read($buff, 4) or return 0;
640 my $entries = unpack 'N', $buff;
641 $entries < 256 or return 0;
642 $exifTool->Options('Verbose') and $exifTool->VerboseDir('Fuji', $entries);
643 SetByteOrder('MM');
644 my $pos = $offset + 4;
645 for ($index=0; $index<$entries; ++$index) {
646 $raf->Read($buff,4) or return 0;
647 $pos += 4;
648 my ($tag, $len) = unpack 'nn', $buff;
649 my ($val, $vbuf);
650 $raf->Read($vbuf, $len) or return 0;
651 my $tagInfo = $exifTool->GetTagInfo($tagTablePtr, $tag);
652 if ($tagInfo and $$tagInfo{Format}) {
653 $val = ReadValue(\$vbuf, 0, $$tagInfo{Format}, $$tagInfo{Count}, $len);
654 next unless defined $val;
655 } elsif ($len == 4) {
656 # interpret unknown 4-byte values as int32u
657 $val = Get32u(\$vbuf, 0);
658 } else {
659 # treat other unknown values as binary data
660 $val = \$vbuf;
661 }
662 $exifTool->HandleTag($tagTablePtr, $tag, $val,
663 Index => $index,
664 DataPt => \$vbuf,
665 DataPos => $pos,
666 Size => $len,
667 TagInfo => $tagInfo,
668 );
669 $pos += $len;
670 }
671 return 1;
672}
673
674#------------------------------------------------------------------------------
675# write information to FujiFilm RAW file (RAF)
676# Inputs: 0) ExifTool object reference, 1) dirInfo reference
677# Returns: 1 on success, 0 if this wasn't a valid RAF file, or -1 on write error
678sub WriteRAF($$)
679{
680 my ($exifTool, $dirInfo) = @_;
681 my $raf = $$dirInfo{RAF};
682 my ($hdr, $jpeg, $outJpeg, $offset, $err, $buff);
683
684 $raf->Read($hdr,0x94) == 0x94 or return 0;
685 $hdr =~ /^FUJIFILM/ or return 0;
686 my $ver = substr($hdr, 0x3c, 4);
687 $ver =~ /^\d{4}$/ or return 0;
688
689 # get the position and size of embedded JPEG
690 my ($jpos, $jlen) = unpack('x84NN', $hdr);
691 # check to be sure the JPEG starts in the expected location
692 if ($jpos > 0x94 or $jpos < 0x68 or $jpos & 0x03) {
693 $exifTool->Error("Unsupported or corrupted RAF image (version $ver)");
694 return 1;
695 }
696 # check to make sure this version of RAF has been tested
697 unless ($testedRAF{$ver}) {
698 $exifTool->Error("RAF version $ver not yet tested", 1) and return 1;
699 }
700 # read the embedded JPEG
701 unless ($raf->Seek($jpos, 0) and $raf->Read($jpeg, $jlen) == $jlen) {
702 $exifTool->Error('Error reading RAF meta information');
703 return 1;
704 }
705 # use same write directories as JPEG
706 $exifTool->InitWriteDirs('JPEG');
707 # rewrite the embedded JPEG in memory
708 my %jpegInfo = (
709 Parent => 'RAF',
710 RAF => new File::RandomAccess(\$jpeg),
711 OutFile => \$outJpeg,
712 );
713 $$exifTool{FILE_TYPE} = 'JPEG';
714 my $success = $exifTool->WriteJPEG(\%jpegInfo);
715 $$exifTool{FILE_TYPE} = 'RAF';
716 unless ($success and $outJpeg) {
717 $exifTool->Error("Invalid RAF format");
718 return 1;
719 }
720 return -1 if $success < 0;
721
722 # rewrite the RAF image
723 SetByteOrder('MM');
724 my $jpegLen = length $outJpeg;
725 # pad JPEG to an even 4 bytes (ALWAYS use padding as Fuji does)
726 my $pad = "\0" x (4 - ($jpegLen % 4));
727 # update JPEG size in header (size without padding)
728 Set32u(length($outJpeg), \$hdr, 0x58);
729 # get pointer to start of the next RAF block
730 my $nextPtr = Get32u(\$hdr, 0x5c);
731 # determine the length of padding at the end of the original JPEG
732 my $oldPadLen = $nextPtr - ($jpos + $jlen);
733 if ($oldPadLen) {
734 if ($oldPadLen > 1000000 or $oldPadLen < 0 or
735 not $raf->Seek($jpos+$jlen, 0) or
736 $raf->Read($buff, $oldPadLen) != $oldPadLen)
737 {
738 $exifTool->Error('Bad RAF pointer at 0x5c');
739 return 1;
740 }
741 # make sure padding is only zero bytes (can be >100k for HS10)
742 if ($buff =~ /[^\0]/) {
743 $exifTool->Error('Non-null bytes found in padding');
744 return 1;
745 }
746 }
747 # calculate offset difference due to change in JPEG size
748 my $ptrDiff = length($outJpeg) + length($pad) - ($jlen + $oldPadLen);
749 # update necessary pointers in header
750 foreach $offset (0x5c, 0x64, 0x78, 0x80) {
751 last if $offset >= $jpos; # some versions have a short header
752 my $oldPtr = Get32u(\$hdr, $offset);
753 next unless $oldPtr; # don't update if pointer is zero
754 Set32u($oldPtr + $ptrDiff, \$hdr, $offset);
755 }
756 # write the new header
757 my $outfile = $$dirInfo{OutFile};
758 Write($outfile, substr($hdr, 0, $jpos)) or $err = 1;
759 # write the updated JPEG plus padding
760 Write($outfile, $outJpeg, $pad) or $err = 1;
761 # copy over the rest of the RAF image
762 unless ($raf->Seek($nextPtr, 0)) {
763 $exifTool->Error('Error reading RAF image');
764 return 1;
765 }
766 while ($raf->Read($buff, 65536)) {
767 Write($outfile, $buff) or $err = 1, last;
768 }
769 return $err ? -1 : 1;
770}
771
772#------------------------------------------------------------------------------
773# get information from FujiFilm RAW file (RAF)
774# Inputs: 0) ExifTool object reference, 1) dirInfo reference
775# Returns: 1 if this was a valid RAF file
776sub ProcessRAF($$)
777{
778 my ($exifTool, $dirInfo) = @_;
779 my ($buff, $jpeg, $warn, $offset);
780
781 my $raf = $$dirInfo{RAF};
782 $raf->Read($buff,0x5c) == 0x5c or return 0;
783 $buff =~ /^FUJIFILM/ or return 0;
784 my ($jpos, $jlen) = unpack('x84NN', $buff);
785 $jpos & 0x8000 and return 0;
786 $raf->Seek($jpos, 0) or return 0;
787 $raf->Read($jpeg, $jlen) == $jlen or return 0;
788
789 $exifTool->FoundTag('RAFVersion', substr($buff, 0x3c, 4));
790
791 # extract information from embedded JPEG
792 my %dirInfo = (
793 Parent => 'RAF',
794 RAF => new File::RandomAccess(\$jpeg),
795 );
796 $$exifTool{BASE} += $jpos;
797 my $rtnVal = $exifTool->ProcessJPEG(\%dirInfo);
798 $$exifTool{BASE} -= $jpos;
799 $exifTool->FoundTag('PreviewImage', \$jpeg) if $rtnVal;
800
801 # extract information from Fuji RAF directories
802 my $num = '';
803 foreach $offset (0x5c, 0x78) {
804 last if $offset >= $jpos;
805 unless ($raf->Seek($offset, 0) and $raf->Read($buff, 4)) {
806 $warn = 1;
807 last;
808 }
809 my $start = unpack('N',$buff);
810 next unless $start;
811
812 %dirInfo = (
813 RAF => $raf,
814 DirStart => $start,
815 );
816 $$exifTool{SET_GROUP1} = "RAF$num";
817 my $tagTablePtr = GetTagTable('Image::ExifTool::FujiFilm::RAF');
818 $exifTool->ProcessDirectory(\%dirInfo, $tagTablePtr) or $warn = 1;
819 delete $$exifTool{SET_GROUP1};
820
821 $num = ($num || 1) + 1;
822 }
823 $warn and $exifTool->Warn('Possibly corrupt RAF information');
824
825 return $rtnVal;
826}
827
8281; # end
829
830__END__
831
832=head1 NAME
833
834Image::ExifTool::FujiFilm - Read/write FujiFilm maker notes and RAF images
835
836=head1 SYNOPSIS
837
838This module is loaded automatically by Image::ExifTool when required.
839
840=head1 DESCRIPTION
841
842This module contains definitions required by Image::ExifTool to interpret
843FujiFilm maker notes in EXIF information, and to read/write FujiFilm RAW
844(RAF) images.
845
846=head1 AUTHOR
847
848Copyright 2003-2011, Phil Harvey (phil at owl.phy.queensu.ca)
849
850This library is free software; you can redistribute it and/or modify it
851under the same terms as Perl itself.
852
853=head1 REFERENCES
854
855=over 4
856
857=item L<http://park2.wakwak.com/~tsuruzoh/Computer/Digicams/exif-e.html>
858
859=item L<http://homepage3.nifty.com/kamisaka/makernote/makernote_fuji.htm>
860
861=item L<http://www.cybercom.net/~dcoffin/dcraw/>
862
863=item (...plus testing with my own FinePix 2400 Zoom)
864
865=back
866
867=head1 ACKNOWLEDGEMENTS
868
869Thanks to Michael Meissner, Paul Samuelson and Jens Duttke for help decoding
870some FujiFilm information.
871
872=head1 SEE ALSO
873
874L<Image::ExifTool::TagNames/FujiFilm Tags>,
875L<Image::ExifTool(3pm)|Image::ExifTool>
876
877=cut
Note: See TracBrowser for help on using the repository browser.