File Coverage

blib/lib/Image/ExifTool/Ricoh.pm
Criterion Covered Total %
statement 68 132 51.5
branch 28 86 32.5
condition 5 23 21.7
subroutine 5 6 83.3
pod 0 2 0.0
total 106 249 42.5


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   4933 use strict;
  21         68  
  21         881  
18 21     21   138 use vars qw($VERSION);
  21         64  
  21         1060  
19 21     21   213 use Image::ExifTool qw(:DataAccess :Utils);
  21         50  
  21         4825  
20 21     21   1443 use Image::ExifTool::Exif;
  21         75  
  21         64359  
21              
22             $VERSION = '1.36';
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 0         0 $et->VerboseDir('RicohText', undef, $dirLen);
953             # validate text maker notes
954 0 0       0 unless ($data =~ /^(Rev|Rv)/) {
955 0         0 $et->Warn('Bad Ricoh maker notes');
956 0         0 return 0;
957             }
958 0         0 while ($data =~ m/([A-Z][a-z]{1,2})([0-9A-F]+);/sg) {
959 0         0 my $tag = $1;
960 0         0 my $val = $2;
961 0         0 my $tagInfo = $et->GetTagInfo($tagTablePtr, $tag);
962 0 0       0 if ($verbose) {
963 0         0 $et->VerboseInfo($tag, $tagInfo,
964             Table => $tagTablePtr,
965             Value => $val,
966             );
967             }
968 0 0       0 unless ($tagInfo) {
969 0 0       0 next unless $$et{OPTIONS}{Unknown};
970 0         0 $tagInfo = {
971             Name => "Ricoh_Text_$tag",
972             Unknown => 1,
973             PrintConv => 'length($val) > 60 ? substr($val,0,55) . "[...]" : $val',
974             };
975             # add tag information to table
976 0         0 AddTagToTable($tagTablePtr, $tag, $tagInfo);
977             }
978 0         0 $et->FoundTag($tagInfo, $val);
979             }
980 0         0 return 1;
981             }
982              
983             #------------------------------------------------------------------------------
984             # Process Ricoh APP5 RMETA information
985             # Inputs: 0) ExifTool ref, 1) dirInfo ref, 2) tag table ref
986             # Returns: 1 on success, otherwise returns 0 and sets a Warning
987             sub ProcessRicohRMETA($$$)
988             {
989 20     20 0 83 my ($et, $dirInfo, $tagTablePtr) = @_;
990 20         68 my $dataPt = $$dirInfo{DataPt};
991 20         60 my $dirStart = $$dirInfo{DirStart};
992 20         86 my $dataLen = length($$dataPt);
993 20         51 my $dirLen = $dataLen - $dirStart;
994 20         83 my $verbose = $et->Options('Verbose');
995              
996 20 50       154 $et->VerboseDir('Ricoh RMETA') if $verbose;
997 20 50       173 $dirLen < 20 and $et->Warn('Truncated Ricoh RMETA data', 1), return 0;
998 20         75 my $byteOrder = substr($$dataPt, $dirStart, 2);
999 20 50       85 $byteOrder = GetByteOrder() if $byteOrder eq "\0\0"; # (same order as container)
1000 20 50       82 SetByteOrder($byteOrder) or $et->Warn('Bad Ricoh RMETA data', 1), return 0;
1001             # get the RMETA segment number
1002 20         131 my $rmetaNum = Get16u($dataPt, $dirStart+4);
1003 20 50       110 if ($rmetaNum != 0) {
1004             # not sure how to recognize audio, so do it by checking for "RIFF" header
1005             # and assume all subsequent RMETA segments are part of the audio data
1006             # (but it looks like the int16u at $dirStart+6 is the next block number
1007             # if the data is continued, or 0 for the last block)
1008 0 0       0 $dirLen < 14 and $et->Warn('Short Ricoh RMETA block', 1), return 0;
1009 0 0       0 if ($$dataPt =~ /^.{20}BARCODE/s) {
    0          
1010 0         0 my $val = substr($$dataPt, 20);
1011 0         0 $val =~ s/\0.*//s;
1012 0         0 $val =~ s/^BARCODE\w+,\d{2},//;
1013 0         0 my @codes;
1014 0         0 for (;;) {
1015 0 0 0     0 $val =~ s/(\d+),// and length $val >= $1 or last;
1016 0         0 push @codes, substr($val, 0, $1);
1017 0 0       0 last unless length $val > $1;
1018 0         0 $val = substr($val, $1+1);
1019             }
1020 0 0       0 $et->HandleTag($tagTablePtr, '_barcode', \@codes) if @codes;
1021 0         0 return 1;
1022             } elsif ($$dataPt =~ /^.{18}ASCII/s) {
1023             # (ignore barcode tag names for now)
1024 0         0 return 1;
1025             }
1026 0         0 my $audioLen = Get16u($dataPt, $dirStart+12);
1027 0 0       0 $audioLen + 14 > $dirLen and $et->Warn('Truncated Ricoh RMETA audio data', 1), return 0;
1028 0         0 my $buff = substr($$dataPt, $dirStart + 14, $audioLen);
1029 0 0 0     0 if ($audioLen >= 4 and substr($buff, 0, 4) eq 'RIFF') {
    0          
1030 0         0 $et->HandleTag($tagTablePtr, '_audio', \$buff);
1031             } elsif ($$et{VALUE}{SoundFile}) {
1032 0         0 ${$$et{VALUE}{SoundFile}} .= $buff;
  0         0  
1033             } else {
1034 0         0 $et->Warn('Unknown Ricoh RMETA type', 1);
1035 0         0 return 0;
1036             }
1037 0         0 return 1;
1038             }
1039             # decode standard RMETA tag directory
1040 20         76 my (@tags, @vals, @nums, $valPos, $numPos);
1041 20         84 my $pos = $dirStart + Get16u($dataPt, $dirStart+8);
1042 20         92 my $numEntries = Get16u($dataPt, $pos);
1043 20 50       116 $numEntries > 100 and $et->Warn('Bad RMETA entry count'), return 0;
1044 20         81 $pos += 10; # start of first RMETA section
1045             # loop through RMETA sections
1046 20         123 while ($pos <= $dataLen - 4) {
1047 80         202 my $type = Get16u($dataPt, $pos);
1048 80         250 my $size = Get16u($dataPt, $pos + 2);
1049 80 50       217 last unless $size;
1050 80         122 $pos += 4;
1051 80         125 $size -= 2;
1052 80 50 33     328 if ($size < 0 or $pos + $size > $dataLen) {
1053 0         0 $et->Warn('Corrupted Ricoh RMETA data', 1);
1054 0         0 last;
1055             }
1056 80         207 my $dat = substr($$dataPt, $pos, $size);
1057 80 50       182 if ($verbose) {
1058 0         0 $et->VPrint(2, "$$et{INDENT}RMETA section type=$type size=$size\n");
1059 0         0 $et->VerboseDump(\$dat, Addr => $$dirInfo{DataPos} + $pos);
1060             }
1061 80 100 66     653 if ($type == 1) { # section 1: tag names
    100          
    100          
    50          
1062             # save the tag names
1063 20         164 @tags = split /\0/, $dat, $numEntries+1;
1064             } elsif ($type == 2 || $type == 18) { # section 2/18: string values (G800 uses type 18)
1065             # save the tag values (assume "ASCII\0" encoding since others never seen)
1066 20         126 @vals = split /\0/, $dat, $numEntries+1;
1067 20         50 $valPos = $pos; # save position of first string value
1068             } elsif ($type == 3) { # section 3: numerical values
1069 20 50       91 if ($size < $numEntries * 2) {
1070 0         0 $et->Warn('Truncated RMETA section 3');
1071             } else {
1072             # save the numerical tag values
1073             # (0=empty, 0xffff=text input, otherwise menu item number)
1074 20 50       112 @nums = unpack(($byteOrder eq 'MM' ? 'n' : 'v').$numEntries, $dat);
1075 20         60 $numPos = $pos; # save position of numerical values
1076             }
1077             } elsif ($type != 16) {
1078 0         0 $et->Warn("Unrecognized RMETA section (type $type, len $size)");
1079             }
1080 80         207 $pos += $size;
1081             }
1082 20 50 33     101 return 1 unless @tags or @vals;
1083 20 50       64 $valPos or $valPos = 0; # (just in case there was no value section)
1084             # find next tag in null-delimited list
1085             # unpack numerical values from block of int16u values
1086 20         51 my ($i, $name);
1087 20         94 for ($i=0; $i<$numEntries; ++$i) {
1088 100         213 my $tag = $tags[$i];
1089 100         203 my $val = $vals[$i];
1090 100 50       232 $val = '' unless defined $val;
1091 100 50 33     656 unless (defined $tag and length $tag) {
1092 0 0       0 length $val or ++$valPos, next; # (skip empty entries)
1093 0         0 $tag = '';
1094             }
1095 100         500 ($name = $tag) =~ s/\b([a-z])/\U$1/gs; # capitalize all words
1096 100         302 $name =~ s/ (\w)/\U$1/g; # remove special characters
1097 100 50       237 $name = 'RMETA_Unknown' unless length($name);
1098 100         188 my $num = $nums[$i];
1099 100         292 my $tagInfo = $et->GetTagInfo($tagTablePtr, $tag);
1100 100 50       290 if ($tagInfo) {
1101             # make sure print conversion is defined
1102 100 50       326 $$tagInfo{PrintConv} = { } unless ref $$tagInfo{PrintConv} eq 'HASH';
1103             } else {
1104             # create tagInfo hash
1105 0         0 $tagInfo = { Name => $name, PrintConv => { } };
1106 0         0 AddTagToTable($tagTablePtr, $tag, $tagInfo);
1107             }
1108             # use string value directly if no numerical value
1109 100 50       215 $num = $val unless defined $num;
1110             # add conversion for this value (replacing any existing entry)
1111 100 50       371 $tagInfo->{PrintConv}->{$num} = length $val ? $val : $num;
1112 100 50       201 if ($verbose) {
1113 0         0 my %datParms;
1114 0 0       0 if (length $val) {
    0          
1115 0         0 %datParms = ( Start => $valPos, Size => length($val), Format => 'string' );
1116             } elsif ($numPos) {
1117 0         0 %datParms = ( Start => $numPos + $i * 2, Size => 2, Format => 'int16u' );
1118             }
1119 0 0       0 %datParms and $datParms{DataPt} = $dataPt, $datParms{DataPos} = $$dirInfo{DataPos};
1120 0         0 $et->VerboseInfo($tag, $tagInfo, Table=>$tagTablePtr, Value=>$num, %datParms);
1121             }
1122 100         313 $et->FoundTag($tagInfo, $num);
1123 100         357 $valPos += length($val) + 1;
1124             }
1125 20         143 return 1;
1126             }
1127              
1128             1; # end
1129              
1130             __END__