File Coverage

blib/lib/Image/ExifTool/Ricoh.pm
Criterion Covered Total %
statement 68 131 51.9
branch 28 86 32.5
condition 5 23 21.7
subroutine 5 6 83.3
pod 0 2 0.0
total 106 248 42.7


line stmt bran cond sub pod time code
1             #------------------------------------------------------------------------------
2             # File: Ricoh.pm
3             #
4             # Description: Ricoh EXIF maker notes tags
5             #
6             # Revisions: 03/28/2005 - P. Harvey Created
7             #
8             # References: 1) http://www.ozhiker.com/electronics/pjmt/jpeg_info/ricoh_mn.html
9             # 2) http://homepage3.nifty.com/kamisaka/makernote/makernote_ricoh.htm
10             # 3) Tim Gray private communication (GR)
11             # 4) https://github.com/atotto/ricoh-theta-tools/
12             # IB) Iliah Borg private communication (LibRaw)
13             #------------------------------------------------------------------------------
14              
15             package Image::ExifTool::Ricoh;
16              
17 21     21   3655 use strict;
  21         39  
  21         694  
18 21     21   108 use vars qw($VERSION);
  21         49  
  21         828  
19 21     21   113 use Image::ExifTool qw(:DataAccess :Utils);
  21         49  
  21         3772  
20 21     21   1170 use Image::ExifTool::Exif;
  21         82  
  21         51631  
21              
22             $VERSION = '1.35';
23              
24             sub ProcessRicohText($$$);
25             sub ProcessRicohRMETA($$$);
26              
27             # lens types for Ricoh GXR
28             my %ricohLensIDs = (
29             Notes => q{
30             Lens units available for the GXR, used by the Ricoh Composite LensID tag. Note
31             that unlike lenses for all other makes of cameras, the focal lengths in these
32             model names have already been scaled to include the 35mm crop factor.
33             },
34             # (the exact lens model names used by Ricoh, except for a change in case)
35             'RL1' => 'GR Lens A12 50mm F2.5 Macro',
36             'RL2' => 'Ricoh Lens S10 24-70mm F2.5-4.4 VC',
37             'RL3' => 'Ricoh Lens P10 28-300mm F3.5-5.6 VC',
38             'RL5' => 'GR Lens A12 28mm F2.5',
39             'RL8' => 'Mount A12',
40             'RL6' => 'Ricoh Lens A16 24-85mm F3.5-5.5',
41             );
42              
43             %Image::ExifTool::Ricoh::Main = (
44             GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
45             WRITE_PROC => \&Image::ExifTool::Exif::WriteExif,
46             CHECK_PROC => \&Image::ExifTool::Exif::CheckExif,
47             WRITABLE => 1,
48             0x0001 => { Name => 'MakerNoteType', Writable => 'string' },
49             0x0002 => { #PH
50             Name => 'FirmwareVersion',
51             Writable => 'string',
52             # eg. "Rev0113" is firmware version 1.13
53             PrintConv => '$val=~/^Rev(\d+)$/ ? sprintf("%.2f",$1/100) : $val',
54             PrintConvInv => '$val=~/^(\d+)\.(\d+)$/ ? sprintf("Rev%.2d%.2d",$1,$2) : $val',
55             },
56             0x0005 => [ #PH
57             {
58             Condition => '$$valPt =~ /^[-\w ]+$/',
59             Name => 'SerialNumber', # (verified for GXR)
60             Writable => 'undef',
61             Count => 16,
62             Notes => q{
63             the serial number stamped on the camera begins with 2 model-specific letters
64             followed by the last 8 digits of this value. For the GXR, this is the
65             serial number of the lens unit
66             },
67             PrintConv => '$val=~s/^(.*)(.{8})$/($1)$2/; $val',
68             PrintConvInv => '$val=~tr/()//d; $val',
69             },{
70             Name => 'InternalSerialNumber',
71             Writable => 'undef',
72             Count => 16,
73             ValueConv => 'unpack("H*", $val)',
74             ValueConvInv => 'pack("H*", $val)',
75             },
76             ],
77             0x0e00 => {
78             Name => 'PrintIM',
79             Writable => 0,
80             Description => 'Print Image Matching',
81             SubDirectory => { TagTable => 'Image::ExifTool::PrintIM::Main' },
82             },
83             0x1000 => { #3
84             Name => 'RecordingFormat',
85             Writable => 'int16u',
86             PrintConv => {
87             2 => 'JPEG',
88             3 => 'DNG',
89             },
90             },
91             0x1001 => [{
92             Name => 'ImageInfo',
93             Condition => '$format ne "int16u"',
94             SubDirectory => { TagTable => 'Image::ExifTool::Ricoh::ImageInfo' },
95             },{ #3
96             Name => 'ExposureProgram',
97             Writable => 'int16u',
98             Notes => 'GR',
99             PrintConv => {
100             1 => 'Auto',
101             2 => 'Program AE',
102             3 => 'Aperture-priority AE',
103             4 => 'Shutter speed priority AE',
104             5 => 'Shutter/aperture priority AE', # TAv
105             6 => 'Manual',
106             7 => 'Movie', #PH
107             },
108             }],
109             0x1002 => { #3
110             Name => 'DriveMode',
111             Condition => '$format eq "int16u"',
112             Notes => 'valid only for some models',
113             Writable => 'int16u',
114             PrintConv => {
115             0 => 'Single-frame',
116             1 => 'Continuous',
117             8 => 'AF-priority Continuous',
118             },
119             },
120             0x1003 => [{
121             Name => 'Sharpness',
122             Condition => '$format ne "int16u"',
123             Writable => 'int32u',
124             PrintConv => {
125             0 => 'Sharp',
126             1 => 'Normal',
127             2 => 'Soft',
128             },
129             },{ #3
130             Name => 'WhiteBalance',
131             Writable => 'int16u',
132             Notes => 'GR',
133             PrintConv => {
134             0 => 'Auto',
135             1 => 'Multi-P Auto',
136             2 => 'Daylight',
137             3 => 'Cloudy',
138             4 => 'Incandescent 1',
139             5 => 'Incandescent 2',
140             6 => 'Daylight Fluorescent',
141             7 => 'Neutral White Fluorescent',
142             8 => 'Cool White Fluorescent',
143             9 => 'Warm White Fluorescent',
144             10 => 'Manual',
145             11 => 'Kelvin',
146             12 => 'Shade', #IB
147             },
148             }],
149             0x1004 => { #3
150             Name => 'WhiteBalanceFineTune',
151             Condition => '$format eq "int16u"',
152             Format => 'int16s',
153             Writable => 'int16u',
154             Notes => q{
155             2 numbers: amount of adjustment towards Amber and Green. Not valid for all
156             models
157             },
158             },
159             # 0x1005 int16u - 5
160             0x1006 => { #3
161             Name => 'FocusMode',
162             Writable => 'int16u',
163             PrintConv => {
164             1 => 'Manual',
165             2 => 'Multi AF',
166             3 => 'Spot AF',
167             4 => 'Snap',
168             5 => 'Infinity',
169             7 => 'Face Detect', #PH
170             8 => 'Subject Tracking',
171             9 => 'Pinpoint AF',
172             10 => 'Movie', #PH
173             },
174             },
175             0x1007 => { #3
176             Name => 'AutoBracketing',
177             Writable => 'int16u',
178             PrintConv => {
179             0 => 'Off',
180             9 => 'AE',
181             11 => 'WB',
182             16 => 'DR', # (dynamic range)
183             17 => 'Contrast',
184             18 => 'WB2', # (selects two different WB presets besides normal)
185             19 => 'Effect',
186             },
187             },
188             0x1009 => { #3
189             Name => 'MacroMode',
190             Writable => 'int16u',
191             PrintConv => { 0 => 'Off', 1 => 'On' },
192             },
193             0x100a => { #3
194             Name => 'FlashMode',
195             Writable => 'int16u',
196             PrintConv => {
197             0 => 'Off',
198             1 => 'Auto, Fired',
199             2 => 'On',
200             3 => 'Auto, Fired, Red-eye reduction',
201             4 => 'Slow Sync',
202             5 => 'Manual',
203             6 => 'On, Red-eye reduction',
204             7 => 'Synchro, Red-eye reduction',
205             8 => 'Auto, Did not fire',
206             },
207             },
208             0x100b => { #3
209             Name => 'FlashExposureComp',
210             Writable => 'rational64s',
211             PrintConv => '$val ? sprintf("%+.1f",$val) : 0',
212             PrintConvInv => '$val',
213             },
214             0x100c => { #3
215             Name => 'ManualFlashOutput',
216             Writable => 'rational64s',
217             PrintConv => {
218             0 => 'Full',
219             -24 => '1/1.4',
220             -48 => '1/2',
221             -72 => '1/2.8',
222             -96 => '1/4',
223             -120 => '1/5.6',
224             -144 => '1/8',
225             -168 => '1/11',
226             -192 => '1/16',
227             -216 => '1/22',
228             -240 => '1/32',
229             -288 => '1/64',
230             },
231             },
232             0x100d => { #3
233             Name => 'FullPressSnap',
234             Writable => 'int16u',
235             PrintConv => { 0 => 'Off', 1 => 'On' },
236             },
237             0x100e => { #3
238             Name => 'DynamicRangeExpansion',
239             Writable => 'int16u',
240             PrintConv => {
241             0 => 'Off',
242             3 => 'Weak',
243             4 => 'Medium',
244             5 => 'Strong',
245             },
246             },
247             0x100f => { #3
248             Name => 'NoiseReduction',
249             Writable => 'int16u',
250             PrintConv => {
251             0 => 'Off',
252             1 => 'Weak',
253             2 => 'Medium',
254             3 => 'Strong',
255             },
256             },
257             0x1010 => { #3
258             Name => 'ImageEffects',
259             Writable => 'int16u',
260             PrintConv => {
261             0 => 'Standard',
262             1 => 'Vivid',
263             3 => 'Black & White',
264             5 => 'B&W Toning Effect',
265             6 => 'Setting 1',
266             7 => 'Setting 2',
267             9 => 'High-contrast B&W',
268             10 => 'Cross Process',
269             11 => 'Positive Film',
270             12 => 'Bleach Bypass',
271             13 => 'Retro',
272             15 => 'Miniature',
273             17 => 'High Key',
274             },
275             },
276             0x1011 => { #3
277             Name => 'Vignetting',
278             Writable => 'int16u',
279             PrintConv => {
280             0 => 'Off',
281             1 => 'Low',
282             2 => 'Medium',
283             3 => 'High',
284             },
285             },
286             0x1012 => { #PH
287             Name => 'Contrast',
288             Writable => 'int32u',
289             Format => 'int32s', #3 (high-contrast B&W also has -1 and -2 settings)
290             PrintConv => {
291             OTHER => sub { shift },
292             2147483647 => 'MAX', #3 (high-contrast B&W effect MAX setting)
293             },
294             },
295             0x1013 => { Name => 'Saturation', Writable => 'int32u' }, #PH
296             0x1014 => { Name => 'Sharpness', Writable => 'int32u' }, #3
297             0x1015 => { #3
298             Name => 'ToningEffect',
299             Writable => 'int16u',
300             PrintConv => {
301             0 => 'Off',
302             1 => 'Sepia',
303             2 => 'Red',
304             3 => 'Green',
305             4 => 'Blue',
306             5 => 'Purple',
307             6 => 'B&W',
308             7 => 'Color',
309             },
310             },
311             0x1016 => { #3
312             Name => 'HueAdjust',
313             Writable => 'int16u',
314             PrintConv => {
315             0 => 'Off',
316             1 => 'Basic',
317             2 => 'Magenta',
318             3 => 'Yellow',
319             4 => 'Normal',
320             5 => 'Warm',
321             6 => 'Cool',
322             },
323             },
324             0x1017 => { #3
325             Name => 'WideAdapter',
326             Writable => 'int16u',
327             PrintConv => {
328             0 => 'Not Attached',
329             2 => 'Attached', # (21mm)
330             },
331             },
332             0x1018 => { #3
333             Name => 'CropMode',
334             Writable => 'int16u',
335             PrintConv => {
336             0 => 'Off',
337             1 => 'On (35mm)',
338             2 => 'On (47mm)', #IB
339             },
340             },
341             0x1019 => { #3
342             Name => 'NDFilter',
343             Writable => 'int16u',
344             PrintConv => { 0 => 'Off', 1 => 'On' },
345             },
346             0x101a => { Name => 'WBBracketShotNumber', Writable => 'int16u' }, #3
347             # 0x1100 - related to DR correction (ref 3)
348             0x1307 => { Name => 'ColorTempKelvin', Writable => 'int32u' }, #3
349             0x1308 => { Name => 'ColorTemperature', Writable => 'int32u' }, #3
350             0x1500 => { #3
351             Name => 'FocalLength',
352             Writable => 'rational64u',
353             PrintConv => 'sprintf("%.1f mm",$val)',
354             PrintConvInv => '$val=~s/\s*mm$//;$val',
355             },
356             0x1200 => { #3
357             Name => 'AFStatus',
358             Writable => 'int16u',
359             PrintConv => {
360             0 => 'Out of Focus',
361             1 => 'In Focus',
362             },
363             },
364             # 0x1201-0x1204 - related to focus points (ref 3)
365             0x1201 => { #PH (NC)
366             Name => 'AFAreaXPosition1',
367             Writable => 'int32u',
368             Notes => 'manual AF area position in a 1280x864 image',
369             },
370             0x1202 => { Name => 'AFAreaYPosition1', Writable => 'int32u' }, #PH (NC)
371             0x1203 => { #PH (NC)
372             Name => 'AFAreaXPosition',
373             Writable => 'int32u',
374             Notes => 'manual AF area position in the full image',
375             # (coordinates change to correspond with smaller image
376             # when recording reduced-size JPEG)
377             },
378             0x1204 => { Name => 'AFAreaYPosition', Writable => 'int32u' }, #PH (NC)
379             0x1205 => { #3
380             Name => 'AFAreaMode',
381             Writable => 'int16u',
382             PrintConv => {
383             0 => 'Auto',
384             2 => 'Manual',
385             },
386             },
387             0x1601 => { Name => 'SensorWidth', Writable => 'int32u' }, #3
388             0x1602 => { Name => 'SensorHeight', Writable => 'int32u' }, #3
389             0x1603 => { Name => 'CroppedImageWidth', Writable => 'int32u' }, #3
390             0x1604 => { Name => 'CroppedImageHeight', Writable => 'int32u' }, #3
391             # 0x1700 - Composite? (0=normal image, 1=interval composite, 2=multi-exposure composite) (ref 3)
392             # 0x1703 - 0=normal, 1=final composite (ref 3)
393             # 0x1704 - 0=normal, 2=final composite (ref 3)
394             0x2001 => [
395             {
396             Name => 'RicohSubdir',
397             Condition => q{
398             $self->{Model} !~ /^Caplio RR1\b/ and
399             ($format ne 'int32u' or $count != 1)
400             },
401             SubDirectory => {
402             Validate => '$val =~ /^\[Ricoh Camera Info\]/',
403             TagTable => 'Image::ExifTool::Ricoh::Subdir',
404             Start => '$valuePtr + 20',
405             ByteOrder => 'BigEndian',
406             },
407             },
408             {
409             Name => 'RicohSubdirIFD',
410             # the CX6 and GR Digital 4 write an int32u pointer in AVI videos -- doh!
411             Condition => '$self->{Model} !~ /^Caplio RR1\b/',
412             Flags => 'SubIFD',
413             SubDirectory => {
414             TagTable => 'Image::ExifTool::Ricoh::Subdir',
415             Start => '$val + 20', # (skip over "[Ricoh Camera Info]\0" header)
416             ByteOrder => 'BigEndian',
417             },
418             },
419             {
420             Name => 'RicohRR1Subdir',
421             SubDirectory => {
422             Validate => '$val =~ /^\[Ricoh Camera Info\]/',
423             TagTable => 'Image::ExifTool::Ricoh::Subdir',
424             Start => '$valuePtr + 20',
425             ByteOrder => 'BigEndian',
426             # the Caplio RR1 uses a different base address -- doh!
427             Base => '$start-20',
428             },
429             },
430             ],
431             0x4001 => {
432             Name => 'ThetaSubdir',
433             Groups => { 1 => 'MakerNotes' }, # SubIFD needs group 1 set
434             Flags => 'SubIFD',
435             SubDirectory => {
436             TagTable => 'Image::ExifTool::Ricoh::ThetaSubdir',
437             Start => '$val',
438             },
439             },
440             );
441              
442             # Ricoh type 2 maker notes (ref PH)
443             # (similar to Kodak::Type11 and GE::Main)
444             %Image::ExifTool::Ricoh::Type2 = (
445             GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
446             NOTES => q{
447             Tags written by models such as the Ricoh HZ15 and the Pentax XG-1. These
448             are not writable due to numerous formatting errors as written by these
449             cameras.
450             },
451             # 0x104 - int32u: 1
452             # 0x200 - int32u[3]: 0 0 0
453             # 0x202 - int16u: 0 (GE Macro?)
454             # 0x203 - int16u: 0,3 (Kodak PictureEffect?)
455             # 0x204 - rational64u: 0/10
456             # 0x205 - rational64u: 150/1
457             # 0x206 - float[6]: (not really float because size should be 2 bytes)
458             0x207 => {
459             Name => 'RicohModel',
460             Writable => 'string',
461             },
462             0x300 => {
463             # brutal. There are lots of errors in the XG-1 maker notes. For the XG-1,
464             # 0x300 has a value of "XG-1Pentax". The "XG-1" part is likely an improperly
465             # stored 0x207 RicohModel, resulting in an erroneous 4-byte offset for this tag
466             Name => 'RicohMake',
467             Writable => 'undef',
468             ValueConv => '$val =~ s/ *$//; $val',
469             },
470             # 0x306 - int16u: 1
471             # 0x500 - int16u: 0,1
472             # 0x501 - int16u: 0
473             # 0x502 - int16u: 0
474             # 0x9c9c - int8u[6]: ?
475             # 0xadad - int8u[20480]: ?
476             );
477              
478             # Ricoh image info (ref 2)
479             %Image::ExifTool::Ricoh::ImageInfo = (
480             GROUPS => { 0 => 'MakerNotes', 2 => 'Image' },
481             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
482             WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
483             CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
484             WRITABLE => 1,
485             PRIORITY => 0,
486             FORMAT => 'int8u',
487             FIRST_ENTRY => 0,
488             IS_OFFSET => [ 28 ], # tag 28 is 'IsOffset'
489             0 => {
490             Name => 'RicohImageWidth',
491             Format => 'int16u',
492             },
493             2 => {
494             Name => 'RicohImageHeight',
495             Format => 'int16u',
496             },
497             6 => {
498             Name => 'RicohDate',
499             Groups => { 2 => 'Time' },
500             Format => 'int8u[7]',
501             # (what an insane way to encode the date)
502             ValueConv => q{
503             sprintf("%.2x%.2x:%.2x:%.2x %.2x:%.2x:%.2x",
504             split(' ', $val));
505             },
506             ValueConvInv => q{
507             my @vals = ($val =~ /(\d{1,2})/g);
508             push @vals, 0 if @vals < 7;
509             join(' ', map(hex, @vals));
510             },
511             },
512             28 => {
513             Name => 'PreviewImageStart',
514             Format => 'int16u', # ha! (only the lower 16 bits, even if > 0xffff)
515             Flags => 'IsOffset',
516             OffsetPair => 30, # associated byte count tagID
517             DataTag => 'PreviewImage',
518             Protected => 2,
519             WriteGroup => 'MakerNotes',
520             # prevent preview from being written to MakerNotes of DNG images
521             RawConvInv => q{
522             return $val if $$self{FILE_TYPE} eq "JPEG";
523             warn "\n"; # suppress warning
524             return undef;
525             },
526             },
527             30 => {
528             Name => 'PreviewImageLength',
529             Format => 'int16u',
530             OffsetPair => 28, # point to associated offset
531             DataTag => 'PreviewImage',
532             Protected => 2,
533             WriteGroup => 'MakerNotes',
534             RawConvInv => q{
535             return $val if $$self{FILE_TYPE} eq "JPEG";
536             warn "\n"; # suppress warning
537             return undef;
538             },
539             },
540             32 => {
541             Name => 'FlashMode',
542             PrintConv => {
543             0 => 'Off',
544             1 => 'Auto', #PH
545             2 => 'On',
546             },
547             },
548             33 => {
549             Name => 'Macro',
550             PrintConv => { 0 => 'Off', 1 => 'On' },
551             },
552             34 => {
553             Name => 'Sharpness',
554             PrintConv => {
555             0 => 'Sharp',
556             1 => 'Normal',
557             2 => 'Soft',
558             },
559             },
560             38 => {
561             Name => 'WhiteBalance',
562             PrintConv => {
563             0 => 'Auto',
564             1 => 'Daylight',
565             2 => 'Cloudy',
566             3 => 'Tungsten',
567             4 => 'Fluorescent',
568             5 => 'Manual', #PH (GXR)
569             7 => 'Detail',
570             9 => 'Multi-pattern Auto', #PH (GXR)
571             },
572             },
573             39 => {
574             Name => 'ISOSetting',
575             PrintConv => {
576             0 => 'Auto',
577             1 => 64,
578             2 => 100,
579             4 => 200,
580             6 => 400,
581             7 => 800,
582             8 => 1600,
583             9 => 'Auto', #PH (? CX3)
584             10 => 3200, #PH (A16)
585             11 => '100 (Low)', #PH (A16)
586             },
587             },
588             40 => {
589             Name => 'Saturation',
590             PrintConv => {
591             0 => 'High',
592             1 => 'Normal',
593             2 => 'Low',
594             3 => 'B&W',
595             6 => 'Toning Effect', #PH (GXR Sepia,Red,Green,Blue,Purple)
596             9 => 'Vivid', #PH (GXR)
597             10 => 'Natural', #PH (GXR)
598             },
599             },
600             );
601              
602             # Ricoh subdirectory tags (ref PH)
603             # NOTE: this subdir is currently not writable because the offsets would require
604             # special code to handle the funny start location and base offset
605             %Image::ExifTool::Ricoh::Subdir = (
606             GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
607             WRITE_PROC => \&Image::ExifTool::Exif::WriteExif,
608             CHECK_PROC => \&Image::ExifTool::Exif::CheckExif,
609             # the significance of the following 2 dates is not known. They are usually
610             # within a month of each other, but I have seen differences of nearly a year.
611             # Sometimes the first is more recent, and sometimes the second.
612             # 0x0003 - int32u[1]
613             0x0004 => { # (NC)
614             Name => 'ManufactureDate1',
615             Groups => { 2 => 'Time' },
616             Writable => 'string',
617             Count => 20,
618             },
619             0x0005 => { # (NC)
620             Name => 'ManufactureDate2',
621             Groups => { 2 => 'Time' },
622             Writable => 'string',
623             Count => 20,
624             },
625             # 0x0006 - undef[16] ?
626             # 0x0007 - int32u[1] ?
627             # 0x000c - int32u[2] 1st number is a counter (file number? shutter count?) - PH
628             # 0x0014 - int8u[338] could contain some data related to face detection? - PH
629             # 0x0015 - int8u[2]: related to noise reduction?
630             0x001a => { #PH
631             Name => 'FaceInfo',
632             SubDirectory => { TagTable => 'Image::ExifTool::Ricoh::FaceInfo' },
633             },
634             0x0029 => {
635             Name => 'FirmwareInfo',
636             SubDirectory => { TagTable => 'Image::ExifTool::Ricoh::FirmwareInfo' },
637             },
638             0x002a => {
639             Name => 'NoiseReduction',
640             # this is the applied value if NR is set to "Auto"
641             Writable => 'int32u',
642             PrintConv => {
643             0 => 'Off',
644             1 => 'Weak',
645             2 => 'Strong',
646             3 => 'Max',
647             },
648             },
649             0x002c => { # (GXR)
650             Name => 'SerialInfo',
651             SubDirectory => { TagTable => 'Image::ExifTool::Ricoh::SerialInfo' },
652             }
653             # 0x000E ProductionNumber? (ref 2) [no. zero for most models - PH]
654             );
655              
656             # Ricoh Theta subdirectory tags - Contains orientation information (ref 4)
657             %Image::ExifTool::Ricoh::ThetaSubdir = (
658             GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
659             WRITE_PROC => \&Image::ExifTool::Exif::WriteExif,
660             CHECK_PROC => \&Image::ExifTool::Exif::CheckExif,
661             # 0x0001 - int16u[1] ?
662             # 0x0002 - int16u[1] ?
663             0x0003 => {
664             Name => 'Accelerometer',
665             Writable => 'rational64s',
666             Count => 2,
667             },
668             0x0004 => {
669             Name => 'Compass',
670             Writable => 'rational64u',
671             },
672             # 0x0005 - int16u[1] ?
673             # 0x0006 - int16u[1] ?
674             # 0x0007 - int16u[1] ?
675             # 0x0008 - int16u[1] ?
676             # 0x0009 - int16u[1] ?
677             0x000a => {
678             Name => 'TimeZone',
679             Writable => 'string',
680             },
681             # 0x0101 - int16u[4] ISO (why 4 values?)
682             # 0x0102 - rational64s[2] FNumber (why 2 values?)
683             # 0x0103 - rational64u[2] ExposureTime (why 2 values?)
684             # 0x0104 - string[9] SerialNumber?
685             # 0x0105 - string[9] SerialNumber?
686             );
687              
688             # face detection information (ref PH, CX4)
689             %Image::ExifTool::Ricoh::FaceInfo = (
690             GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
691             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
692             WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
693             CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
694             WRITABLE => 1,
695             FIRST_ENTRY => 0,
696             DATAMEMBER => [ 181 ],
697             0xb5 => { # (should be int16u at 0xb4?)
698             Name => 'FacesDetected',
699             DataMember => 'FacesDetected',
700             RawConv => '$$self{FacesDetected} = $val',
701             },
702             0xb6 => {
703             Name => 'FaceDetectFrameSize',
704             Format => 'int16u[2]',
705             },
706             0xbc => {
707             Name => 'Face1Position',
708             Condition => '$$self{FacesDetected} >= 1',
709             Format => 'int16u[4]',
710             Notes => q{
711             left, top, width and height of detected face in coordinates of
712             FaceDetectFrameSize with increasing Y downwards
713             },
714             },
715             0xc8 => {
716             Name => 'Face2Position',
717             Condition => '$$self{FacesDetected} >= 2',
718             Format => 'int16u[4]',
719             },
720             0xd4 => {
721             Name => 'Face3Position',
722             Condition => '$$self{FacesDetected} >= 3',
723             Format => 'int16u[4]',
724             },
725             0xe0 => {
726             Name => 'Face4Position',
727             Condition => '$$self{FacesDetected} >= 4',
728             Format => 'int16u[4]',
729             },
730             0xec => {
731             Name => 'Face5Position',
732             Condition => '$$self{FacesDetected} >= 5',
733             Format => 'int16u[4]',
734             },
735             0xf8 => {
736             Name => 'Face6Position',
737             Condition => '$$self{FacesDetected} >= 6',
738             Format => 'int16u[4]',
739             },
740             0x104 => {
741             Name => 'Face7Position',
742             Condition => '$$self{FacesDetected} >= 7',
743             Format => 'int16u[4]',
744             },
745             0x110 => {
746             Name => 'Face8Position',
747             Condition => '$$self{FacesDetected} >= 8',
748             Format => 'int16u[4]',
749             },
750             );
751              
752             # firmware version information (ref PH)
753             %Image::ExifTool::Ricoh::FirmwareInfo = (
754             GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
755             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
756             WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
757             CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
758             WRITABLE => 1,
759             0x00 => {
760             Name => 'FirmwareRevision',
761             Format => 'string[12]',
762             },
763             0x0c => {
764             Name => 'FirmwareRevision2',
765             Format => 'string[12]',
766             },
767             );
768              
769             # serial/version number information written by GXR (ref PH)
770             %Image::ExifTool::Ricoh::SerialInfo = (
771             GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
772             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
773             WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
774             CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
775             WRITABLE => 1,
776             NOTES => 'This information is found in images from the GXR.',
777             0 => {
778             Name => 'BodyFirmware', #(NC)
779             Format => 'string[16]',
780             # observed: "RS1 :V00560000" --> FirmwareVersion "Rev0056"
781             # "RS1 :V01020200" --> FirmwareVersion "Rev0102"
782             },
783             16 => {
784             Name => 'BodySerialNumber',
785             Format => 'string[16]',
786             # observed: "SID:00100056" --> "WD00100056" on plate
787             },
788             32 => {
789             Name => 'LensFirmware', #(NC)
790             Format => 'string[16]',
791             # observed: "RL1 :V00560000", "RL1 :V01020200" - A12 50mm F2.5 Macro
792             # "RL2 :V00560000", "RL2 :V01020300" - S10 24-70mm F2.5-4.4 VC
793             # --> used in a Composite tag to determine LensType
794             },
795             48 => {
796             Name => 'LensSerialNumber',
797             Format => 'string[16]',
798             # observed: (S10) "LID:00010024" --> "WF00010024" on plate
799             # (A12) "LID:00010054" --> "WE00010029" on plate??
800             },
801             );
802              
803             # Ricoh text-type maker notes (PH)
804             %Image::ExifTool::Ricoh::Text = (
805             GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
806             PROCESS_PROC => \&ProcessRicohText,
807             NOTES => q{
808             Some Ricoh DC and RDC models use a text-based format for their maker notes
809             instead of the IFD format used by the Caplio models. Below is a list of known
810             tags in this information.
811             },
812             Rev => {
813             Name => 'FirmwareVersion',
814             PrintConv => '$val=~/^\d+$/ ? sprintf("%.2f",$val/100) : $val',
815             PrintConvInv => '$val=~/^(\d+)\.(\d+)$/ ? sprintf("%.2d%.2d",$1,$2) : $val',
816             },
817             Rv => {
818             Name => 'FirmwareVersion',
819             PrintConv => '$val=~/^\d+$/ ? sprintf("%.2f",$val/100) : $val',
820             PrintConvInv => '$val=~/^(\d+)\.(\d+)$/ ? sprintf("%.2d%.2d",$1,$2) : $val',
821             },
822             Rg => 'RedGain',
823             Gg => 'GreenGain',
824             Bg => 'BlueGain',
825             );
826              
827             %Image::ExifTool::Ricoh::RMETA = (
828             GROUPS => { 0 => 'APP5', 1 => 'RMETA', 2 => 'Image' },
829             PROCESS_PROC => \&Image::ExifTool::Ricoh::ProcessRicohRMETA,
830             NOTES => q{
831             The Ricoh Caplio Pro G3 has the ability to add custom fields to the APP5
832             "RMETA" segment of JPEG images. While only a few observed tags have been
833             defined below, ExifTool will extract any information found here.
834             },
835             'Sign type' => { Name => 'SignType', PrintConv => {
836             1 => 'Directional',
837             2 => 'Warning',
838             3 => 'Information',
839             } },
840             Location => { PrintConv => {
841             1 => 'Verge',
842             2 => 'Gantry',
843             3 => 'Central reservation',
844             4 => 'Roundabout',
845             } },
846             Lit => { PrintConv => {
847             1 => 'Yes',
848             2 => 'No',
849             } },
850             Condition => { PrintConv => {
851             1 => 'Good',
852             2 => 'Fair',
853             3 => 'Poor',
854             4 => 'Damaged',
855             } },
856             Azimuth => { PrintConv => {
857             1 => 'N',
858             2 => 'NNE',
859             3 => 'NE',
860             4 => 'ENE',
861             5 => 'E',
862             6 => 'ESE',
863             7 => 'SE',
864             8 => 'SSE',
865             9 => 'S',
866             10 => 'SSW',
867             11 => 'SW',
868             12 => 'WSW',
869             13 => 'W',
870             14 => 'WNW',
871             15 => 'NW',
872             16 => 'NNW',
873             } },
874             _audio => {
875             Name => 'SoundFile',
876             Notes => 'audio data recorded in JPEG images by the G700SE',
877             },
878             _barcode => { Name => 'Barcodes', List => 1 },
879             );
880              
881             # information stored in Ricoh AVI images (ref PH)
882             %Image::ExifTool::Ricoh::AVI = (
883             GROUPS => { 0 => 'MakerNotes', 2 => 'Video' },
884             ucmt => {
885             Name => 'Comment',
886             # Ricoh writes a "Unicode" header even when text is ASCII (spaces anyway)
887             ValueConv => '$_=$val; s/^(Unicode\0|ASCII\0\0\0)//; tr/\0//d; s/\s+$//; $_',
888             },
889             mnrt => {
890             Name => 'MakerNoteRicoh',
891             SubDirectory => {
892             TagTable => 'Image::ExifTool::Ricoh::Main',
893             Start => '$valuePtr + 8',
894             ByteOrder => 'BigEndian',
895             Base => '8',
896             },
897             },
898             rdc2 => {
899             Name => 'RicohRDC2',
900             Unknown => 1,
901             ValueConv => 'unpack("H*",$val)',
902             # have seen values like 0a000444 and 00000000 - PH
903             },
904             thum => {
905             Name => 'ThumbnailImage',
906             Groups => { 2 => 'Preview' },
907             Binary => 1,
908             },
909             );
910              
911             # Ricoh composite tags
912             %Image::ExifTool::Ricoh::Composite = (
913             GROUPS => { 2 => 'Camera' },
914             LensID => {
915             SeparateTable => 'Ricoh LensID',
916             Require => 'Ricoh:LensFirmware',
917             RawConv => '$val[0] ? $val[0] : undef',
918             ValueConv => '$val=~s/\s*:.*//; $val',
919             PrintConv => \%ricohLensIDs,
920             },
921             RicohPitch => {
922             Require => 'Ricoh:Accelerometer',
923             ValueConv => 'my @v = split(" ",$val); $v[1]',
924             },
925             RicohRoll => {
926             Require => 'Ricoh:Accelerometer',
927             ValueConv => 'my @v = split(" ",$val); $v[0] <= 180 ? $v[0] : $v[0] - 360',
928             },
929             );
930              
931             # add our composite tags
932             Image::ExifTool::AddCompositeTags('Image::ExifTool::Ricoh');
933              
934              
935             #------------------------------------------------------------------------------
936             # Process Ricoh text-based maker notes
937             # Inputs: 0) ExifTool object reference
938             # 1) Reference to directory information hash
939             # 2) Pointer to tag table for this directory
940             # Returns: 1 on success, otherwise returns 0 and sets a Warning
941             sub ProcessRicohText($$$)
942             {
943 0     0 0 0 my ($et, $dirInfo, $tagTablePtr) = @_;
944 0         0 my $dataPt = $$dirInfo{DataPt};
945 0         0 my $dataLen = $$dirInfo{DataLen};
946 0   0     0 my $dirStart = $$dirInfo{DirStart} || 0;
947 0   0     0 my $dirLen = $$dirInfo{DirLen} || $dataLen - $dirStart;
948 0         0 my $verbose = $et->Options('Verbose');
949              
950 0         0 my $data = substr($$dataPt, $dirStart, $dirLen);
951 0 0       0 return 1 if $data =~ /^\0/; # blank Ricoh maker notes
952             # validate text maker notes
953 0 0       0 unless ($data =~ /^(Rev|Rv)/) {
954 0         0 $et->Warn('Bad Ricoh maker notes');
955 0         0 return 0;
956             }
957 0         0 while ($data =~ m/([A-Z][a-z]{1,2})([0-9A-F]+);/sg) {
958 0         0 my $tag = $1;
959 0         0 my $val = $2;
960 0         0 my $tagInfo = $et->GetTagInfo($tagTablePtr, $tag);
961 0 0       0 if ($verbose) {
962 0         0 $et->VerboseInfo($tag, $tagInfo,
963             Table => $tagTablePtr,
964             Value => $val,
965             );
966             }
967 0 0       0 unless ($tagInfo) {
968 0 0       0 next unless $$et{OPTIONS}{Unknown};
969 0         0 $tagInfo = {
970             Name => "Ricoh_Text_$tag",
971             Unknown => 1,
972             PrintConv => 'length($val) > 60 ? substr($val,0,55) . "[...]" : $val',
973             };
974             # add tag information to table
975 0         0 AddTagToTable($tagTablePtr, $tag, $tagInfo);
976             }
977 0         0 $et->FoundTag($tagInfo, $val);
978             }
979 0         0 return 1;
980             }
981              
982             #------------------------------------------------------------------------------
983             # Process Ricoh APP5 RMETA information
984             # Inputs: 0) ExifTool ref, 1) dirInfo ref, 2) tag table ref
985             # Returns: 1 on success, otherwise returns 0 and sets a Warning
986             sub ProcessRicohRMETA($$$)
987             {
988 20     20 0 65 my ($et, $dirInfo, $tagTablePtr) = @_;
989 20         53 my $dataPt = $$dirInfo{DataPt};
990 20         44 my $dirStart = $$dirInfo{DirStart};
991 20         42 my $dataLen = length($$dataPt);
992 20         38 my $dirLen = $dataLen - $dirStart;
993 20         77 my $verbose = $et->Options('Verbose');
994              
995 20 50       73 $et->VerboseDir('Ricoh RMETA') if $verbose;
996 20 50       75 $dirLen < 20 and $et->Warn('Truncated Ricoh RMETA data', 1), return 0;
997 20         59 my $byteOrder = substr($$dataPt, $dirStart, 2);
998 20 50       68 $byteOrder = GetByteOrder() if $byteOrder eq "\0\0"; # (same order as container)
999 20 50       64 SetByteOrder($byteOrder) or $et->Warn('Bad Ricoh RMETA data', 1), return 0;
1000             # get the RMETA segment number
1001 20         80 my $rmetaNum = Get16u($dataPt, $dirStart+4);
1002 20 50       86 if ($rmetaNum != 0) {
1003             # not sure how to recognize audio, so do it by checking for "RIFF" header
1004             # and assume all subsequent RMETA segments are part of the audio data
1005             # (but it looks like the int16u at $dirStart+6 is the next block number
1006             # if the data is continued, or 0 for the last block)
1007 0 0       0 $dirLen < 14 and $et->Warn('Short Ricoh RMETA block', 1), return 0;
1008 0 0       0 if ($$dataPt =~ /^.{20}BARCODE/s) {
    0          
1009 0         0 my $val = substr($$dataPt, 20);
1010 0         0 $val =~ s/\0.*//s;
1011 0         0 $val =~ s/^BARCODE\w+,\d{2},//;
1012 0         0 my @codes;
1013 0         0 for (;;) {
1014 0 0 0     0 $val =~ s/(\d+),// and length $val >= $1 or last;
1015 0         0 push @codes, substr($val, 0, $1);
1016 0 0       0 last unless length $val > $1;
1017 0         0 $val = substr($val, $1+1);
1018             }
1019 0 0       0 $et->HandleTag($tagTablePtr, '_barcode', \@codes) if @codes;
1020 0         0 return 1;
1021             } elsif ($$dataPt =~ /^.{18}ASCII/s) {
1022             # (ignore barcode tag names for now)
1023 0         0 return 1;
1024             }
1025 0         0 my $audioLen = Get16u($dataPt, $dirStart+12);
1026 0 0       0 $audioLen + 14 > $dirLen and $et->Warn('Truncated Ricoh RMETA audio data', 1), return 0;
1027 0         0 my $buff = substr($$dataPt, $dirStart + 14, $audioLen);
1028 0 0 0     0 if ($audioLen >= 4 and substr($buff, 0, 4) eq 'RIFF') {
    0          
1029 0         0 $et->HandleTag($tagTablePtr, '_audio', \$buff);
1030             } elsif ($$et{VALUE}{SoundFile}) {
1031 0         0 ${$$et{VALUE}{SoundFile}} .= $buff;
  0         0  
1032             } else {
1033 0         0 $et->Warn('Unknown Ricoh RMETA type', 1);
1034 0         0 return 0;
1035             }
1036 0         0 return 1;
1037             }
1038             # decode standard RMETA tag directory
1039 20         51 my (@tags, @vals, @nums, $valPos, $numPos);
1040 20         68 my $pos = $dirStart + Get16u($dataPt, $dirStart+8);
1041 20         60 my $numEntries = Get16u($dataPt, $pos);
1042 20 50       64 $numEntries > 100 and $et->Warn('Bad RMETA entry count'), return 0;
1043 20         40 $pos += 10; # start of first RMETA section
1044             # loop through RMETA sections
1045 20         71 while ($pos <= $dataLen - 4) {
1046 80         165 my $type = Get16u($dataPt, $pos);
1047 80         179 my $size = Get16u($dataPt, $pos + 2);
1048 80 50       169 last unless $size;
1049 80         107 $pos += 4;
1050 80         109 $size -= 2;
1051 80 50 33     244 if ($size < 0 or $pos + $size > $dataLen) {
1052 0         0 $et->Warn('Corrupted Ricoh RMETA data', 1);
1053 0         0 last;
1054             }
1055 80         167 my $dat = substr($$dataPt, $pos, $size);
1056 80 50       135 if ($verbose) {
1057 0         0 $et->VPrint(2, "$$et{INDENT}RMETA section type=$type size=$size\n");
1058 0         0 $et->VerboseDump(\$dat, Addr => $$dirInfo{DataPos} + $pos);
1059             }
1060 80 100 66     398 if ($type == 1) { # section 1: tag names
    100          
    100          
    50          
1061             # save the tag names
1062 20         162 @tags = split /\0/, $dat, $numEntries+1;
1063             } elsif ($type == 2 || $type == 18) { # section 2/18: string values (G800 uses type 18)
1064             # save the tag values (assume "ASCII\0" encoding since others never seen)
1065 20         98 @vals = split /\0/, $dat, $numEntries+1;
1066 20         42 $valPos = $pos; # save position of first string value
1067             } elsif ($type == 3) { # section 3: numerical values
1068 20 50       61 if ($size < $numEntries * 2) {
1069 0         0 $et->Warn('Truncated RMETA section 3');
1070             } else {
1071             # save the numerical tag values
1072             # (0=empty, 0xffff=text input, otherwise menu item number)
1073 20 50       112 @nums = unpack(($byteOrder eq 'MM' ? 'n' : 'v').$numEntries, $dat);
1074 20         49 $numPos = $pos; # save position of numerical values
1075             }
1076             } elsif ($type != 16) {
1077 0         0 $et->Warn("Unrecognized RMETA section (type $type, len $size)");
1078             }
1079 80         168 $pos += $size;
1080             }
1081 20 50 33     87 return 1 unless @tags or @vals;
1082 20 50       56 $valPos or $valPos = 0; # (just in case there was no value section)
1083             # find next tag in null-delimited list
1084             # unpack numerical values from block of int16u values
1085 20         44 my ($i, $name);
1086 20         78 for ($i=0; $i<$numEntries; ++$i) {
1087 100         166 my $tag = $tags[$i];
1088 100         149 my $val = $vals[$i];
1089 100 50       176 $val = '' unless defined $val;
1090 100 50 33     304 unless (defined $tag and length $tag) {
1091 0 0       0 length $val or ++$valPos, next; # (skip empty entries)
1092 0         0 $tag = '';
1093             }
1094 100         434 ($name = $tag) =~ s/\b([a-z])/\U$1/gs; # capitalize all words
1095 100         234 $name =~ s/ (\w)/\U$1/g; # remove special characters
1096 100 50       193 $name = 'RMETA_Unknown' unless length($name);
1097 100         137 my $num = $nums[$i];
1098 100         229 my $tagInfo = $et->GetTagInfo($tagTablePtr, $tag);
1099 100 50       213 if ($tagInfo) {
1100             # make sure print conversion is defined
1101 100 50       247 $$tagInfo{PrintConv} = { } unless ref $$tagInfo{PrintConv} eq 'HASH';
1102             } else {
1103             # create tagInfo hash
1104 0         0 $tagInfo = { Name => $name, PrintConv => { } };
1105 0         0 AddTagToTable($tagTablePtr, $tag, $tagInfo);
1106             }
1107             # use string value directly if no numerical value
1108 100 50       180 $num = $val unless defined $num;
1109             # add conversion for this value (replacing any existing entry)
1110 100 50       245 $tagInfo->{PrintConv}->{$num} = length $val ? $val : $num;
1111 100 50       185 if ($verbose) {
1112 0         0 my %datParms;
1113 0 0       0 if (length $val) {
    0          
1114 0         0 %datParms = ( Start => $valPos, Size => length($val), Format => 'string' );
1115             } elsif ($numPos) {
1116 0         0 %datParms = ( Start => $numPos + $i * 2, Size => 2, Format => 'int16u' );
1117             }
1118 0 0       0 %datParms and $datParms{DataPt} = $dataPt, $datParms{DataPos} = $$dirInfo{DataPos};
1119 0         0 $et->VerboseInfo($tag, $tagInfo, Table=>$tagTablePtr, Value=>$num, %datParms);
1120             }
1121 100         251 $et->FoundTag($tagInfo, $num);
1122 100         291 $valPos += length($val) + 1;
1123             }
1124 20         108 return 1;
1125             }
1126              
1127             1; # end
1128              
1129             __END__