File Coverage

blib/lib/Image/ExifTool/Exif.pm
Criterion Covered Total %
statement 618 977 63.2
branch 378 780 48.4
condition 253 547 46.2
subroutine 30 33 90.9
pod 0 28 0.0
total 1279 2365 54.0


line stmt bran cond sub pod time code
1             #------------------------------------------------------------------------------
2             # File: Exif.pm
3             #
4             # Description: Read EXIF/TIFF meta information
5             #
6             # Revisions: 11/25/2003 - P. Harvey Created
7             # 02/06/2004 - P. Harvey Moved processing functions from ExifTool
8             # 03/19/2004 - P. Harvey Check PreviewImage for validity
9             # 11/11/2004 - P. Harvey Split off maker notes into MakerNotes.pm
10             # 12/13/2004 - P. Harvey Added AUTOLOAD to load write routines
11             #
12             # References: 0) http://www.exif.org/Exif2-2.PDF
13             # 1) http://partners.adobe.com/asn/developer/pdfs/tn/TIFF6.pdf
14             # 2) http://www.adobe.com/products/dng/pdfs/dng_spec_1_3_0_0.pdf
15             # 3) http://www.awaresystems.be/imaging/tiff/tifftags.html
16             # 4) http://www.remotesensing.org/libtiff/TIFFTechNote2.html
17             # 5) http://www.exif.org/dcf.PDF
18             # 6) http://park2.wakwak.com/~tsuruzoh/Computer/Digicams/exif-e.html
19             # 7) http://www.fine-view.com/jp/lab/doc/ps6ffspecsv2.pdf
20             # 8) http://www.ozhiker.com/electronics/pjmt/jpeg_info/meta.html
21             # 9) http://hul.harvard.edu/jhove/tiff-tags.html
22             # 10) http://partners.adobe.com/public/developer/en/tiff/TIFFPM6.pdf
23             # 11) Robert Mucke private communication
24             # 12) http://www.broomscloset.com/closet/photo/exif/TAG2000-22_DIS12234-2.PDF
25             # 13) http://www.microsoft.com/whdc/xps/wmphoto.mspx
26             # 14) http://www.asmail.be/msg0054681802.html
27             # 15) http://crousseau.free.fr/imgfmt_raw.htm
28             # 16) http://www.cybercom.net/~dcoffin/dcraw/
29             # 17) http://www.digitalpreservation.gov/formats/content/tiff_tags.shtml
30             # 18) http://www.asmail.be/msg0055568584.html
31             # 19) http://libpsd.graphest.com/files/Photoshop%20File%20Formats.pdf
32             # 20) http://tiki-lounge.com/~raf/tiff/fields.html
33             # 21) http://community.roxen.com/developers/idocs/rfc/rfc3949.html
34             # 22) http://tools.ietf.org/html/draft-ietf-fax-tiff-fx-extension1-01
35             # 23) MetaMorph Stack (STK) Image File Format:
36             # --> ftp://ftp.meta.moleculardevices.com/support/stack/STK.doc
37             # 24) http://www.cipa.jp/std/documents/e/DC-008-2012_E.pdf (Exif 2.3)
38             # 25) Vesa Kivisto private communication (7D)
39             # 26) Jeremy Brown private communication
40             # 27) Gregg Lee private communication
41             # 28) http://wwwimages.adobe.com/www.adobe.com/content/dam/Adobe/en/devnet/cinemadng/pdfs/CinemaDNG_Format_Specification_v1_1.pdf
42             # 29) http://www.libtiff.org
43             # 30) http://geotiff.maptools.org/spec/geotiffhome.html
44             # 31) https://android.googlesource.com/platform/external/dng_sdk/+/refs/heads/master/source/dng_tag_codes.h
45             # 32) Jeffry Friedl private communication
46             # IB) Iliah Borg private communication (LibRaw)
47             # JD) Jens Duttke private communication
48             #------------------------------------------------------------------------------
49              
50             package Image::ExifTool::Exif;
51              
52 105     105   1110 use strict;
  105         279  
  105         4570  
53 105         13151 use vars qw($VERSION $AUTOLOAD @formatSize @formatName %formatNumber %intFormat
54             %lightSource %flash %compression %photometricInterpretation %orientation
55 105     105   642 %subfileType %saveForValidate);
  105         220  
56 105     105   672 use Image::ExifTool qw(:DataAccess :Utils);
  105         223  
  105         26491  
57 105     105   71885 use Image::ExifTool::MakerNotes;
  105         859  
  105         2340624  
58              
59             $VERSION = '4.42';
60              
61             sub ProcessExif($$$);
62             sub WriteExif($$$);
63             sub CheckExif($$$);
64             sub RebuildMakerNotes($$$);
65             sub EncodeExifText($$);
66             sub ValidateIFD($;$);
67             sub ValidateImageData($$$;$);
68             sub ProcessTiffIFD($$$);
69             sub PrintParameter($$$);
70             sub GetOffList($$$$$);
71             sub PrintOpcode($$$);
72             sub PrintLensInfo($);
73             sub ConvertLensInfo($);
74              
75             # size limit for loading binary data block into memory
76 675     675 0 2152 sub BINARY_DATA_LIMIT { return 10 * 1024 * 1024; }
77              
78             # byte sizes for the various EXIF format types below
79             @formatSize = (undef,1,1,2,4,8,1,1,2,4,8,4,8,4,2,8,8,8,8);
80              
81             @formatName = (
82             undef, 'int8u', 'string', 'int16u',
83             'int32u', 'rational64u','int8s', 'undef',
84             'int16s', 'int32s', 'rational64s','float',
85             'double', 'ifd', 'unicode', 'complex',
86             'int64u', 'int64s', 'ifd64', # (new BigTIFF formats)
87             );
88              
89             # hash to look up EXIF format numbers by name
90             # (format types are all lower case)
91             %formatNumber = (
92             'int8u' => 1, # BYTE
93             'string' => 2, # ASCII
94             'int16u' => 3, # SHORT
95             'int32u' => 4, # LONG
96             'rational64u' => 5, # RATIONAL
97             'int8s' => 6, # SBYTE
98             'undef' => 7, # UNDEFINED
99             'binary' => 7, # (same as undef)
100             'int16s' => 8, # SSHORT
101             'int32s' => 9, # SLONG
102             'rational64s' => 10, # SRATIONAL
103             'float' => 11, # FLOAT
104             'double' => 12, # DOUBLE
105             'ifd' => 13, # IFD (with int32u format)
106             'unicode' => 14, # UNICODE [see Note below]
107             'complex' => 15, # COMPLEX [see Note below]
108             'int64u' => 16, # LONG8 [BigTIFF]
109             'int64s' => 17, # SLONG8 [BigTIFF]
110             'ifd64' => 18, # IFD8 (with int64u format) [BigTIFF]
111             # Note: unicode and complex types are not yet properly supported by ExifTool.
112             # These are types which have been observed in the Adobe DNG SDK code, but
113             # aren't fully supported there either. We know the sizes, but that's about it.
114             # We don't know if the unicode is null terminated, or the format for complex
115             # (although I suspect it would be two 4-byte floats, real and imaginary).
116             );
117              
118             # lookup for integer format strings
119             %intFormat = (
120             'int8u' => 1,
121             'int16u' => 3,
122             'int32u' => 4,
123             'int8s' => 6,
124             'int16s' => 8,
125             'int32s' => 9,
126             'ifd' => 13,
127             'int64u' => 16,
128             'int64s' => 17,
129             'ifd64' => 18,
130             );
131              
132             # EXIF LightSource PrintConv values
133             %lightSource = (
134             0 => 'Unknown',
135             1 => 'Daylight',
136             2 => 'Fluorescent',
137             3 => 'Tungsten (Incandescent)',
138             4 => 'Flash',
139             9 => 'Fine Weather',
140             10 => 'Cloudy',
141             11 => 'Shade',
142             12 => 'Daylight Fluorescent', # (D 5700 - 7100K)
143             13 => 'Day White Fluorescent', # (N 4600 - 5500K)
144             14 => 'Cool White Fluorescent', # (W 3800 - 4500K)
145             15 => 'White Fluorescent', # (WW 3250 - 3800K)
146             16 => 'Warm White Fluorescent', # (L 2600 - 3250K)
147             17 => 'Standard Light A',
148             18 => 'Standard Light B',
149             19 => 'Standard Light C',
150             20 => 'D55',
151             21 => 'D65',
152             22 => 'D75',
153             23 => 'D50',
154             24 => 'ISO Studio Tungsten',
155             255 => 'Other',
156             );
157              
158             # EXIF Flash values
159             %flash = (
160             OTHER => sub {
161             # translate "Off" and "On" when writing
162             my ($val, $inv) = @_;
163             return undef unless $inv and $val =~ /^(off|on)$/i;
164             return lc $val eq 'off' ? 0x00 : 0x01;
165             },
166             0x00 => 'No Flash',
167             0x01 => 'Fired',
168             0x05 => 'Fired, Return not detected',
169             0x07 => 'Fired, Return detected',
170             0x08 => 'On, Did not fire', # not charged up?
171             0x09 => 'On, Fired',
172             0x0d => 'On, Return not detected',
173             0x0f => 'On, Return detected',
174             0x10 => 'Off, Did not fire',
175             0x14 => 'Off, Did not fire, Return not detected',
176             0x18 => 'Auto, Did not fire',
177             0x19 => 'Auto, Fired',
178             0x1d => 'Auto, Fired, Return not detected',
179             0x1f => 'Auto, Fired, Return detected',
180             0x20 => 'No flash function',
181             0x30 => 'Off, No flash function',
182             0x41 => 'Fired, Red-eye reduction',
183             0x45 => 'Fired, Red-eye reduction, Return not detected',
184             0x47 => 'Fired, Red-eye reduction, Return detected',
185             0x49 => 'On, Red-eye reduction',
186             0x4d => 'On, Red-eye reduction, Return not detected',
187             0x4f => 'On, Red-eye reduction, Return detected',
188             0x50 => 'Off, Red-eye reduction',
189             0x58 => 'Auto, Did not fire, Red-eye reduction',
190             0x59 => 'Auto, Fired, Red-eye reduction',
191             0x5d => 'Auto, Fired, Red-eye reduction, Return not detected',
192             0x5f => 'Auto, Fired, Red-eye reduction, Return detected',
193             );
194              
195             # TIFF Compression values
196             # (values with format "Xxxxx XXX Compressed" are used to identify RAW file types)
197             %compression = (
198             1 => 'Uncompressed',
199             2 => 'CCITT 1D',
200             3 => 'T4/Group 3 Fax',
201             4 => 'T6/Group 4 Fax',
202             5 => 'LZW',
203             6 => 'JPEG (old-style)', #3
204             7 => 'JPEG', #4
205             8 => 'Adobe Deflate', #3
206             9 => 'JBIG B&W', #3
207             10 => 'JBIG Color', #3
208             99 => 'JPEG', #16
209             262 => 'Kodak 262', #16
210             32766 => 'Next', #3
211             32767 => 'Sony ARW Compressed', #16
212             32769 => 'Packed RAW', #PH (used by Epson, Nikon, Samsung)
213             32770 => 'Samsung SRW Compressed', #PH
214             32771 => 'CCIRLEW', #3
215             32772 => 'Samsung SRW Compressed 2', #PH (NX3000,NXmini)
216             32773 => 'PackBits',
217             32809 => 'Thunderscan', #3
218             32867 => 'Kodak KDC Compressed', #PH
219             32895 => 'IT8CTPAD', #3
220             32896 => 'IT8LW', #3
221             32897 => 'IT8MP', #3
222             32898 => 'IT8BL', #3
223             32908 => 'PixarFilm', #3
224             32909 => 'PixarLog', #3
225             # 32910,32911 - Pixar reserved
226             32946 => 'Deflate', #3
227             32947 => 'DCS', #3
228             33003 => 'Aperio JPEG 2000 YCbCr', #https://openslide.org/formats/aperio/
229             33005 => 'Aperio JPEG 2000 RGB', #https://openslide.org/formats/aperio/
230             34661 => 'JBIG', #3
231             34676 => 'SGILog', #3
232             34677 => 'SGILog24', #3
233             34712 => 'JPEG 2000', #3
234             34713 => 'Nikon NEF Compressed', #PH
235             34715 => 'JBIG2 TIFF FX', #20
236             34718 => 'Microsoft Document Imaging (MDI) Binary Level Codec', #18
237             34719 => 'Microsoft Document Imaging (MDI) Progressive Transform Codec', #18
238             34720 => 'Microsoft Document Imaging (MDI) Vector', #18
239             34887 => 'ESRI Lerc', #LibTiff
240             # 34888,34889 - ESRI reserved
241             34892 => 'Lossy JPEG', # (DNG 1.4)
242             34925 => 'LZMA2', #LibTiff
243             34926 => 'Zstd', #LibTiff
244             34927 => 'WebP', #LibTiff
245             34933 => 'PNG', # (TIFF mail list)
246             34934 => 'JPEG XR', # (TIFF mail list)
247             65000 => 'Kodak DCR Compressed', #PH
248             65535 => 'Pentax PEF Compressed', #Jens
249             );
250              
251             %photometricInterpretation = (
252             0 => 'WhiteIsZero',
253             1 => 'BlackIsZero',
254             2 => 'RGB',
255             3 => 'RGB Palette',
256             4 => 'Transparency Mask',
257             5 => 'CMYK',
258             6 => 'YCbCr',
259             8 => 'CIELab',
260             9 => 'ICCLab', #3
261             10 => 'ITULab', #3
262             32803 => 'Color Filter Array', #2
263             32844 => 'Pixar LogL', #3
264             32845 => 'Pixar LogLuv', #3
265             32892 => 'Sequential Color Filter', #JR (Sony ARQ)
266             34892 => 'Linear Raw', #2
267             51177 => 'Depth Map', # (DNG 1.5)
268             52527 => 'Semantic Mask', # (DNG 1.6)
269             );
270              
271             %orientation = (
272             1 => 'Horizontal (normal)',
273             2 => 'Mirror horizontal',
274             3 => 'Rotate 180',
275             4 => 'Mirror vertical',
276             5 => 'Mirror horizontal and rotate 270 CW',
277             6 => 'Rotate 90 CW',
278             7 => 'Mirror horizontal and rotate 90 CW',
279             8 => 'Rotate 270 CW',
280             );
281              
282             %subfileType = (
283             0 => 'Full-resolution image',
284             1 => 'Reduced-resolution image',
285             2 => 'Single page of multi-page image',
286             3 => 'Single page of multi-page reduced-resolution image',
287             4 => 'Transparency mask',
288             5 => 'Transparency mask of reduced-resolution image',
289             6 => 'Transparency mask of multi-page image',
290             7 => 'Transparency mask of reduced-resolution multi-page image',
291             8 => 'Depth map', # (DNG 1.5)
292             9 => 'Depth map of reduced-resolution image', # (DNG 1.5)
293             16 => 'Enhanced image data', # (DNG 1.5)
294             0x10001 => 'Alternate reduced-resolution image', # (DNG 1.2)
295             0x10004 => 'Semantic Mask', # (DNG 1.6)
296             0xffffffff => 'invalid', #(found in E5700 NEF's)
297             BITMASK => {
298             0 => 'Reduced resolution',
299             1 => 'Single page',
300             2 => 'Transparency mask',
301             3 => 'TIFF/IT final page', #20 (repurposed as DepthMap repurposes by DNG 1.5)
302             4 => 'TIFF-FX mixed raster content', #20 (repurposed as EnhancedImageData by DNG 1.5)
303             },
304             );
305              
306             # PrintConv for parameter tags
307             %Image::ExifTool::Exif::printParameter = (
308             PrintConv => {
309             0 => 'Normal',
310             OTHER => \&Image::ExifTool::Exif::PrintParameter,
311             },
312             );
313              
314             # convert DNG UTF-8 string values (may be string or int8u format)
315             my %utf8StringConv = (
316             Writable => 'string',
317             Format => 'string',
318             ValueConv => '$self->Decode($val, "UTF8")',
319             ValueConvInv => '$self->Encode($val,"UTF8")',
320             );
321              
322             # ValueConv that makes long values binary type
323             my %longBin = (
324             ValueConv => 'length($val) > 64 ? \$val : $val',
325             ValueConvInv => '$val',
326             LongBinary => 1, # flag to avoid decoding values of a large array
327             );
328              
329             # PrintConv for SampleFormat (0x153)
330             my %sampleFormat = (
331             1 => 'Unsigned', # unsigned integer
332             2 => 'Signed', # two's complement signed integer
333             3 => 'Float', # IEEE floating point
334             4 => 'Undefined',
335             5 => 'Complex int', # complex integer (ref 3)
336             6 => 'Complex float', # complex IEEE floating point (ref 3)
337             );
338              
339             # save the values of these tags for additional validation checks
340             %saveForValidate = (
341             0x100 => 1, # ImageWidth
342             0x101 => 1, # ImageHeight
343             0x102 => 1, # BitsPerSample
344             0x103 => 1, # Compression
345             0x115 => 1, # SamplesPerPixel
346             );
347              
348             # conversions for DNG OpcodeList tags
349             my %opcodeInfo = (
350             Writable => 'undef',
351             WriteGroup => 'SubIFD',
352             Protected => 1,
353             Binary => 1,
354             ConvertBinary => 1, # needed because the ValueConv value is binary
355             PrintConvColumns => 2,
356             PrintConv => {
357             OTHER => \&PrintOpcode,
358             1 => 'WarpRectilinear',
359             2 => 'WarpFisheye',
360             3 => 'FixVignetteRadial',
361             4 => 'FixBadPixelsConstant',
362             5 => 'FixBadPixelsList',
363             6 => 'TrimBounds',
364             7 => 'MapTable',
365             8 => 'MapPolynomial',
366             9 => 'GainMap',
367             10 => 'DeltaPerRow',
368             11 => 'DeltaPerColumn',
369             12 => 'ScalePerRow',
370             13 => 'ScalePerColumn',
371             14 => 'WarpRectilinear2', # (DNG 1.6)
372             },
373             PrintConvInv => undef, # (so the inverse conversion is not performed)
374             );
375              
376             # main EXIF tag table
377             %Image::ExifTool::Exif::Main = (
378             GROUPS => { 0 => 'EXIF', 1 => 'IFD0', 2 => 'Image'},
379             WRITE_PROC => \&WriteExif,
380             CHECK_PROC => \&CheckExif,
381             WRITE_GROUP => 'ExifIFD', # default write group
382             SET_GROUP1 => 1, # set group1 name to directory name for all tags in table
383             0x1 => {
384             Name => 'InteropIndex',
385             Description => 'Interoperability Index',
386             Protected => 1,
387             Writable => 'string',
388             WriteGroup => 'InteropIFD',
389             PrintConv => {
390             R98 => 'R98 - DCF basic file (sRGB)',
391             R03 => 'R03 - DCF option file (Adobe RGB)',
392             THM => 'THM - DCF thumbnail file',
393             },
394             },
395             0x2 => { #5
396             Name => 'InteropVersion',
397             Description => 'Interoperability Version',
398             Protected => 1,
399             Writable => 'undef',
400             Mandatory => 1,
401             WriteGroup => 'InteropIFD',
402             RawConv => '$val=~s/\0+$//; $val', # (some idiots add null terminators)
403             },
404             0x0b => { #PH
405             Name => 'ProcessingSoftware',
406             Writable => 'string',
407             WriteGroup => 'IFD0',
408             Notes => 'used by ACD Systems Digital Imaging',
409             },
410             0xfe => {
411             Name => 'SubfileType',
412             Notes => 'called NewSubfileType by the TIFF specification',
413             Protected => 1,
414             Writable => 'int32u',
415             WriteGroup => 'IFD0',
416             # set priority directory if this is the full resolution image
417             DataMember => 'SubfileType',
418             RawConv => q{
419             if ($val == ($val & 0x02)) {
420             $self->SetPriorityDir() if $val == 0;
421             $$self{PageCount} = ($$self{PageCount} || 0) + 1;
422             $$self{MultiPage} = 1 if $val == 2 or $$self{PageCount} > 1;
423             }
424             $$self{SubfileType} = $val;
425             },
426             PrintConv => \%subfileType,
427             },
428             0xff => {
429             Name => 'OldSubfileType',
430             Notes => 'called SubfileType by the TIFF specification',
431             Protected => 1,
432             Writable => 'int16u',
433             WriteGroup => 'IFD0',
434             # set priority directory if this is the full resolution image
435             RawConv => q{
436             if ($val == 1 or $val == 3) {
437             $self->SetPriorityDir() if $val == 1;
438             $$self{PageCount} = ($$self{PageCount} || 0) + 1;
439             $$self{MultiPage} = 1 if $val == 3 or $$self{PageCount} > 1;
440             }
441             $val;
442             },
443             PrintConv => {
444             1 => 'Full-resolution image',
445             2 => 'Reduced-resolution image',
446             3 => 'Single page of multi-page image',
447             },
448             },
449             0x100 => {
450             Name => 'ImageWidth',
451             # even though Group 1 is set dynamically we need to register IFD1 once
452             # so it will show up in the group lists
453             Groups => { 1 => 'IFD1' },
454             Protected => 1,
455             Writable => 'int32u',
456             WriteGroup => 'IFD0',
457             # Note: priority 0 tags automatically have their priority increased for the
458             # priority directory (the directory with a SubfileType of "Full-resolution image")
459             Priority => 0,
460             },
461             0x101 => {
462             Name => 'ImageHeight',
463             Notes => 'called ImageLength by the EXIF spec.',
464             Protected => 1,
465             Writable => 'int32u',
466             WriteGroup => 'IFD0',
467             Priority => 0,
468             },
469             0x102 => {
470             Name => 'BitsPerSample',
471             Protected => 1,
472             Writable => 'int16u',
473             WriteGroup => 'IFD0',
474             Count => -1, # can be 1 or 3: -1 means 'variable'
475             Priority => 0,
476             },
477             0x103 => {
478             Name => 'Compression',
479             Protected => 1,
480             Writable => 'int16u',
481             WriteGroup => 'IFD0',
482             Mandatory => 1,
483             DataMember => 'Compression',
484             SeparateTable => 'Compression',
485             RawConv => q{
486             Image::ExifTool::Exif::IdentifyRawFile($self, $val);
487             return $$self{Compression} = $val;
488             },
489             PrintConv => \%compression,
490             Priority => 0,
491             },
492             0x106 => {
493             Name => 'PhotometricInterpretation',
494             Protected => 1,
495             Writable => 'int16u',
496             WriteGroup => 'IFD0',
497             PrintConv => \%photometricInterpretation,
498             Priority => 0,
499             },
500             0x107 => {
501             Name => 'Thresholding',
502             Protected => 1,
503             Writable => 'int16u',
504             WriteGroup => 'IFD0',
505             PrintConv => {
506             1 => 'No dithering or halftoning',
507             2 => 'Ordered dither or halftone',
508             3 => 'Randomized dither',
509             },
510             },
511             0x108 => {
512             Name => 'CellWidth',
513             Protected => 1,
514             Writable => 'int16u',
515             WriteGroup => 'IFD0',
516             },
517             0x109 => {
518             Name => 'CellLength',
519             Protected => 1,
520             Writable => 'int16u',
521             WriteGroup => 'IFD0',
522             },
523             0x10a => {
524             Name => 'FillOrder',
525             Protected => 1,
526             Writable => 'int16u',
527             WriteGroup => 'IFD0',
528             PrintConv => {
529             1 => 'Normal',
530             2 => 'Reversed',
531             },
532             },
533             0x10d => {
534             Name => 'DocumentName',
535             Writable => 'string',
536             WriteGroup => 'IFD0',
537             },
538             0x10e => {
539             Name => 'ImageDescription',
540             Writable => 'string',
541             WriteGroup => 'IFD0',
542             Priority => 0,
543             },
544             0x10f => {
545             Name => 'Make',
546             Groups => { 2 => 'Camera' },
547             Writable => 'string',
548             WriteGroup => 'IFD0',
549             DataMember => 'Make',
550             # remove trailing blanks and save as an ExifTool member variable
551             RawConv => '$val =~ s/\s+$//; $$self{Make} = $val',
552             # NOTE: trailing "blanks" (spaces) are removed from all EXIF tags which
553             # may be "unknown" (filled with spaces) according to the EXIF spec.
554             # This allows conditional replacement with "exiftool -TAG-= -TAG=VALUE".
555             # - also removed are any other trailing whitespace characters
556             },
557             0x110 => {
558             Name => 'Model',
559             Description => 'Camera Model Name',
560             Groups => { 2 => 'Camera' },
561             Writable => 'string',
562             WriteGroup => 'IFD0',
563             DataMember => 'Model',
564             # remove trailing blanks and save as an ExifTool member variable
565             RawConv => '$val =~ s/\s+$//; $$self{Model} = $val',
566             },
567             0x111 => [
568             {
569             Condition => q[
570             $$self{TIFF_TYPE} eq 'MRW' and $$self{DIR_NAME} eq 'IFD0' and
571             $$self{Model} =~ /^DiMAGE A200/
572             ],
573             Name => 'StripOffsets',
574             IsOffset => 1,
575             OffsetPair => 0x117, # point to associated byte counts
576             # A200 stores this information in the wrong byte order!!
577             ValueConv => '$val=join(" ",unpack("N*",pack("V*",split(" ",$val))));\$val',
578             ByteOrder => 'LittleEndian',
579             },
580             {
581             # (APP1 IFD2 is for Leica JPEG preview)
582             Condition => q[
583             not ($$self{TIFF_TYPE} eq 'CR2' and $$self{DIR_NAME} eq 'IFD0') and
584             not ($$self{TIFF_TYPE} =~ /^(DNG|TIFF)$/ and $$self{Compression} eq '7' and $$self{SubfileType} ne '0') and
585             not ($$self{TIFF_TYPE} eq 'APP1' and $$self{DIR_NAME} eq 'IFD2')
586             ],
587             Name => 'StripOffsets',
588             IsOffset => 1,
589             OffsetPair => 0x117, # point to associated byte counts
590             ValueConv => 'length($val) > 32 ? \$val : $val',
591             },
592             {
593             # PreviewImageStart in IFD0 of CR2 images
594             Condition => '$$self{TIFF_TYPE} eq "CR2"',
595             Name => 'PreviewImageStart',
596             IsOffset => 1,
597             OffsetPair => 0x117,
598             Notes => q{
599             called StripOffsets in most locations, but it is PreviewImageStart in IFD0
600             of CR2 images and various IFD's of DNG images except for SubIFD2 where it is
601             JpgFromRawStart
602             },
603             DataTag => 'PreviewImage',
604             Writable => 'int32u',
605             WriteGroup => 'IFD0',
606             Protected => 2,
607             Permanent => 1,
608             },
609             {
610             # PreviewImageStart in various IFD's of DNG images except SubIFD2
611             Condition => '$$self{DIR_NAME} ne "SubIFD2"',
612             Name => 'PreviewImageStart',
613             IsOffset => 1,
614             OffsetPair => 0x117,
615             DataTag => 'PreviewImage',
616             Writable => 'int32u',
617             WriteGroup => 'All', # (writes to specific group of associated Composite tag)
618             Protected => 2,
619             Permanent => 1,
620             },
621             {
622             # JpgFromRawStart in various IFD's of DNG images except SubIFD2
623             Name => 'JpgFromRawStart',
624             IsOffset => 1,
625             OffsetPair => 0x117,
626             DataTag => 'JpgFromRaw',
627             Writable => 'int32u',
628             WriteGroup => 'SubIFD2',
629             Protected => 2,
630             Permanent => 1,
631             },
632             ],
633             0x112 => {
634             Name => 'Orientation',
635             Writable => 'int16u',
636             WriteGroup => 'IFD0',
637             PrintConv => \%orientation,
638             Priority => 0, # so PRIORITY_DIR takes precedence
639             },
640             0x115 => {
641             Name => 'SamplesPerPixel',
642             Protected => 1,
643             Writable => 'int16u',
644             WriteGroup => 'IFD0',
645             Priority => 0,
646             },
647             0x116 => {
648             Name => 'RowsPerStrip',
649             Protected => 1,
650             Writable => 'int32u',
651             WriteGroup => 'IFD0',
652             Priority => 0,
653             },
654             0x117 => [
655             {
656             Condition => q[
657             $$self{TIFF_TYPE} eq 'MRW' and $$self{DIR_NAME} eq 'IFD0' and
658             $$self{Model} =~ /^DiMAGE A200/
659             ],
660             Name => 'StripByteCounts',
661             OffsetPair => 0x111, # point to associated offset
662             # A200 stores this information in the wrong byte order!!
663             ValueConv => '$val=join(" ",unpack("N*",pack("V*",split(" ",$val))));\$val',
664             ByteOrder => 'LittleEndian',
665             },
666             {
667             # (APP1 IFD2 is for Leica JPEG preview)
668             Condition => q[
669             not ($$self{TIFF_TYPE} eq 'CR2' and $$self{DIR_NAME} eq 'IFD0') and
670             not ($$self{TIFF_TYPE} =~ /^(DNG|TIFF)$/ and $$self{Compression} eq '7' and $$self{SubfileType} ne '0') and
671             not ($$self{TIFF_TYPE} eq 'APP1' and $$self{DIR_NAME} eq 'IFD2')
672             ],
673             Name => 'StripByteCounts',
674             OffsetPair => 0x111, # point to associated offset
675             ValueConv => 'length($val) > 32 ? \$val : $val',
676             },
677             {
678             # PreviewImageLength in IFD0 of CR2 images
679             Condition => '$$self{TIFF_TYPE} eq "CR2"',
680             Name => 'PreviewImageLength',
681             OffsetPair => 0x111,
682             Notes => q{
683             called StripByteCounts in most locations, but it is PreviewImageLength in
684             IFD0 of CR2 images and various IFD's of DNG images except for SubIFD2 where
685             it is JpgFromRawLength
686             },
687             DataTag => 'PreviewImage',
688             Writable => 'int32u',
689             WriteGroup => 'IFD0',
690             Protected => 2,
691             Permanent => 1,
692             },
693             {
694             # PreviewImageLength in various IFD's of DNG images except SubIFD2
695             Condition => '$$self{DIR_NAME} ne "SubIFD2"',
696             Name => 'PreviewImageLength',
697             OffsetPair => 0x111,
698             DataTag => 'PreviewImage',
699             Writable => 'int32u',
700             WriteGroup => 'All', # (writes to specific group of associated Composite tag)
701             Protected => 2,
702             Permanent => 1,
703             },
704             {
705             # JpgFromRawLength in SubIFD2 of DNG images
706             Name => 'JpgFromRawLength',
707             OffsetPair => 0x111,
708             DataTag => 'JpgFromRaw',
709             Writable => 'int32u',
710             WriteGroup => 'SubIFD2',
711             Protected => 2,
712             Permanent => 1,
713             },
714             ],
715             0x118 => {
716             Name => 'MinSampleValue',
717             Writable => 'int16u',
718             WriteGroup => 'IFD0',
719             },
720             0x119 => {
721             Name => 'MaxSampleValue',
722             Writable => 'int16u',
723             WriteGroup => 'IFD0',
724             },
725             0x11a => {
726             Name => 'XResolution',
727             Writable => 'rational64u',
728             WriteGroup => 'IFD0',
729             Mandatory => 1,
730             Priority => 0, # so PRIORITY_DIR takes precedence
731             },
732             0x11b => {
733             Name => 'YResolution',
734             Writable => 'rational64u',
735             WriteGroup => 'IFD0',
736             Mandatory => 1,
737             Priority => 0,
738             },
739             0x11c => {
740             Name => 'PlanarConfiguration',
741             Protected => 1,
742             Writable => 'int16u',
743             WriteGroup => 'IFD0',
744             PrintConv => {
745             1 => 'Chunky',
746             2 => 'Planar',
747             },
748             Priority => 0,
749             },
750             0x11d => {
751             Name => 'PageName',
752             Writable => 'string',
753             WriteGroup => 'IFD0',
754             },
755             0x11e => {
756             Name => 'XPosition',
757             Writable => 'rational64u',
758             WriteGroup => 'IFD0',
759             },
760             0x11f => {
761             Name => 'YPosition',
762             Writable => 'rational64u',
763             WriteGroup => 'IFD0',
764             },
765             # FreeOffsets/FreeByteCounts are used by Ricoh for RMETA information
766             # in TIFF images (not yet supported)
767             0x120 => {
768             Name => 'FreeOffsets',
769             IsOffset => 1,
770             OffsetPair => 0x121,
771             ValueConv => 'length($val) > 32 ? \$val : $val',
772             },
773             0x121 => {
774             Name => 'FreeByteCounts',
775             OffsetPair => 0x120,
776             ValueConv => 'length($val) > 32 ? \$val : $val',
777             },
778             0x122 => {
779             Name => 'GrayResponseUnit',
780             Writable => 'int16u',
781             WriteGroup => 'IFD0',
782             PrintConv => { #3
783             1 => 0.1,
784             2 => 0.001,
785             3 => 0.0001,
786             4 => 0.00001,
787             5 => 0.000001,
788             },
789             },
790             0x123 => {
791             Name => 'GrayResponseCurve',
792             Binary => 1,
793             },
794             0x124 => {
795             Name => 'T4Options',
796             PrintConv => { BITMASK => {
797             0 => '2-Dimensional encoding',
798             1 => 'Uncompressed',
799             2 => 'Fill bits added',
800             } }, #3
801             },
802             0x125 => {
803             Name => 'T6Options',
804             PrintConv => { BITMASK => {
805             1 => 'Uncompressed',
806             } }, #3
807             },
808             0x128 => {
809             Name => 'ResolutionUnit',
810             Notes => 'the value 1 is not standard EXIF',
811             Writable => 'int16u',
812             WriteGroup => 'IFD0',
813             Mandatory => 1,
814             PrintConv => {
815             1 => 'None',
816             2 => 'inches',
817             3 => 'cm',
818             },
819             Priority => 0,
820             },
821             0x129 => {
822             Name => 'PageNumber',
823             Writable => 'int16u',
824             WriteGroup => 'IFD0',
825             Count => 2,
826             },
827             0x12c => 'ColorResponseUnit', #9
828             0x12d => {
829             Name => 'TransferFunction',
830             Protected => 1,
831             Writable => 'int16u',
832             WriteGroup => 'IFD0',
833             Count => 768,
834             Binary => 1,
835             },
836             0x131 => {
837             Name => 'Software',
838             Writable => 'string',
839             WriteGroup => 'IFD0',
840             DataMember => 'Software',
841             RawConv => '$val =~ s/\s+$//; $$self{Software} = $val', # trim trailing blanks
842             },
843             0x132 => {
844             Name => 'ModifyDate',
845             Groups => { 2 => 'Time' },
846             Notes => 'called DateTime by the EXIF spec.',
847             Writable => 'string',
848             Shift => 'Time',
849             WriteGroup => 'IFD0',
850             Validate => 'ValidateExifDate($val)',
851             PrintConv => '$self->ConvertDateTime($val)',
852             PrintConvInv => '$self->InverseDateTime($val,0)',
853             },
854             0x13b => {
855             Name => 'Artist',
856             Groups => { 2 => 'Author' },
857             Notes => 'becomes a list-type tag when the MWG module is loaded',
858             Writable => 'string',
859             WriteGroup => 'IFD0',
860             RawConv => '$val =~ s/\s+$//; $val', # trim trailing blanks
861             },
862             0x13c => {
863             Name => 'HostComputer',
864             Writable => 'string',
865             WriteGroup => 'IFD0',
866             },
867             0x13d => {
868             Name => 'Predictor',
869             Protected => 1,
870             Writable => 'int16u',
871             WriteGroup => 'IFD0',
872             PrintConv => {
873             1 => 'None',
874             2 => 'Horizontal differencing',
875             3 => 'Floating point', # (DNG 1.5)
876             34892 => 'Horizontal difference X2', # (DNG 1.5)
877             34893 => 'Horizontal difference X4', # (DNG 1.5)
878             34894 => 'Floating point X2', # (DNG 1.5)
879             34895 => 'Floating point X4', # (DNG 1.5)
880             },
881             },
882             0x13e => {
883             Name => 'WhitePoint',
884             Groups => { 2 => 'Camera' },
885             Writable => 'rational64u',
886             WriteGroup => 'IFD0',
887             Count => 2,
888             },
889             0x13f => {
890             Name => 'PrimaryChromaticities',
891             Writable => 'rational64u',
892             WriteGroup => 'IFD0',
893             Count => 6,
894             Priority => 0,
895             },
896             0x140 => {
897             Name => 'ColorMap',
898             Format => 'binary',
899             Binary => 1,
900             },
901             0x141 => {
902             Name => 'HalftoneHints',
903             Writable => 'int16u',
904             WriteGroup => 'IFD0',
905             Count => 2,
906             },
907             0x142 => {
908             Name => 'TileWidth',
909             Protected => 1,
910             Writable => 'int32u',
911             WriteGroup => 'IFD0',
912             },
913             0x143 => {
914             Name => 'TileLength',
915             Protected => 1,
916             Writable => 'int32u',
917             WriteGroup => 'IFD0',
918             },
919             0x144 => {
920             Name => 'TileOffsets',
921             IsOffset => 1,
922             OffsetPair => 0x145,
923             ValueConv => 'length($val) > 32 ? \$val : $val',
924             },
925             0x145 => {
926             Name => 'TileByteCounts',
927             OffsetPair => 0x144,
928             ValueConv => 'length($val) > 32 ? \$val : $val',
929             },
930             0x146 => 'BadFaxLines', #3
931             0x147 => { #3
932             Name => 'CleanFaxData',
933             PrintConv => {
934             0 => 'Clean',
935             1 => 'Regenerated',
936             2 => 'Unclean',
937             },
938             },
939             0x148 => 'ConsecutiveBadFaxLines', #3
940             0x14a => [
941             {
942             Name => 'SubIFD',
943             # use this opportunity to identify an ARW image, and if so we
944             # must decide if this is a SubIFD or the A100 raw data
945             # (use SubfileType, Compression and FILE_TYPE to identify ARW/SR2,
946             # then call SetARW to finish the job)
947             Condition => q{
948             $$self{DIR_NAME} ne 'IFD0' or $$self{FILE_TYPE} ne 'TIFF' or
949             $$self{Make} !~ /^SONY/ or
950             not $$self{SubfileType} or $$self{SubfileType} != 1 or
951             not $$self{Compression} or $$self{Compression} != 6 or
952             not require Image::ExifTool::Sony or
953             Image::ExifTool::Sony::SetARW($self, $valPt)
954             },
955             Groups => { 1 => 'SubIFD' },
956             Flags => 'SubIFD',
957             SubDirectory => {
958             Start => '$val',
959             MaxSubdirs => 10, # (have seen 5 in a DNG 1.4 image)
960             },
961             },
962             { #16
963             Name => 'A100DataOffset',
964             Notes => 'the data offset in original Sony DSLR-A100 ARW images',
965             DataMember => 'A100DataOffset',
966             RawConv => '$$self{A100DataOffset} = $val',
967             WriteGroup => 'IFD0', # (only for Validate)
968             IsOffset => 1,
969             Protected => 2,
970             },
971             ],
972             0x14c => {
973             Name => 'InkSet',
974             Writable => 'int16u',
975             WriteGroup => 'IFD0',
976             PrintConv => { #3
977             1 => 'CMYK',
978             2 => 'Not CMYK',
979             },
980             },
981             0x14d => 'InkNames', #3
982             0x14e => 'NumberofInks', #3
983             0x150 => 'DotRange',
984             0x151 => {
985             Name => 'TargetPrinter',
986             Writable => 'string',
987             WriteGroup => 'IFD0',
988             },
989             0x152 => {
990             Name => 'ExtraSamples',
991             PrintConv => { #20
992             0 => 'Unspecified',
993             1 => 'Associated Alpha',
994             2 => 'Unassociated Alpha',
995             },
996             },
997             0x153 => {
998             Name => 'SampleFormat',
999             Notes => 'SamplesPerPixel values',
1000             WriteGroup => 'SubIFD', # (only for Validate)
1001             PrintConvColumns => 2,
1002             PrintConv => [ \%sampleFormat, \%sampleFormat, \%sampleFormat, \%sampleFormat ],
1003             },
1004             0x154 => 'SMinSampleValue',
1005             0x155 => 'SMaxSampleValue',
1006             0x156 => 'TransferRange',
1007             0x157 => 'ClipPath', #3
1008             0x158 => 'XClipPathUnits', #3
1009             0x159 => 'YClipPathUnits', #3
1010             0x15a => { #3
1011             Name => 'Indexed',
1012             PrintConv => { 0 => 'Not indexed', 1 => 'Indexed' },
1013             },
1014             0x15b => {
1015             Name => 'JPEGTables',
1016             Binary => 1,
1017             },
1018             0x15f => { #10
1019             Name => 'OPIProxy',
1020             PrintConv => {
1021             0 => 'Higher resolution image does not exist',
1022             1 => 'Higher resolution image exists',
1023             },
1024             },
1025             # 0x181 => 'Decode', #20 (typo! - should be 0x1b1, ref 21)
1026             # 0x182 => 'DefaultImageColor', #20 (typo! - should be 0x1b2, ref 21)
1027             0x190 => { #3
1028             Name => 'GlobalParametersIFD',
1029             Groups => { 1 => 'GlobParamIFD' },
1030             Flags => 'SubIFD',
1031             SubDirectory => {
1032             DirName => 'GlobParamIFD',
1033             Start => '$val',
1034             MaxSubdirs => 1,
1035             },
1036             },
1037             0x191 => { #3
1038             Name => 'ProfileType',
1039             PrintConv => { 0 => 'Unspecified', 1 => 'Group 3 FAX' },
1040             },
1041             0x192 => { #3
1042             Name => 'FaxProfile',
1043             PrintConv => {
1044             0 => 'Unknown',
1045             1 => 'Minimal B&W lossless, S',
1046             2 => 'Extended B&W lossless, F',
1047             3 => 'Lossless JBIG B&W, J',
1048             4 => 'Lossy color and grayscale, C',
1049             5 => 'Lossless color and grayscale, L',
1050             6 => 'Mixed raster content, M',
1051             7 => 'Profile T', #20
1052             255 => 'Multi Profiles', #20
1053             },
1054             },
1055             0x193 => { #3
1056             Name => 'CodingMethods',
1057             PrintConv => { BITMASK => {
1058             0 => 'Unspecified compression',
1059             1 => 'Modified Huffman',
1060             2 => 'Modified Read',
1061             3 => 'Modified MR',
1062             4 => 'JBIG',
1063             5 => 'Baseline JPEG',
1064             6 => 'JBIG color',
1065             } },
1066             },
1067             0x194 => 'VersionYear', #3
1068             0x195 => 'ModeNumber', #3
1069             0x1b1 => 'Decode', #3
1070             0x1b2 => 'DefaultImageColor', #3 (changed to ImageBaseColor, ref 21)
1071             0x1b3 => 'T82Options', #20
1072             0x1b5 => { #19
1073             Name => 'JPEGTables',
1074             Binary => 1,
1075             },
1076             0x200 => {
1077             Name => 'JPEGProc',
1078             PrintConv => {
1079             1 => 'Baseline',
1080             14 => 'Lossless',
1081             },
1082             },
1083             0x201 => [
1084             {
1085             Name => 'ThumbnailOffset',
1086             Notes => q{
1087             called JPEGInterchangeFormat in the specification, this is ThumbnailOffset
1088             in IFD1 of JPEG and some TIFF-based images, IFD0 of MRW images and AVI and
1089             MOV videos, and the SubIFD in IFD1 of SRW images; PreviewImageStart in
1090             MakerNotes and IFD0 of ARW and SR2 images; JpgFromRawStart in SubIFD of NEF
1091             images and IFD2 of PEF images; and OtherImageStart in everything else
1092             },
1093             # thumbnail is found in IFD1 of JPEG and TIFF images, and
1094             # IFD0 of EXIF information in FujiFilm AVI (RIFF) and MOV videos
1095             Condition => q{
1096             # recognize NRW file from a JPEG-compressed thumbnail in IFD0
1097             if ($$self{TIFF_TYPE} eq 'NEF' and $$self{DIR_NAME} eq 'IFD0' and $$self{Compression} == 6) {
1098             $self->OverrideFileType($$self{TIFF_TYPE} = 'NRW');
1099             }
1100             $$self{DIR_NAME} eq 'IFD1' or
1101             ($$self{DIR_NAME} eq 'IFD0' and $$self{FILE_TYPE} =~ /^(RIFF|MOV)$/)
1102             },
1103             IsOffset => 1,
1104             OffsetPair => 0x202,
1105             DataTag => 'ThumbnailImage',
1106             Writable => 'int32u',
1107             WriteGroup => 'IFD1',
1108             # according to the EXIF spec. a JPEG-compressed thumbnail image may not
1109             # be stored in a TIFF file, but these TIFF-based RAW image formats
1110             # use IFD1 for a JPEG-compressed thumbnail: CR2, ARW, SR2 and PEF.
1111             # (SRF also stores a JPEG image in IFD1, but it is actually a preview
1112             # and we don't yet write SRF anyway)
1113             WriteCondition => q{
1114             $$self{FILE_TYPE} ne "TIFF" or
1115             $$self{TIFF_TYPE} =~ /^(CR2|ARW|SR2|PEF)$/
1116             },
1117             Protected => 2,
1118             },
1119             {
1120             Name => 'ThumbnailOffset',
1121             # thumbnail in IFD0 of MRW images (Minolta A200)
1122             # and IFD0 of NRW images (Nikon Coolpix P6000,P7000,P7100)
1123             Condition => '$$self{DIR_NAME} eq "IFD0" and $$self{TIFF_TYPE} =~ /^(MRW|NRW)$/',
1124             IsOffset => 1,
1125             OffsetPair => 0x202,
1126             # A200 uses the wrong base offset for this pointer!!
1127             WrongBase => '$$self{Model} =~ /^DiMAGE A200/ ? $$self{MRW_WrongBase} : undef',
1128             DataTag => 'ThumbnailImage',
1129             Writable => 'int32u',
1130             WriteGroup => 'IFD0',
1131             Protected => 2,
1132             Permanent => 1,
1133             },
1134             {
1135             Name => 'ThumbnailOffset',
1136             # in SubIFD of IFD1 in Samsung SRW images
1137             Condition => q{
1138             $$self{TIFF_TYPE} eq 'SRW' and $$self{DIR_NAME} eq 'SubIFD' and
1139             $$self{PATH}[-2] eq 'IFD1'
1140             },
1141             IsOffset => 1,
1142             OffsetPair => 0x202,
1143             DataTag => 'ThumbnailImage',
1144             Writable => 'int32u',
1145             WriteGroup => 'SubIFD',
1146             Protected => 2,
1147             Permanent => 1,
1148             },
1149             {
1150             Name => 'PreviewImageStart',
1151             Condition => '$$self{DIR_NAME} eq "MakerNotes"',
1152             IsOffset => 1,
1153             OffsetPair => 0x202,
1154             DataTag => 'PreviewImage',
1155             Writable => 'int32u',
1156             WriteGroup => 'MakerNotes',
1157             Protected => 2,
1158             Permanent => 1,
1159             },
1160             {
1161             Name => 'PreviewImageStart',
1162             # PreviewImage in IFD0 of ARW and SR2 files for all models
1163             Condition => '$$self{DIR_NAME} eq "IFD0" and $$self{TIFF_TYPE} =~ /^(ARW|SR2)$/',
1164             IsOffset => 1,
1165             OffsetPair => 0x202,
1166             DataTag => 'PreviewImage',
1167             Writable => 'int32u',
1168             WriteGroup => 'IFD0',
1169             Protected => 2,
1170             Permanent => 1,
1171             },
1172             {
1173             Name => 'JpgFromRawStart',
1174             Condition => '$$self{DIR_NAME} eq "SubIFD"',
1175             IsOffset => 1,
1176             OffsetPair => 0x202,
1177             DataTag => 'JpgFromRaw',
1178             Writable => 'int32u',
1179             WriteGroup => 'SubIFD',
1180             # JpgFromRaw is in SubIFD of NEF, NRW and SRW files
1181             Protected => 2,
1182             Permanent => 1,
1183             },
1184             {
1185             Name => 'JpgFromRawStart',
1186             Condition => '$$self{DIR_NAME} eq "IFD2"',
1187             IsOffset => 1,
1188             OffsetPair => 0x202,
1189             DataTag => 'JpgFromRaw',
1190             Writable => 'int32u',
1191             WriteGroup => 'IFD2',
1192             # JpgFromRaw is in IFD2 of PEF files
1193             Protected => 2,
1194             Permanent => 1,
1195             },
1196             {
1197             Name => 'OtherImageStart',
1198             Condition => '$$self{DIR_NAME} eq "SubIFD1"',
1199             IsOffset => 1,
1200             OffsetPair => 0x202,
1201             DataTag => 'OtherImage',
1202             Writable => 'int32u',
1203             WriteGroup => 'SubIFD1',
1204             Protected => 2,
1205             Permanent => 1,
1206             },
1207             {
1208             Name => 'OtherImageStart',
1209             Condition => '$$self{DIR_NAME} eq "SubIFD2"',
1210             IsOffset => 1,
1211             OffsetPair => 0x202,
1212             DataTag => 'OtherImage',
1213             Writable => 'int32u',
1214             WriteGroup => 'SubIFD2',
1215             Protected => 2,
1216             Permanent => 1,
1217             },
1218             {
1219             Name => 'OtherImageStart',
1220             IsOffset => 1,
1221             OffsetPair => 0x202,
1222             },
1223             ],
1224             0x202 => [
1225             {
1226             Name => 'ThumbnailLength',
1227             Notes => q{
1228             called JPEGInterchangeFormatLength in the specification, this is
1229             ThumbnailLength in IFD1 of JPEG and some TIFF-based images, IFD0 of MRW
1230             images and AVI and MOV videos, and the SubIFD in IFD1 of SRW images;
1231             PreviewImageLength in MakerNotes and IFD0 of ARW and SR2 images;
1232             JpgFromRawLength in SubIFD of NEF images, and IFD2 of PEF images; and
1233             OtherImageLength in everything else
1234             },
1235             Condition => q{
1236             $$self{DIR_NAME} eq 'IFD1' or
1237             ($$self{DIR_NAME} eq 'IFD0' and $$self{FILE_TYPE} =~ /^(RIFF|MOV)$/)
1238             },
1239             OffsetPair => 0x201,
1240             DataTag => 'ThumbnailImage',
1241             Writable => 'int32u',
1242             WriteGroup => 'IFD1',
1243             WriteCondition => q{
1244             $$self{FILE_TYPE} ne "TIFF" or
1245             $$self{TIFF_TYPE} =~ /^(CR2|ARW|SR2|PEF)$/
1246             },
1247             Protected => 2,
1248             },
1249             {
1250             Name => 'ThumbnailLength',
1251             # thumbnail in IFD0 of MRW images (Minolta A200)
1252             # and IFD0 of NRW images (Nikon Coolpix P6000,P7000,P7100)
1253             Condition => '$$self{DIR_NAME} eq "IFD0" and $$self{TIFF_TYPE} =~ /^(MRW|NRW)$/',
1254             OffsetPair => 0x201,
1255             DataTag => 'ThumbnailImage',
1256             Writable => 'int32u',
1257             WriteGroup => 'IFD0',
1258             Protected => 2,
1259             Permanent => 1,
1260             },
1261             {
1262             Name => 'ThumbnailLength',
1263             # in SubIFD of IFD1 in Samsung SRW images
1264             Condition => q{
1265             $$self{TIFF_TYPE} eq 'SRW' and $$self{DIR_NAME} eq 'SubIFD' and
1266             $$self{PATH}[-2] eq 'IFD1'
1267             },
1268             OffsetPair => 0x201,
1269             DataTag => 'ThumbnailImage',
1270             Writable => 'int32u',
1271             WriteGroup => 'SubIFD',
1272             Protected => 2,
1273             Permanent => 1,
1274             },
1275             {
1276             Name => 'PreviewImageLength',
1277             Condition => '$$self{DIR_NAME} eq "MakerNotes"',
1278             OffsetPair => 0x201,
1279             DataTag => 'PreviewImage',
1280             Writable => 'int32u',
1281             WriteGroup => 'MakerNotes',
1282             Protected => 2,
1283             Permanent => 1,
1284             },
1285             {
1286             Name => 'PreviewImageLength',
1287             # PreviewImage in IFD0 of ARW and SR2 files for all models
1288             Condition => '$$self{DIR_NAME} eq "IFD0" and $$self{TIFF_TYPE} =~ /^(ARW|SR2)$/',
1289             OffsetPair => 0x201,
1290             DataTag => 'PreviewImage',
1291             Writable => 'int32u',
1292             WriteGroup => 'IFD0',
1293             Protected => 2,
1294             Permanent => 1,
1295             },
1296             {
1297             Name => 'JpgFromRawLength',
1298             Condition => '$$self{DIR_NAME} eq "SubIFD"',
1299             OffsetPair => 0x201,
1300             DataTag => 'JpgFromRaw',
1301             Writable => 'int32u',
1302             WriteGroup => 'SubIFD',
1303             Protected => 2,
1304             Permanent => 1,
1305             },
1306             {
1307             Name => 'JpgFromRawLength',
1308             Condition => '$$self{DIR_NAME} eq "IFD2"',
1309             OffsetPair => 0x201,
1310             DataTag => 'JpgFromRaw',
1311             Writable => 'int32u',
1312             WriteGroup => 'IFD2',
1313             Protected => 2,
1314             Permanent => 1,
1315             },
1316             {
1317             Name => 'OtherImageLength',
1318             Condition => '$$self{DIR_NAME} eq "SubIFD1"',
1319             OffsetPair => 0x201,
1320             DataTag => 'OtherImage',
1321             Writable => 'int32u',
1322             WriteGroup => 'SubIFD1',
1323             Protected => 2,
1324             Permanent => 1,
1325             },
1326             {
1327             Name => 'OtherImageLength',
1328             Condition => '$$self{DIR_NAME} eq "SubIFD2"',
1329             OffsetPair => 0x201,
1330             DataTag => 'OtherImage',
1331             Writable => 'int32u',
1332             WriteGroup => 'SubIFD2',
1333             Protected => 2,
1334             Permanent => 1,
1335             },
1336             {
1337             Name => 'OtherImageLength',
1338             OffsetPair => 0x201,
1339             },
1340             ],
1341             0x203 => 'JPEGRestartInterval',
1342             0x205 => 'JPEGLosslessPredictors',
1343             0x206 => 'JPEGPointTransforms',
1344             0x207 => {
1345             Name => 'JPEGQTables',
1346             IsOffset => 1,
1347             # this tag is not supported for writing, so define an
1348             # invalid offset pair to cause a "No size tag" error to be
1349             # generated if we try to write a file containing this tag
1350             OffsetPair => -1,
1351             },
1352             0x208 => {
1353             Name => 'JPEGDCTables',
1354             IsOffset => 1,
1355             OffsetPair => -1, # (see comment for JPEGQTables)
1356             },
1357             0x209 => {
1358             Name => 'JPEGACTables',
1359             IsOffset => 1,
1360             OffsetPair => -1, # (see comment for JPEGQTables)
1361             },
1362             0x211 => {
1363             Name => 'YCbCrCoefficients',
1364             Protected => 1,
1365             Writable => 'rational64u',
1366             WriteGroup => 'IFD0',
1367             Count => 3,
1368             Priority => 0,
1369             },
1370             0x212 => {
1371             Name => 'YCbCrSubSampling',
1372             Protected => 1,
1373             Writable => 'int16u',
1374             WriteGroup => 'IFD0',
1375             Count => 2,
1376             PrintConvColumns => 2,
1377             PrintConv => \%Image::ExifTool::JPEG::yCbCrSubSampling,
1378             Priority => 0,
1379             },
1380             0x213 => {
1381             Name => 'YCbCrPositioning',
1382             Protected => 1,
1383             Writable => 'int16u',
1384             WriteGroup => 'IFD0',
1385             Mandatory => 1,
1386             PrintConv => {
1387             1 => 'Centered',
1388             2 => 'Co-sited',
1389             },
1390             Priority => 0,
1391             },
1392             0x214 => {
1393             Name => 'ReferenceBlackWhite',
1394             Writable => 'rational64u',
1395             WriteGroup => 'IFD0',
1396             Count => 6,
1397             Priority => 0,
1398             },
1399             # 0x220 - int32u: 0 (IFD0, Xaiomi Redmi models)
1400             # 0x221 - int32u: 0 (IFD0, Xaiomi Redmi models)
1401             # 0x222 - int32u: 0 (IFD0, Xaiomi Redmi models)
1402             # 0x223 - int32u: 0 (IFD0, Xaiomi Redmi models)
1403             # 0x224 - int32u: 0,1 (IFD0, Xaiomi Redmi models)
1404             # 0x225 - string: "" (IFD0, Xaiomi Redmi models)
1405             0x22f => 'StripRowCounts',
1406             0x2bc => {
1407             Name => 'ApplicationNotes', # (writable directory!)
1408             Format => 'undef',
1409             Writable => 'int8u',
1410             WriteGroup => 'IFD0', # (only for Validate)
1411             Flags => [ 'Binary', 'Protected' ],
1412             # this could be an XMP block
1413             SubDirectory => {
1414             DirName => 'XMP',
1415             TagTable => 'Image::ExifTool::XMP::Main',
1416             },
1417             },
1418             0x3e7 => 'USPTOMiscellaneous', #20
1419             0x1000 => { #5
1420             Name => 'RelatedImageFileFormat',
1421             Protected => 1,
1422             Writable => 'string',
1423             WriteGroup => 'InteropIFD',
1424             },
1425             0x1001 => { #5
1426             Name => 'RelatedImageWidth',
1427             Protected => 1,
1428             Writable => 'int16u',
1429             WriteGroup => 'InteropIFD',
1430             },
1431             0x1002 => { #5
1432             Name => 'RelatedImageHeight',
1433             Notes => 'called RelatedImageLength by the DCF spec.',
1434             Protected => 1,
1435             Writable => 'int16u',
1436             WriteGroup => 'InteropIFD',
1437             },
1438             # (0x474x tags written by MicrosoftPhoto)
1439             0x4746 => { #PH
1440             Name => 'Rating',
1441             Writable => 'int16u',
1442             WriteGroup => 'IFD0',
1443             Avoid => 1,
1444             },
1445             0x4747 => { # (written by Digital Image Pro)
1446             Name => 'XP_DIP_XML',
1447             Format => 'undef',
1448             # the following reference indicates this is Unicode:
1449             # http://social.msdn.microsoft.com/Forums/en-US/isvvba/thread/ce6edcbb-8fc2-40c6-ad98-85f5d835ddfb
1450             ValueConv => '$self->Decode($val,"UCS2","II")',
1451             },
1452             0x4748 => {
1453             Name => 'StitchInfo',
1454             SubDirectory => {
1455             TagTable => 'Image::ExifTool::Microsoft::Stitch',
1456             ByteOrder => 'LittleEndian', #PH (NC)
1457             },
1458             },
1459             0x4749 => { #PH
1460             Name => 'RatingPercent',
1461             Writable => 'int16u',
1462             WriteGroup => 'IFD0',
1463             Avoid => 1,
1464             },
1465             0x7000 => { #JR
1466             Name => 'SonyRawFileType',
1467             # (only valid if Sony:FileFormat >= ARW 2.0, ref IB)
1468             # Writable => 'int16u', (don't allow writes for now)
1469             PrintConv => {
1470             0 => 'Sony Uncompressed 14-bit RAW',
1471             1 => 'Sony Uncompressed 12-bit RAW', #IB
1472             2 => 'Sony Compressed RAW', # (lossy, ref IB)
1473             3 => 'Sony Lossless Compressed RAW', #IB
1474             4 => 'Sony Lossless Compressed RAW 2', #JR (ILCE-1)
1475             },
1476             },
1477             # 0x7001 - int16u[1] (in SubIFD of Sony ARW images) - values: 0,1
1478             0x7010 => { #IB
1479             Name => 'SonyToneCurve',
1480             # int16u[4] (in SubIFD of Sony ARW images -- don't allow writes for now)
1481             # - only the middle 4 points are stored (lower comes from black level,
1482             # and upper from data maximum)
1483             },
1484             # 0x7011 - int16u[4] (in SubIFD of Sony ARW images) - values: "0 4912 8212 12287","4000 7200 10050 12075"
1485             # 0x7020 - int32u[1] (in SubIFD of Sony ARW images) - values: 0,3
1486             0x7031 => {
1487             Name => 'VignettingCorrection',
1488             Notes => 'found in Sony ARW images',
1489             Protected => 1,
1490             Writable => 'int16s',
1491             WriteGroup => 'SubIFD',
1492             PrintConv => {
1493             256 => 'Off',
1494             257 => 'Auto',
1495             272 => 'Auto (ILCE-1)', #JR
1496             511 => 'No correction params available',
1497             },
1498             },
1499             0x7032 => {
1500             Name => 'VignettingCorrParams', #forum7640
1501             Notes => 'found in Sony ARW images',
1502             Protected => 1,
1503             Writable => 'int16s',
1504             WriteGroup => 'SubIFD',
1505             Count => 17,
1506             },
1507             0x7034 => {
1508             Name => 'ChromaticAberrationCorrection',
1509             Notes => 'found in Sony ARW images',
1510             Protected => 1,
1511             Writable => 'int16s',
1512             WriteGroup => 'SubIFD',
1513             PrintConv => {
1514             0 => 'Off',
1515             1 => 'Auto',
1516             255 => 'No correction params available',
1517             },
1518             },
1519             0x7035 => {
1520             Name => 'ChromaticAberrationCorrParams', #forum6509
1521             Notes => 'found in Sony ARW images',
1522             Protected => 1,
1523             Writable => 'int16s',
1524             WriteGroup => 'SubIFD',
1525             Count => 33,
1526             },
1527             0x7036 => {
1528             Name => 'DistortionCorrection',
1529             Notes => 'found in Sony ARW images',
1530             Protected => 1,
1531             Writable => 'int16s',
1532             WriteGroup => 'SubIFD',
1533             PrintConv => {
1534             0 => 'Off',
1535             1 => 'Auto',
1536             17 => 'Auto fixed by lens',
1537             255 => 'No correction params available',
1538             },
1539             },
1540             0x7037 => {
1541             Name => 'DistortionCorrParams', #forum6509
1542             Notes => 'found in Sony ARW images',
1543             Protected => 1,
1544             Writable => 'int16s',
1545             WriteGroup => 'SubIFD',
1546             Count => 17,
1547             },
1548             0x74c7 => { #IB (in ARW images from some Sony cameras)
1549             Name => 'SonyCropTopLeft',
1550             Writable => 'int32u',
1551             WriteGroup => 'SubIFD',
1552             Count => 2,
1553             Permanent => 1,
1554             Protected => 1,
1555             },
1556             0x74c8 => { #IB (in ARW images from some Sony cameras)
1557             Name => 'SonyCropSize',
1558             Writable => 'int32u',
1559             WriteGroup => 'SubIFD',
1560             Count => 2,
1561             Permanent => 1,
1562             Protected => 1,
1563             },
1564             0x800d => 'ImageID', #10
1565             0x80a3 => { Name => 'WangTag1', Binary => 1 }, #20
1566             0x80a4 => { Name => 'WangAnnotation', Binary => 1 },
1567             0x80a5 => { Name => 'WangTag3', Binary => 1 }, #20
1568             0x80a6 => { #20
1569             Name => 'WangTag4',
1570             PrintConv => 'length($val) <= 64 ? $val : \$val',
1571             },
1572             # tags 0x80b8-0x80bc are registered to Island Graphics
1573             0x80b9 => 'ImageReferencePoints', #29
1574             0x80ba => 'RegionXformTackPoint', #29
1575             0x80bb => 'WarpQuadrilateral', #29
1576             0x80bc => 'AffineTransformMat', #29
1577             0x80e3 => 'Matteing', #9
1578             0x80e4 => 'DataType', #9
1579             0x80e5 => 'ImageDepth', #9
1580             0x80e6 => 'TileDepth', #9
1581             # tags 0x8214-0x8219 are registered to Pixar
1582             0x8214 => 'ImageFullWidth', #29
1583             0x8215 => 'ImageFullHeight', #29
1584             0x8216 => 'TextureFormat', #29
1585             0x8217 => 'WrapModes', #29
1586             0x8218 => 'FovCot', #29
1587             0x8219 => 'MatrixWorldToScreen', #29
1588             0x821a => 'MatrixWorldToCamera', #29
1589             0x827d => 'Model2', #29 (Eastman Kodak)
1590             0x828d => { #12
1591             Name => 'CFARepeatPatternDim',
1592             Protected => 1,
1593             Writable => 'int16u',
1594             WriteGroup => 'SubIFD',
1595             Count => 2,
1596             },
1597             0x828e => {
1598             Name => 'CFAPattern2', #12
1599             Format => 'int8u', # (written incorrectly as 'undef' in Nikon NRW images)
1600             Protected => 1,
1601             Writable => 'int8u',
1602             WriteGroup => 'SubIFD',
1603             Count => -1,
1604             },
1605             0x828f => { #12
1606             Name => 'BatteryLevel',
1607             Groups => { 2 => 'Camera' },
1608             },
1609             0x8290 => {
1610             Name => 'KodakIFD',
1611             Groups => { 1 => 'KodakIFD' },
1612             Flags => 'SubIFD',
1613             Notes => 'used in various types of Kodak images',
1614             SubDirectory => {
1615             TagTable => 'Image::ExifTool::Kodak::IFD',
1616             DirName => 'KodakIFD',
1617             Start => '$val',
1618             MaxSubdirs => 1,
1619             },
1620             },
1621             0x8298 => {
1622             Name => 'Copyright',
1623             Groups => { 2 => 'Author' },
1624             Format => 'undef',
1625             Writable => 'string',
1626             WriteGroup => 'IFD0',
1627             Notes => q{
1628             may contain copyright notices for photographer and editor, separated by a
1629             newline. As per the EXIF specification, the newline is replaced by a null
1630             byte when writing to file, but this may be avoided by disabling the print
1631             conversion
1632             },
1633             # internally the strings are separated by a null character in this format:
1634             # Photographer only: photographer + NULL
1635             # Both: photographer + NULL + editor + NULL
1636             # Editor only: SPACE + NULL + editor + NULL
1637             # (this is done as a RawConv so conditional replaces will work properly)
1638             RawConv => sub {
1639             my ($val, $self) = @_;
1640             $val =~ s/ *\0/\n/; # translate first NULL to a newline, removing trailing blanks
1641             $val =~ s/ *\0.*//s; # truncate at second NULL and remove trailing blanks
1642             $val =~ s/\n$//; # remove trailing newline if it exists
1643             # decode if necessary (note: this is the only non-'string' EXIF value like this)
1644             my $enc = $self->Options('CharsetEXIF');
1645             $val = $self->Decode($val,$enc) if $enc;
1646             return $val;
1647             },
1648             RawConvInv => '$val . "\0"',
1649             PrintConvInv => sub {
1650             my ($val, $self) = @_;
1651             # encode if necessary (not automatic because Format is 'undef')
1652             my $enc = $self->Options('CharsetEXIF');
1653             $val = $self->Encode($val,$enc) if $enc and $val !~ /\0/;
1654             if ($val =~ /(.*?)\s*[\n\r]+\s*(.*)/s) {
1655             return $1 unless length $2;
1656             # photographer copyright set to ' ' if it doesn't exist, according to spec.
1657             return((length($1) ? $1 : ' ') . "\0" . $2);
1658             }
1659             return $val;
1660             },
1661             },
1662             0x829a => {
1663             Name => 'ExposureTime',
1664             Writable => 'rational64u',
1665             PrintConv => 'Image::ExifTool::Exif::PrintExposureTime($val)',
1666             PrintConvInv => '$val',
1667             },
1668             0x829d => {
1669             Name => 'FNumber',
1670             Writable => 'rational64u',
1671             PrintConv => 'Image::ExifTool::Exif::PrintFNumber($val)',
1672             PrintConvInv => '$val',
1673             },
1674             0x82a5 => { #3
1675             Name => 'MDFileTag',
1676             Notes => 'tags 0x82a5-0x82ac are used in Molecular Dynamics GEL files',
1677             },
1678             0x82a6 => 'MDScalePixel', #3
1679             0x82a7 => 'MDColorTable', #3
1680             0x82a8 => 'MDLabName', #3
1681             0x82a9 => 'MDSampleInfo', #3
1682             0x82aa => 'MDPrepDate', #3
1683             0x82ab => 'MDPrepTime', #3
1684             0x82ac => 'MDFileUnits', #3
1685             0x830e => { #30 (GeoTiff)
1686             Name => 'PixelScale',
1687             Writable => 'double',
1688             WriteGroup => 'IFD0',
1689             Count => 3,
1690             },
1691             0x8335 => 'AdventScale', #20
1692             0x8336 => 'AdventRevision', #20
1693             0x835c => 'UIC1Tag', #23
1694             0x835d => 'UIC2Tag', #23
1695             0x835e => 'UIC3Tag', #23
1696             0x835f => 'UIC4Tag', #23
1697             0x83bb => { #12
1698             Name => 'IPTC-NAA', # (writable directory! -- but see note below)
1699             # this should actually be written as 'undef' (see
1700             # http://www.awaresystems.be/imaging/tiff/tifftags/iptc.html),
1701             # but Photoshop writes it as int32u and Nikon Capture won't read
1702             # anything else, so we do the same thing here... Doh!
1703             Format => 'undef', # convert binary values as undef
1704             Writable => 'int32u', # but write int32u format code in IFD
1705             WriteGroup => 'IFD0',
1706             Flags => [ 'Binary', 'Protected' ],
1707             SubDirectory => {
1708             DirName => 'IPTC',
1709             TagTable => 'Image::ExifTool::IPTC::Main',
1710             },
1711             # Note: This directory may be written as a block via the IPTC-NAA tag,
1712             # but this technique is not recommended. Instead, it is better to
1713             # write the Extra IPTC tag and let ExifTool decide where it should go.
1714             },
1715             0x847e => 'IntergraphPacketData', #3
1716             0x847f => 'IntergraphFlagRegisters', #3
1717             0x8480 => { #30 (GeoTiff, obsolete)
1718             Name => 'IntergraphMatrix',
1719             Writable => 'double',
1720             WriteGroup => 'IFD0',
1721             Count => -1,
1722             },
1723             0x8481 => 'INGRReserved', #20
1724             0x8482 => { #30 (GeoTiff)
1725             Name => 'ModelTiePoint',
1726             Groups => { 2 => 'Location' },
1727             Writable => 'double',
1728             WriteGroup => 'IFD0',
1729             Count => -1,
1730             },
1731             0x84e0 => 'Site', #9
1732             0x84e1 => 'ColorSequence', #9
1733             0x84e2 => 'IT8Header', #9
1734             0x84e3 => { #9
1735             Name => 'RasterPadding',
1736             PrintConv => { #20
1737             0 => 'Byte',
1738             1 => 'Word',
1739             2 => 'Long Word',
1740             9 => 'Sector',
1741             10 => 'Long Sector',
1742             },
1743             },
1744             0x84e4 => 'BitsPerRunLength', #9
1745             0x84e5 => 'BitsPerExtendedRunLength', #9
1746             0x84e6 => 'ColorTable', #9
1747             0x84e7 => { #9
1748             Name => 'ImageColorIndicator',
1749             PrintConv => { #20
1750             0 => 'Unspecified Image Color',
1751             1 => 'Specified Image Color',
1752             },
1753             },
1754             0x84e8 => { #9
1755             Name => 'BackgroundColorIndicator',
1756             PrintConv => { #20
1757             0 => 'Unspecified Background Color',
1758             1 => 'Specified Background Color',
1759             },
1760             },
1761             0x84e9 => 'ImageColorValue', #9
1762             0x84ea => 'BackgroundColorValue', #9
1763             0x84eb => 'PixelIntensityRange', #9
1764             0x84ec => 'TransparencyIndicator', #9
1765             0x84ed => 'ColorCharacterization', #9
1766             0x84ee => { #9
1767             Name => 'HCUsage',
1768             PrintConv => { #20
1769             0 => 'CT',
1770             1 => 'Line Art',
1771             2 => 'Trap',
1772             },
1773             },
1774             0x84ef => 'TrapIndicator', #17
1775             0x84f0 => 'CMYKEquivalent', #17
1776             0x8546 => { #11
1777             Name => 'SEMInfo',
1778             Notes => 'found in some scanning electron microscope images',
1779             Writable => 'string',
1780             WriteGroup => 'IFD0',
1781             },
1782             0x8568 => {
1783             Name => 'AFCP_IPTC',
1784             SubDirectory => {
1785             # must change directory name so we don't create this directory
1786             DirName => 'AFCP_IPTC',
1787             TagTable => 'Image::ExifTool::IPTC::Main',
1788             },
1789             },
1790             0x85b8 => 'PixelMagicJBIGOptions', #20
1791             0x85d7 => 'JPLCartoIFD', #exifprobe (NC)
1792             0x85d8 => { #30 (GeoTiff)
1793             Name => 'ModelTransform',
1794             Groups => { 2 => 'Location' },
1795             Writable => 'double',
1796             WriteGroup => 'IFD0',
1797             Count => 16,
1798             },
1799             0x8602 => { #16
1800             Name => 'WB_GRGBLevels',
1801             Notes => 'found in IFD0 of Leaf MOS images',
1802             },
1803             # 0x8603 - Leaf CatchLight color matrix (ref 16)
1804             0x8606 => {
1805             Name => 'LeafData',
1806             Format => 'undef', # avoid converting huge block to string of int8u's!
1807             SubDirectory => {
1808             DirName => 'LeafIFD',
1809             TagTable => 'Image::ExifTool::Leaf::Main',
1810             },
1811             },
1812             0x8649 => { #19
1813             Name => 'PhotoshopSettings',
1814             Format => 'binary',
1815             WriteGroup => 'IFD0', # (only for Validate)
1816             SubDirectory => {
1817             DirName => 'Photoshop',
1818             TagTable => 'Image::ExifTool::Photoshop::Main',
1819             },
1820             },
1821             0x8769 => {
1822             Name => 'ExifOffset',
1823             Groups => { 1 => 'ExifIFD' },
1824             WriteGroup => 'IFD0', # (only for Validate)
1825             SubIFD => 2,
1826             SubDirectory => {
1827             DirName => 'ExifIFD',
1828             Start => '$val',
1829             },
1830             },
1831             0x8773 => {
1832             Name => 'ICC_Profile',
1833             WriteGroup => 'IFD0', # (only for Validate)
1834             SubDirectory => {
1835             TagTable => 'Image::ExifTool::ICC_Profile::Main',
1836             },
1837             },
1838             0x877f => { #20
1839             Name => 'TIFF_FXExtensions',
1840             PrintConv => { BITMASK => {
1841             0 => 'Resolution/Image Width',
1842             1 => 'N Layer Profile M',
1843             2 => 'Shared Data',
1844             3 => 'B&W JBIG2',
1845             4 => 'JBIG2 Profile M',
1846             }},
1847             },
1848             0x8780 => { #20
1849             Name => 'MultiProfiles',
1850             PrintConv => { BITMASK => {
1851             0 => 'Profile S',
1852             1 => 'Profile F',
1853             2 => 'Profile J',
1854             3 => 'Profile C',
1855             4 => 'Profile L',
1856             5 => 'Profile M',
1857             6 => 'Profile T',
1858             7 => 'Resolution/Image Width',
1859             8 => 'N Layer Profile M',
1860             9 => 'Shared Data',
1861             10 => 'JBIG2 Profile M',
1862             }},
1863             },
1864             0x8781 => { #22
1865             Name => 'SharedData',
1866             IsOffset => 1,
1867             # this tag is not supported for writing, so define an
1868             # invalid offset pair to cause a "No size tag" error to be
1869             # generated if we try to write a file containing this tag
1870             OffsetPair => -1,
1871             },
1872             0x8782 => 'T88Options', #20
1873             0x87ac => 'ImageLayer',
1874             0x87af => { #30
1875             Name => 'GeoTiffDirectory',
1876             Format => 'undef',
1877             Writable => 'int16u',
1878             Notes => q{
1879             these "GeoTiff" tags may read and written as a block, but they aren't
1880             extracted unless specifically requested. Byte order changes are handled
1881             automatically when copying between TIFF images with different byte order
1882             },
1883             WriteGroup => 'IFD0',
1884             Binary => 1,
1885             RawConv => '$val . GetByteOrder()', # save byte order
1886             # swap byte order if necessary
1887             RawConvInv => q{
1888             return $val if length $val < 2;
1889             my $order = substr($val, -2);
1890             return $val unless $order eq 'II' or $order eq 'MM';
1891             $val = substr($val, 0, -2);
1892             return $val if $order eq GetByteOrder();
1893             return pack('v*',unpack('n*',$val));
1894             },
1895             },
1896             0x87b0 => { #30
1897             Name => 'GeoTiffDoubleParams',
1898             Format => 'undef',
1899             Writable => 'double',
1900             WriteGroup => 'IFD0',
1901             Binary => 1,
1902             RawConv => '$val . GetByteOrder()', # save byte order
1903             # swap byte order if necessary
1904             RawConvInv => q{
1905             return $val if length $val < 2;
1906             my $order = substr($val, -2);
1907             return $val unless $order eq 'II' or $order eq 'MM';
1908             $val = substr($val, 0, -2);
1909             return $val if $order eq GetByteOrder();
1910             $val =~ s/(.{4})(.{4})/$2$1/sg; # swap words
1911             return pack('V*',unpack('N*',$val));
1912             },
1913             },
1914             0x87b1 => { #30
1915             Name => 'GeoTiffAsciiParams',
1916             Format => 'undef',
1917             Writable => 'string',
1918             WriteGroup => 'IFD0',
1919             Binary => 1,
1920             },
1921             0x87be => 'JBIGOptions', #29
1922             0x8822 => {
1923             Name => 'ExposureProgram',
1924             Groups => { 2 => 'Camera' },
1925             Notes => 'the value of 9 is not standard EXIF, but is used by the Canon EOS 7D',
1926             Writable => 'int16u',
1927             PrintConv => {
1928             0 => 'Not Defined',
1929             1 => 'Manual',
1930             2 => 'Program AE',
1931             3 => 'Aperture-priority AE',
1932             4 => 'Shutter speed priority AE',
1933             5 => 'Creative (Slow speed)',
1934             6 => 'Action (High speed)',
1935             7 => 'Portrait',
1936             8 => 'Landscape',
1937             9 => 'Bulb', #25
1938             },
1939             },
1940             0x8824 => {
1941             Name => 'SpectralSensitivity',
1942             Groups => { 2 => 'Camera' },
1943             Writable => 'string',
1944             },
1945             0x8825 => {
1946             Name => 'GPSInfo',
1947             Groups => { 1 => 'GPS' },
1948             WriteGroup => 'IFD0', # (only for Validate)
1949             Flags => 'SubIFD',
1950             SubDirectory => {
1951             DirName => 'GPS',
1952             TagTable => 'Image::ExifTool::GPS::Main',
1953             Start => '$val',
1954             MaxSubdirs => 1,
1955             },
1956             },
1957             0x8827 => {
1958             Name => 'ISO',
1959             Notes => q{
1960             called ISOSpeedRatings by EXIF 2.2, then PhotographicSensitivity by the EXIF
1961             2.3 spec.
1962             },
1963             Writable => 'int16u',
1964             Count => -1,
1965             PrintConv => '$val=~s/\s+/, /g; $val',
1966             PrintConvInv => '$val=~tr/,//d; $val',
1967             },
1968             0x8828 => {
1969             Name => 'Opto-ElectricConvFactor',
1970             Notes => 'called OECF by the EXIF spec.',
1971             Binary => 1,
1972             },
1973             0x8829 => 'Interlace', #12
1974             0x882a => { #12
1975             Name => 'TimeZoneOffset',
1976             Writable => 'int16s',
1977             Count => -1, # can be 1 or 2
1978             Notes => q{
1979             1 or 2 values: 1. The time zone offset of DateTimeOriginal from GMT in
1980             hours, 2. If present, the time zone offset of ModifyDate
1981             },
1982             },
1983             0x882b => { #12
1984             Name => 'SelfTimerMode',
1985             Writable => 'int16u',
1986             },
1987             0x8830 => { #24
1988             Name => 'SensitivityType',
1989             Notes => 'applies to EXIF:ISO tag',
1990             Writable => 'int16u',
1991             PrintConv => {
1992             0 => 'Unknown',
1993             1 => 'Standard Output Sensitivity',
1994             2 => 'Recommended Exposure Index',
1995             3 => 'ISO Speed',
1996             4 => 'Standard Output Sensitivity and Recommended Exposure Index',
1997             5 => 'Standard Output Sensitivity and ISO Speed',
1998             6 => 'Recommended Exposure Index and ISO Speed',
1999             7 => 'Standard Output Sensitivity, Recommended Exposure Index and ISO Speed',
2000             },
2001             },
2002             0x8831 => { #24
2003             Name => 'StandardOutputSensitivity',
2004             Writable => 'int32u',
2005             },
2006             0x8832 => { #24
2007             Name => 'RecommendedExposureIndex',
2008             Writable => 'int32u',
2009             },
2010             0x8833 => { #24
2011             Name => 'ISOSpeed',
2012             Writable => 'int32u',
2013             },
2014             0x8834 => { #24
2015             Name => 'ISOSpeedLatitudeyyy',
2016             Description => 'ISO Speed Latitude yyy',
2017             Writable => 'int32u',
2018             },
2019             0x8835 => { #24
2020             Name => 'ISOSpeedLatitudezzz',
2021             Description => 'ISO Speed Latitude zzz',
2022             Writable => 'int32u',
2023             },
2024             0x885c => 'FaxRecvParams', #9
2025             0x885d => 'FaxSubAddress', #9
2026             0x885e => 'FaxRecvTime', #9
2027             0x8871 => 'FedexEDR', #exifprobe (NC)
2028             # 0x8889 - string: "portrait" (ExifIFD, Xiaomi POCO F1)
2029             0x888a => { #PH
2030             Name => 'LeafSubIFD',
2031             Format => 'int32u', # Leaf incorrectly uses 'undef' format!
2032             Groups => { 1 => 'LeafSubIFD' },
2033             Flags => 'SubIFD',
2034             SubDirectory => {
2035             TagTable => 'Image::ExifTool::Leaf::SubIFD',
2036             Start => '$val',
2037             },
2038             },
2039             # 0x8891 - int16u: 35 (ExifIFD, Xiaomi POCO F1)
2040             # 0x8894 - int16u: 0 (ExifIFD, Xiaomi POCO F1)
2041             # 0x8895 - int16u: 0 (ExifIFD, Xiaomi POCO F1)
2042             # 0x889a - int16u: 0 (ExifIFD, Xiaomi POCO F1)
2043             # 0x89ab - seen "11 100 130 16 0 0 0 0" in IFD0 of TIFF image from IR scanner (forum8470)
2044             0x9000 => {
2045             Name => 'ExifVersion',
2046             Writable => 'undef',
2047             Mandatory => 1,
2048             RawConv => '$val=~s/\0+$//; $val', # (some idiots add null terminators)
2049             # (allow strings like "2.31" when writing)
2050             PrintConvInv => '$val=~tr/.//d; $val=~/^\d{4}$/ ? $val : $val =~ /^\d{3}$/ ? "0$val" : undef',
2051             },
2052             0x9003 => {
2053             Name => 'DateTimeOriginal',
2054             Description => 'Date/Time Original',
2055             Groups => { 2 => 'Time' },
2056             Notes => 'date/time when original image was taken',
2057             Writable => 'string',
2058             Shift => 'Time',
2059             Validate => 'ValidateExifDate($val)',
2060             PrintConv => '$self->ConvertDateTime($val)',
2061             PrintConvInv => '$self->InverseDateTime($val,0)',
2062             },
2063             0x9004 => {
2064             Name => 'CreateDate',
2065             Groups => { 2 => 'Time' },
2066             Notes => 'called DateTimeDigitized by the EXIF spec.',
2067             Writable => 'string',
2068             Shift => 'Time',
2069             Validate => 'ValidateExifDate($val)',
2070             PrintConv => '$self->ConvertDateTime($val)',
2071             PrintConvInv => '$self->InverseDateTime($val,0)',
2072             },
2073             0x9009 => { # undef[44] (or undef[11]) written by Google Plus uploader - PH
2074             Name => 'GooglePlusUploadCode',
2075             Format => 'int8u',
2076             Writable => 'undef',
2077             Count => -1,
2078             },
2079             0x9010 => {
2080             Name => 'OffsetTime',
2081             Groups => { 2 => 'Time' },
2082             Notes => 'time zone for ModifyDate',
2083             Writable => 'string',
2084             Shift => 'Time',
2085             PrintConvInv => q{
2086             return "+00:00" if $val =~ /\d{2}Z$/;
2087             return sprintf("%s%.2d:%.2d",$1,$2,$3) if $val =~ /([-+])(\d{1,2}):(\d{2})/;
2088             return undef;
2089             },
2090             },
2091             0x9011 => {
2092             Name => 'OffsetTimeOriginal',
2093             Groups => { 2 => 'Time' },
2094             Notes => 'time zone for DateTimeOriginal',
2095             Writable => 'string',
2096             Shift => 'Time',
2097             PrintConvInv => q{
2098             return "+00:00" if $val =~ /\d{2}Z$/;
2099             return sprintf("%s%.2d:%.2d",$1,$2,$3) if $val =~ /([-+])(\d{1,2}):(\d{2})/;
2100             return undef;
2101             },
2102             },
2103             0x9012 => {
2104             Name => 'OffsetTimeDigitized',
2105             Groups => { 2 => 'Time' },
2106             Notes => 'time zone for CreateDate',
2107             Writable => 'string',
2108             Shift => 'Time',
2109             PrintConvInv => q{
2110             return "+00:00" if $val =~ /\d{2}Z$/;
2111             return sprintf("%s%.2d:%.2d",$1,$2,$3) if $val =~ /([-+])(\d{1,2}):(\d{2})/;
2112             return undef;
2113             },
2114             },
2115             0x9101 => {
2116             Name => 'ComponentsConfiguration',
2117             Format => 'int8u',
2118             Protected => 1,
2119             Writable => 'undef',
2120             Count => 4,
2121             Mandatory => 1,
2122             ValueConvInv => '$val=~tr/,//d; $val', # (so we can copy from XMP with -n)
2123             PrintConvColumns => 2,
2124             PrintConv => {
2125             0 => '-',
2126             1 => 'Y',
2127             2 => 'Cb',
2128             3 => 'Cr',
2129             4 => 'R',
2130             5 => 'G',
2131             6 => 'B',
2132             OTHER => sub {
2133             my ($val, $inv, $conv) = @_;
2134             my @a = split /,?\s+/, $val;
2135             if ($inv) {
2136             my %invConv;
2137             $invConv{lc $$conv{$_}} = $_ foreach keys %$conv;
2138             # strings like "YCbCr" and "RGB" still work for writing
2139             @a = $a[0] =~ /(Y|Cb|Cr|R|G|B)/g if @a == 1;
2140             foreach (@a) {
2141             $_ = $invConv{lc $_};
2142             return undef unless defined $_;
2143             }
2144             push @a, 0 while @a < 4;
2145             } else {
2146             foreach (@a) {
2147             $_ = $$conv{$_} || "Err ($_)";
2148             }
2149             }
2150             return join ', ', @a;
2151             },
2152             },
2153             },
2154             0x9102 => {
2155             Name => 'CompressedBitsPerPixel',
2156             Protected => 1,
2157             Writable => 'rational64u',
2158             },
2159             # 0x9103 - int16u: 1 (found in Pentax XG-1 samples)
2160             0x9201 => {
2161             Name => 'ShutterSpeedValue',
2162             Notes => 'displayed in seconds, but stored as an APEX value',
2163             Format => 'rational64s', # Leica M8 patch (incorrectly written as rational64u)
2164             Writable => 'rational64s',
2165             ValueConv => 'IsFloat($val) && abs($val)<100 ? 2**(-$val) : 0',
2166             ValueConvInv => '$val>0 ? -log($val)/log(2) : -100',
2167             PrintConv => 'Image::ExifTool::Exif::PrintExposureTime($val)',
2168             PrintConvInv => 'Image::ExifTool::Exif::ConvertFraction($val)',
2169             },
2170             0x9202 => {
2171             Name => 'ApertureValue',
2172             Notes => 'displayed as an F number, but stored as an APEX value',
2173             Writable => 'rational64u',
2174             ValueConv => '2 ** ($val / 2)',
2175             ValueConvInv => '$val>0 ? 2*log($val)/log(2) : 0',
2176             PrintConv => 'sprintf("%.1f",$val)',
2177             PrintConvInv => '$val',
2178             },
2179             # Wikipedia: BrightnessValue = Bv = Av + Tv - Sv
2180             # ExifTool: LightValue = LV = Av + Tv - Sv + 5 (5 is the Sv for ISO 100 in Exif usage)
2181             0x9203 => {
2182             Name => 'BrightnessValue',
2183             Writable => 'rational64s',
2184             },
2185             0x9204 => {
2186             Name => 'ExposureCompensation',
2187             Format => 'rational64s', # Leica M8 patch (incorrectly written as rational64u)
2188             Notes => 'called ExposureBiasValue by the EXIF spec.',
2189             Writable => 'rational64s',
2190             PrintConv => 'Image::ExifTool::Exif::PrintFraction($val)',
2191             PrintConvInv => '$val',
2192             },
2193             0x9205 => {
2194             Name => 'MaxApertureValue',
2195             Notes => 'displayed as an F number, but stored as an APEX value',
2196             Groups => { 2 => 'Camera' },
2197             Writable => 'rational64u',
2198             ValueConv => '2 ** ($val / 2)',
2199             ValueConvInv => '$val>0 ? 2*log($val)/log(2) : 0',
2200             PrintConv => 'sprintf("%.1f",$val)',
2201             PrintConvInv => '$val',
2202             },
2203             0x9206 => {
2204             Name => 'SubjectDistance',
2205             Groups => { 2 => 'Camera' },
2206             Writable => 'rational64u',
2207             PrintConv => '$val =~ /^(inf|undef)$/ ? $val : "${val} m"',
2208             PrintConvInv => '$val=~s/\s*m$//;$val',
2209             },
2210             0x9207 => {
2211             Name => 'MeteringMode',
2212             Groups => { 2 => 'Camera' },
2213             Writable => 'int16u',
2214             PrintConv => {
2215             0 => 'Unknown',
2216             1 => 'Average',
2217             2 => 'Center-weighted average',
2218             3 => 'Spot',
2219             4 => 'Multi-spot',
2220             5 => 'Multi-segment',
2221             6 => 'Partial',
2222             255 => 'Other',
2223             },
2224             },
2225             0x9208 => {
2226             Name => 'LightSource',
2227             Groups => { 2 => 'Camera' },
2228             Writable => 'int16u',
2229             SeparateTable => 'LightSource',
2230             PrintConv => \%lightSource,
2231             },
2232             0x9209 => {
2233             Name => 'Flash',
2234             Groups => { 2 => 'Camera' },
2235             Writable => 'int16u',
2236             Flags => 'PrintHex',
2237             SeparateTable => 'Flash',
2238             PrintConv => \%flash,
2239             },
2240             0x920a => {
2241             Name => 'FocalLength',
2242             Groups => { 2 => 'Camera' },
2243             Writable => 'rational64u',
2244             PrintConv => 'sprintf("%.1f mm",$val)',
2245             PrintConvInv => '$val=~s/\s*mm$//;$val',
2246             },
2247             # Note: tags 0x920b-0x9217 are duplicates of 0xa20b-0xa217
2248             # (The EXIF standard uses 0xa2xx, but you'll find both in images)
2249             0x920b => { #12
2250             Name => 'FlashEnergy',
2251             Groups => { 2 => 'Camera' },
2252             },
2253             0x920c => 'SpatialFrequencyResponse', #12 (not in Fuji images - PH)
2254             0x920d => 'Noise', #12
2255             0x920e => 'FocalPlaneXResolution', #12
2256             0x920f => 'FocalPlaneYResolution', #12
2257             0x9210 => { #12
2258             Name => 'FocalPlaneResolutionUnit',
2259             Groups => { 2 => 'Camera' },
2260             PrintConv => {
2261             1 => 'None',
2262             2 => 'inches',
2263             3 => 'cm',
2264             4 => 'mm',
2265             5 => 'um',
2266             },
2267             },
2268             0x9211 => { #12
2269             Name => 'ImageNumber',
2270             Writable => 'int32u',
2271             },
2272             0x9212 => { #12
2273             Name => 'SecurityClassification',
2274             Writable => 'string',
2275             PrintConv => {
2276             T => 'Top Secret',
2277             S => 'Secret',
2278             C => 'Confidential',
2279             R => 'Restricted',
2280             U => 'Unclassified',
2281             },
2282             },
2283             0x9213 => { #12
2284             Name => 'ImageHistory',
2285             Writable => 'string',
2286             },
2287             0x9214 => {
2288             Name => 'SubjectArea',
2289             Groups => { 2 => 'Camera' },
2290             Writable => 'int16u',
2291             Count => -1, # 2, 3 or 4 values
2292             },
2293             0x9215 => 'ExposureIndex', #12
2294             0x9216 => 'TIFF-EPStandardID', #12
2295             0x9217 => { #12
2296             Name => 'SensingMethod',
2297             Groups => { 2 => 'Camera' },
2298             PrintConv => {
2299             # (values 1 and 6 are not used by corresponding EXIF tag 0xa217)
2300             1 => 'Monochrome area',
2301             2 => 'One-chip color area',
2302             3 => 'Two-chip color area',
2303             4 => 'Three-chip color area',
2304             5 => 'Color sequential area',
2305             6 => 'Monochrome linear',
2306             7 => 'Trilinear',
2307             8 => 'Color sequential linear',
2308             },
2309             },
2310             0x923a => 'CIP3DataFile', #20
2311             0x923b => 'CIP3Sheet', #20
2312             0x923c => 'CIP3Side', #20
2313             0x923f => 'StoNits', #9
2314             # handle maker notes as a conditional list
2315             0x927c => \@Image::ExifTool::MakerNotes::Main,
2316             0x9286 => {
2317             Name => 'UserComment',
2318             # I have seen other applications write it incorrectly as 'string' or 'int8u'
2319             Format => 'undef',
2320             Writable => 'undef',
2321             RawConv => 'Image::ExifTool::Exif::ConvertExifText($self,$val,1,$tag)',
2322             # (starts with "ASCII\0\0\0", "UNICODE\0", "JIS\0\0\0\0\0" or "\0\0\0\0\0\0\0\0")
2323             RawConvInv => 'Image::ExifTool::Exif::EncodeExifText($self,$val)',
2324             # SHOULD ADD SPECIAL LOGIC TO ALLOW CONDITIONAL OVERWRITE OF
2325             # "UNKNOWN" VALUES FILLED WITH SPACES
2326             },
2327             0x9290 => {
2328             Name => 'SubSecTime',
2329             Groups => { 2 => 'Time' },
2330             Notes => 'fractional seconds for ModifyDate',
2331             Writable => 'string',
2332             ValueConv => '$val=~s/ +$//; $val', # trim trailing blanks
2333             # extract fractional seconds from a full date/time value
2334             ValueConvInv => '$val=~/^(\d+)\s*$/ ? $1 : ($val=~/\.(\d+)/ ? $1 : undef)',
2335             },
2336             0x9291 => {
2337             Name => 'SubSecTimeOriginal',
2338             Groups => { 2 => 'Time' },
2339             Notes => 'fractional seconds for DateTimeOriginal',
2340             Writable => 'string',
2341             ValueConv => '$val=~s/ +$//; $val', # trim trailing blanks
2342             ValueConvInv => '$val=~/^(\d+)\s*$/ ? $1 : ($val=~/\.(\d+)/ ? $1 : undef)',
2343             },
2344             0x9292 => {
2345             Name => 'SubSecTimeDigitized',
2346             Groups => { 2 => 'Time' },
2347             Notes => 'fractional seconds for CreateDate',
2348             Writable => 'string',
2349             ValueConv => '$val=~s/ +$//; $val', # trim trailing blanks
2350             ValueConvInv => '$val=~/^(\d+)\s*$/ ? $1 : ($val=~/\.(\d+)/ ? $1 : undef)',
2351             },
2352             # The following 3 tags are found in MSOffice TIFF images
2353             # References:
2354             # http://social.msdn.microsoft.com/Forums/en-US/os_standocs/thread/03086d55-294a-49d5-967a-5303d34c40f8/
2355             # http://blogs.msdn.com/openspecification/archive/2009/12/08/details-of-three-tiff-tag-extensions-that-microsoft-office-document-imaging-modi-software-may-write-into-the-tiff-files-it-generates.aspx
2356             # http://www.microsoft.com/downloads/details.aspx?FamilyID=0dbc435d-3544-4f4b-9092-2f2643d64a39&displaylang=en#filelist
2357             0x932f => 'MSDocumentText',
2358             0x9330 => {
2359             Name => 'MSPropertySetStorage',
2360             Binary => 1,
2361             },
2362             0x9331 => {
2363             Name => 'MSDocumentTextPosition',
2364             Binary => 1, # (just in case -- don't know what format this is)
2365             },
2366             0x935c => { #3/19
2367             Name => 'ImageSourceData', # (writable directory!)
2368             Writable => 'undef',
2369             WriteGroup => 'IFD0',
2370             SubDirectory => { TagTable => 'Image::ExifTool::Photoshop::DocumentData' },
2371             Binary => 1,
2372             Protected => 1, # (because this can be hundreds of megabytes)
2373             ReadFromRAF => 1, # don't load into memory when reading
2374             },
2375             0x9400 => {
2376             Name => 'AmbientTemperature',
2377             Notes => 'ambient temperature in degrees C, called Temperature by the EXIF spec.',
2378             Writable => 'rational64s',
2379             PrintConv => '"$val C"',
2380             PrintConvInv => '$val=~s/ ?C//; $val',
2381             },
2382             0x9401 => {
2383             Name => 'Humidity',
2384             Notes => 'ambient relative humidity in percent',
2385             Writable => 'rational64u',
2386             },
2387             0x9402 => {
2388             Name => 'Pressure',
2389             Notes => 'air pressure in hPa or mbar',
2390             Writable => 'rational64u',
2391             },
2392             0x9403 => {
2393             Name => 'WaterDepth',
2394             Notes => 'depth under water in metres, negative for above water',
2395             Writable => 'rational64s',
2396             },
2397             0x9404 => {
2398             Name => 'Acceleration',
2399             Notes => 'directionless camera acceleration in units of mGal, or 10-5 m/s2',
2400             Writable => 'rational64u',
2401             },
2402             0x9405 => {
2403             Name => 'CameraElevationAngle',
2404             Writable => 'rational64s',
2405             },
2406             # 0x9999 - string: camera settings (ExifIFD, Xiaomi POCO F1)
2407             # 0x9aaa - int8u[2176]: ? (ExifIFD, Xiaomi POCO F1)
2408             0x9c9b => {
2409             Name => 'XPTitle',
2410             Format => 'undef',
2411             Writable => 'int8u',
2412             WriteGroup => 'IFD0',
2413             Notes => q{
2414             tags 0x9c9b-0x9c9f are used by Windows Explorer; special characters
2415             in these values are converted to UTF-8 by default, or Windows Latin1
2416             with the -L option. XPTitle is ignored by Windows Explorer if
2417             ImageDescription exists
2418             },
2419             ValueConv => '$self->Decode($val,"UCS2","II")',
2420             ValueConvInv => '$self->Encode($val,"UCS2","II") . "\0\0"',
2421             },
2422             0x9c9c => {
2423             Name => 'XPComment',
2424             Format => 'undef',
2425             Writable => 'int8u',
2426             WriteGroup => 'IFD0',
2427             ValueConv => '$self->Decode($val,"UCS2","II")',
2428             ValueConvInv => '$self->Encode($val,"UCS2","II") . "\0\0"',
2429             },
2430             0x9c9d => {
2431             Name => 'XPAuthor',
2432             Groups => { 2 => 'Author' },
2433             Format => 'undef',
2434             Writable => 'int8u',
2435             WriteGroup => 'IFD0',
2436             Notes => 'ignored by Windows Explorer if Artist exists',
2437             ValueConv => '$self->Decode($val,"UCS2","II")',
2438             ValueConvInv => '$self->Encode($val,"UCS2","II") . "\0\0"',
2439             },
2440             0x9c9e => {
2441             Name => 'XPKeywords',
2442             Format => 'undef',
2443             Writable => 'int8u',
2444             WriteGroup => 'IFD0',
2445             ValueConv => '$self->Decode($val,"UCS2","II")',
2446             ValueConvInv => '$self->Encode($val,"UCS2","II") . "\0\0"',
2447             },
2448             0x9c9f => {
2449             Name => 'XPSubject',
2450             Format => 'undef',
2451             Writable => 'int8u',
2452             WriteGroup => 'IFD0',
2453             ValueConv => '$self->Decode($val,"UCS2","II")',
2454             ValueConvInv => '$self->Encode($val,"UCS2","II") . "\0\0"',
2455             },
2456             0xa000 => {
2457             Name => 'FlashpixVersion',
2458             Writable => 'undef',
2459             Mandatory => 1,
2460             RawConv => '$val=~s/\0+$//; $val', # (some idiots add null terminators)
2461             PrintConvInv => '$val=~tr/.//d; $val=~/^\d{4}$/ ? $val : undef',
2462             },
2463             0xa001 => {
2464             Name => 'ColorSpace',
2465             Notes => q{
2466             the value of 0x2 is not standard EXIF. Instead, an Adobe RGB image is
2467             indicated by "Uncalibrated" with an InteropIndex of "R03". The values
2468             0xfffd and 0xfffe are also non-standard, and are used by some Sony cameras
2469             },
2470             Writable => 'int16u',
2471             Mandatory => 1,
2472             PrintHex => 1,
2473             PrintConv => {
2474             1 => 'sRGB',
2475             2 => 'Adobe RGB',
2476             0xffff => 'Uncalibrated',
2477             # Sony uses these definitions: (ref JD)
2478             # 0xffff => 'Adobe RGB', (conflicts with Uncalibrated)
2479             0xfffe => 'ICC Profile',
2480             0xfffd => 'Wide Gamut RGB',
2481             },
2482             },
2483             0xa002 => {
2484             Name => 'ExifImageWidth',
2485             Notes => 'called PixelXDimension by the EXIF spec.',
2486             Writable => 'int16u',
2487             Mandatory => 1,
2488             },
2489             0xa003 => {
2490             Name => 'ExifImageHeight',
2491             Notes => 'called PixelYDimension by the EXIF spec.',
2492             Writable => 'int16u',
2493             Mandatory => 1,
2494             },
2495             0xa004 => {
2496             Name => 'RelatedSoundFile',
2497             Writable => 'string',
2498             },
2499             0xa005 => {
2500             Name => 'InteropOffset',
2501             Groups => { 1 => 'InteropIFD' },
2502             Flags => 'SubIFD',
2503             Description => 'Interoperability Offset',
2504             SubDirectory => {
2505             DirName => 'InteropIFD',
2506             Start => '$val',
2507             MaxSubdirs => 1,
2508             },
2509             },
2510             # the following 4 tags found in SubIFD1 of some Samsung SRW images
2511             0xa010 => {
2512             Name => 'SamsungRawPointersOffset',
2513             IsOffset => 1,
2514             OffsetPair => 0xa011, # point to associated byte count
2515             },
2516             0xa011 => {
2517             Name => 'SamsungRawPointersLength',
2518             OffsetPair => 0xa010, # point to associated offset
2519             },
2520             0xa101 => {
2521             Name => 'SamsungRawByteOrder',
2522             Format => 'undef',
2523             # this is written incorrectly as string[1], but is "\0\0MM" or "II\0\0"
2524             FixedSize => 4,
2525             Count => 1,
2526             },
2527             0xa102 => {
2528             Name => 'SamsungRawUnknown',
2529             Unknown => 1,
2530             },
2531             0xa20b => {
2532             Name => 'FlashEnergy',
2533             Groups => { 2 => 'Camera' },
2534             Writable => 'rational64u',
2535             },
2536             0xa20c => {
2537             Name => 'SpatialFrequencyResponse',
2538             PrintConv => 'Image::ExifTool::Exif::PrintSFR($val)',
2539             },
2540             0xa20d => 'Noise',
2541             0xa20e => {
2542             Name => 'FocalPlaneXResolution',
2543             Groups => { 2 => 'Camera' },
2544             Writable => 'rational64u',
2545             },
2546             0xa20f => {
2547             Name => 'FocalPlaneYResolution',
2548             Groups => { 2 => 'Camera' },
2549             Writable => 'rational64u',
2550             },
2551             0xa210 => {
2552             Name => 'FocalPlaneResolutionUnit',
2553             Groups => { 2 => 'Camera' },
2554             Notes => 'values 1, 4 and 5 are not standard EXIF',
2555             Writable => 'int16u',
2556             PrintConv => {
2557             1 => 'None', # (not standard EXIF)
2558             2 => 'inches',
2559             3 => 'cm',
2560             4 => 'mm', # (not standard EXIF)
2561             5 => 'um', # (not standard EXIF)
2562             },
2563             },
2564             0xa211 => 'ImageNumber',
2565             0xa212 => 'SecurityClassification',
2566             0xa213 => 'ImageHistory',
2567             0xa214 => {
2568             Name => 'SubjectLocation',
2569             Groups => { 2 => 'Camera' },
2570             Writable => 'int16u',
2571             Count => 2,
2572             },
2573             0xa215 => { Name => 'ExposureIndex', Writable => 'rational64u' },
2574             0xa216 => 'TIFF-EPStandardID',
2575             0xa217 => {
2576             Name => 'SensingMethod',
2577             Groups => { 2 => 'Camera' },
2578             Writable => 'int16u',
2579             PrintConv => {
2580             1 => 'Not defined',
2581             2 => 'One-chip color area',
2582             3 => 'Two-chip color area',
2583             4 => 'Three-chip color area',
2584             5 => 'Color sequential area',
2585             7 => 'Trilinear',
2586             8 => 'Color sequential linear',
2587             # 15 - used by DJI XT2
2588             },
2589             },
2590             0xa300 => {
2591             Name => 'FileSource',
2592             Writable => 'undef',
2593             ValueConvInv => '($val=~/^\d+$/ and $val < 256) ? chr($val) : $val',
2594             PrintConv => {
2595             1 => 'Film Scanner',
2596             2 => 'Reflection Print Scanner',
2597             3 => 'Digital Camera',
2598             # handle the case where Sigma incorrectly gives this tag a count of 4
2599             "\3\0\0\0" => 'Sigma Digital Camera',
2600             },
2601             },
2602             0xa301 => {
2603             Name => 'SceneType',
2604             Writable => 'undef',
2605             ValueConvInv => 'chr($val & 0xff)',
2606             PrintConv => {
2607             1 => 'Directly photographed',
2608             },
2609             },
2610             0xa302 => {
2611             Name => 'CFAPattern',
2612             Writable => 'undef',
2613             RawConv => 'Image::ExifTool::Exif::DecodeCFAPattern($self, $val)',
2614             RawConvInv => q{
2615             my @a = split ' ', $val;
2616             return $val if @a <= 2; # also accept binary data for backward compatibility
2617             return pack(GetByteOrder() eq 'II' ? 'v2C*' : 'n2C*', @a);
2618             },
2619             PrintConv => 'Image::ExifTool::Exif::PrintCFAPattern($val)',
2620             PrintConvInv => 'Image::ExifTool::Exif::GetCFAPattern($val)',
2621             },
2622             0xa401 => {
2623             Name => 'CustomRendered',
2624             Writable => 'int16u',
2625             Notes => q{
2626             only 0 and 1 are standard EXIF, but other values are used by Apple iOS
2627             devices
2628             },
2629             PrintConv => {
2630             0 => 'Normal',
2631             1 => 'Custom',
2632             2 => 'HDR (no original saved)', #32 non-standard (Apple iOS)
2633             3 => 'HDR (original saved)', #32 non-standard (Apple iOS)
2634             4 => 'Original (for HDR)', #32 non-standard (Apple iOS)
2635             6 => 'Panorama', # non-standard (Apple iOS, horizontal or vertical)
2636             7 => 'Portrait HDR', #32 non-standard (Apple iOS)
2637             8 => 'Portrait', # non-standard (Apple iOS, blurred background)
2638             # 9 - also seen (Apple iOS) (HDR Portrait?)
2639             },
2640             },
2641             0xa402 => {
2642             Name => 'ExposureMode',
2643             Groups => { 2 => 'Camera' },
2644             Writable => 'int16u',
2645             PrintConv => {
2646             0 => 'Auto',
2647             1 => 'Manual',
2648             2 => 'Auto bracket',
2649             # have seen 3 from Samsung EX1, NX30, NX200 - PH
2650             },
2651             },
2652             0xa403 => {
2653             Name => 'WhiteBalance',
2654             Groups => { 2 => 'Camera' },
2655             Writable => 'int16u',
2656             # set Priority to zero to keep this WhiteBalance from overriding the
2657             # MakerNotes WhiteBalance, since the MakerNotes WhiteBalance and is more
2658             # accurate and contains more information (if it exists)
2659             Priority => 0,
2660             PrintConv => {
2661             0 => 'Auto',
2662             1 => 'Manual',
2663             },
2664             },
2665             0xa404 => {
2666             Name => 'DigitalZoomRatio',
2667             Groups => { 2 => 'Camera' },
2668             Writable => 'rational64u',
2669             },
2670             0xa405 => {
2671             Name => 'FocalLengthIn35mmFormat',
2672             Notes => 'called FocalLengthIn35mmFilm by the EXIF spec.',
2673             Groups => { 2 => 'Camera' },
2674             Writable => 'int16u',
2675             PrintConv => '"$val mm"',
2676             PrintConvInv => '$val=~s/\s*mm$//;$val',
2677             },
2678             0xa406 => {
2679             Name => 'SceneCaptureType',
2680             Groups => { 2 => 'Camera' },
2681             Writable => 'int16u',
2682             Notes => 'the value of 4 is non-standard, and used by some Samsung models',
2683             PrintConv => {
2684             0 => 'Standard',
2685             1 => 'Landscape',
2686             2 => 'Portrait',
2687             3 => 'Night',
2688             4 => 'Other', # (non-standard Samsung, ref forum 5724)
2689             },
2690             },
2691             0xa407 => {
2692             Name => 'GainControl',
2693             Groups => { 2 => 'Camera' },
2694             Writable => 'int16u',
2695             PrintConv => {
2696             0 => 'None',
2697             1 => 'Low gain up',
2698             2 => 'High gain up',
2699             3 => 'Low gain down',
2700             4 => 'High gain down',
2701             },
2702             },
2703             0xa408 => {
2704             Name => 'Contrast',
2705             Groups => { 2 => 'Camera' },
2706             Writable => 'int16u',
2707             PrintConv => {
2708             0 => 'Normal',
2709             1 => 'Low',
2710             2 => 'High',
2711             },
2712             PrintConvInv => 'Image::ExifTool::Exif::ConvertParameter($val)',
2713             },
2714             0xa409 => {
2715             Name => 'Saturation',
2716             Groups => { 2 => 'Camera' },
2717             Writable => 'int16u',
2718             PrintConv => {
2719             0 => 'Normal',
2720             1 => 'Low',
2721             2 => 'High',
2722             },
2723             PrintConvInv => 'Image::ExifTool::Exif::ConvertParameter($val)',
2724             },
2725             0xa40a => {
2726             Name => 'Sharpness',
2727             Groups => { 2 => 'Camera' },
2728             Writable => 'int16u',
2729             PrintConv => {
2730             0 => 'Normal',
2731             1 => 'Soft',
2732             2 => 'Hard',
2733             },
2734             PrintConvInv => 'Image::ExifTool::Exif::ConvertParameter($val)',
2735             },
2736             0xa40b => {
2737             Name => 'DeviceSettingDescription',
2738             Groups => { 2 => 'Camera' },
2739             Binary => 1,
2740             },
2741             0xa40c => {
2742             Name => 'SubjectDistanceRange',
2743             Groups => { 2 => 'Camera' },
2744             Writable => 'int16u',
2745             PrintConv => {
2746             0 => 'Unknown',
2747             1 => 'Macro',
2748             2 => 'Close',
2749             3 => 'Distant',
2750             },
2751             },
2752             # 0xa40d - int16u: 0 (GE E1486 TW)
2753             # 0xa40e - int16u: 1 (GE E1486 TW)
2754             0xa420 => { Name => 'ImageUniqueID', Writable => 'string' },
2755             0xa430 => { #24
2756             Name => 'OwnerName',
2757             Notes => 'called CameraOwnerName by the EXIF spec.',
2758             Writable => 'string',
2759             },
2760             0xa431 => { #24
2761             Name => 'SerialNumber',
2762             Notes => 'called BodySerialNumber by the EXIF spec.',
2763             Writable => 'string',
2764             },
2765             0xa432 => { #24
2766             Name => 'LensInfo',
2767             Notes => q{
2768             4 rational values giving focal and aperture ranges, called LensSpecification
2769             by the EXIF spec.
2770             },
2771             Writable => 'rational64u',
2772             Count => 4,
2773             # convert to the form "12-20mm f/3.8-4.5" or "50mm f/1.4"
2774             PrintConv => \&PrintLensInfo,
2775             PrintConvInv => \&ConvertLensInfo,
2776             },
2777             0xa433 => { Name => 'LensMake', Writable => 'string' }, #24
2778             0xa434 => { Name => 'LensModel', Writable => 'string' }, #24
2779             0xa435 => { Name => 'LensSerialNumber', Writable => 'string' }, #24
2780             0xa460 => { #Exif2.32
2781             Name => 'CompositeImage',
2782             Writable => 'int16u',
2783             PrintConv => {
2784             0 => 'Unknown',
2785             1 => 'Not a Composite Image',
2786             2 => 'General Composite Image',
2787             3 => 'Composite Image Captured While Shooting',
2788             },
2789             },
2790             0xa461 => { #Exif2.32
2791             Name => 'CompositeImageCount',
2792             Notes => q{
2793             2 values: 1. Number of source images, 2. Number of images used. Called
2794             SourceImageNumberOfCompositeImage by the EXIF spec.
2795             },
2796             Writable => 'int16u',
2797             Count => 2,
2798             },
2799             0xa462 => { #Exif2.32
2800             Name => 'CompositeImageExposureTimes',
2801             Notes => q{
2802             11 or more values: 1. Total exposure time period, 2. Total exposure of all
2803             source images, 3. Total exposure of all used images, 4. Max exposure time of
2804             source images, 5. Max exposure time of used images, 6. Min exposure time of
2805             source images, 7. Min exposure of used images, 8. Number of sequences, 9.
2806             Number of source images in sequence. 10-N. Exposure times of each source
2807             image. Called SourceExposureTimesOfCompositeImage by the EXIF spec.
2808             },
2809             Writable => 'undef',
2810             RawConv => sub {
2811             my $val = shift;
2812             my @v;
2813             my $i = 0;
2814             for (;;) {
2815             if ($i == 56 or $i == 58) {
2816             last if $i + 2 > length $val;
2817             push @v, Get16u(\$val, $i);
2818             $i += 2;
2819             } else {
2820             last if $i + 8 > length $val;
2821             push @v, Image::ExifTool::GetRational64u(\$val, $i);
2822             $i += 8;
2823             }
2824             }
2825             return join ' ', @v;
2826             },
2827             RawConvInv => sub {
2828             my $val = shift;
2829             my @v = split ' ', $val;
2830             my $i;
2831             for ($i=0; ; ++$i) {
2832             last unless defined $v[$i];
2833             $v[$i] = ($i == 7 or $i == 8) ? Set16u($v[$i]) : Image::ExifTool::SetRational64u($v[$i]);
2834             }
2835             return join '', @v;
2836             },
2837             PrintConv => sub {
2838             my $val = shift;
2839             my @v = split ' ', $val;
2840             my $i;
2841             for ($i=0; ; ++$i) {
2842             last unless defined $v[$i];
2843             $v[$i] = PrintExposureTime($v[$i]) unless $i == 7 or $i == 8;
2844             }
2845             return join ' ', @v;
2846             },
2847             PrintConvInv => '$val',
2848             },
2849             0xa480 => { Name => 'GDALMetadata', Writable => 'string', WriteGroup => 'IFD0' }, #3
2850             0xa481 => { Name => 'GDALNoData', Writable => 'string', WriteGroup => 'IFD0' }, #3
2851             0xa500 => { Name => 'Gamma', Writable => 'rational64u' },
2852             0xafc0 => 'ExpandSoftware', #JD (Opanda)
2853             0xafc1 => 'ExpandLens', #JD (Opanda)
2854             0xafc2 => 'ExpandFilm', #JD (Opanda)
2855             0xafc3 => 'ExpandFilterLens', #JD (Opanda)
2856             0xafc4 => 'ExpandScanner', #JD (Opanda)
2857             0xafc5 => 'ExpandFlashLamp', #JD (Opanda)
2858             0xb4c3 => { Name => 'HasselbladRawImage', Format => 'undef', Binary => 1 }, #IB
2859             #
2860             # Windows Media Photo / HD Photo (WDP/HDP) tags
2861             #
2862             0xbc01 => { #13
2863             Name => 'PixelFormat',
2864             PrintHex => 1,
2865             Format => 'undef',
2866             Notes => q{
2867             tags 0xbc** are used in Windows HD Photo (HDP and WDP) images. The actual
2868             PixelFormat values are 16-byte GUID's but the leading 15 bytes,
2869             '6fddc324-4e03-4bfe-b1853-d77768dc9', have been removed below to avoid
2870             unnecessary clutter
2871             },
2872             ValueConv => q{
2873             require Image::ExifTool::ASF;
2874             $val = Image::ExifTool::ASF::GetGUID($val);
2875             # GUID's are too long, so remove redundant information
2876             $val =~ s/^6fddc324-4e03-4bfe-b185-3d77768dc9//i and $val = hex($val);
2877             return $val;
2878             },
2879             PrintConv => {
2880             0x0d => '24-bit RGB',
2881             0x0c => '24-bit BGR',
2882             0x0e => '32-bit BGR',
2883             0x15 => '48-bit RGB',
2884             0x12 => '48-bit RGB Fixed Point',
2885             0x3b => '48-bit RGB Half',
2886             0x18 => '96-bit RGB Fixed Point',
2887             0x1b => '128-bit RGB Float',
2888             0x0f => '32-bit BGRA',
2889             0x16 => '64-bit RGBA',
2890             0x1d => '64-bit RGBA Fixed Point',
2891             0x3a => '64-bit RGBA Half',
2892             0x1e => '128-bit RGBA Fixed Point',
2893             0x19 => '128-bit RGBA Float',
2894             0x10 => '32-bit PBGRA',
2895             0x17 => '64-bit PRGBA',
2896             0x1a => '128-bit PRGBA Float',
2897             0x1c => '32-bit CMYK',
2898             0x2c => '40-bit CMYK Alpha',
2899             0x1f => '64-bit CMYK',
2900             0x2d => '80-bit CMYK Alpha',
2901             0x20 => '24-bit 3 Channels',
2902             0x21 => '32-bit 4 Channels',
2903             0x22 => '40-bit 5 Channels',
2904             0x23 => '48-bit 6 Channels',
2905             0x24 => '56-bit 7 Channels',
2906             0x25 => '64-bit 8 Channels',
2907             0x2e => '32-bit 3 Channels Alpha',
2908             0x2f => '40-bit 4 Channels Alpha',
2909             0x30 => '48-bit 5 Channels Alpha',
2910             0x31 => '56-bit 6 Channels Alpha',
2911             0x32 => '64-bit 7 Channels Alpha',
2912             0x33 => '72-bit 8 Channels Alpha',
2913             0x26 => '48-bit 3 Channels',
2914             0x27 => '64-bit 4 Channels',
2915             0x28 => '80-bit 5 Channels',
2916             0x29 => '96-bit 6 Channels',
2917             0x2a => '112-bit 7 Channels',
2918             0x2b => '128-bit 8 Channels',
2919             0x34 => '64-bit 3 Channels Alpha',
2920             0x35 => '80-bit 4 Channels Alpha',
2921             0x36 => '96-bit 5 Channels Alpha',
2922             0x37 => '112-bit 6 Channels Alpha',
2923             0x38 => '128-bit 7 Channels Alpha',
2924             0x39 => '144-bit 8 Channels Alpha',
2925             0x08 => '8-bit Gray',
2926             0x0b => '16-bit Gray',
2927             0x13 => '16-bit Gray Fixed Point',
2928             0x3e => '16-bit Gray Half',
2929             0x3f => '32-bit Gray Fixed Point',
2930             0x11 => '32-bit Gray Float',
2931             0x05 => 'Black & White',
2932             0x09 => '16-bit BGR555',
2933             0x0a => '16-bit BGR565',
2934             0x13 => '32-bit BGR101010',
2935             0x3d => '32-bit RGBE',
2936             },
2937             },
2938             0xbc02 => { #13
2939             Name => 'Transformation',
2940             PrintConv => {
2941             0 => 'Horizontal (normal)',
2942             1 => 'Mirror vertical',
2943             2 => 'Mirror horizontal',
2944             3 => 'Rotate 180',
2945             4 => 'Rotate 90 CW',
2946             5 => 'Mirror horizontal and rotate 90 CW',
2947             6 => 'Mirror horizontal and rotate 270 CW',
2948             7 => 'Rotate 270 CW',
2949             },
2950             },
2951             0xbc03 => { #13
2952             Name => 'Uncompressed',
2953             PrintConv => { 0 => 'No', 1 => 'Yes' },
2954             },
2955             0xbc04 => { #13
2956             Name => 'ImageType',
2957             PrintConv => { BITMASK => {
2958             0 => 'Preview',
2959             1 => 'Page',
2960             } },
2961             },
2962             0xbc80 => 'ImageWidth', #13
2963             0xbc81 => 'ImageHeight', #13
2964             0xbc82 => 'WidthResolution', #13
2965             0xbc83 => 'HeightResolution', #13
2966             0xbcc0 => { #13
2967             Name => 'ImageOffset',
2968             IsOffset => 1,
2969             OffsetPair => 0xbcc1, # point to associated byte count
2970             },
2971             0xbcc1 => { #13
2972             Name => 'ImageByteCount',
2973             OffsetPair => 0xbcc0, # point to associated offset
2974             },
2975             0xbcc2 => { #13
2976             Name => 'AlphaOffset',
2977             IsOffset => 1,
2978             OffsetPair => 0xbcc3, # point to associated byte count
2979             },
2980             0xbcc3 => { #13
2981             Name => 'AlphaByteCount',
2982             OffsetPair => 0xbcc2, # point to associated offset
2983             },
2984             0xbcc4 => { #13
2985             Name => 'ImageDataDiscard',
2986             PrintConv => {
2987             0 => 'Full Resolution',
2988             1 => 'Flexbits Discarded',
2989             2 => 'HighPass Frequency Data Discarded',
2990             3 => 'Highpass and LowPass Frequency Data Discarded',
2991             },
2992             },
2993             0xbcc5 => { #13
2994             Name => 'AlphaDataDiscard',
2995             PrintConv => {
2996             0 => 'Full Resolution',
2997             1 => 'Flexbits Discarded',
2998             2 => 'HighPass Frequency Data Discarded',
2999             3 => 'Highpass and LowPass Frequency Data Discarded',
3000             },
3001             },
3002             #
3003             0xc427 => 'OceScanjobDesc', #3
3004             0xc428 => 'OceApplicationSelector', #3
3005             0xc429 => 'OceIDNumber', #3
3006             0xc42a => 'OceImageLogic', #3
3007             0xc44f => { Name => 'Annotations', Binary => 1 }, #7/19
3008             0xc4a5 => {
3009             Name => 'PrintIM', # (writable directory!)
3010             # must set Writable here so this tag will be saved with MakerNotes option
3011             Writable => 'undef',
3012             WriteGroup => 'IFD0',
3013             Binary => 1,
3014             # (don't make Binary/Protected because we can't copy individual PrintIM tags anyway)
3015             Description => 'Print Image Matching',
3016             SubDirectory => {
3017             TagTable => 'Image::ExifTool::PrintIM::Main',
3018             },
3019             PrintConvInv => '$val =~ /^PrintIM/ ? $val : undef', # quick validation
3020             },
3021             0xc51b => { # (Hasselblad H3D)
3022             Name => 'HasselbladExif',
3023             Format => 'undef',
3024             RawConv => q{
3025             $$self{DOC_NUM} = ++$$self{DOC_COUNT};
3026             $self->ExtractInfo(\$val, { ReEntry => 1 });
3027             $$self{DOC_NUM} = 0;
3028             return undef;
3029             },
3030             },
3031             0xc573 => { #PH
3032             Name => 'OriginalFileName',
3033             Notes => 'used by some obscure software', # (possibly Swizzy Photosmacker?)
3034             # (it is a 'string', but obscure, so don't make it writable)
3035             },
3036             0xc580 => { #20
3037             Name => 'USPTOOriginalContentType',
3038             PrintConv => {
3039             0 => 'Text or Drawing',
3040             1 => 'Grayscale',
3041             2 => 'Color',
3042             },
3043             },
3044             # 0xc5d8 - found in CR2 images
3045             # 0xc5d9 - found in CR2 images
3046             0xc5e0 => { #forum8153 (CR2 images)
3047             Name => 'CR2CFAPattern',
3048             ValueConv => {
3049             1 => '0 1 1 2',
3050             2 => '2 1 1 0',
3051             3 => '1 2 0 1',
3052             4 => '1 0 2 1',
3053             },
3054             PrintConv => {
3055             '0 1 1 2' => '[Red,Green][Green,Blue]',
3056             '2 1 1 0' => '[Blue,Green][Green,Red]',
3057             '1 2 0 1' => '[Green,Blue][Red,Green]',
3058             '1 0 2 1' => '[Green,Red][Blue,Green]',
3059             },
3060             },
3061             #
3062             # DNG tags 0xc6XX, 0xc7XX and 0xcdXX (ref 2 unless otherwise stated)
3063             #
3064             0xc612 => {
3065             Name => 'DNGVersion',
3066             Notes => q{
3067             tags 0xc612-0xcd3b are defined by the DNG specification unless otherwise
3068             noted. See L for
3069             the specification
3070             },
3071             Writable => 'int8u',
3072             WriteGroup => 'IFD0',
3073             Count => 4,
3074             Protected => 1, # (confuses Apple Preview if written to a TIFF image)
3075             DataMember => 'DNGVersion',
3076             RawConv => '$$self{DNGVersion} = $val',
3077             PrintConv => '$val =~ tr/ /./; $val',
3078             PrintConvInv => '$val =~ tr/./ /; $val',
3079             },
3080             0xc613 => {
3081             Name => 'DNGBackwardVersion',
3082             Writable => 'int8u',
3083             WriteGroup => 'IFD0',
3084             Count => 4,
3085             Protected => 1,
3086             PrintConv => '$val =~ tr/ /./; $val',
3087             PrintConvInv => '$val =~ tr/./ /; $val',
3088             },
3089             0xc614 => {
3090             Name => 'UniqueCameraModel',
3091             Writable => 'string',
3092             WriteGroup => 'IFD0',
3093             },
3094             0xc615 => {
3095             Name => 'LocalizedCameraModel',
3096             WriteGroup => 'IFD0',
3097             %utf8StringConv,
3098             PrintConv => '$self->Printable($val, 0)',
3099             PrintConvInv => '$val',
3100             },
3101             0xc616 => {
3102             Name => 'CFAPlaneColor',
3103             WriteGroup => 'SubIFD', # (only for Validate)
3104             PrintConv => q{
3105             my @cols = qw(Red Green Blue Cyan Magenta Yellow White);
3106             my @vals = map { $cols[$_] || "Unknown($_)" } split(' ', $val);
3107             return join(',', @vals);
3108             },
3109             },
3110             0xc617 => {
3111             Name => 'CFALayout',
3112             WriteGroup => 'SubIFD', # (only for Validate)
3113             PrintConv => {
3114             1 => 'Rectangular',
3115             2 => 'Even columns offset down 1/2 row',
3116             3 => 'Even columns offset up 1/2 row',
3117             4 => 'Even rows offset right 1/2 column',
3118             5 => 'Even rows offset left 1/2 column',
3119             # the following are new for DNG 1.3:
3120             6 => 'Even rows offset up by 1/2 row, even columns offset left by 1/2 column',
3121             7 => 'Even rows offset up by 1/2 row, even columns offset right by 1/2 column',
3122             8 => 'Even rows offset down by 1/2 row, even columns offset left by 1/2 column',
3123             9 => 'Even rows offset down by 1/2 row, even columns offset right by 1/2 column',
3124             },
3125             },
3126             0xc618 => {
3127             Name => 'LinearizationTable',
3128             Writable => 'int16u',
3129             WriteGroup => 'SubIFD',
3130             Count => -1,
3131             Protected => 1,
3132             Binary => 1,
3133             },
3134             0xc619 => {
3135             Name => 'BlackLevelRepeatDim',
3136             Writable => 'int16u',
3137             WriteGroup => 'SubIFD',
3138             Count => 2,
3139             Protected => 1,
3140             },
3141             0xc61a => {
3142             Name => 'BlackLevel',
3143             Writable => 'rational64u',
3144             WriteGroup => 'SubIFD',
3145             Count => -1,
3146             Protected => 1,
3147             },
3148             0xc61b => {
3149             Name => 'BlackLevelDeltaH',
3150             %longBin,
3151             Writable => 'rational64s',
3152             WriteGroup => 'SubIFD',
3153             Count => -1,
3154             Protected => 1,
3155             },
3156             0xc61c => {
3157             Name => 'BlackLevelDeltaV',
3158             %longBin,
3159             Writable => 'rational64s',
3160             WriteGroup => 'SubIFD',
3161             Count => -1,
3162             Protected => 1,
3163             },
3164             0xc61d => {
3165             Name => 'WhiteLevel',
3166             Writable => 'int32u',
3167             WriteGroup => 'SubIFD',
3168             Count => -1,
3169             Protected => 1,
3170             },
3171             0xc61e => {
3172             Name => 'DefaultScale',
3173             Writable => 'rational64u',
3174             WriteGroup => 'SubIFD',
3175             Count => 2,
3176             Protected => 1,
3177             },
3178             0xc61f => {
3179             Name => 'DefaultCropOrigin',
3180             Writable => 'int32u',
3181             WriteGroup => 'SubIFD',
3182             Count => 2,
3183             Protected => 1,
3184             },
3185             0xc620 => {
3186             Name => 'DefaultCropSize',
3187             Writable => 'int32u',
3188             WriteGroup => 'SubIFD',
3189             Count => 2,
3190             Protected => 1,
3191             },
3192             0xc621 => {
3193             Name => 'ColorMatrix1',
3194             Writable => 'rational64s',
3195             WriteGroup => 'IFD0',
3196             Count => -1,
3197             Protected => 1,
3198             },
3199             0xc622 => {
3200             Name => 'ColorMatrix2',
3201             Writable => 'rational64s',
3202             WriteGroup => 'IFD0',
3203             Count => -1,
3204             Protected => 1,
3205             },
3206             0xc623 => {
3207             Name => 'CameraCalibration1',
3208             Writable => 'rational64s',
3209             WriteGroup => 'IFD0',
3210             Count => -1,
3211             Protected => 1,
3212             },
3213             0xc624 => {
3214             Name => 'CameraCalibration2',
3215             Writable => 'rational64s',
3216             WriteGroup => 'IFD0',
3217             Count => -1,
3218             Protected => 1,
3219             },
3220             0xc625 => {
3221             Name => 'ReductionMatrix1',
3222             Writable => 'rational64s',
3223             WriteGroup => 'IFD0',
3224             Count => -1,
3225             Protected => 1,
3226             },
3227             0xc626 => {
3228             Name => 'ReductionMatrix2',
3229             Writable => 'rational64s',
3230             WriteGroup => 'IFD0',
3231             Count => -1,
3232             Protected => 1,
3233             },
3234             0xc627 => {
3235             Name => 'AnalogBalance',
3236             Writable => 'rational64u',
3237             WriteGroup => 'IFD0',
3238             Count => -1,
3239             Protected => 1,
3240             },
3241             0xc628 => {
3242             Name => 'AsShotNeutral',
3243             Writable => 'rational64u',
3244             WriteGroup => 'IFD0',
3245             Count => -1,
3246             Protected => 1,
3247             },
3248             0xc629 => {
3249             Name => 'AsShotWhiteXY',
3250             Writable => 'rational64u',
3251             WriteGroup => 'IFD0',
3252             Count => 2,
3253             Protected => 1,
3254             },
3255             0xc62a => {
3256             Name => 'BaselineExposure',
3257             Writable => 'rational64s',
3258             WriteGroup => 'IFD0',
3259             Protected => 1,
3260             },
3261             0xc62b => {
3262             Name => 'BaselineNoise',
3263             Writable => 'rational64u',
3264             WriteGroup => 'IFD0',
3265             Protected => 1,
3266             },
3267             0xc62c => {
3268             Name => 'BaselineSharpness',
3269             Writable => 'rational64u',
3270             WriteGroup => 'IFD0',
3271             Protected => 1,
3272             },
3273             0xc62d => {
3274             Name => 'BayerGreenSplit',
3275             Writable => 'int32u',
3276             WriteGroup => 'SubIFD',
3277             Protected => 1,
3278             },
3279             0xc62e => {
3280             Name => 'LinearResponseLimit',
3281             Writable => 'rational64u',
3282             WriteGroup => 'IFD0',
3283             Protected => 1,
3284             },
3285             0xc62f => {
3286             Name => 'CameraSerialNumber',
3287             Groups => { 2 => 'Camera' },
3288             Writable => 'string',
3289             WriteGroup => 'IFD0',
3290             },
3291             0xc630 => {
3292             Name => 'DNGLensInfo',
3293             Groups => { 2 => 'Camera' },
3294             Writable => 'rational64u',
3295             WriteGroup => 'IFD0',
3296             Count => 4,
3297             PrintConv =>\&PrintLensInfo,
3298             PrintConvInv => \&ConvertLensInfo,
3299             },
3300             0xc631 => {
3301             Name => 'ChromaBlurRadius',
3302             Writable => 'rational64u',
3303             WriteGroup => 'SubIFD',
3304             Protected => 1,
3305             },
3306             0xc632 => {
3307             Name => 'AntiAliasStrength',
3308             Writable => 'rational64u',
3309             WriteGroup => 'SubIFD',
3310             Protected => 1,
3311             },
3312             0xc633 => {
3313             Name => 'ShadowScale',
3314             Writable => 'rational64u',
3315             WriteGroup => 'IFD0',
3316             Protected => 1,
3317             },
3318             0xc634 => [
3319             {
3320             Condition => '$$self{TIFF_TYPE} =~ /^(ARW|SR2)$/',
3321             Name => 'SR2Private',
3322             Groups => { 1 => 'SR2' },
3323             Flags => 'SubIFD',
3324             Format => 'int32u',
3325             # some utilities have problems unless this is int8u format:
3326             # - Adobe Camera Raw 5.3 gives an error
3327             # - Apple Preview 10.5.8 gets the wrong white balance
3328             FixFormat => 'int8u', # (stupid Sony)
3329             WriteGroup => 'IFD0', # (for Validate)
3330             SubDirectory => {
3331             DirName => 'SR2Private',
3332             TagTable => 'Image::ExifTool::Sony::SR2Private',
3333             Start => '$val',
3334             },
3335             },
3336             {
3337             Condition => '$$valPt =~ /^Adobe\0/',
3338             Name => 'DNGAdobeData',
3339             Flags => [ 'Binary', 'Protected' ],
3340             Writable => 'undef', # (writable directory!) (to make it possible to delete this mess)
3341             WriteGroup => 'IFD0',
3342             NestedHtmlDump => 1,
3343             SubDirectory => { TagTable => 'Image::ExifTool::DNG::AdobeData' },
3344             Format => 'undef', # but written as int8u (change to undef for speed)
3345             },
3346             {
3347             # Pentax/Samsung models that write AOC maker notes in JPG images:
3348             # K-5,K-7,K-m,K-x,K-r,K10D,K20D,K100D,K110D,K200D,K2000,GX10,GX20
3349             # (Note: the following expression also appears in WriteExif.pl)
3350             Condition => q{
3351             $$valPt =~ /^(PENTAX |SAMSUNG)\0/ and
3352             $$self{Model} =~ /\b(K(-[57mrx]|(10|20|100|110|200)D|2000)|GX(10|20))\b/
3353             },
3354             Name => 'MakerNotePentax',
3355             MakerNotes => 1, # (causes "MakerNotes header" to be identified in HtmlDump output)
3356             Binary => 1,
3357             WriteGroup => 'IFD0', # (for Validate)
3358             # Note: Don't make this block-writable for a few reasons:
3359             # 1) It would be dangerous (possibly confusing Pentax software)
3360             # 2) It is a different format from the JPEG version of MakerNotePentax
3361             # 3) It is converted to JPEG format by RebuildMakerNotes() when copying
3362             SubDirectory => {
3363             TagTable => 'Image::ExifTool::Pentax::Main',
3364             Start => '$valuePtr + 10',
3365             Base => '$start - 10',
3366             ByteOrder => 'Unknown', # easier to do this than read byteorder word
3367             },
3368             Format => 'undef', # but written as int8u (change to undef for speed)
3369             },
3370             {
3371             # must duplicate the above tag with a different name for more recent
3372             # Pentax models which use the "PENTAX" instead of the "AOC" maker notes
3373             # in JPG images (needed when copying maker notes from DNG to JPG)
3374             Condition => '$$valPt =~ /^(PENTAX |SAMSUNG)\0/',
3375             Name => 'MakerNotePentax5',
3376             MakerNotes => 1,
3377             Binary => 1,
3378             WriteGroup => 'IFD0', # (for Validate)
3379             SubDirectory => {
3380             TagTable => 'Image::ExifTool::Pentax::Main',
3381             Start => '$valuePtr + 10',
3382             Base => '$start - 10',
3383             ByteOrder => 'Unknown',
3384             },
3385             Format => 'undef',
3386             },
3387             {
3388             # Ricoh models such as the GR III
3389             Condition => '$$valPt =~ /^RICOH\0(II|MM)/',
3390             Name => 'MakerNoteRicohPentax',
3391             MakerNotes => 1,
3392             Binary => 1,
3393             WriteGroup => 'IFD0', # (for Validate)
3394             SubDirectory => {
3395             TagTable => 'Image::ExifTool::Pentax::Main',
3396             Start => '$valuePtr + 8',
3397             Base => '$start - 8',
3398             ByteOrder => 'Unknown',
3399             },
3400             Format => 'undef',
3401             },
3402             # the DJI FC2103 writes some interesting stuff here (with sections labelled
3403             # awb_dbg_info, ae_dbg_info, ae_histogram_info, af_dbg_info, hiso, xidiri) - PH
3404             {
3405             Name => 'MakerNoteDJIInfo',
3406             Condition => '$$valPt =~ /^\[ae_dbg_info:/',
3407             MakerNotes => 1,
3408             Binary => 1,
3409             WriteGroup => 'IFD0', # (for Validate)
3410             SubDirectory => { TagTable => 'Image::ExifTool::DJI::Info' },
3411             Format => 'undef',
3412             },
3413             {
3414             Name => 'DNGPrivateData',
3415             Flags => [ 'Binary', 'Protected' ],
3416             Format => 'undef',
3417             Writable => 'int8u',
3418             WriteGroup => 'IFD0',
3419             },
3420             ],
3421             0xc635 => {
3422             Name => 'MakerNoteSafety',
3423             Writable => 'int16u',
3424             WriteGroup => 'IFD0',
3425             PrintConv => {
3426             0 => 'Unsafe',
3427             1 => 'Safe',
3428             },
3429             },
3430             0xc640 => { #15
3431             Name => 'RawImageSegmentation',
3432             # (int16u[3], not writable)
3433             Notes => q{
3434             used in segmented Canon CR2 images. 3 numbers: 1. Number of segments minus
3435             one; 2. Pixel width of segments except last; 3. Pixel width of last segment
3436             },
3437             },
3438             0xc65a => {
3439             Name => 'CalibrationIlluminant1',
3440             Writable => 'int16u',
3441             WriteGroup => 'IFD0',
3442             Protected => 1,
3443             SeparateTable => 'LightSource',
3444             PrintConv => \%lightSource,
3445             },
3446             0xc65b => {
3447             Name => 'CalibrationIlluminant2',
3448             Writable => 'int16u',
3449             WriteGroup => 'IFD0',
3450             Protected => 1,
3451             SeparateTable => 'LightSource',
3452             PrintConv => \%lightSource,
3453             },
3454             0xc65c => {
3455             Name => 'BestQualityScale',
3456             Writable => 'rational64u',
3457             WriteGroup => 'SubIFD',
3458             Protected => 1,
3459             },
3460             0xc65d => {
3461             Name => 'RawDataUniqueID',
3462             Format => 'undef',
3463             Writable => 'int8u',
3464             WriteGroup => 'IFD0',
3465             Count => 16,
3466             Protected => 1,
3467             ValueConv => 'uc(unpack("H*",$val))',
3468             ValueConvInv => 'pack("H*", $val)',
3469             },
3470             0xc660 => { #3
3471             Name => 'AliasLayerMetadata',
3472             Notes => 'used by Alias Sketchbook Pro',
3473             },
3474             0xc68b => {
3475             Name => 'OriginalRawFileName',
3476             WriteGroup => 'IFD0',
3477             Protected => 1,
3478             %utf8StringConv,
3479             },
3480             0xc68c => {
3481             Name => 'OriginalRawFileData', # (writable directory!)
3482             Writable => 'undef', # must be defined here so tag will be extracted if specified
3483             WriteGroup => 'IFD0',
3484             Flags => [ 'Binary', 'Protected' ],
3485             SubDirectory => {
3486             TagTable => 'Image::ExifTool::DNG::OriginalRaw',
3487             },
3488             },
3489             0xc68d => {
3490             Name => 'ActiveArea',
3491             Writable => 'int32u',
3492             WriteGroup => 'SubIFD',
3493             Count => 4,
3494             Protected => 1,
3495             },
3496             0xc68e => {
3497             Name => 'MaskedAreas',
3498             Writable => 'int32u',
3499             WriteGroup => 'SubIFD',
3500             Count => -1,
3501             Protected => 1,
3502             },
3503             0xc68f => {
3504             Name => 'AsShotICCProfile', # (writable directory)
3505             Binary => 1,
3506             Writable => 'undef', # must be defined here so tag will be extracted if specified
3507             WriteGroup => 'IFD0',
3508             Protected => 1,
3509             WriteCheck => q{
3510             require Image::ExifTool::ICC_Profile;
3511             return Image::ExifTool::ICC_Profile::ValidateICC(\$val);
3512             },
3513             SubDirectory => {
3514             DirName => 'AsShotICCProfile',
3515             TagTable => 'Image::ExifTool::ICC_Profile::Main',
3516             },
3517             },
3518             0xc690 => {
3519             Name => 'AsShotPreProfileMatrix',
3520             Writable => 'rational64s',
3521             WriteGroup => 'IFD0',
3522             Count => -1,
3523             Protected => 1,
3524             },
3525             0xc691 => {
3526             Name => 'CurrentICCProfile', # (writable directory)
3527             Binary => 1,
3528             Writable => 'undef', # must be defined here so tag will be extracted if specified
3529             SubDirectory => {
3530             DirName => 'CurrentICCProfile',
3531             TagTable => 'Image::ExifTool::ICC_Profile::Main',
3532             },
3533             Writable => 'undef',
3534             WriteGroup => 'IFD0',
3535             Protected => 1,
3536             WriteCheck => q{
3537             require Image::ExifTool::ICC_Profile;
3538             return Image::ExifTool::ICC_Profile::ValidateICC(\$val);
3539             },
3540             },
3541             0xc692 => {
3542             Name => 'CurrentPreProfileMatrix',
3543             Writable => 'rational64s',
3544             WriteGroup => 'IFD0',
3545             Count => -1,
3546             Protected => 1,
3547             },
3548             0xc6bf => {
3549             Name => 'ColorimetricReference',
3550             Writable => 'int16u',
3551             WriteGroup => 'IFD0',
3552             Protected => 1,
3553             },
3554             0xc6c5 => { Name => 'SRawType', Description => 'SRaw Type', WriteGroup => 'IFD0' }, #exifprobe (CR2 proprietary)
3555             0xc6d2 => { #JD (Panasonic DMC-TZ5)
3556             # this text is UTF-8 encoded (hooray!) - PH (TZ5)
3557             Name => 'PanasonicTitle',
3558             Format => 'string', # written incorrectly as 'undef'
3559             Notes => 'proprietary Panasonic tag used for baby/pet name, etc',
3560             Writable => 'undef',
3561             WriteGroup => 'IFD0',
3562             # panasonic always records this tag (64 zero bytes),
3563             # so ignore it unless it contains valid information
3564             RawConv => 'length($val) ? $val : undef',
3565             ValueConv => '$self->Decode($val, "UTF8")',
3566             ValueConvInv => '$self->Encode($val,"UTF8")',
3567             },
3568             0xc6d3 => { #PH (Panasonic DMC-FS7)
3569             Name => 'PanasonicTitle2',
3570             Format => 'string', # written incorrectly as 'undef'
3571             Notes => 'proprietary Panasonic tag used for baby/pet name with age',
3572             Writable => 'undef',
3573             WriteGroup => 'IFD0',
3574             # panasonic always records this tag (128 zero bytes),
3575             # so ignore it unless it contains valid information
3576             RawConv => 'length($val) ? $val : undef',
3577             ValueConv => '$self->Decode($val, "UTF8")',
3578             ValueConvInv => '$self->Encode($val,"UTF8")',
3579             },
3580             # 0xc6dc - int32u[4]: found in CR2 images (PH, 7DmkIII)
3581             # 0xc6dd - int16u[256]: found in CR2 images (PH, 5DmkIV)
3582             0xc6f3 => {
3583             Name => 'CameraCalibrationSig',
3584             WriteGroup => 'IFD0',
3585             Protected => 1,
3586             %utf8StringConv,
3587             },
3588             0xc6f4 => {
3589             Name => 'ProfileCalibrationSig',
3590             WriteGroup => 'IFD0',
3591             Protected => 1,
3592             %utf8StringConv,
3593             },
3594             0xc6f5 => {
3595             Name => 'ProfileIFD', # (ExtraCameraProfiles)
3596             Groups => { 1 => 'ProfileIFD' },
3597             Flags => 'SubIFD',
3598             WriteGroup => 'IFD0', # (only for Validate)
3599             SubDirectory => {
3600             ProcessProc => \&ProcessTiffIFD,
3601             WriteProc => \&ProcessTiffIFD,
3602             DirName => 'ProfileIFD',
3603             Start => '$val',
3604             Base => '$start', # offsets relative to start of TIFF-like header
3605             MaxSubdirs => 10,
3606             Magic => 0x4352, # magic number for TIFF-like header
3607             },
3608             },
3609             0xc6f6 => {
3610             Name => 'AsShotProfileName',
3611             WriteGroup => 'IFD0',
3612             Protected => 1,
3613             %utf8StringConv,
3614             },
3615             0xc6f7 => {
3616             Name => 'NoiseReductionApplied',
3617             Writable => 'rational64u',
3618             WriteGroup => 'SubIFD',
3619             Protected => 1,
3620             },
3621             0xc6f8 => {
3622             Name => 'ProfileName',
3623             WriteGroup => 'IFD0',
3624             Protected => 1,
3625             %utf8StringConv,
3626             },
3627             0xc6f9 => {
3628             Name => 'ProfileHueSatMapDims',
3629             Writable => 'int32u',
3630             WriteGroup => 'IFD0',
3631             Count => 3,
3632             Protected => 1,
3633             },
3634             0xc6fa => {
3635             Name => 'ProfileHueSatMapData1',
3636             %longBin,
3637             Writable => 'float',
3638             WriteGroup => 'IFD0',
3639             Count => -1,
3640             Protected => 1,
3641             },
3642             0xc6fb => {
3643             Name => 'ProfileHueSatMapData2',
3644             %longBin,
3645             Writable => 'float',
3646             WriteGroup => 'IFD0',
3647             Count => -1,
3648             Protected => 1,
3649             },
3650             0xc6fc => {
3651             Name => 'ProfileToneCurve',
3652             %longBin,
3653             Writable => 'float',
3654             WriteGroup => 'IFD0',
3655             Count => -1,
3656             Protected => 1,
3657             },
3658             0xc6fd => {
3659             Name => 'ProfileEmbedPolicy',
3660             Writable => 'int32u',
3661             WriteGroup => 'IFD0',
3662             Protected => 1,
3663             PrintConv => {
3664             0 => 'Allow Copying',
3665             1 => 'Embed if Used',
3666             2 => 'Never Embed',
3667             3 => 'No Restrictions',
3668             },
3669             },
3670             0xc6fe => {
3671             Name => 'ProfileCopyright',
3672             WriteGroup => 'IFD0',
3673             Protected => 1,
3674             %utf8StringConv,
3675             },
3676             0xc714 => {
3677             Name => 'ForwardMatrix1',
3678             Writable => 'rational64s',
3679             WriteGroup => 'IFD0',
3680             Count => -1,
3681             Protected => 1,
3682             },
3683             0xc715 => {
3684             Name => 'ForwardMatrix2',
3685             Writable => 'rational64s',
3686             WriteGroup => 'IFD0',
3687             Count => -1,
3688             Protected => 1,
3689             },
3690             0xc716 => {
3691             Name => 'PreviewApplicationName',
3692             WriteGroup => 'IFD0',
3693             Protected => 1,
3694             %utf8StringConv,
3695             },
3696             0xc717 => {
3697             Name => 'PreviewApplicationVersion',
3698             Writable => 'string',
3699             WriteGroup => 'IFD0',
3700             Protected => 1,
3701             %utf8StringConv,
3702             },
3703             0xc718 => {
3704             Name => 'PreviewSettingsName',
3705             Writable => 'string',
3706             WriteGroup => 'IFD0',
3707             Protected => 1,
3708             %utf8StringConv,
3709             },
3710             0xc719 => {
3711             Name => 'PreviewSettingsDigest',
3712             Format => 'undef',
3713             Writable => 'int8u',
3714             WriteGroup => 'IFD0',
3715             Protected => 1,
3716             ValueConv => 'unpack("H*", $val)',
3717             ValueConvInv => 'pack("H*", $val)',
3718             },
3719             0xc71a => {
3720             Name => 'PreviewColorSpace',
3721             Writable => 'int32u',
3722             WriteGroup => 'IFD0',
3723             Protected => 1,
3724             PrintConv => {
3725             0 => 'Unknown',
3726             1 => 'Gray Gamma 2.2',
3727             2 => 'sRGB',
3728             3 => 'Adobe RGB',
3729             4 => 'ProPhoto RGB',
3730             },
3731             },
3732             0xc71b => {
3733             Name => 'PreviewDateTime',
3734             Groups => { 2 => 'Time' },
3735             Writable => 'string',
3736             Shift => 'Time',
3737             WriteGroup => 'IFD0',
3738             Protected => 1,
3739             ValueConv => q{
3740             require Image::ExifTool::XMP;
3741             return Image::ExifTool::XMP::ConvertXMPDate($val);
3742             },
3743             ValueConvInv => q{
3744             require Image::ExifTool::XMP;
3745             return Image::ExifTool::XMP::FormatXMPDate($val);
3746             },
3747             PrintConv => '$self->ConvertDateTime($val)',
3748             PrintConvInv => '$self->InverseDateTime($val,1,1)',
3749             },
3750             0xc71c => {
3751             Name => 'RawImageDigest',
3752             Format => 'undef',
3753             Writable => 'int8u',
3754             WriteGroup => 'IFD0',
3755             Count => 16,
3756             Protected => 1,
3757             ValueConv => 'unpack("H*", $val)',
3758             ValueConvInv => 'pack("H*", $val)',
3759             },
3760             0xc71d => {
3761             Name => 'OriginalRawFileDigest',
3762             Format => 'undef',
3763             Writable => 'int8u',
3764             WriteGroup => 'IFD0',
3765             Count => 16,
3766             Protected => 1,
3767             ValueConv => 'unpack("H*", $val)',
3768             ValueConvInv => 'pack("H*", $val)',
3769             },
3770             0xc71e => 'SubTileBlockSize',
3771             0xc71f => 'RowInterleaveFactor',
3772             0xc725 => {
3773             Name => 'ProfileLookTableDims',
3774             Writable => 'int32u',
3775             WriteGroup => 'IFD0',
3776             Count => 3,
3777             Protected => 1,
3778             },
3779             0xc726 => {
3780             Name => 'ProfileLookTableData',
3781             %longBin,
3782             Writable => 'float',
3783             WriteGroup => 'IFD0',
3784             Count => -1,
3785             Protected => 1,
3786             },
3787             0xc740 => { Name => 'OpcodeList1', %opcodeInfo }, # DNG 1.3
3788             0xc741 => { Name => 'OpcodeList2', %opcodeInfo }, # DNG 1.3
3789             0xc74e => { Name => 'OpcodeList3', %opcodeInfo }, # DNG 1.3
3790             0xc761 => { # DNG 1.3
3791             Name => 'NoiseProfile',
3792             Writable => 'double',
3793             WriteGroup => 'SubIFD',
3794             Count => -1,
3795             Protected => 1,
3796             },
3797             0xc763 => { #28
3798             Name => 'TimeCodes',
3799             Writable => 'int8u',
3800             WriteGroup => 'IFD0',
3801             Count => -1, # (8 * number of time codes, max 10)
3802             ValueConv => q{
3803             my @a = split ' ', $val;
3804             my @v;
3805             push @v, join('.', map { sprintf('%.2x',$_) } splice(@a,0,8)) while @a >= 8;
3806             join ' ', @v;
3807             },
3808             ValueConvInv => q{
3809             my @a = map hex, split /[. ]+/, $val;
3810             join ' ', @a;
3811             },
3812             # Note: Currently ignore the flags:
3813             # byte 0 0x80 - color frame
3814             # byte 0 0x40 - drop frame
3815             # byte 1 0x80 - field phase
3816             PrintConv => q{
3817             my @a = map hex, split /[. ]+/, $val;
3818             my @v;
3819             while (@a >= 8) {
3820             my $str = sprintf("%.2x:%.2x:%.2x.%.2x", $a[3]&0x3f,
3821             $a[2]&0x7f, $a[1]&0x7f, $a[0]&0x3f);
3822             if ($a[3] & 0x80) { # date+timezone exist if BGF2 is set
3823             my $tz = $a[7] & 0x3f;
3824             my $bz = sprintf('%.2x', $tz);
3825             $bz = 100 if $bz =~ /[a-f]/i; # not BCD
3826             if ($bz < 26) {
3827             $tz = ($bz < 13 ? 0 : 26) - $bz;
3828             } elsif ($bz == 32) {
3829             $tz = 12.75;
3830             } elsif ($bz >= 28 and $bz <= 31) {
3831             $tz = 0; # UTC
3832             } elsif ($bz < 100) {
3833             undef $tz; # undefined or user-defined
3834             } elsif ($tz < 0x20) {
3835             $tz = (($tz < 0x10 ? 10 : 20) - $tz) - 0.5;
3836             } else {
3837             $tz = (($tz < 0x30 ? 53 : 63) - $tz) + 0.5;
3838             }
3839             if ($a[7] & 0x80) { # MJD format (/w UTC time)
3840             my ($h,$m,$s,$f) = split /[:.]/, $str;
3841             my $jday = sprintf('%x%.2x%.2x', reverse @a[4..6]);
3842             $str = ConvertUnixTime(($jday - 40587) * 24 * 3600
3843             + ((($h+$tz) * 60) + $m) * 60 + $s) . ".$f";
3844             $str =~ s/^(\d+):(\d+):(\d+) /$1-$2-${3}T/;
3845             } else { # YYMMDD (Note: CinemaDNG 1.1 example seems wrong)
3846             my $yr = sprintf('%.2x',$a[6]) + 1900;
3847             $yr += 100 if $yr < 1970;
3848             $str = sprintf('%d-%.2x-%.2xT%s',$yr,$a[5],$a[4],$str);
3849             }
3850             $str .= TimeZoneString($tz*60) if defined $tz;
3851             }
3852             push @v, $str;
3853             splice @a, 0, 8;
3854             }
3855             join ' ', @v;
3856             },
3857             PrintConvInv => q{
3858             my @a = split ' ', $val;
3859             my @v;
3860             foreach (@a) {
3861             my @td = reverse split /T/;
3862             my $tz = 0x39; # default to unknown timezone
3863             if ($td[0] =~ s/([-+])(\d+):(\d+)$//) {
3864             if ($3 == 0) {
3865             $tz = hex(($1 eq '-') ? $2 : 0x26 - $2);
3866             } elsif ($3 == 30) {
3867             if ($1 eq '-') {
3868             $tz = $2 + 0x0a;
3869             $tz += 0x0a if $tz > 0x0f;
3870             } else {
3871             $tz = 0x3f - $2;
3872             $tz -= 0x0a if $tz < 0x3a;
3873             }
3874             } elsif ($3 == 45) {
3875             $tz = 0x32 if $1 eq '+' and $2 == 12;
3876             }
3877             }
3878             my @t = split /[:.]/, $td[0];
3879             push @t, '00' while @t < 4;
3880             my $bg;
3881             if ($td[1]) {
3882             # date was specified: fill in date & timezone
3883             my @d = split /[-]/, $td[1];
3884             next if @d < 3;
3885             $bg = sprintf('.%.2d.%.2d.%.2d.%.2x', $d[2], $d[1], $d[0]%100, $tz);
3886             $t[0] = sprintf('%.2x', hex($t[0]) + 0xc0); # set BGF1+BGF2
3887             } else { # time only
3888             $bg = '.00.00.00.00';
3889             }
3890             push @v, join('.', reverse(@t[0..3])) . $bg;
3891             }
3892             join ' ', @v;
3893             },
3894             },
3895             0xc764 => { #28
3896             Name => 'FrameRate',
3897             Writable => 'rational64s',
3898             WriteGroup => 'IFD0',
3899             PrintConv => 'int($val * 1000 + 0.5) / 1000',
3900             PrintConvInv => '$val',
3901             },
3902             0xc772 => { #28
3903             Name => 'TStop',
3904             Writable => 'rational64u',
3905             WriteGroup => 'IFD0',
3906             Count => -1, # (1 or 2)
3907             PrintConv => 'join("-", map { sprintf("%.2f",$_) } split " ", $val)',
3908             PrintConvInv => '$val=~tr/-/ /; $val',
3909             },
3910             0xc789 => { #28
3911             Name => 'ReelName',
3912             Writable => 'string',
3913             WriteGroup => 'IFD0',
3914             },
3915             0xc791 => { # DNG 1.4
3916             Name => 'OriginalDefaultFinalSize',
3917             Writable => 'int32u',
3918             WriteGroup => 'IFD0',
3919             Count => 2,
3920             Protected => 1,
3921             },
3922             0xc792 => { # DNG 1.4
3923             Name => 'OriginalBestQualitySize',
3924             Notes => 'called OriginalBestQualityFinalSize by the DNG spec',
3925             Writable => 'int32u',
3926             WriteGroup => 'IFD0',
3927             Count => 2,
3928             Protected => 1,
3929             },
3930             0xc793 => { # DNG 1.4
3931             Name => 'OriginalDefaultCropSize',
3932             Writable => 'rational64u',
3933             WriteGroup => 'IFD0',
3934             Count => 2,
3935             Protected => 1,
3936             },
3937             0xc7a1 => { #28
3938             Name => 'CameraLabel',
3939             Writable => 'string',
3940             WriteGroup => 'IFD0',
3941             },
3942             0xc7a3 => { # DNG 1.4
3943             Name => 'ProfileHueSatMapEncoding',
3944             Writable => 'int32u',
3945             WriteGroup => 'IFD0',
3946             Protected => 1,
3947             PrintConv => {
3948             0 => 'Linear',
3949             1 => 'sRGB',
3950             },
3951             },
3952             0xc7a4 => { # DNG 1.4
3953             Name => 'ProfileLookTableEncoding',
3954             Writable => 'int32u',
3955             WriteGroup => 'IFD0',
3956             Protected => 1,
3957             PrintConv => {
3958             0 => 'Linear',
3959             1 => 'sRGB',
3960             },
3961             },
3962             0xc7a5 => { # DNG 1.4
3963             Name => 'BaselineExposureOffset',
3964             Writable => 'rational64s', # (incorrectly "RATIONAL" in DNG 1.4 spec)
3965             WriteGroup => 'IFD0',
3966             Protected => 1,
3967             },
3968             0xc7a6 => { # DNG 1.4
3969             Name => 'DefaultBlackRender',
3970             Writable => 'int32u',
3971             WriteGroup => 'IFD0',
3972             Protected => 1,
3973             PrintConv => {
3974             0 => 'Auto',
3975             1 => 'None',
3976             },
3977             },
3978             0xc7a7 => { # DNG 1.4
3979             Name => 'NewRawImageDigest',
3980             Format => 'undef',
3981             Writable => 'int8u',
3982             WriteGroup => 'IFD0',
3983             Count => 16,
3984             Protected => 1,
3985             ValueConv => 'unpack("H*", $val)',
3986             ValueConvInv => 'pack("H*", $val)',
3987             },
3988             0xc7a8 => { # DNG 1.4
3989             Name => 'RawToPreviewGain',
3990             Writable => 'double',
3991             WriteGroup => 'IFD0',
3992             Protected => 1,
3993             },
3994             # 0xc7a9 - CacheBlob (ref 31)
3995             0xc7aa => { #31 undocumented DNG tag written by LR4 (val=256, related to fast load data?)
3996             Name => 'CacheVersion',
3997             Writable => 'int32u',
3998             WriteGroup => 'SubIFD2',
3999             Format => 'int8u',
4000             Count => 4,
4001             Protected => 1,
4002             PrintConv => '$val =~ tr/ /./; $val',
4003             PrintConvInv => '$val =~ tr/./ /; $val',
4004             },
4005             0xc7b5 => { # DNG 1.4
4006             Name => 'DefaultUserCrop',
4007             Writable => 'rational64u',
4008             WriteGroup => 'SubIFD',
4009             Count => 4,
4010             Protected => 1,
4011             },
4012             0xc7d5 => { #PH (in SubIFD1 of Nikon Z6/Z7 NEF images)
4013             Name => 'NikonNEFInfo',
4014             Condition => '$$valPt =~ /^Nikon\0/',
4015             SubDirectory => {
4016             TagTable => 'Image::ExifTool::Nikon::NEFInfo',
4017             Start => '$valuePtr + 18',
4018             Base => '$start - 8',
4019             ByteOrder => 'Unknown',
4020             },
4021             },
4022             # 0xc7d6 - int8u: 1 (SubIFD1 of Nikon Z6/Z7 NEF)
4023             0xc7e9 => { # DNG 1.5
4024             Name => 'DepthFormat',
4025             Writable => 'int16u',
4026             Notes => 'tags 0xc7e9-0xc7ee added by DNG 1.5.0.0',
4027             Protected => 1,
4028             WriteGroup => 'IFD0',
4029             PrintConv => {
4030             0 => 'Unknown',
4031             1 => 'Linear',
4032             2 => 'Inverse',
4033             },
4034             },
4035             0xc7ea => { # DNG 1.5
4036             Name => 'DepthNear',
4037             Writable => 'rational64u',
4038             Protected => 1,
4039             WriteGroup => 'IFD0',
4040             },
4041             0xc7eb => { # DNG 1.5
4042             Name => 'DepthFar',
4043             Writable => 'rational64u',
4044             Protected => 1,
4045             WriteGroup => 'IFD0',
4046             },
4047             0xc7ec => { # DNG 1.5
4048             Name => 'DepthUnits',
4049             Writable => 'int16u',
4050             Protected => 1,
4051             WriteGroup => 'IFD0',
4052             PrintConv => {
4053             0 => 'Unknown',
4054             1 => 'Meters',
4055             },
4056             },
4057             0xc7ed => { # DNG 1.5
4058             Name => 'DepthMeasureType',
4059             Writable => 'int16u',
4060             Protected => 1,
4061             WriteGroup => 'IFD0',
4062             PrintConv => {
4063             0 => 'Unknown',
4064             1 => 'Optical Axis',
4065             2 => 'Optical Ray',
4066             },
4067             },
4068             0xc7ee => { # DNG 1.5
4069             Name => 'EnhanceParams',
4070             Writable => 'string',
4071             Protected => 1,
4072             WriteGroup => 'IFD0',
4073             },
4074             0xcd2d => { # DNG 1.6
4075             Name => 'ProfileGainTableMap',
4076             Writable => 'undef',
4077             WriteGroup => 'SubIFD',
4078             Protected => 1,
4079             Binary => 1,
4080             },
4081             0xcd2e => { # DNG 1.6
4082             Name => 'SemanticName',
4083             # Writable => 'string',
4084             WriteGroup => 'SubIFD' #? (NC) Semantic Mask IFD (only for Validate)
4085             },
4086             0xcd30 => { # DNG 1.6
4087             Name => 'SemanticInstanceID',
4088             # Writable => 'string',
4089             WriteGroup => 'SubIFD' #? (NC) Semantic Mask IFD (only for Validate)
4090             },
4091             0xcd31 => { # DNG 1.6
4092             Name => 'CalibrationIlluminant3',
4093             Writable => 'int16u',
4094             WriteGroup => 'IFD0',
4095             Protected => 1,
4096             SeparateTable => 'LightSource',
4097             PrintConv => \%lightSource,
4098             },
4099             0xcd32 => { # DNG 1.6
4100             Name => 'CameraCalibration3',
4101             Writable => 'rational64s',
4102             WriteGroup => 'IFD0',
4103             Count => -1,
4104             Protected => 1,
4105             },
4106             0xcd33 => { # DNG 1.6
4107             Name => 'ColorMatrix3',
4108             Writable => 'rational64s',
4109             WriteGroup => 'IFD0',
4110             Count => -1,
4111             Protected => 1,
4112             },
4113             0xcd34 => { # DNG 1.6
4114             Name => 'ForwardMatrix3',
4115             Writable => 'rational64s',
4116             WriteGroup => 'IFD0',
4117             Count => -1,
4118             Protected => 1,
4119             },
4120             0xcd35 => { # DNG 1.6
4121             Name => 'IlluminantData1',
4122             Writable => 'undef',
4123             WriteGroup => 'IFD0',
4124             Protected => 1,
4125             },
4126             0xcd36 => { # DNG 1.6
4127             Name => 'IlluminantData2',
4128             Writable => 'undef',
4129             WriteGroup => 'IFD0',
4130             Protected => 1,
4131             },
4132             0xcd37 => { # DNG 1.6
4133             Name => 'IlluminantData3',
4134             Writable => 'undef',
4135             WriteGroup => 'IFD0',
4136             Protected => 1,
4137             },
4138             0xcd38 => { # DNG 1.6
4139             Name => 'MaskSubArea',
4140             # Writable => 'int32u',
4141             WriteGroup => 'SubIFD', #? (NC) Semantic Mask IFD (only for Validate)
4142             Count => 4,
4143             },
4144             0xcd39 => { # DNG 1.6
4145             Name => 'ProfileHueSatMapData3',
4146             %longBin,
4147             Writable => 'float',
4148             WriteGroup => 'IFD0',
4149             Count => -1,
4150             Protected => 1,
4151             },
4152             0xcd3a => { # DNG 1.6
4153             Name => 'ReductionMatrix3',
4154             Writable => 'rational64s',
4155             WriteGroup => 'IFD0',
4156             Count => -1,
4157             Protected => 1,
4158             },
4159             0xcd3b => { # DNG 1.6
4160             Name => 'RGBTables',
4161             Writable => 'undef',
4162             WriteGroup => 'IFD0',
4163             Protected => 1,
4164             },
4165             0xea1c => { #13
4166             Name => 'Padding',
4167             Binary => 1,
4168             Protected => 1,
4169             Writable => 'undef',
4170             # must start with 0x1c 0xea by the WM Photo specification
4171             # (not sure what should happen if padding is only 1 byte)
4172             # (why does MicrosoftPhoto write "1c ea 00 00 00 08"?)
4173             RawConvInv => '$val=~s/^../\x1c\xea/s; $val',
4174             },
4175             0xea1d => {
4176             Name => 'OffsetSchema',
4177             Notes => "Microsoft's ill-conceived maker note offset difference",
4178             Protected => 1,
4179             Writable => 'int32s',
4180             # From the Microsoft documentation:
4181             #
4182             # Any time the "Maker Note" is relocated by Windows, the Exif MakerNote
4183             # tag (37500) is updated automatically to reference the new location. In
4184             # addition, Windows records the offset (or difference) between the old and
4185             # new locations in the Exif OffsetSchema tag (59933). If the "Maker Note"
4186             # contains relative references, the developer can add the value in
4187             # OffsetSchema to the original references to find the correct information.
4188             #
4189             # My recommendation is for other developers to ignore this tag because the
4190             # information it contains is unreliable. It will be wrong if the image has
4191             # been subsequently edited by another application that doesn't recognize the
4192             # new Microsoft tag.
4193             #
4194             # The new tag unfortunately only gives the difference between the new maker
4195             # note offset and the original offset. Instead, it should have been designed
4196             # to store the original offset. The new offset may change if the image is
4197             # edited, which will invalidate the tag as currently written. If instead the
4198             # original offset had been stored, the new difference could be easily
4199             # calculated because the new maker note offset is known.
4200             #
4201             # I exchanged emails with a Microsoft technical representative, pointing out
4202             # this problem shortly after they released the update (Feb 2007), but so far
4203             # they have taken no steps to address this.
4204             },
4205             # 0xefee - int16u: 0 - seen this from a WIC-scanned image
4206              
4207             # tags in the range 0xfde8-0xfe58 have been observed in PS7 files
4208             # generated from RAW images. They are all strings with the
4209             # tag name at the start of the string. To accommodate these types
4210             # of tags, all tags with values above 0xf000 are handled specially
4211             # by ProcessExif().
4212             0xfde8 => {
4213             Name => 'OwnerName',
4214             Condition => '$$self{TIFF_TYPE} ne "DCR"', # (used for another purpose in Kodak DCR images)
4215             Avoid => 1,
4216             PSRaw => 1,
4217             Writable => 'string',
4218             ValueConv => '$val=~s/^.*: //;$val',
4219             ValueConvInv => q{"Owner's Name: $val"},
4220             Notes => q{
4221             tags 0xfde8-0xfdea and 0xfe4c-0xfe58 are generated by Photoshop Camera RAW.
4222             Some names are the same as other EXIF tags, but ExifTool will avoid writing
4223             these unless they already exist in the file
4224             },
4225             },
4226             0xfde9 => {
4227             Name => 'SerialNumber',
4228             Condition => '$$self{TIFF_TYPE} ne "DCR"', # (used for another purpose in Kodak DCR SubIFD)
4229             Avoid => 1,
4230             PSRaw => 1,
4231             Writable => 'string',
4232             ValueConv => '$val=~s/^.*: //;$val',
4233             ValueConvInv => q{"Serial Number: $val"},
4234             },
4235             0xfdea => {
4236             Name => 'Lens',
4237             Condition => '$$self{TIFF_TYPE} ne "DCR"', # (used for another purpose in Kodak DCR SubIFD)
4238             Avoid => 1,
4239             PSRaw => 1,
4240             Writable => 'string',
4241             ValueConv => '$val=~s/^.*: //;$val',
4242             ValueConvInv => q{"Lens: $val"},
4243             },
4244             0xfe4c => {
4245             Name => 'RawFile',
4246             Avoid => 1,
4247             PSRaw => 1,
4248             Writable => 'string',
4249             ValueConv => '$val=~s/^.*: //;$val',
4250             ValueConvInv => q{"Raw File: $val"},
4251             },
4252             0xfe4d => {
4253             Name => 'Converter',
4254             Avoid => 1,
4255             PSRaw => 1,
4256             Writable => 'string',
4257             ValueConv => '$val=~s/^.*: //;$val',
4258             ValueConvInv => q{"Converter: $val"},
4259             },
4260             0xfe4e => {
4261             Name => 'WhiteBalance',
4262             Avoid => 1,
4263             PSRaw => 1,
4264             Writable => 'string',
4265             ValueConv => '$val=~s/^.*: //;$val',
4266             ValueConvInv => q{"White Balance: $val"},
4267             },
4268             0xfe51 => {
4269             Name => 'Exposure',
4270             Avoid => 1,
4271             PSRaw => 1,
4272             Writable => 'string',
4273             ValueConv => '$val=~s/^.*: //;$val',
4274             ValueConvInv => q{"Exposure: $val"},
4275             },
4276             0xfe52 => {
4277             Name => 'Shadows',
4278             Avoid => 1,
4279             PSRaw => 1,
4280             Writable => 'string',
4281             ValueConv => '$val=~s/^.*: //;$val',
4282             ValueConvInv => q{"Shadows: $val"},
4283             },
4284             0xfe53 => {
4285             Name => 'Brightness',
4286             Avoid => 1,
4287             PSRaw => 1,
4288             Writable => 'string',
4289             ValueConv => '$val=~s/^.*: //;$val',
4290             ValueConvInv => q{"Brightness: $val"},
4291             },
4292             0xfe54 => {
4293             Name => 'Contrast',
4294             Avoid => 1,
4295             PSRaw => 1,
4296             Writable => 'string',
4297             ValueConv => '$val=~s/^.*: //;$val',
4298             ValueConvInv => q{"Contrast: $val"},
4299             },
4300             0xfe55 => {
4301             Name => 'Saturation',
4302             Avoid => 1,
4303             PSRaw => 1,
4304             Writable => 'string',
4305             ValueConv => '$val=~s/^.*: //;$val',
4306             ValueConvInv => q{"Saturation: $val"},
4307             },
4308             0xfe56 => {
4309             Name => 'Sharpness',
4310             Avoid => 1,
4311             PSRaw => 1,
4312             Writable => 'string',
4313             ValueConv => '$val=~s/^.*: //;$val',
4314             ValueConvInv => q{"Sharpness: $val"},
4315             },
4316             0xfe57 => {
4317             Name => 'Smoothness',
4318             Avoid => 1,
4319             PSRaw => 1,
4320             Writable => 'string',
4321             ValueConv => '$val=~s/^.*: //;$val',
4322             ValueConvInv => q{"Smoothness: $val"},
4323             },
4324             0xfe58 => {
4325             Name => 'MoireFilter',
4326             Avoid => 1,
4327             PSRaw => 1,
4328             Writable => 'string',
4329             ValueConv => '$val=~s/^.*: //;$val',
4330             ValueConvInv => q{"Moire Filter: $val"},
4331             },
4332              
4333             #-------------
4334             0xfe00 => {
4335             Name => 'KDC_IFD',
4336             Groups => { 1 => 'KDC_IFD' },
4337             Flags => 'SubIFD',
4338             Notes => 'used in some Kodak KDC images',
4339             SubDirectory => {
4340             TagTable => 'Image::ExifTool::Kodak::KDC_IFD',
4341             DirName => 'KDC_IFD',
4342             Start => '$val',
4343             },
4344             },
4345             );
4346              
4347             # conversions for Composite SubSec date/time tags
4348             my %subSecConv = (
4349             # @val array: 0) date/time, 1) sub-seconds, 2) time zone offset
4350             RawConv => q{
4351             my $v;
4352             if (defined $val[1] and $val[1]=~/^(\d+)/) {
4353             my $subSec = $1;
4354             # be careful here just in case the time already contains sub-seconds or a timezone (contrary to spec)
4355             undef $v unless ($v = $val[0]) =~ s/( \d{2}:\d{2}:\d{2})(?!\.\d+)/$1\.$subSec/;
4356             }
4357             if (defined $val[2] and $val[0]!~/[-+]/ and $val[2]=~/^([-+])(\d{1,2}):(\d{2})/) {
4358             $v = ($v || $val[0]) . sprintf('%s%.2d:%.2d', $1, $2, $3);
4359             }
4360             return $v;
4361             },
4362             PrintConv => '$self->ConvertDateTime($val)',
4363             PrintConvInv => '$self->InverseDateTime($val)',
4364             );
4365              
4366             # EXIF Composite tags (plus other more general Composite tags)
4367             %Image::ExifTool::Exif::Composite = (
4368             GROUPS => { 2 => 'Image' },
4369             ImageSize => {
4370             Require => {
4371             0 => 'ImageWidth',
4372             1 => 'ImageHeight',
4373             },
4374             Desire => {
4375             2 => 'ExifImageWidth',
4376             3 => 'ExifImageHeight',
4377             4 => 'RawImageCroppedSize', # (FujiFilm RAF images)
4378             },
4379             # use ExifImageWidth/Height only for Canon and Phase One TIFF-base RAW images
4380             ValueConv => q{
4381             return $val[4] if $val[4];
4382             return "$val[2] $val[3]" if $val[2] and $val[3] and
4383             $$self{TIFF_TYPE} =~ /^(CR2|Canon 1D RAW|IIQ|EIP)$/;
4384             return "$val[0] $val[1]" if IsFloat($val[0]) and IsFloat($val[1]);
4385             return undef;
4386             },
4387             PrintConv => '$val =~ tr/ /x/; $val',
4388             },
4389             Megapixels => {
4390             Require => 'ImageSize',
4391             ValueConv => 'my @d = ($val =~ /\d+/g); $d[0] * $d[1] / 1000000',
4392             PrintConv => 'sprintf("%.*f", ($val >= 1 ? 1 : ($val >= 0.001 ? 3 : 6)), $val)',
4393             },
4394             # pick the best shutter speed value
4395             ShutterSpeed => {
4396             Desire => {
4397             0 => 'ExposureTime',
4398             1 => 'ShutterSpeedValue',
4399             2 => 'BulbDuration',
4400             },
4401             ValueConv => '($val[2] and $val[2]>0) ? $val[2] : (defined($val[0]) ? $val[0] : $val[1])',
4402             PrintConv => 'Image::ExifTool::Exif::PrintExposureTime($val)',
4403             },
4404             Aperture => {
4405             Desire => {
4406             0 => 'FNumber',
4407             1 => 'ApertureValue',
4408             },
4409             RawConv => '($val[0] || $val[1]) ? $val : undef',
4410             ValueConv => '$val[0] || $val[1]',
4411             PrintConv => 'Image::ExifTool::Exif::PrintFNumber($val)',
4412             },
4413             LightValue => {
4414             Notes => q{
4415             calculated LV = 2 * log2(Aperture) - log2(ShutterSpeed) - log2(ISO/100);
4416             similar to exposure value but normalized to ISO 100
4417             },
4418             Require => {
4419             0 => 'Aperture',
4420             1 => 'ShutterSpeed',
4421             2 => 'ISO',
4422             },
4423             ValueConv => 'Image::ExifTool::Exif::CalculateLV($val[0],$val[1],$prt[2])',
4424             PrintConv => 'sprintf("%.1f",$val)',
4425             },
4426             FocalLength35efl => { #26/PH
4427             Description => 'Focal Length',
4428             Notes => 'this value may be incorrect if the image has been resized',
4429             Groups => { 2 => 'Camera' },
4430             Require => {
4431             0 => 'FocalLength',
4432             },
4433             Desire => {
4434             1 => 'ScaleFactor35efl',
4435             },
4436             ValueConv => 'ToFloat(@val); ($val[0] || 0) * ($val[1] || 1)',
4437             PrintConv => '$val[1] ? sprintf("%.1f mm (35 mm equivalent: %.1f mm)", $val[0], $val) : sprintf("%.1f mm", $val)',
4438             },
4439             ScaleFactor35efl => { #26/PH
4440             Description => 'Scale Factor To 35 mm Equivalent',
4441             Notes => q{
4442             this value and any derived values may be incorrect if the image has been
4443             resized
4444             },
4445             Groups => { 2 => 'Camera' },
4446             Desire => {
4447             0 => 'FocalLength',
4448             1 => 'FocalLengthIn35mmFormat',
4449             2 => 'Composite:DigitalZoom',
4450             3 => 'FocalPlaneDiagonal',
4451             4 => 'SensorSize',
4452             5 => 'FocalPlaneXSize',
4453             6 => 'FocalPlaneYSize',
4454             7 => 'FocalPlaneResolutionUnit',
4455             8 => 'FocalPlaneXResolution',
4456             9 => 'FocalPlaneYResolution',
4457             10 => 'ExifImageWidth',
4458             11 => 'ExifImageHeight',
4459             12 => 'CanonImageWidth',
4460             13 => 'CanonImageHeight',
4461             14 => 'ImageWidth',
4462             15 => 'ImageHeight',
4463             },
4464             ValueConv => 'Image::ExifTool::Exif::CalcScaleFactor35efl($self, @val)',
4465             PrintConv => 'sprintf("%.1f", $val)',
4466             },
4467             CircleOfConfusion => {
4468             Notes => q{
4469             calculated as D/1440, where D is the focal plane diagonal in mm. This value
4470             may be incorrect if the image has been resized
4471             },
4472             Groups => { 2 => 'Camera' },
4473             Require => 'ScaleFactor35efl',
4474             ValueConv => 'sqrt(24*24+36*36) / ($val * 1440)',
4475             PrintConv => 'sprintf("%.3f mm",$val)',
4476             },
4477             HyperfocalDistance => {
4478             Notes => 'this value may be incorrect if the image has been resized',
4479             Groups => { 2 => 'Camera' },
4480             Require => {
4481             0 => 'FocalLength',
4482             1 => 'Aperture',
4483             2 => 'CircleOfConfusion',
4484             },
4485             ValueConv => q{
4486             ToFloat(@val);
4487             return 'inf' unless $val[1] and $val[2];
4488             return $val[0] * $val[0] / ($val[1] * $val[2] * 1000);
4489             },
4490             PrintConv => 'sprintf("%.2f m", $val)',
4491             },
4492             DOF => {
4493             Description => 'Depth Of Field',
4494             Notes => 'this value may be incorrect if the image has been resized',
4495             Require => {
4496             0 => 'FocalLength',
4497             1 => 'Aperture',
4498             2 => 'CircleOfConfusion',
4499             },
4500             Desire => {
4501             3 => 'FocusDistance', # focus distance in metres (0 is infinity)
4502             4 => 'SubjectDistance',
4503             5 => 'ObjectDistance',
4504             6 => 'ApproximateFocusDistance',
4505             7 => 'FocusDistanceLower',
4506             8 => 'FocusDistanceUpper',
4507             },
4508             ValueConv => q{
4509             ToFloat(@val);
4510             my ($d, $f) = ($val[3], $val[0]);
4511             if (defined $d) {
4512             $d or $d = 1e10; # (use large number for infinity)
4513             } else {
4514             $d = $val[4] || $val[5] || $val[6];
4515             unless (defined $d) {
4516             return undef unless defined $val[7] and defined $val[8];
4517             $d = ($val[7] + $val[8]) / 2;
4518             }
4519             }
4520             return 0 unless $f and $val[2];
4521             my $t = $val[1] * $val[2] * ($d * 1000 - $f) / ($f * $f);
4522             my @v = ($d / (1 + $t), $d / (1 - $t));
4523             $v[1] < 0 and $v[1] = 0; # 0 means 'inf'
4524             return join(' ',@v);
4525             },
4526             PrintConv => q{
4527             $val =~ tr/,/./; # in case locale is whacky
4528             my @v = split ' ', $val;
4529             $v[1] or return sprintf("inf (%.2f m - inf)", $v[0]);
4530             my $dof = $v[1] - $v[0];
4531             my $fmt = ($dof>0 and $dof<0.02) ? "%.3f" : "%.2f";
4532             return sprintf("$fmt m ($fmt - $fmt m)",$dof,$v[0],$v[1]);
4533             },
4534             },
4535             FOV => {
4536             Description => 'Field Of View',
4537             Notes => q{
4538             calculated for the long image dimension. This value may be incorrect for
4539             fisheye lenses, or if the image has been resized
4540             },
4541             Require => {
4542             0 => 'FocalLength',
4543             1 => 'ScaleFactor35efl',
4544             },
4545             Desire => {
4546             2 => 'FocusDistance', # (multiply by 1000 to convert to mm)
4547             },
4548             # ref http://www.bobatkins.com/photography/technical/field_of_view.html
4549             # (calculations below apply to rectilinear lenses only, not fisheye)
4550             ValueConv => q{
4551             ToFloat(@val);
4552             return undef unless $val[0] and $val[1];
4553             my $corr = 1;
4554             if ($val[2]) {
4555             my $d = 1000 * $val[2] - $val[0];
4556             $corr += $val[0]/$d if $d > 0;
4557             }
4558             my $fd2 = atan2(36, 2*$val[0]*$val[1]*$corr);
4559             my @fov = ( $fd2 * 360 / 3.14159 );
4560             if ($val[2] and $val[2] > 0 and $val[2] < 10000) {
4561             push @fov, 2 * $val[2] * sin($fd2) / cos($fd2);
4562             }
4563             return join(' ', @fov);
4564             },
4565             PrintConv => q{
4566             my @v = split(' ',$val);
4567             my $str = sprintf("%.1f deg", $v[0]);
4568             $str .= sprintf(" (%.2f m)", $v[1]) if $v[1];
4569             return $str;
4570             },
4571             },
4572             # generate DateTimeOriginal from Date and Time Created if not extracted already
4573             DateTimeOriginal => {
4574             Condition => 'not defined $$self{VALUE}{DateTimeOriginal}',
4575             Description => 'Date/Time Original',
4576             Groups => { 2 => 'Time' },
4577             Desire => {
4578             0 => 'DateTimeCreated',
4579             1 => 'DateCreated',
4580             2 => 'TimeCreated',
4581             },
4582             RawConv => '($val[1] and $val[2]) ? $val : undef',
4583             ValueConv => q{
4584             return $val[0] if $val[0] and $val[0]=~/ /;
4585             return "$val[1] $val[2]";
4586             },
4587             PrintConv => '$self->ConvertDateTime($val)',
4588             },
4589             ThumbnailImage => {
4590             Groups => { 0 => 'EXIF', 1 => 'IFD1', 2 => 'Preview' },
4591             Writable => 1,
4592             WriteGroup => 'All',
4593             WriteCheck => '$self->CheckImage(\$val)',
4594             WriteAlso => {
4595             # (the 0xfeedfeed values are translated in the Exif write routine)
4596             ThumbnailOffset => 'defined $val ? 0xfeedfeed : undef',
4597             ThumbnailLength => 'defined $val ? 0xfeedfeed : undef',
4598             },
4599             Require => {
4600             0 => 'ThumbnailOffset',
4601             1 => 'ThumbnailLength',
4602             },
4603             Notes => q{
4604             this tag is writable, and may be used to update existing thumbnails, but may
4605             only create a thumbnail in IFD1 of certain types of files. Note that for
4606             this and other Composite embedded-image tags the family 0 and 1 groups match
4607             those of the originating tags
4608             },
4609             # retrieve the thumbnail from our EXIF data
4610             RawConv => q{
4611             @grps = $self->GetGroup($$val{0}); # set groups from ThumbnailOffsets
4612             Image::ExifTool::Exif::ExtractImage($self,$val[0],$val[1],"ThumbnailImage");
4613             },
4614             },
4615             ThumbnailTIFF => {
4616             Groups => { 2 => 'Preview' },
4617             Require => {
4618             0 => 'SubfileType',
4619             1 => 'Compression',
4620             2 => 'ImageWidth',
4621             3 => 'ImageHeight',
4622             4 => 'BitsPerSample',
4623             5 => 'PhotometricInterpretation',
4624             6 => 'StripOffsets',
4625             7 => 'SamplesPerPixel',
4626             8 => 'RowsPerStrip',
4627             9 => 'StripByteCounts',
4628             },
4629             Desire => {
4630             10 => 'PlanarConfiguration',
4631             11 => 'Orientation',
4632             },
4633             # rebuild the TIFF thumbnail from our EXIF data
4634             RawConv => q{
4635             my $tiff;
4636             ($tiff, @grps) = Image::ExifTool::Exif::RebuildTIFF($self, @val);
4637             return $tiff;
4638             },
4639             },
4640             PreviewImage => {
4641             Groups => { 0 => 'EXIF', 1 => 'SubIFD', 2 => 'Preview' },
4642             Writable => 1,
4643             WriteGroup => 'All',
4644             WriteCheck => '$self->CheckImage(\$val)',
4645             DelCheck => '$val = ""; return undef', # can't delete, so set to empty string
4646             WriteAlso => {
4647             PreviewImageStart => 'defined $val ? 0xfeedfeed : undef',
4648             PreviewImageLength => 'defined $val ? 0xfeedfeed : undef',
4649             PreviewImageValid => 'defined $val and length $val ? 1 : 0', # (for Olympus)
4650             },
4651             Require => {
4652             0 => 'PreviewImageStart',
4653             1 => 'PreviewImageLength',
4654             },
4655             Desire => {
4656             2 => 'PreviewImageValid',
4657             # (DNG and A100 ARW may be have 2 preview images)
4658             3 => 'PreviewImageStart (1)',
4659             4 => 'PreviewImageLength (1)',
4660             },
4661             Notes => q{
4662             this tag is writable, and may be used to update existing embedded images,
4663             but not create or delete them
4664             },
4665             # note: extract 2nd preview, but ignore double-referenced preview
4666             # (in A100 ARW images, the 2nd PreviewImageLength from IFD0 may be wrong anyway)
4667             RawConv => q{
4668             if ($val[3] and $val[4] and $val[0] ne $val[3]) {
4669             my %val = (
4670             0 => 'PreviewImageStart (1)',
4671             1 => 'PreviewImageLength (1)',
4672             2 => 'PreviewImageValid',
4673             );
4674             $self->FoundTag($tagInfo, \%val);
4675             }
4676             return undef if defined $val[2] and not $val[2];
4677             @grps = $self->GetGroup($$val{0});
4678             return Image::ExifTool::Exif::ExtractImage($self,$val[0],$val[1],'PreviewImage');
4679             },
4680             },
4681             JpgFromRaw => {
4682             Groups => { 0 => 'EXIF', 1 => 'SubIFD', 2 => 'Preview' },
4683             Writable => 1,
4684             WriteGroup => 'All',
4685             WriteCheck => '$self->CheckImage(\$val)',
4686             # Note: ExifTool 10.38 had disabled the ability to delete this -- why?
4687             # --> added the DelCheck in 10.61 to re-enable this
4688             DelCheck => '$val = ""; return undef', # can't delete, so set to empty string
4689             WriteAlso => {
4690             JpgFromRawStart => 'defined $val ? 0xfeedfeed : undef',
4691             JpgFromRawLength => 'defined $val ? 0xfeedfeed : undef',
4692             },
4693             Require => {
4694             0 => 'JpgFromRawStart',
4695             1 => 'JpgFromRawLength',
4696             },
4697             Notes => q{
4698             this tag is writable, and may be used to update existing embedded images,
4699             but not create or delete them
4700             },
4701             RawConv => q{
4702             @grps = $self->GetGroup($$val{0});
4703             return Image::ExifTool::Exif::ExtractImage($self,$val[0],$val[1],"JpgFromRaw");
4704             },
4705             },
4706             OtherImage => {
4707             Groups => { 0 => 'EXIF', 1 => 'SubIFD', 2 => 'Preview' },
4708             Writable => 1,
4709             WriteGroup => 'All',
4710             WriteCheck => '$self->CheckImage(\$val)',
4711             DelCheck => '$val = ""; return undef', # can't delete, so set to empty string
4712             WriteAlso => {
4713             OtherImageStart => 'defined $val ? 0xfeedfeed : undef',
4714             OtherImageLength => 'defined $val ? 0xfeedfeed : undef',
4715             },
4716             Require => {
4717             0 => 'OtherImageStart',
4718             1 => 'OtherImageLength',
4719             },
4720             Notes => q{
4721             this tag is writable, and may be used to update existing embedded images,
4722             but not create or delete them
4723             },
4724             # retrieve the thumbnail from our EXIF data
4725             RawConv => q{
4726             @grps = $self->GetGroup($$val{0});
4727             Image::ExifTool::Exif::ExtractImage($self,$val[0],$val[1],"OtherImage");
4728             },
4729             },
4730             PreviewImageSize => {
4731             Require => {
4732             0 => 'PreviewImageWidth',
4733             1 => 'PreviewImageHeight',
4734             },
4735             ValueConv => '"$val[0]x$val[1]"',
4736             },
4737             SubSecDateTimeOriginal => {
4738             Description => 'Date/Time Original',
4739             Groups => { 2 => 'Time' },
4740             Writable => 1,
4741             Shift => 0, # don't shift this tag
4742             Require => {
4743             0 => 'EXIF:DateTimeOriginal',
4744             },
4745             Desire => {
4746             1 => 'SubSecTimeOriginal',
4747             2 => 'OffsetTimeOriginal',
4748             },
4749             WriteAlso => {
4750             'EXIF:DateTimeOriginal' => '($val and $val=~/^(\d{4}:\d{2}:\d{2} \d{2}:\d{2}:\d{2})/) ? $1 : undef',
4751             'EXIF:SubSecTimeOriginal' => '($val and $val=~/\.(\d+)/) ? $1 : undef',
4752             'EXIF:OffsetTimeOriginal' => '($val and $val=~/([-+]\d{2}:\d{2}|Z)$/) ? ($1 eq "Z" ? "+00:00" : $1) : undef',
4753             },
4754             %subSecConv,
4755             },
4756             SubSecCreateDate => {
4757             Description => 'Create Date',
4758             Groups => { 2 => 'Time' },
4759             Writable => 1,
4760             Shift => 0, # don't shift this tag
4761             Require => {
4762             0 => 'EXIF:CreateDate',
4763             },
4764             Desire => {
4765             1 => 'SubSecTimeDigitized',
4766             2 => 'OffsetTimeDigitized',
4767             },
4768             WriteAlso => {
4769             'EXIF:CreateDate' => '($val and $val=~/^(\d{4}:\d{2}:\d{2} \d{2}:\d{2}:\d{2})/) ? $1 : undef',
4770             'EXIF:SubSecTimeDigitized' => '($val and $val=~/\.(\d+)/) ? $1 : undef',
4771             'EXIF:OffsetTimeDigitized' => '($val and $val=~/([-+]\d{2}:\d{2}|Z)$/) ? ($1 eq "Z" ? "+00:00" : $1) : undef',
4772             },
4773             %subSecConv,
4774             },
4775             SubSecModifyDate => {
4776             Description => 'Modify Date',
4777             Groups => { 2 => 'Time' },
4778             Writable => 1,
4779             Shift => 0, # don't shift this tag
4780             Require => {
4781             0 => 'EXIF:ModifyDate',
4782             },
4783             Desire => {
4784             1 => 'SubSecTime',
4785             2 => 'OffsetTime',
4786             },
4787             WriteAlso => {
4788             'EXIF:ModifyDate' => '($val and $val=~/^(\d{4}:\d{2}:\d{2} \d{2}:\d{2}:\d{2})/) ? $1 : undef',
4789             'EXIF:SubSecTime' => '($val and $val=~/\.(\d+)/) ? $1 : undef',
4790             'EXIF:OffsetTime' => '($val and $val=~/([-+]\d{2}:\d{2}|Z)$/) ? ($1 eq "Z" ? "+00:00" : $1) : undef',
4791             },
4792             %subSecConv,
4793             },
4794             CFAPattern => {
4795             Require => {
4796             0 => 'CFARepeatPatternDim',
4797             1 => 'CFAPattern2',
4798             },
4799             # generate CFAPattern
4800             ValueConv => q{
4801             my @a = split / /, $val[0];
4802             my @b = split / /, $val[1];
4803             return '?' unless @a==2 and @b==$a[0]*$a[1];
4804             return "$a[0] $a[1] @b";
4805             },
4806             PrintConv => 'Image::ExifTool::Exif::PrintCFAPattern($val)',
4807             },
4808             RedBalance => {
4809             Groups => { 2 => 'Camera' },
4810             Desire => {
4811             0 => 'WB_RGGBLevels',
4812             1 => 'WB_RGBGLevels',
4813             2 => 'WB_RBGGLevels',
4814             3 => 'WB_GRBGLevels',
4815             4 => 'WB_GRGBLevels',
4816             5 => 'WB_GBRGLevels',
4817             6 => 'WB_RGBLevels',
4818             7 => 'WB_GRBLevels',
4819             8 => 'WB_RBLevels',
4820             9 => 'WBRedLevel', # red
4821             10 => 'WBGreenLevel',
4822             },
4823             ValueConv => 'Image::ExifTool::Exif::RedBlueBalance(0,@val)',
4824             PrintConv => 'int($val * 1e6 + 0.5) * 1e-6',
4825             },
4826             BlueBalance => {
4827             Groups => { 2 => 'Camera' },
4828             Desire => {
4829             0 => 'WB_RGGBLevels',
4830             1 => 'WB_RGBGLevels',
4831             2 => 'WB_RBGGLevels',
4832             3 => 'WB_GRBGLevels',
4833             4 => 'WB_GRGBLevels',
4834             5 => 'WB_GBRGLevels',
4835             6 => 'WB_RGBLevels',
4836             7 => 'WB_GRBLevels',
4837             8 => 'WB_RBLevels',
4838             9 => 'WBBlueLevel', # blue
4839             10 => 'WBGreenLevel',
4840             },
4841             ValueConv => 'Image::ExifTool::Exif::RedBlueBalance(1,@val)',
4842             PrintConv => 'int($val * 1e6 + 0.5) * 1e-6',
4843             },
4844             GPSPosition => {
4845             Groups => { 2 => 'Location' },
4846             Writable => 1,
4847             Protected => 1,
4848             WriteAlso => {
4849             GPSLatitude => '(defined $val and $val =~ /(.*?)( ?[NS])?,/) ? $1 : undef',
4850             GPSLatitudeRef => '(defined $val and $val =~ /(-?)(.*?) ?([NS]?),/) ? ($3 || ($1 ? "S" : "N")) : undef',
4851             GPSLongitude => '(defined $val and $val =~ /, ?(.*?)( ?[EW]?)$/) ? $1 : undef',
4852             GPSLongitudeRef => '(defined $val and $val =~ /, ?(-?)(.*?) ?([EW]?)$/) ? ($3 || ($1 ? "W" : "E")) : undef',
4853             },
4854             PrintConvInv => q{
4855             return undef unless $val =~ /(.*? ?[NS]?), ?(.*? ?[EW]?)$/;
4856             my ($lat, $lon) = ($1, $2);
4857             require Image::ExifTool::GPS;
4858             $lat = Image::ExifTool::GPS::ToDegrees($lat, 1, "lat");
4859             $lon = Image::ExifTool::GPS::ToDegrees($lon, 1, "lon");
4860             return "$lat, $lon";
4861             },
4862             Require => {
4863             0 => 'GPSLatitude',
4864             1 => 'GPSLongitude',
4865             },
4866             Priority => 0,
4867             Notes => q{
4868             when written, writes GPSLatitude, GPSLatitudeRef, GPSLongitude and
4869             GPSLongitudeRef. This tag may be written using the same coordinate
4870             format as provided by Google Maps when right-clicking on a location
4871             },
4872             ValueConv => '(length($val[0]) or length($val[1])) ? "$val[0] $val[1]" : undef',
4873             PrintConv => '"$prt[0], $prt[1]"',
4874             },
4875             LensID => {
4876             Groups => { 2 => 'Camera' },
4877             Require => 'LensType',
4878             Desire => {
4879             1 => 'FocalLength',
4880             2 => 'MaxAperture',
4881             3 => 'MaxApertureValue',
4882             4 => 'MinFocalLength',
4883             5 => 'MaxFocalLength',
4884             6 => 'LensModel',
4885             7 => 'LensFocalRange',
4886             8 => 'LensSpec',
4887             9 => 'LensType2',
4888             10 => 'LensType3',
4889             11 => 'LensFocalLength', # (for Pentax to check for converter)
4890             12 => 'RFLensType',
4891             },
4892             Notes => q{
4893             attempt to identify the actual lens from all lenses with a given LensType.
4894             Applies only to LensType values with a lookup table. May be configured
4895             by adding user-defined lenses
4896             },
4897             # this LensID is only valid if the LensType has a PrintConv or is a model name
4898             RawConv => q{
4899             my $printConv = $$self{TAG_INFO}{LensType}{PrintConv};
4900             return $val if ref $printConv eq 'HASH' or (ref $printConv eq 'ARRAY' and
4901             ref $$printConv[0] eq 'HASH') or $val[0] =~ /(mm|\d\/F)/;
4902             return undef;
4903             },
4904             ValueConv => '$val',
4905             PrintConv => q{
4906             my $pcv;
4907             # use LensType2 instead of LensType if available and valid (Sony E-mount lenses)
4908             # (0x8000 or greater; 0 for several older/3rd-party E-mount lenses)
4909             if (defined $val[9] and ($val[9] & 0x8000 or $val[9] == 0)) {
4910             $val[0] = $val[9];
4911             $prt[0] = $prt[9];
4912             # Particularly GM lenses: often LensType2=0 but LensType3 is available and valid: use LensType3.
4913             if ($val[9] == 0 and $val[10] & 0x8000) {
4914             $val[0] = $val[10];
4915             $prt[0] = $prt[10];
4916             }
4917             $pcv = $$self{TAG_INFO}{LensType2}{PrintConv};
4918             }
4919             # use Canon RFLensType if available
4920             if ($val[12]) {
4921             $val[0] = $val[12];
4922             $prt[0] = $prt[12];
4923             $pcv = $$self{TAG_INFO}{RFLensType}{PrintConv};
4924             }
4925             my $lens = Image::ExifTool::Exif::PrintLensID($self, $prt[0], $pcv, $prt[8], @val);
4926             # check for use of lens converter (Pentax K-3)
4927             if ($val[11] and $val[1] and $lens) {
4928             my $conv = $val[1] / $val[11];
4929             $lens .= sprintf(' + %.1fx converter', $conv) if $conv > 1.1;
4930             }
4931             return $lens;
4932             },
4933             },
4934             'LensID-2' => {
4935             Name => 'LensID',
4936             Groups => { 2 => 'Camera' },
4937             Desire => {
4938             0 => 'LensModel',
4939             1 => 'Lens',
4940             2 => 'XMP-aux:LensID',
4941             3 => 'Make',
4942             },
4943             Inhibit => {
4944             4 => 'Composite:LensID',
4945             },
4946             RawConv => q{
4947             return undef if defined $val[2] and defined $val[3];
4948             return $val if defined $val[0] and $val[0] =~ /(mm|\d\/F)/;
4949             return $val if defined $val[1] and $val[1] =~ /(mm|\d\/F)/;
4950             return undef;
4951             },
4952             ValueConv => q{
4953             return $val[0] if defined $val[0] and $val[0] =~ /(mm|\d\/F)/;
4954             return $val[1];
4955             },
4956             PrintConv => '$_=$val; s/(\d)\/F/$1mm F/; s/mmF/mm F/; s/(\d) mm/${1}mm/; s/ - /-/; $_',
4957             },
4958             );
4959              
4960             # table for unknown IFD entries
4961             %Image::ExifTool::Exif::Unknown = (
4962             GROUPS => { 0 => 'EXIF', 1 => 'UnknownIFD', 2 => 'Image'},
4963             WRITE_PROC => \&WriteExif,
4964             );
4965              
4966             # add our composite tags
4967             Image::ExifTool::AddCompositeTags('Image::ExifTool::Exif');
4968              
4969              
4970             #------------------------------------------------------------------------------
4971             # AutoLoad our writer routines when necessary
4972             #
4973             sub AUTOLOAD
4974             {
4975 41     41   353 return Image::ExifTool::DoAutoLoad($AUTOLOAD, @_);
4976             }
4977              
4978             #------------------------------------------------------------------------------
4979             # Identify RAW file type for some TIFF-based formats using Compression value
4980             # Inputs: 0) ExifTool object reference, 1) Compression value
4981             # - sets TIFF_TYPE and FileType if identified
4982             sub IdentifyRawFile($$)
4983             {
4984 270     270 0 1103 my ($et, $comp) = @_;
4985 270 100 66     5343 if ($$et{FILE_TYPE} eq 'TIFF' and not $$et{IdentifiedRawFile}) {
4986 71 100 66     1874 if ($compression{$comp} and $compression{$comp} =~ /^\w+ ([A-Z]{3}) Compressed$/) {
4987 3         31 $et->OverrideFileType($$et{TIFF_TYPE} = $1);
4988 3         65 $$et{IdentifiedRawFile} = 1;
4989             }
4990             }
4991             }
4992              
4993             #------------------------------------------------------------------------------
4994             # Calculate LV (Light Value)
4995             # Inputs: 0) Aperture, 1) ShutterSpeed, 2) ISO
4996             # Returns: LV value (and converts input values to floating point if necessary)
4997             sub CalculateLV($$$)
4998             {
4999 126     126 0 435 local $_;
5000             # do validity checks on arguments
5001 126 50       709 return undef unless @_ >= 3;
5002 126         469 foreach (@_) {
5003 378 50 66     3803 return undef unless $_ and /([+-]?(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?)/ and $1 > 0;
      66        
5004 373         1069 $_ = $1; # extract float from any other garbage
5005             }
5006             # (A light value of 0 is defined as f/1.0 at 1 second with ISO 100)
5007 121         1841 return log($_[0] * $_[0] * 100 / ($_[1] * $_[2])) / log(2);
5008             }
5009              
5010             #------------------------------------------------------------------------------
5011             # Calculate scale factor for 35mm effective focal length (ref 26/PH)
5012             # Inputs: 0) ExifTool object ref
5013             # 1) Focal length
5014             # 2) Focal length in 35mm format
5015             # 3) Canon digital zoom factor
5016             # 4) Focal plane diagonal size (in mm)
5017             # 5) Sensor size (X and Y in mm)
5018             # 6/7) Focal plane X/Y size (in mm)
5019             # 8) focal plane resolution units (1=None,2=inches,3=cm,4=mm,5=um)
5020             # 9/10) Focal plane X/Y resolution
5021             # 11/12,13/14...) Image width/height in order of precedence (first valid pair is used)
5022             # Returns: 35mm conversion factor (or undefined if it can't be calculated)
5023             sub CalcScaleFactor35efl
5024             {
5025 556     556 0 1926 my $et = shift;
5026 556         1094 my $res = $_[7]; # save resolution units (in case they have been converted to string)
5027 556         974 my $sensXY = $_[4];
5028 556         2378 Image::ExifTool::ToFloat(@_);
5029 556         1072 my $focal = shift;
5030 556         1062 my $foc35 = shift;
5031              
5032 556 100 100     2712 return $foc35 / $focal if $focal and $foc35;
5033              
5034 528   50     2128 my $digz = shift || 1;
5035 528         1104 my $diag = shift;
5036 528         893 my $sens = shift;
5037             # calculate Canon sensor size using a dedicated algorithm
5038 528 100       2007 if ($$et{Make} eq 'Canon') {
5039 55         4238 require Image::ExifTool::Canon;
5040             my $canonDiag = Image::ExifTool::Canon::CalcSensorDiag(
5041             $$et{RATIONAL}{FocalPlaneXResolution},
5042             $$et{RATIONAL}{FocalPlaneYResolution},
5043 55         522 );
5044 55 100       236 $diag = $canonDiag if $canonDiag;
5045             }
5046 528 100 66     1866 unless ($diag and Image::ExifTool::IsFloat($diag)) {
5047 493 50 33     1531 if ($sens and $sensXY =~ / (\d+(\.?\d*)?)$/) {
5048 0         0 $diag = sqrt($sens * $sens + $1 * $1);
5049             } else {
5050 493         848 undef $diag;
5051 493         838 my $xsize = shift;
5052 493         790 my $ysize = shift;
5053 493 100 66     1553 if ($xsize and $ysize) {
5054             # validate by checking aspect ratio because FocalPlaneX/YSize is not reliable
5055 19         71 my $a = $xsize / $ysize;
5056 19 50 66     145 if (abs($a-1.3333) < .1 or abs($a-1.5) < .1) {
5057 19         74 $diag = sqrt($xsize * $xsize + $ysize * $ysize);
5058             }
5059             }
5060             }
5061 493 100       1242 unless ($diag) {
5062             # get number of mm in units (assume inches unless otherwise specified)
5063 474         3014 my %lkup = ( 3=>10, 4=>1, 5=>0.001 , cm=>10, mm=>1, um=>0.001 );
5064 474   100     3620 my $units = $lkup{ shift() || $res || '' } || 25.4;
5065 474   100     4245 my $x_res = shift || return undef;
5066 122   66     290 my $y_res = shift || $x_res;
5067 122 50 33     415 Image::ExifTool::IsFloat($x_res) and $x_res != 0 or return undef;
5068 122 50 33     362 Image::ExifTool::IsFloat($y_res) and $y_res != 0 or return undef;
5069 122         277 my ($w, $h);
5070 122         186 for (;;) {
5071 124 50       334 @_ < 2 and return undef;
5072 124         288 $w = shift;
5073 124         200 $h = shift;
5074 124 100 66     489 next unless $w and $h;
5075 122         297 my $a = $w / $h;
5076 122 50 33     639 last if $a > 0.5 and $a < 2; # stop if we get a reasonable value
5077             }
5078             # calculate focal plane size in mm
5079 122         264 $w *= $units / $x_res;
5080 122         229 $h *= $units / $y_res;
5081 122         265 $diag = sqrt($w*$w+$h*$h);
5082             # make sure size is reasonable
5083 122 100 100     1490 return undef unless $diag > 1 and $diag < 100;
5084             }
5085             }
5086 60         702 return sqrt(36*36+24*24) * $digz / $diag;
5087             }
5088              
5089             #------------------------------------------------------------------------------
5090             # Print exposure compensation fraction
5091             sub PrintFraction($)
5092             {
5093 286     286 0 879 my $val = shift;
5094 286         572 my $str;
5095 286 50       1012 if (defined $val) {
5096 286         1038 $val *= 1.00001; # avoid round-off errors
5097 286 100       1159 if (not $val) {
    100          
    50          
    100          
5098 258         745 $str = '0';
5099             } elsif (int($val)/$val > 0.999) {
5100 5         28 $str = sprintf("%+d", int($val));
5101             } elsif ((int($val*2))/($val*2) > 0.999) {
5102 0         0 $str = sprintf("%+d/2", int($val * 2));
5103             } elsif ((int($val*3))/($val*3) > 0.999) {
5104 10         52 $str = sprintf("%+d/3", int($val * 3));
5105             } else {
5106 13         130 $str = sprintf("%+.3g", $val);
5107             }
5108             }
5109 286         2071 return $str;
5110             }
5111              
5112             #------------------------------------------------------------------------------
5113             # Convert fraction or number to floating point value (or 'undef' or 'inf')
5114             sub ConvertFraction($)
5115             {
5116 1167     1167 0 3056 my $val = shift;
5117 1167 100       4180 if ($val =~ m{([-+]?\d+)/(\d+)}) {
5118 436 0       2004 $val = $2 ? $1 / $2 : ($1 ? 'inf' : 'undef');
    50          
5119             }
5120 1167         9108 return $val;
5121             }
5122              
5123             #------------------------------------------------------------------------------
5124             # Convert EXIF text to something readable
5125             # Inputs: 0) ExifTool object reference, 1) EXIF text,
5126             # 2) [optional] 1 to apply CharsetEXIF to ASCII text,
5127             # 3) tag name for warning message (may be argument 2)
5128             # Returns: text encoded according to Charset option (with trailing spaces removed)
5129             sub ConvertExifText($$;$$)
5130             {
5131 87     87 0 488 my ($et, $val, $asciiFlex, $tag) = @_;
5132 87 50       417 return $val if length($val) < 8;
5133 87         330 my $id = substr($val, 0, 8);
5134 87         292 my $str = substr($val, 8);
5135 87         227 my $type;
5136              
5137 87         260 delete $$et{WrongByteOrder};
5138 87 50 66     488 if ($$et{OPTIONS}{Validate} and $id =~ /^(ASCII|UNICODE|JIS)?\0* \0*$/) {
5139 0 0 0     0 $et->Warn(($1 || 'Undefined') . ' text header' . ($tag ? " for $tag" : '') . ' has spaces instead of nulls');
5140             }
5141             # Note: allow spaces instead of nulls in the ID codes because
5142             # it is fairly common for camera manufacturers to get this wrong
5143             # (also handle Canon ZoomBrowser EX 4.5 null followed by 7 bytes of garbage)
5144 87 50       623 if ($id =~ /^(ASCII)?(\0|[\0 ]+$)/) {
    0          
    0          
5145             # truncate at null terminator (shouldn't have a null based on the
5146             # EXIF spec, but it seems that few people actually read the spec)
5147 87         464 $str =~ s/\0.*//s;
5148             # allow ASCII text to contain any other specified encoding
5149 87 100 66     685 if ($asciiFlex and $asciiFlex eq '1') {
5150 86         426 my $enc = $et->Options('CharsetEXIF');
5151 86 50       419 $str = $et->Decode($str, $enc) if $enc;
5152             }
5153             # by the EXIF spec, the following string should be "UNICODE\0", but
5154             # apparently Kodak sometimes uses "Unicode\0" in the APP3 "Meta" information.
5155             # However, unfortunately Ricoh uses "Unicode\0" in the RR30 EXIF UserComment
5156             # when the text is actually ASCII, so only recognize uppercase "UNICODE\0".
5157             } elsif ($id =~ /^(UNICODE)[\0 ]$/) {
5158 0         0 $type = $1;
5159             # MicrosoftPhoto writes as little-endian even in big-endian EXIF,
5160             # so we must guess at the true byte ordering
5161 0         0 $str = $et->Decode($str, 'UTF16', 'Unknown');
5162             } elsif ($id =~ /^(JIS)[\0 ]{5}$/) {
5163 0         0 $type = $1;
5164 0         0 $str = $et->Decode($str, 'JIS', 'Unknown');
5165             } else {
5166 0 0 0     0 $tag = $asciiFlex if $asciiFlex and $asciiFlex ne '1';
5167 0 0       0 $et->Warn('Invalid EXIF text encoding' . ($tag ? " for $tag" : ''));
5168 0         0 $str = $id . $str;
5169             }
5170 87 0 33     383 if ($$et{WrongByteOrder} and $$et{OPTIONS}{Validate}) {
5171 0 0       0 $et->Warn('Wrong byte order for EXIF' . ($tag ? " $tag" : '') .
    0          
5172             ($type ? " $type" : '') . ' text');
5173             }
5174 87         268 $str =~ s/ +$//; # trim trailing blanks
5175 87         889 return $str;
5176             }
5177              
5178             #------------------------------------------------------------------------------
5179             # Print conversion for SpatialFrequencyResponse
5180             sub PrintSFR($)
5181             {
5182 0     0 0 0 my $val = shift;
5183 0 0       0 return $val unless length $val > 4;
5184 0         0 my ($n, $m) = (Get16u(\$val, 0), Get16u(\$val, 2));
5185 0         0 my @cols = split /\0/, substr($val, 4), $n+1;
5186 0         0 my $pos = length($val) - 8 * $n * $m;
5187 0 0 0     0 return $val unless @cols == $n+1 and $pos >= 4;
5188 0         0 pop @cols;
5189 0         0 my ($i, $j);
5190 0         0 for ($i=0; $i<$n; ++$i) {
5191 0         0 my @rows;
5192 0         0 for ($j=0; $j<$m; ++$j) {
5193 0         0 push @rows, Image::ExifTool::GetRational64u(\$val, $pos + 8*($i+$j*$n));
5194             }
5195 0         0 $cols[$i] .= '=' . join(',',@rows) . '';
5196             }
5197 0         0 return join '; ', @cols;
5198             }
5199              
5200             #------------------------------------------------------------------------------
5201             # Print numerical parameter value (with sign, or 'Normal' for zero)
5202             # Inputs: 0) value, 1) flag for inverse conversion, 2) conversion hash reference
5203             sub PrintParameter($$$)
5204             {
5205 195     195 0 567 my ($val, $inv, $conv) = @_;
5206 195 100       926 return $val if $inv;
5207 48 50       156 if ($val > 0) {
5208 48 50       125 if ($val > 0xfff0) { # a negative value in disguise?
5209 0         0 $val = $val - 0x10000;
5210             } else {
5211 48         146 $val = "+$val";
5212             }
5213             }
5214 48         161 return $val;
5215             }
5216              
5217             #------------------------------------------------------------------------------
5218             # Convert parameter back to standard EXIF value
5219             # 0,0.00,etc or "Normal" => 0
5220             # -1,-2,etc or "Soft" or "Low" => 1
5221             # +1,+2,1,2,etc or "Hard" or "High" => 2
5222             sub ConvertParameter($)
5223             {
5224 67     67 0 224 my $val = shift;
5225 67         244 my $isFloat = Image::ExifTool::IsFloat($val);
5226             # normal is a value of zero
5227 67 100 100     745 return 0 if $val =~ /\bn/i or ($isFloat and $val == 0);
      100        
5228             # "soft", "low" or any negative number is a value of 1
5229 39 50 66     326 return 1 if $val =~ /\b(s|l)/i or ($isFloat and $val < 0);
      33        
5230             # "hard", "high" or any positive number is a value of 2
5231 39 100 100     478 return 2 if $val =~ /\bh/i or $isFloat;
5232 2         17 return undef;
5233             }
5234              
5235             #------------------------------------------------------------------------------
5236             # Calculate Red/BlueBalance
5237             # Inputs: 0) 0=red, 1=blue, 1-8) WB_RGGB/RGBG/RBGG/GRBG/GRGB/RGB/GRB/RBLevels,
5238             # 8) red or blue level, 9) green level
5239             my @rggbLookup = (
5240             # indices for R, G, G and B components in input value
5241             [ 0, 1, 2, 3 ], # 0 RGGB
5242             [ 0, 1, 3, 2 ], # 1 RGBG
5243             [ 0, 2, 3, 1 ], # 2 RBGG
5244             [ 1, 0, 3, 2 ], # 3 GRBG
5245             [ 1, 0, 2, 3 ], # 4 GRGB
5246             [ 2, 3, 0, 1 ], # 5 GBRG
5247             [ 0, 1, 1, 2 ], # 6 RGB
5248             [ 1, 0, 0, 2 ], # 7 GRB
5249             [ 0, 256, 256, 1 ], # 8 RB (green level is 256)
5250             );
5251             sub RedBlueBalance($@)
5252             {
5253 86     86 0 285 my $blue = shift;
5254 86         217 my ($i, $val, $levels);
5255 86         372 for ($i=0; $i<@rggbLookup; ++$i) {
5256 228 100       600 $levels = shift or next;
5257 82         398 my @levels = split ' ', $levels;
5258 82 50       307 next if @levels < 2;
5259 82         227 my $lookup = $rggbLookup[$i];
5260 82         223 my $g = $$lookup[1]; # get green level or index
5261 82 100       304 if ($g < 4) {
    50          
5262 74 50       236 next if @levels < 3;
5263 74 50       447 $g = ($levels[$g] + $levels[$$lookup[2]]) / 2 or next;
5264             } elsif ($levels[$$lookup[$blue * 3]] < 4) {
5265 0         0 $g = 1; # Some Nikon cameras use a scaling factor of 1 (E5700)
5266             }
5267 82         354 $val = $levels[$$lookup[$blue * 3]] / $g;
5268 82         267 last;
5269             }
5270 86 50 33     291 $val = $_[0] / $_[1] if not defined $val and ($_[0] and $_[1]);
      66        
5271 86         706 return $val;
5272             }
5273              
5274             #------------------------------------------------------------------------------
5275             # Print exposure time as a fraction
5276             sub PrintExposureTime($)
5277             {
5278 574     574 0 1552 my $secs = shift;
5279 574 50       1792 return $secs unless Image::ExifTool::IsFloat($secs);
5280 574 100 100     3655 if ($secs < 0.25001 and $secs > 0) {
5281 447         4861 return sprintf("1/%d",int(0.5 + 1/$secs));
5282             }
5283 127         876 $_ = sprintf("%.1f",$secs);
5284 127         635 s/\.0$//;
5285 127         980 return $_;
5286             }
5287              
5288             #------------------------------------------------------------------------------
5289             # Print FNumber
5290             sub PrintFNumber($)
5291             {
5292 354     354 0 1046 my $val = shift;
5293 354 50 33     1265 if (Image::ExifTool::IsFloat($val) and $val > 0) {
5294             # round to 1 decimal place, or 2 for values < 1.0
5295 354 100       3071 $val = sprintf(($val<1 ? "%.2f" : "%.1f"), $val);
5296             }
5297 354         2885 return $val;
5298             }
5299              
5300             #------------------------------------------------------------------------------
5301             # Decode raw CFAPattern value
5302             # Inputs: 0) ExifTool ref, 1) binary value
5303             # Returns: string of numbers
5304             sub DecodeCFAPattern($$)
5305             {
5306 5     5 0 25 my ($self, $val) = @_;
5307             # some panasonic cameras (SV-AS3, SV-AS30) write this in ascii (very odd)
5308 5 50       33 if ($val =~ /^[0-6]+$/) {
5309 0         0 $self->Warn('Incorrectly formatted CFAPattern', 1);
5310 0         0 $val =~ tr/0-6/\x00-\x06/;
5311             }
5312 5 50       32 return $val unless length($val) >= 4;
5313 5 100       28 my @a = unpack(GetByteOrder() eq 'II' ? 'v2C*' : 'n2C*', $val);
5314 5         25 my $end = 2 + $a[0] * $a[1];
5315 5 100       34 if ($end > @a) {
5316             # try swapping byte order (I have seen this order different than in EXIF)
5317 2         19 my ($x, $y) = unpack('n2',pack('v2',$a[0],$a[1]));
5318 2 50       12 if (@a < 2 + $x * $y) {
5319 0         0 $self->Warn('Invalid CFAPattern', 1);
5320             } else {
5321 2         8 ($a[0], $a[1]) = ($x, $y);
5322             # (can't technically be wrong because the order isn't well defined by the EXIF spec)
5323             # $self->Warn('Wrong byte order for CFAPattern');
5324             }
5325             }
5326 5         72 return "@a";
5327             }
5328              
5329             #------------------------------------------------------------------------------
5330             # Print CFA Pattern
5331             sub PrintCFAPattern($)
5332             {
5333 6     6 0 24 my $val = shift;
5334 6         37 my @a = split ' ', $val;
5335 6 50       39 return '' unless @a >= 2;
5336 6 50 33     52 return '' unless $a[0] and $a[1];
5337 6         21 my $end = 2 + $a[0] * $a[1];
5338 6 50       26 return '' if $end > @a;
5339 6         29 my @cfaColor = qw(Red Green Blue Cyan Magenta Yellow White);
5340 6         18 my ($pos, $rtnVal) = (2, '[');
5341 6         13 for (;;) {
5342 24   50     64 $rtnVal .= $cfaColor[$a[$pos]] || 'Unknown';
5343 24 100       60 last if ++$pos >= $end;
5344 18 100       53 ($pos - 2) % $a[1] and $rtnVal .= ',', next;
5345 6         15 $rtnVal .= '][';
5346             }
5347 6         59 return $rtnVal . ']';
5348             }
5349              
5350             #------------------------------------------------------------------------------
5351             # Print Opcode List
5352             # Inputs: 0) value, 1) flag for inverse conversion, 2) conversion hash reference
5353             # Returns: converted value
5354             sub PrintOpcode($$$)
5355             {
5356 0     0 0 0 my ($val, $inv, $conv) = @_;
5357 0 0       0 return undef if $inv; # (can't do inverse conversion)
5358 0 0       0 return '' unless length $$val > 4;
5359 0         0 my $num = unpack('N', $$val);
5360 0         0 my $pos = 4;
5361 0         0 my ($i, @ops);
5362 0         0 for ($i=0; $i<$num; ++$i) {
5363 0 0       0 $pos + 16 <= length $$val or push(@ops, ''), last;
5364 0         0 my ($op, $ver, $flags, $len) = unpack("x${pos}N4", $$val);
5365 0   0     0 push @ops, $$conv{$op} || "[opcode $op]";
5366 0         0 $pos += 16 + $len;
5367             }
5368 0         0 return join ', ', @ops;
5369             }
5370              
5371             #------------------------------------------------------------------------------
5372             # Print conversion for lens info
5373             # Inputs: 0) string of values (min focal, max focal, min F, max F)
5374             # Returns: string in the form "12-20mm f/3.8-4.5" or "50mm f/1.4"
5375             sub PrintLensInfo($)
5376             {
5377 10     10 0 37 my $val = shift;
5378 10         58 my @vals = split ' ', $val;
5379 10 50       50 return $val unless @vals == 4;
5380 10         27 my $c = 0;
5381 10         38 foreach (@vals) {
5382 40 100       98 Image::ExifTool::IsFloat($_) and ++$c, next;
5383 4 50       15 $_ eq 'inf' and $_ = '?', ++$c, next;
5384 4 50       13 $_ eq 'undef' and $_ = '?', ++$c, next;
5385             }
5386 10 50       49 return $val unless $c == 4;
5387 10         21 $val = $vals[0];
5388             # (the Pentax Q writes zero for upper value of fixed-focal-length lenses)
5389 10 100 66     71 $val .= "-$vals[1]" if $vals[1] and $vals[1] ne $vals[0];
5390 10         39 $val .= "mm f/$vals[2]";
5391 10 100 100     56 $val .= "-$vals[3]" if $vals[3] and $vals[3] ne $vals[2];
5392 10         43 return $val;
5393             }
5394              
5395             #------------------------------------------------------------------------------
5396             # Get lens info from lens model string
5397             # Inputs: 0) lens string, 1) flag to allow unknown "?" values
5398             # Returns: 0) min focal, 1) max focal, 2) min aperture, 3) max aperture
5399             # Notes: returns empty list if lens string could not be parsed
5400             sub GetLensInfo($;$)
5401             {
5402 53     53 0 128 my ($lens, $unk) = @_;
5403             # extract focal length and aperture ranges for this lens
5404 53         96 my $pat = '\\d+(?:\\.\\d+)?';
5405 53 100       140 $pat .= '|\\?' if $unk;
5406 53 100       1058 return () unless $lens =~ /($pat)(?:-($pat))?\s*mm.*?(?:[fF]\/?\s*)($pat)(?:-($pat))?/;
5407             # ($1=short focal, $2=long focal, $3=max aperture wide, $4=max aperture tele)
5408 33         141 my @a = ($1, $2, $3, $4);
5409 33 50       81 $a[1] or $a[1] = $a[0];
5410 33 50       71 $a[3] or $a[3] = $a[2];
5411 33 100       70 if ($unk) {
5412 1         3 local $_;
5413 1   50     14 $_ eq '?' and $_ = 'undef' foreach @a;
5414             }
5415 33         138 return @a;
5416             }
5417              
5418             #------------------------------------------------------------------------------
5419             # Match lens in list of possbilities based on value of LensModel
5420             # Inputs: 0) reference to list of possible models, 1) LensModel string
5421             # - updates list on return; guaranteed not to remove all list entries
5422             sub MatchLensModel($$)
5423             {
5424 4     4 0 18 my ($try, $lensModel) = @_;
5425 4 50 33     28 if (@$try > 1 and $lensModel) {
5426 0         0 my (@filt, $pat);
5427             # filter by focal length
5428 0 0       0 if ($lensModel =~ /((\d+-)?\d+mm)/) {
5429 0         0 my $focal = $1;
5430 0         0 @filt = grep /$focal/, @$try;
5431 0 0 0     0 @$try = @filt if @filt and @filt < @$try;
5432             }
5433             # filter by aperture
5434 0 0 0     0 if (@$try > 1 and $lensModel =~ m{(?:F/?|1:)(\d+(\.\d+)?)}i) {
5435 0         0 my $fnum = $1;
5436 0         0 @filt = grep m{(F/?|1:)$fnum(\b|[A-Z])}i, @$try;
5437 0 0 0     0 @$try = @filt if @filt and @filt < @$try;
5438             }
5439             # filter by model version, and other lens parameters
5440 0         0 foreach $pat ('I+', 'USM') {
5441 0 0 0     0 next unless @$try > 1 and $lensModel =~ /\b($pat)\b/;
5442 0         0 my $val = $1;
5443 0         0 @filt = grep /\b$val\b/, @$try;
5444 0 0 0     0 @$try = @filt if @filt and @filt < @$try;
5445             }
5446             }
5447             }
5448              
5449             #------------------------------------------------------------------------------
5450             # Attempt to identify the specific lens if multiple lenses have the same LensType
5451             # Inputs: 0) ExifTool object ref, 1) LensType print value, 2) PrintConv hash ref,
5452             # 3) LensSpec print value, 4) LensType numerical value, 5) FocalLength,
5453             # 6) MaxAperture, 7) MaxApertureValue, 8) MinFocalLength, 9) MaxFocalLength,
5454             # 10) LensModel, 11) LensFocalRange, 12) LensSpec
5455             my %sonyEtype;
5456             sub PrintLensID($$@)
5457             {
5458 34     34 0 243 my ($et, $lensTypePrt, $printConv, $lensSpecPrt, $lensType, $focalLength,
5459             $maxAperture, $maxApertureValue, $shortFocal, $longFocal, $lensModel,
5460             $lensFocalRange, $lensSpec) = @_;
5461             # this logic relies on the LensType lookup:
5462 34 50       161 return undef unless defined $lensType;
5463             # get print conversion hash if necessary
5464 34 50       171 $printConv or $printConv = $$et{TAG_INFO}{LensType}{PrintConv};
5465             # just copy LensType PrintConv value if it was a lens name
5466             # (Olympus or Panasonic -- just exclude things like Nikon and Leaf LensType)
5467 34 50       144 unless (ref $printConv eq 'HASH') {
5468 0 0 0     0 if (ref $printConv eq 'ARRAY' and ref $$printConv[0] eq 'HASH') {
5469 0         0 $printConv = $$printConv[0];
5470 0         0 $lensTypePrt =~ s/;.*//;
5471 0         0 $lensType =~ s/ .*//;
5472             } else {
5473 0 0       0 return $lensTypePrt if $lensTypePrt =~ /mm/;
5474 0 0       0 return $lensTypePrt if $lensTypePrt =~ s/(\d)\/F/$1mm F/;
5475 0         0 return undef;
5476             }
5477             }
5478             # get LensSpec information if available (Sony)
5479 34         106 my ($sf0, $lf0, $sa0, $la0);
5480 34 50       130 if ($lensSpecPrt) {
5481 0         0 ($sf0, $lf0, $sa0, $la0) = GetLensInfo($lensSpecPrt);
5482 0 0       0 undef $sf0 unless $sa0; # (make sure aperture isn't zero)
5483             }
5484             # use MaxApertureValue if MaxAperture is not available
5485 34 100       116 $maxAperture = $maxApertureValue unless $maxAperture;
5486 34 100 100     170 if ($lensFocalRange and $lensFocalRange =~ /^(\d+)(?: (?:to )?(\d+))?$/) {
5487 1   33     9 ($shortFocal, $longFocal) = ($1, $2 || $1);
5488             }
5489 34 50 66     438 if ($$et{Make} eq 'SONY') {
    100 66        
      66        
5490 0 0       0 if ($lensType eq 65535) {
    0          
5491             # handle Sony E-type lenses when LensType2 isn't valid (NEX/ILCE models only)
5492 0 0       0 if ($$et{Model} =~ /NEX|ILCE/) {
5493 0 0       0 unless (%sonyEtype) {
5494 0         0 my ($index, $i, %did, $lens);
5495 0         0 require Image::ExifTool::Sony;
5496 0         0 foreach (sort keys %Image::ExifTool::Sony::sonyLensTypes2) {
5497 0         0 ($lens = $Image::ExifTool::Sony::sonyLensTypes2{$_}) =~ s/ or .*//;
5498 0 0       0 next if $did{$lens};
5499 0 0       0 ($i, $index) = $index ? ("65535.$index", $index + 1) : (65535, 1);
5500 0         0 $did{$sonyEtype{$i} = $lens} = 1;
5501             }
5502             }
5503 0         0 $printConv = \%sonyEtype;
5504             }
5505             } elsif ($lensType != 0xff00) {
5506             # Patch for Metabones or other adapters on Sony E-mount cameras (ref Jos Roost)
5507             # Metabones Canon EF to E-mount adapters add 0xef00, 0xbc00 or 0x7700 to the
5508             # high byte for 2-byte Canon LensType values, so we need to adjust for these.
5509             # Offset 0xef00 is also used by Sigma MC-11, Fotodiox and Viltrox EF-E adapters.
5510             # Have to exclude A-mount Sigma Filtermatic with 'odd' LensType=0xff00.
5511 0         0 require Image::ExifTool::Minolta;
5512 0 0 0     0 if ($Image::ExifTool::Minolta::metabonesID{$lensType & 0xff00}) {
    0          
5513 0 0       0 $lensType -= ($lensType >= 0xef00 ? 0xef00 : $lensType >= 0xbc00 ? 0xbc00 : 0x7700);
    0          
5514 0         0 require Image::ExifTool::Canon;
5515 0         0 $printConv = \%Image::ExifTool::Canon::canonLensTypes;
5516 0 0       0 $lensTypePrt = $$printConv{$lensType} if $$printConv{$lensType};
5517             # Test for Sigma MC-11 SA-E adapter with Sigma SA lens using 0x4900 offset.
5518             # (upper limit of test cuts off two highest Sigma lenses, but prevents
5519             # conflict with old Minolta 25xxx and higher ID's)
5520             } elsif ($lensType >= 0x4900 and $lensType <= 0x590a) {
5521 0         0 require Image::ExifTool::Sigma;
5522 0         0 $lensType -= 0x4900;
5523 0         0 $printConv = \%Image::ExifTool::Sigma::sigmaLensTypes;
5524 0 0       0 $lensTypePrt = $$printConv{$lensType} if $$printConv{$lensType};
5525             }
5526             }
5527             # (Min/MaxFocalLength may report the current focal length for Tamron zoom lenses)
5528             } elsif ($shortFocal and $longFocal and (not $lensModel or $lensModel !~ /^TAMRON.*-\d+mm/)) {
5529             # Canon (and some other makes) include makernote information
5530             # which allows better lens identification
5531 28         2231 require Image::ExifTool::Canon;
5532 28         225 return Image::ExifTool::Canon::PrintLensID($printConv, $lensType,
5533             $shortFocal, $longFocal, $maxAperture, $lensModel);
5534             }
5535 6         21 my $lens = $$printConv{$lensType};
5536 6 100 33     48 return ($lensModel || $lensTypePrt) unless $lens;
5537 5 100       50 return $lens unless $$printConv{"$lensType.1"};
5538 4         29 $lens =~ s/ or .*//s; # remove everything after "or"
5539             # make list of all possible matching lenses
5540 4         15 my @lenses = ( $lens );
5541 4         10 my $i;
5542 4         27 for ($i=1; $$printConv{"$lensType.$i"}; ++$i) {
5543 28         112 push @lenses, $$printConv{"$lensType.$i"};
5544             }
5545             # attempt to determine actual lens
5546 4         18 my (@matches, @best, @user, $diff);
5547 4         15 foreach $lens (@lenses) {
5548 32 50       87 push @user, $lens if $Image::ExifTool::userLens{$lens};
5549             # sf = short focal
5550             # lf = long focal
5551             # sa = max aperture at short focal
5552             # la = max aperture at long focal
5553 32         68 my ($sf, $lf, $sa, $la) = GetLensInfo($lens);
5554 32 100       75 next unless $sf;
5555             # check against LensSpec parameters if available
5556 28 50       68 if ($sf0) {
5557 0 0 0     0 next if abs($sf - $sf0) > 0.5 or abs($sa - $sa0) > 0.15 or
      0        
      0        
5558             abs($lf - $lf0) > 0.5 or abs($la - $la0) > 0.15;
5559             # the basic parameters match, but also check against additional lens features:
5560             # for Sony A and E lenses, the full LensSpec string should match with end of LensType,
5561             # excluding any part between () at the end, and preceded by a space (the space
5562             # ensures that e.g. Zeiss Loxia 21mm having LensSpec "E 21mm F2.8" will not be
5563             # identified as "Sony FE 21mm F2.8 (SEL28F20 + SEL075UWC)")
5564 0 0 0     0 $lensSpecPrt and $lens =~ / \Q$lensSpecPrt\E( \(| GM$|$)/ and @best = ( $lens ), last;
5565             # exactly-matching Sony lens should have been found above, so only add non-Sony lenses
5566 0 0       0 push @best, $lens unless $lens =~ /^Sony /;
5567 0         0 next;
5568             }
5569             # adjust focal length and aperture if teleconverter is attached (Minolta)
5570 28 50       73 if ($lens =~ / \+ .*? (\d+(\.\d+)?)x( |$)/) {
5571 0         0 $sf *= $1; $lf *= $1;
  0         0  
5572 0         0 $sa *= $1; $la *= $1;
  0         0  
5573             }
5574             # see if we can rule out this lens using FocalLength and MaxAperture
5575 28 50       61 if ($focalLength) {
5576 28 100       107 next if $focalLength < $sf - 0.5;
5577 9 100       33 next if $focalLength > $lf + 0.5;
5578             }
5579 8 50       42 if ($maxAperture) {
5580             # it seems that most manufacturers set MaxAperture and MaxApertureValue
5581             # to the maximum aperture (smallest F number) for the current focal length
5582             # of the lens, so assume that MaxAperture varies with focal length and find
5583             # the closest match (this is somewhat contrary to the EXIF specification which
5584             # states "The smallest F number of the lens", without mention of focal length)
5585 8 100       33 next if $maxAperture < $sa - 0.15; # (0.15 is arbitrary)
5586 3 50       17 next if $maxAperture > $la + 0.15;
5587             # now determine the best match for this aperture
5588 3         9 my $aa; # approximate maximum aperture at this focal length
5589 3 50 33     37 if ($sf == $lf or $sa == $la or $focalLength <= $sf) {
    0 33        
5590             # either 1) prime lens, 2) fixed-aperture zoom, or 3) zoom at min focal
5591 3         10 $aa = $sa;
5592             } elsif ($focalLength >= $lf) {
5593 0         0 $aa = $la;
5594             } else {
5595             # assume a log-log variation of max aperture with focal length
5596             # (see http://regex.info/blog/2006-10-05/263)
5597 0         0 $aa = exp(log($sa) + (log($la)-log($sa)) / (log($lf)-log($sf)) *
5598             (log($focalLength)-log($sf)));
5599             # a linear relationship between 1/FocalLength and 1/MaxAperture fits Sony better (ref 27)
5600             #$aa = 1 / (1/$sa + (1/$focalLength - 1/$sf) * (1/$la - 1/$sa) / (1/$lf - 1/$sf));
5601             }
5602 3         11 my $d = abs($maxAperture - $aa);
5603 3 50       15 if (defined $diff) {
5604 0 0       0 $d > $diff + 0.15 and next; # (0.15 is arbitrary)
5605 0 0       0 $d < $diff - 0.15 and undef @best;
5606             }
5607 3         7 $diff = $d;
5608 3         19 push @best, $lens;
5609             }
5610 3         17 push @matches, $lens;
5611             }
5612             # return the user-defined lens if it exists
5613 4 50       41 if (@user) {
5614             # choose the best match if we have more than one
5615 0 0       0 if (@user > 1) {
5616 0         0 my ($try, @good);
5617 0         0 foreach $try (\@best, \@matches) {
5618 0   0     0 $Image::ExifTool::userLens{$_} and push @good, $_ foreach @$try;
5619 0 0       0 return join(' or ', @good) if @good;
5620             }
5621             }
5622 0         0 return join(' or ', @user);
5623             }
5624             # return the best match(es) from the possible lenses, after checking against LensModel
5625 4 100       27 @best = @matches unless @best;
5626 4 100       19 if (@best) {
5627 3         17 MatchLensModel(\@best, $lensModel);
5628 3         92 return join(' or ', @best);
5629             }
5630 1         4 $lens = $$printConv{$lensType};
5631 1 50 33     18 return $lensModel if $lensModel and $lens =~ / or /; # (eg. Sony NEX-5N)
5632 1         30 return $lens;
5633             }
5634              
5635             #------------------------------------------------------------------------------
5636             # Translate date into standard EXIF format
5637             # Inputs: 0) date
5638             # Returns: date in format '2003:10:22'
5639             # - bad formats recognized: '2003-10-22','2003/10/22','2003 10 22','20031022'
5640             # - removes null terminator if it exists
5641             sub ExifDate($)
5642             {
5643 68     68 0 240 my $date = shift;
5644 68         222 $date =~ s/\0$//; # remove any null terminator
5645             # separate year:month:day with colons
5646             # (have seen many other characters, including nulls, used erroneously)
5647 68         761 $date =~ s/(\d{4})[^\d]*(\d{2})[^\d]*(\d{2})$/$1:$2:$3/;
5648 68         601 return $date;
5649             }
5650              
5651             #------------------------------------------------------------------------------
5652             # Translate time into standard EXIF format
5653             # Inputs: 0) time
5654             # Returns: time in format '10:30:55'
5655             # - bad formats recognized: '10 30 55', '103055', '103055+0500'
5656             # - removes null terminator if it exists
5657             # - leaves time zone intact if specified (eg. '10:30:55+05:00')
5658             sub ExifTime($)
5659             {
5660 8     8 0 26 my $time = shift;
5661 8         26 $time =~ tr/ /:/; # use ':' (not ' ') as a separator
5662 8         19 $time =~ s/\0$//; # remove any null terminator
5663             # add separators if they don't exist
5664 8         68 $time =~ s/^(\d{2})(\d{2})(\d{2})/$1:$2:$3/;
5665 8         54 $time =~ s/([+-]\d{2})(\d{2})\s*$/$1:$2/; # to timezone too
5666 8         68 return $time;
5667             }
5668              
5669             #------------------------------------------------------------------------------
5670             # Generate TIFF file from scratch (in current byte order)
5671             # Inputs: 0) hash of IFD entries (TagID => Value; multiple values space-delimited)
5672             # 1) raw image data reference
5673             # Returns: TIFF image data, or undef on error
5674             sub GenerateTIFF($$)
5675             {
5676 4     4 0 14 my ($entries, $dataPt) = @_;
5677 4         8 my ($rtnVal, $tag, $offsetPos);
5678              
5679 4         17 my $num = scalar keys %$entries;
5680 4         15 my $ifdBuff = GetByteOrder() . Set16u(42) . Set32u(8) . Set16u($num);
5681 4         21 my $valBuff = '';
5682 4         20 my $tagTablePtr = GetTagTable('Image::ExifTool::Exif::Main');
5683 4         42 foreach $tag (sort { $a <=> $b } keys %$entries) {
  170         252  
5684 60         168 my $tagInfo = $$tagTablePtr{$tag};
5685 60 100       188 my $fmt = ref $tagInfo eq 'HASH' ? $$tagInfo{Writable} : 'int32u';
5686 60 50       126 return undef unless defined $fmt;
5687 60         169 my $val = Image::ExifTool::WriteValue($$entries{$tag}, $fmt, -1);
5688 60 50       118 return undef unless defined $val;
5689 60         116 my $format = $formatNumber{$fmt};
5690 60         122 $ifdBuff .= Set16u($tag) . Set16u($format) . Set32u(length($val)/$formatSize[$format]);
5691 60 100       145 $offsetPos = length($ifdBuff) if $tag == 0x111; # (remember StripOffsets position)
5692 60 100       125 if (length $val > 4) {
5693 12         48 $ifdBuff .= Set32u(10 + 12 * $num + 4 + length($valBuff));
5694 12         34 $valBuff .= $val;
5695             } else {
5696 48 100       115 $val .= "\0" x (4 - length($val)) if length $val < 4;
5697 48         529 $ifdBuff .= $val;
5698             }
5699             }
5700 4         19 $ifdBuff .= "\0\0\0\0"; # (no IFD1)
5701 4 50       20 return undef unless $offsetPos;
5702 4         22 Set32u(length($ifdBuff) + length($valBuff), \$ifdBuff, $offsetPos);
5703 4         18 return $ifdBuff . $valBuff . $$dataPt;
5704             }
5705              
5706             #------------------------------------------------------------------------------
5707             # Rebuild TIFF thumbnail(s)/preview(s) into stand-alone files with current byte order
5708             # Inputs: 0) ExifTool ref, 1) SubfileType, 2) Compression, 3) ImageWidth, 4) ImageHeight,
5709             # 5) BitsPerSample, 6) PhotometricInterpretation, 7) StripOffsets, 8) SamplesPerPixel,
5710             # 9) RowsPerStrip, 10) StripByteCounts, 10) PlanarConfiguration, 11) Orientation
5711             # Returns: 0) TIFF image or undef, 1/2) Family 0/1 groups for TIFF preview IFD
5712             sub RebuildTIFF($;@)
5713             {
5714 14     14 0 37 local $_;
5715 14         40 my $et = $_[0];
5716 14         48 my $value = $$et{VALUE};
5717 14         45 my ($i, $j, $rtn, $grp0, $grp1);
5718 14 50       82 return undef if $$et{FILE_TYPE} eq 'RWZ';
5719             SubFile:
5720 14         48 for ($i=0; ; ++$i) {
5721 40 100       173 my $key = 'SubfileType' . ($i ? " ($i)" : '');
5722 40 100       145 last unless defined $$value{$key};
5723 26 100       103 next unless $$value{$key} == 1; # (reduced-resolution image)
5724 12         47 my $grp = $et->GetGroup($key, 1);
5725 12         61 my $cmp = $et->FindValue('Compression', $grp);
5726 12 100       48 next unless $cmp == 1; # (no compression)
5727 6         62 my %vals = (Compression=>$cmp, PlanarConfiguration=>1, Orientation=>1);
5728 6         48 foreach (qw(ImageWidth ImageHeight BitsPerSample PhotometricInterpretation
5729             StripOffsets SamplesPerPixel RowsPerStrip StripByteCounts
5730             PlanarConfiguration Orientation))
5731             {
5732 60         156 my $val = $et->FindValue($_, $grp);
5733 60 100       208 defined $val and $vals{$_} = $val, next;
5734 2 50       9 next SubFile unless defined $vals{$_};
5735             }
5736 6         37 my ($w, $h) = @vals{'ImageWidth', 'ImageHeight'};
5737 6         29 my @bits = split ' ', $vals{BitsPerSample};
5738 6         16 my $rowBytes = 0;
5739 6         59 $rowBytes += $w * int(($_+7)/8) foreach @bits;
5740 6         17 my $dat = '';
5741 6         23 my @off = split ' ', $vals{StripOffsets};
5742 6         19 my @len = split ' ', $vals{StripByteCounts};
5743             # read the image data
5744 6         44 for ($j=0; $j<@off; ++$j) {
5745 6 100       47 next SubFile unless $len[$j] == $rowBytes * $vals{RowsPerStrip};
5746 4         23 my $tmp = $et->ExtractBinary($off[$j], $len[$j]);
5747 4 50       14 next SubFile unless defined $tmp;
5748 4         19 $dat .= $tmp;
5749             }
5750             # generate the TIFF image
5751             my %entries = (
5752             0x0fe => 0, # SubfileType = 0
5753             0x100 => $w, # ImageWidth
5754             0x101 => $h, # ImageHeight
5755             0x102 => $vals{BitsPerSample},# BitsPerSample
5756             0x103 => $vals{Compression},# Compression
5757             0x106 => $vals{PhotometricInterpretation}, # PhotometricInterpretation
5758             0x111 => 0, # StripOffsets (will be adjusted later)
5759             0x112 => $vals{Orientation},# Orientation
5760             0x115 => $vals{SamplesPerPixel}, # SamplesPerPixel
5761             0x116 => $h, # RowsPerStrip
5762             0x117 => $h * $rowBytes, # StripByteCounts
5763             0x11a => 72, # XResolution = 72
5764             0x11b => 72, # YResolution = 72
5765             0x11c => $vals{PlanarConfiguration}, # PlanarConfiguration
5766 4         91 0x128 => 2, # ResolutionUnit = 2
5767             );
5768 4         26 my $img = GenerateTIFF(\%entries, \$dat);
5769              
5770 4 50 33     38 if (not defined $img) {
    50          
5771 0 0       0 $et->Warn('Invalid ' . ($w > 256 ? 'Preview' : 'Thumbnail') . 'TIFF data');
5772             } elsif ($rtn or $w > 256) { # (call it a preview if larger than 256 pixels)
5773 0         0 $et->FoundTag('PreviewTIFF', \$img, $et->GetGroup($key));
5774             } else {
5775 4         11 $rtn = \$img;
5776 4         27 ($grp0, $grp1) = $et->GetGroup($key);
5777             }
5778             }
5779 14 50       68 return $rtn unless wantarray;
5780 14         402 return ($rtn, $grp0, $grp1);
5781             }
5782              
5783             #------------------------------------------------------------------------------
5784             # Extract image from file
5785             # Inputs: 0) ExifTool object reference, 1) data offset (in file), 2) data length
5786             # 3) [optional] tag name
5787             # Returns: Reference to Image if specifically requested or "Binary data" message
5788             # Returns undef if there was an error loading the image
5789             sub ExtractImage($$$$)
5790             {
5791 200     200 0 1129 my ($et, $offset, $len, $tag) = @_;
5792 200         642 my $dataPt = \$$et{EXIF_DATA};
5793 200         556 my $dataPos = $$et{EXIF_POS};
5794 200         380 my $image;
5795              
5796             # no image if length is zero, and don't try to extract binary from XMP file
5797 200 100 100     1774 return undef if not $len or $$et{FILE_TYPE} eq 'XMP';
5798              
5799             # take data from EXIF block if possible
5800 139 100 66     1322 if (defined $dataPos and $offset>=$dataPos and $offset+$len<=$dataPos+length($$dataPt)) {
      100        
5801 97         467 $image = substr($$dataPt, $offset-$dataPos, $len);
5802             } else {
5803 42         247 $image = $et->ExtractBinary($offset, $len, $tag);
5804 42 100       200 return undef unless defined $image;
5805             # patch for incorrect ThumbnailOffset in some Sony DSLR-A100 ARW images
5806 37 0 66     300 if ($tag and $tag eq 'ThumbnailImage' and $$et{TIFF_TYPE} eq 'ARW' and
      66        
      33        
      33        
      0        
5807             $$et{Model} eq 'DSLR-A100' and $offset < 0x10000 and
5808             $image !~ /^(Binary data|\xff\xd8\xff)/)
5809             {
5810 0         0 my $try = $et->ExtractBinary($offset + 0x10000, $len, $tag);
5811 0 0 0     0 if (defined $try and $try =~ /^\xff\xd8\xff/) {
5812 0         0 $image = $try;
5813 0         0 $$et{VALUE}{ThumbnailOffset} += 0x10000;
5814 0         0 $et->Warn('Adjusted incorrect A100 ThumbnailOffset', 1);
5815             }
5816             }
5817             }
5818 134         801 return $et->ValidateImage(\$image, $tag);
5819             }
5820              
5821             #------------------------------------------------------------------------------
5822             # Utility routine to return tag ID string for warnings
5823             # Inputs: 0) Tag ID, 1) [optional] TagInfo ref
5824             # Returns: "tag 0xXXXX NAME"
5825             sub TagName($;$)
5826             {
5827 20     20 0 44 my ($tagID, $tagInfo) = @_;
5828 20 100       70 my $tagName = $tagInfo ? ' '.$$tagInfo{Name} : '';
5829 20         101 return sprintf('tag 0x%.4x%s', $tagID, $tagName);
5830             }
5831              
5832             #------------------------------------------------------------------------------
5833             # Get class name of next IFD offset for HtmlDump output
5834             # Inputs: 0) ExifTool ref, 1) current class ID
5835             # Returns: 0) new IFD offset name, 1) new class ID including "Offset_" for new offset
5836             # 2) new "Offset_" ID
5837             sub NextOffsetName($;$)
5838             {
5839 0     0 0 0 my ($et, $id) = @_;
5840 0 0       0 $$et{OffsetNum} = defined $$et{OffsetNum} ? $$et{OffsetNum} + 1 : 0;
5841 0         0 my $offName = 'o' . $$et{OffsetNum};
5842 0         0 my $sid = "Offset_$offName";
5843 0 0       0 $id = (defined $id ? "$id " : '') . $sid;
5844 0         0 return ($offName, $id, $sid);
5845             }
5846              
5847             #------------------------------------------------------------------------------
5848             # Process EXIF directory
5849             # Inputs: 0) ExifTool object reference
5850             # 1) Reference to directory information hash
5851             # 2) Pointer to tag table for this directory
5852             # Returns: 1 on success, otherwise returns 0 and sets a Warning
5853             sub ProcessExif($$$)
5854             {
5855 1056     1056 0 2862 my ($et, $dirInfo, $tagTablePtr) = @_;
5856 1056         2421 my $dataPt = $$dirInfo{DataPt};
5857 1056   100     4132 my $dataPos = $$dirInfo{DataPos} || 0;
5858 1056         2199 my $dataLen = $$dirInfo{DataLen};
5859 1056   100     3863 my $dirStart = $$dirInfo{DirStart} || 0;
5860 1056   100     3042 my $dirLen = $$dirInfo{DirLen} || $dataLen - $dirStart;
5861 1056         2173 my $dirName = $$dirInfo{DirName};
5862 1056   100     3682 my $base = $$dirInfo{Base} || 0;
5863 1056         1881 my $firstBase = $base;
5864 1056         1999 my $raf = $$dirInfo{RAF};
5865 1056         3577 my $verbose = $et->Options('Verbose');
5866 1056         3202 my $validate = $et->Options('Validate');
5867 1056         3106 my $saveFormat = $et->Options('SaveFormat');
5868 1056         2730 my $htmlDump = $$et{HTML_DUMP};
5869 1056         2410 my $success = 1;
5870 1056         2407 my ($tagKey, $dirSize, $makerAddr, $strEnc, %offsetInfo, $offName, $nextOffName);
5871 1056         3264 my $inMakerNotes = $$tagTablePtr{GROUPS}{0} eq 'MakerNotes';
5872 1056         3294 my $isExif = ($tagTablePtr eq \%Image::ExifTool::Exif::Main);
5873              
5874             # set encoding to assume for strings
5875 1056 100       4130 $strEnc = $et->Options('CharsetEXIF') if $$tagTablePtr{GROUPS}{0} eq 'EXIF';
5876              
5877             # ignore non-standard EXIF while in strict MWG compatibility mode
5878 1056 50 100     6569 if (($validate or $Image::ExifTool::MWG::strict) and $dirName eq 'IFD0' and
      100        
      66        
      66        
5879             $isExif and $$et{FILE_TYPE} =~ /^(JPEG|TIFF|PSD)$/)
5880             {
5881 7         34 my $path = $et->MetadataPath();
5882 7 50       52 unless ($path =~ /^(JPEG-APP1-IFD0|TIFF-IFD0|PSD-EXIFInfo-IFD0)$/) {
5883 0 0       0 if ($Image::ExifTool::MWG::strict) {
5884 0         0 $et->Warn("Ignored non-standard EXIF at $path");
5885 0         0 return 0;
5886             } else {
5887 0         0 $et->Warn("Non-standard EXIF at $path", 1);
5888             }
5889             }
5890             }
5891             # mix htmlDump and Validate into verbose so we can test for all at once
5892 1056 50       2582 $verbose = -1 if $htmlDump;
5893 1056 100 66     2929 $verbose = -2 if $validate and not $verbose;
5894 1056 100       2858 $dirName eq 'EXIF' and $dirName = $$dirInfo{DirName} = 'IFD0';
5895 1056 100 100     7198 $$dirInfo{Multi} = 1 if $dirName =~ /^(IFD0|SubIFD)$/ and not defined $$dirInfo{Multi};
5896             # get a more descriptive name for MakerNote sub-directories
5897 1056         2593 my $dir = $$dirInfo{Name};
5898 1056 100 100     5381 $dir = $dirName unless $dir and $inMakerNotes and $dir !~ /^MakerNote/;
      100        
5899              
5900 1056         2133 my ($numEntries, $dirEnd);
5901 1056 100 100     4795 if ($dirStart >= 0 and $dirStart <= $dataLen-2) {
5902             # make sure data is large enough (patches bug in Olympus subdirectory lengths)
5903 980         3000 $numEntries = Get16u($dataPt, $dirStart);
5904 980         2320 $dirSize = 2 + 12 * $numEntries;
5905 980         1789 $dirEnd = $dirStart + $dirSize;
5906 980 100       2876 if ($dirSize > $dirLen) {
5907 363 50 66     2500 if (($verbose > 0 or $validate) and not $$dirInfo{SubIFD}) {
      66        
5908 0         0 my $short = $dirSize - $dirLen;
5909 0         0 $$et{INDENT} =~ s/..$//; # keep indent the same
5910 0         0 $et->Warn("Short directory size for $dir (missing $short bytes)");
5911 0         0 $$et{INDENT} .= '| ';
5912             }
5913 363 50       1117 undef $dirSize if $dirEnd > $dataLen; # read from file if necessary
5914             }
5915             }
5916             # read IFD from file if necessary
5917 1056 100       2683 unless ($dirSize) {
5918 76         156 $success = 0;
5919 76 50       234 if ($raf) {
5920             # read the count of entries in this IFD
5921 76         165 my $offset = $dirStart + $dataPos;
5922 76         160 my ($buff, $buf2);
5923 76 50 33     309 if ($raf->Seek($offset + $base, 0) and $raf->Read($buff,2) == 2) {
5924 76         329 my $len = 12 * Get16u(\$buff,0);
5925             # also read next IFD pointer if available
5926 76 50       320 if ($raf->Read($buf2, $len+4) >= $len) {
5927 76         264 $buff .= $buf2;
5928 76         279 $dataPt = $$dirInfo{DataPt} = \$buff;
5929 76         195 $dataPos = $$dirInfo{DataPos} = $offset;
5930 76         205 $dataLen = $$dirInfo{DataLen} = length $buff;
5931 76         169 $dirStart = $$dirInfo{DirStart} = 0;
5932 76         196 $dirLen = $$dirInfo{DirLen} = length $buff;
5933 76         160 $success = 1;
5934             }
5935             }
5936             }
5937 76 50       289 if ($success) {
5938 76         217 $numEntries = Get16u($dataPt, $dirStart);
5939             } else {
5940 0         0 $et->Warn("Bad $dir directory", $inMakerNotes);
5941 0 0 0     0 return 0 unless $inMakerNotes and $dirLen >= 14 and $dirStart >= 0 and
      0        
      0        
5942             $dirStart + $dirLen <= length($$dataPt);
5943 0         0 $dirSize = $dirLen;
5944 0         0 $numEntries = int(($dirSize - 2) / 12); # read what we can
5945 0         0 Set16u($numEntries, $dataPt, $dirStart);
5946             }
5947 76         553 $dirSize = 2 + 12 * $numEntries;
5948 76         153 $dirEnd = $dirStart + $dirSize;
5949             }
5950 1056 100       2991 $verbose > 0 and $et->VerboseDir($dirName, $numEntries);
5951 1056         2175 my $bytesFromEnd = $dataLen - $dirEnd;
5952 1056 100       2741 if ($bytesFromEnd < 4) {
5953 4 50 33     36 unless ($bytesFromEnd==2 or $bytesFromEnd==0) {
5954 0         0 $et->Warn("Illegal $dir directory size ($numEntries entries)");
5955 0         0 return 0;
5956             }
5957             }
5958             # fix base offset for maker notes if necessary
5959 1056 100       3083 if (defined $$dirInfo{MakerNoteAddr}) {
5960 123         423 $makerAddr = $$dirInfo{MakerNoteAddr};
5961 123         358 delete $$dirInfo{MakerNoteAddr};
5962 123 100       780 if (Image::ExifTool::MakerNotes::FixBase($et, $dirInfo)) {
5963 3         12 $base = $$dirInfo{Base};
5964 3         7 $dataPos = $$dirInfo{DataPos};
5965             }
5966             }
5967 1056 50       2931 if ($htmlDump) {
5968 0         0 $offName = $$dirInfo{OffsetName};
5969 0 0 0     0 my $longName = $dir eq 'MakerNotes' ? ($$dirInfo{Name} || $dir) : $dir;
5970 0 0       0 if (defined $makerAddr) {
5971 0         0 my $hdrLen = $dirStart + $dataPos + $base - $makerAddr;
5972 0 0       0 $et->HDump($makerAddr, $hdrLen, "MakerNotes header", $longName) if $hdrLen > 0;
5973             }
5974 0 0       0 unless ($$dirInfo{NoDumpEntryCount}) {
5975 0         0 $et->HDump($dirStart + $dataPos + $base, 2, "$longName entries",
5976             "Entry count: $numEntries", undef, $offName);
5977             }
5978 0         0 my $tip;
5979 0         0 my $id = $offName;
5980 0 0       0 if ($bytesFromEnd >= 4) {
5981 0 0       0 my $nxt = ($dir =~ /^(.*?)(\d+)$/) ? $1 . ($2 + 1) : 'Next IFD';
5982 0         0 my $off = Get32u($dataPt, $dirEnd);
5983 0         0 $tip = sprintf("$nxt offset: 0x%.4x", $off);
5984 0 0       0 ($nextOffName, $id) = NextOffsetName($et, $offName) if $off;
5985             }
5986 0         0 $et->HDump($dirEnd + $dataPos + $base, 4, "Next IFD", $tip, 0, $id);
5987             }
5988              
5989             # patch for Canon EOS 40D firmware 1.0.4 bug (incorrect directory counts)
5990             # (must do this before parsing directory or CameraSettings offset will be suspicious)
5991 1056 50 66     3916 if ($inMakerNotes and $$et{Model} eq 'Canon EOS 40D' and $numEntries) {
      33        
5992 0         0 my $entry = $dirStart + 2 + 12 * ($numEntries - 1);
5993 0         0 my $fmt = Get16u($dataPt, $entry + 2);
5994 0 0 0     0 if ($fmt < 1 or $fmt > 13) {
5995 0         0 $et->HDump($entry+$dataPos+$base,12,"[invalid IFD entry]",
5996             "Bad format type: $fmt", 1, $offName);
5997             # adjust the number of directory entries
5998 0         0 --$numEntries;
5999 0         0 $dirEnd -= 12;
6000             }
6001             }
6002              
6003             # make sure that Compression and SubfileType are defined for this IFD (for Condition's)
6004 1056         2992 $$et{Compression} = $$et{SubfileType} = '';
6005              
6006             # loop through all entries in an EXIF directory (IFD)
6007 1056         2495 my ($index, $valEnd, $offList, $offHash, $mapFmt, @valPos);
6008 1056 100       3108 $mapFmt = $$tagTablePtr{VARS}{MAP_FORMAT} if $$tagTablePtr{VARS};
6009              
6010 1056         2504 my ($warnCount, $lastID) = (0, -1);
6011 1056         3317 for ($index=0; $index<$numEntries; ++$index) {
6012 15012 50       30472 if ($warnCount > 10) {
6013 0 0       0 $et->Warn("Too many warnings -- $dir parsing aborted", 2) and return 0;
6014             }
6015 15012         27343 my $entry = $dirStart + 2 + 12 * $index;
6016 15012         33555 my $tagID = Get16u($dataPt, $entry);
6017 15012         34829 my $format = Get16u($dataPt, $entry+2);
6018 15012         38900 my $count = Get32u($dataPt, $entry+4);
6019             # (Apple uses the BigTIFF format code 16 in the maker notes of their ProRaw DNG files)
6020 15012 50 33     62632 if (($format < 1 or $format > 13) and not ($format == 16 and $$et{Make} eq 'Apple' and $inMakerNotes)) {
      0        
      33        
6021 0 0 0     0 if ($mapFmt and $$mapFmt{$format}) {
6022 0         0 $format = $$mapFmt{$format};
6023             } else {
6024 0         0 $et->HDump($entry+$dataPos+$base,12,"[invalid IFD entry]",
6025             "Bad format type: $format", 1, $offName);
6026             # warn unless the IFD was just padded with zeros
6027 0 0 0     0 if ($format or $validate) {
6028 0         0 $et->Warn("Bad format ($format) for $dir entry $index", $inMakerNotes);
6029 0         0 ++$warnCount;
6030             }
6031             # assume corrupted IFD if this is our first entry (except Sony ILCE-7M2 firmware 1.21)
6032 0 0 0     0 return 0 unless $index or $$et{Model} eq 'ILCE-7M2';
6033 0         0 next;
6034             }
6035             }
6036 15012         31446 my $formatStr = $formatName[$format]; # get name of this format
6037 15012         22554 my $valueDataPt = $dataPt;
6038 15012         20816 my $valueDataPos = $dataPos;
6039 15012         20963 my $valueDataLen = $dataLen;
6040 15012         22671 my $valuePtr = $entry + 8; # pointer to value within $$dataPt
6041 15012         38825 my $tagInfo = $et->GetTagInfo($tagTablePtr, $tagID);
6042 15012         26878 my ($origFormStr, $bad, $rational, $subOffName);
6043             # save the EXIF format codes if requested
6044 15012 50       29449 $$et{SaveFormat}{$saveFormat = $formatStr} = 1 if $saveFormat;
6045             # hack to patch incorrect count in Kodak SubIFD3 tags
6046 15012 50 100     64681 if ($count < 2 and ref $$tagTablePtr{$tagID} eq 'HASH' and $$tagTablePtr{$tagID}{FixCount}) {
      66        
6047 0 0       0 $offList or ($offList, $offHash) = GetOffList($dataPt, $dirStart, $dataPos,
6048             $numEntries, $tagTablePtr);
6049 0         0 my $i = $$offHash{Get32u($dataPt, $valuePtr)};
6050 0 0 0     0 if (defined $i and $i < $#$offList) {
6051 0         0 my $oldCount = $count;
6052 0         0 $count = int(($$offList[$i+1] - $$offList[$i]) / $formatSize[$format]);
6053 0 0       0 $origFormStr = $formatName[$format] . '[' . $oldCount . ']' if $oldCount != $count;
6054             }
6055             }
6056 15012 100 100     32529 $validate and not $inMakerNotes and Image::ExifTool::Validate::ValidateExif(
6057             $et, $tagTablePtr, $tagID, $tagInfo, $lastID, $dir, $count, $formatStr);
6058 15012         27256 my $size = $count * $formatSize[$format];
6059 15012         22597 my $readSize = $size;
6060 15012 100       29973 if ($size > 4) {
6061 6747 50 0     15349 if ($size > 0x7fffffff and (not $tagInfo or not $$tagInfo{ReadFromRAF})) {
      33        
6062 0         0 $et->Warn(sprintf("Invalid size (%u) for %s %s",$size,$dir,TagName($tagID,$tagInfo)), $inMakerNotes);
6063 0         0 ++$warnCount;
6064 0         0 next;
6065             }
6066 6747         14852 $valuePtr = Get32u($dataPt, $valuePtr);
6067 6747 100 100     16592 if ($validate and not $inMakerNotes) {
6068 20         45 my $tagName = TagName($tagID, $tagInfo);
6069 20 50       56 $et->Warn("Odd offset for $dir $tagName", 1) if $valuePtr & 0x01;
6070 20 50 33     134 if ($valuePtr < 8 || ($valuePtr + $size > length($$dataPt) and
      33        
6071             $valuePtr + $size > $$et{VALUE}{FileSize}))
6072             {
6073 0         0 $et->Warn("Invalid offset for $dir $tagName");
6074 0         0 ++$warnCount;
6075 0         0 next;
6076             }
6077 20 50 33     80 if ($valuePtr + $size > $dirStart + $dataPos and $valuePtr < $dirEnd + $dataPos + 4) {
6078 0         0 $et->Warn("Value for $dir $tagName overlaps IFD");
6079             }
6080 20         46 foreach (@valPos) {
6081 81 50 33     259 next if $$_[0] >= $valuePtr + $size or $$_[0] + $$_[1] <= $valuePtr;
6082 0         0 $et->Warn("Value for $dir $tagName overlaps $$_[2]");
6083             }
6084 20         61 push @valPos, [ $valuePtr, $size, $tagName ];
6085             }
6086             # fix valuePtr if necessary
6087 6747 50       15965 if ($$dirInfo{FixOffsets}) {
6088 0         0 my $wFlag;
6089 0 0       0 $valEnd or $valEnd = $dataPos + $dirEnd + 4;
6090             #### eval FixOffsets ($valuePtr, $valEnd, $size, $tagID, $wFlag)
6091 0         0 eval $$dirInfo{FixOffsets};
6092             }
6093 6747         9938 my $suspect;
6094             # offset shouldn't point into TIFF header
6095 6747 100 66     15240 $valuePtr < 8 and not $$dirInfo{ZeroOffsetOK} and $suspect = $warnCount;
6096             # convert offset to pointer in $$dataPt
6097 6747 100 66     36112 if ($$dirInfo{EntryBased} or (ref $$tagTablePtr{$tagID} eq 'HASH' and
      66        
6098             $$tagTablePtr{$tagID}{EntryBased}))
6099             {
6100 10         19 $valuePtr += $entry;
6101             } else {
6102 6737         11734 $valuePtr -= $dataPos;
6103             }
6104             # value shouldn't overlap our directory
6105 6747 50 66     15573 $suspect = $warnCount if $valuePtr < $dirEnd and $valuePtr+$size > $dirStart;
6106             # load value from file if necessary
6107 6747 100 100     22953 if ($valuePtr < 0 or $valuePtr+$size > $dataLen) {
6108             # get value by seeking in file if we are allowed
6109 449         1029 my $buff;
6110 449 50       1044 if ($raf) {
6111             # avoid loading large binary data unless necessary
6112 449         1140 while ($size > BINARY_DATA_LIMIT) {
6113 0 0       0 if ($tagInfo) {
6114             # make large unknown blocks binary data
6115 0 0       0 $$tagInfo{Binary} = 1 if $$tagInfo{Unknown};
6116 0 0       0 last unless $$tagInfo{Binary}; # must read non-binary data
6117 0 0       0 last if $$tagInfo{SubDirectory};
6118 0         0 my $lcTag = lc($$tagInfo{Name});
6119 0 0 0     0 if ($$et{OPTIONS}{Binary} and
6120             not $$et{EXCL_TAG_LOOKUP}{$lcTag})
6121             {
6122             # read binary data if specified unless tagsFromFile won't use it
6123 0 0 0     0 last unless $$et{TAGS_FROM_FILE} and $$tagInfo{Protected};
6124             }
6125             # must read if tag is specified by name
6126 0 0       0 last if $$et{REQ_TAG_LOOKUP}{$lcTag};
6127             } else {
6128             # must read value if needed for a condition
6129 0 0       0 last if defined $tagInfo;
6130             }
6131             # (note: changing the value without changing $size will cause
6132             # a warning in the verbose output, but we need to maintain the
6133             # proper size for the htmlDump, so we can't change this)
6134 0         0 $buff = "Binary data $size bytes";
6135 0         0 $readSize = length $buff;
6136 0         0 last;
6137             }
6138             # read from file if necessary
6139 449 50       1048 unless (defined $buff) {
6140 449         653 my $wrn;
6141 449   66     1585 my $readFromRAF = ($tagInfo and $$tagInfo{ReadFromRAF});
6142 449 50 33     1545 if (not $raf->Seek($base + $valuePtr + $dataPos, 0)) {
    50 33        
    50          
    50          
6143 0         0 $wrn = "Invalid offset for $dir entry $index";
6144             } elsif ($readFromRAF and $size > BINARY_DATA_LIMIT and
6145             not $$et{REQ_TAG_LOOKUP}{lc $$tagInfo{Name}})
6146             {
6147 0         0 $buff = "$$tagInfo{Name} data $size bytes";
6148 0         0 $readSize = length $buff;
6149             } elsif ($raf->Read($buff,$size) != $size) {
6150 0         0 $wrn = "Error reading value for $dir entry $index";
6151             } elsif ($readFromRAF) {
6152             # seek back to the start of the value
6153 0         0 $raf->Seek($base + $valuePtr + $dataPos, 0);
6154             }
6155 449 50       1557 if ($wrn) {
6156 0         0 $et->Warn($wrn, $inMakerNotes);
6157 0 0 0     0 return 0 unless $inMakerNotes or $htmlDump;
6158 0         0 ++$warnCount;
6159 0 0       0 $buff = '' unless defined $buff;
6160 0         0 $readSize = length $buff;
6161 0         0 $bad = 1;
6162             }
6163             }
6164 449         1068 $valueDataLen = length $buff;
6165 449         876 $valueDataPt = \$buff;
6166 449         804 $valueDataPos = $valuePtr + $dataPos;
6167 449         811 $valuePtr = 0;
6168             } else {
6169 0         0 my ($tagStr, $tmpInfo, $leicaTrailer);
6170 0 0       0 if ($tagInfo) {
    0          
6171 0         0 $tagStr = $$tagInfo{Name};
6172 0         0 $leicaTrailer = $$tagInfo{LeicaTrailer};
6173             } elsif (defined $tagInfo) {
6174 0         0 $tmpInfo = $et->GetTagInfo($tagTablePtr, $tagID, \ '', $formatStr, $count);
6175 0 0       0 if ($tmpInfo) {
6176 0         0 $tagStr = $$tmpInfo{Name};
6177 0         0 $leicaTrailer = $$tmpInfo{LeicaTrailer};
6178             }
6179             }
6180 0 0 0     0 if ($tagInfo and $$tagInfo{ChangeBase}) {
6181             # adjust base offset for this tag only
6182             #### eval ChangeBase ($dirStart,$dataPos)
6183 0         0 my $newBase = eval $$tagInfo{ChangeBase};
6184 0         0 $valuePtr += $newBase;
6185             }
6186 0 0       0 $tagStr or $tagStr = sprintf("tag 0x%.4x",$tagID);
6187             # allow PreviewImage to run outside EXIF data
6188 0 0 0     0 if ($tagStr eq 'PreviewImage' and $$et{RAF}) {
    0 0        
6189 0         0 my $pos = $$et{RAF}->Tell();
6190 0         0 $buff = $et->ExtractBinary($base + $valuePtr + $dataPos, $size, 'PreviewImage');
6191 0         0 $$et{RAF}->Seek($pos, 0);
6192 0         0 $valueDataPt = \$buff;
6193 0         0 $valueDataPos = $valuePtr + $dataPos;
6194 0         0 $valueDataLen = $size;
6195 0         0 $valuePtr = 0;
6196             } elsif ($leicaTrailer and $$et{RAF}) {
6197 0 0       0 if ($verbose > 0) {
6198 0         0 $et->VPrint(0, "$$et{INDENT}$index) $tagStr --> (outside APP1 segment)\n");
6199             }
6200 0 0       0 if ($et->Options('FastScan')) {
6201 0         0 $et->Warn('Ignored Leica MakerNote trailer');
6202             } else {
6203 0         0 require Image::ExifTool::Fixup;
6204             $$et{LeicaTrailer} = {
6205 0   0     0 TagInfo => $tagInfo || $tmpInfo,
6206             Offset => $base + $valuePtr + $dataPos,
6207             Size => $size,
6208             Fixup => new Image::ExifTool::Fixup,
6209             };
6210             }
6211             } else {
6212 0         0 $et->Warn("Bad offset for $dir $tagStr", $inMakerNotes);
6213 0         0 ++$warnCount;
6214             }
6215 0 0       0 unless (defined $buff) {
6216 0         0 $valueDataPt = '';
6217 0         0 $valueDataPos = $valuePtr + $dataPos;
6218 0         0 $valueDataLen = 0;
6219 0         0 $valuePtr = 0;
6220 0         0 $bad = 1;
6221             }
6222             }
6223             }
6224             # warn about suspect offsets if they didn't already cause another warning
6225 6747 100 66     16162 if (defined $suspect and $suspect == $warnCount) {
6226 2 50       7 my $tagStr = $tagInfo ? $$tagInfo{Name} : sprintf('tag 0x%.4x', $tagID);
6227 2 50       11 if ($et->Warn("Suspicious $dir offset for $tagStr", $inMakerNotes)) {
6228 2         5 ++$warnCount;
6229 2 50       9 next unless $verbose;
6230             }
6231             }
6232             }
6233             # treat single unknown byte as int8u
6234 15010 100 100     36996 $formatStr = 'int8u' if $format == 7 and $count == 1;
6235              
6236 15010         23700 my ($val, $subdir, $wrongFormat);
6237 15010 50 33     32024 if ($tagID > 0xf000 and $isExif) {
6238 0         0 my $oldInfo = $$tagTablePtr{$tagID};
6239 0 0 0     0 if ((not $oldInfo or (ref $oldInfo eq 'HASH' and $$oldInfo{Condition} and
      0        
6240             not $$oldInfo{PSRaw})) and not $bad)
6241             {
6242             # handle special case of Photoshop RAW tags (0xfde8-0xfe58)
6243             # --> generate tags from the value if possible
6244 0         0 $val = ReadValue($valueDataPt,$valuePtr,$formatStr,$count,$readSize);
6245 0 0 0     0 if (defined $val and $val =~ /(.*): (.*)/) {
6246 0         0 my $tag = $1;
6247 0         0 $val = $2;
6248 0         0 $tag =~ s/'s//; # remove 's (so "Owner's Name" becomes "OwnerName")
6249 0         0 $tag =~ tr/a-zA-Z0-9_//cd; # remove unknown characters
6250 0 0       0 if ($tag) {
6251 0         0 $tagInfo = {
6252             Name => $tag,
6253             Condition => '$$self{TIFF_TYPE} ne "DCR"',
6254             ValueConv => '$_=$val;s/^.*: //;$_', # remove descr
6255             PSRaw => 1, # (just as flag to avoid adding this again)
6256             };
6257 0         0 AddTagToTable($tagTablePtr, $tagID, $tagInfo);
6258             # generate conditional list if a conditional tag already existed
6259 0 0       0 $$tagTablePtr{$tagID} = [ $oldInfo, $tagInfo ] if $oldInfo;
6260             }
6261             }
6262             }
6263             }
6264 15010 100 100     48975 if (defined $tagInfo and not $tagInfo) {
6265 387 50       1150 if ($bad) {
6266 0         0 undef $tagInfo;
6267             } else {
6268             # GetTagInfo() required the value for a Condition
6269 387 100       1591 my $tmpVal = substr($$valueDataPt, $valuePtr, $readSize < 128 ? $readSize : 128);
6270             # (use original format name in this call -- $formatStr may have been changed to int8u)
6271 387         1572 $tagInfo = $et->GetTagInfo($tagTablePtr, $tagID, \$tmpVal,
6272             $formatName[$format], $count);
6273             }
6274             }
6275             # make sure we are handling the 'ifd' format properly
6276 15010 50 66     51953 if (($format == 13 or $format == 18) and (not $tagInfo or not $$tagInfo{SubIFD})) {
      33        
      66        
6277 0         0 my $str = sprintf('%s tag 0x%.4x IFD format not handled', $dirName, $tagID);
6278 0         0 $et->Warn($str, $inMakerNotes);
6279             }
6280 15010 100       27771 if (defined $tagInfo) {
6281 14462         26781 my $readFormat = $$tagInfo{Format};
6282 14462         25405 $subdir = $$tagInfo{SubDirectory};
6283             # unless otherwise specified, all SubDirectory data except
6284             # EXIF SubIFD offsets should be unformatted
6285 14462 100 100     34858 $readFormat = 'undef' if $subdir and not $$tagInfo{SubIFD} and not $readFormat;
      100        
6286             # override EXIF format if specified
6287 14462 100       28148 if ($readFormat) {
6288 1741         3141 $formatStr = $readFormat;
6289 1741         4108 my $newNum = $formatNumber{$formatStr};
6290 1741 100 66     6556 if ($newNum and $newNum != $format) {
6291 948         2931 $origFormStr = $formatName[$format] . '[' . $count . ']';
6292 948         1612 $format = $newNum;
6293 948 50       5661 $size = $readSize = $$tagInfo{FixedSize} if $$tagInfo{FixedSize};
6294             # adjust number of items for new format size
6295 948         2796 $count = int($size / $formatSize[$format]);
6296             }
6297             }
6298             # verify that offset-type values are integral
6299 14462 50 100     57539 if (($$tagInfo{IsOffset} or $$tagInfo{SubIFD}) and not $intFormat{$formatStr}) {
      66        
6300 0         0 $et->Warn(sprintf('Wrong format (%s) for %s 0x%.4x %s',$formatStr,$dir,$tagID,$$tagInfo{Name}));
6301 0 0       0 if ($validate) {
6302 0         0 $$et{WrongFormat}{"$dir:$$tagInfo{Name}"} = 1;
6303 0         0 $offsetInfo{$tagID} = [ $tagInfo, '' ];
6304             }
6305 0 0       0 next unless $verbose;
6306 0         0 $wrongFormat = 1;
6307             }
6308             } else {
6309 548 100       2068 next unless $verbose;
6310             }
6311 14465 50       28385 unless ($bad) {
6312             # limit maximum length of data to reformat
6313             # (avoids long delays when processing some corrupted files)
6314 14465         20069 my $warned;
6315 14465 50 33     31494 if ($count > 100000 and $formatStr !~ /^(undef|string|binary)$/) {
6316 0 0       0 my $tagName = $tagInfo ? $$tagInfo{Name} : sprintf('tag 0x%.4x', $tagID);
6317             # (count of 196608 is typical for ColorMap)
6318 0 0 0     0 if ($tagName ne 'TransferFunction' or $count != 196608) {
6319 0 0       0 my $minor = $count > 2000000 ? 0 : 2;
6320 0 0       0 if ($et->Warn("Ignoring $dirName $tagName with excessive count", $minor)) {
6321 0 0       0 next unless $$et{OPTIONS}{HtmlDump};
6322 0         0 $warned = 1;
6323             }
6324             }
6325             }
6326 14465 50 100     38160 if ($count > 500 and $formatStr !~ /^(undef|string|binary)$/ and
      33        
      66        
      33        
6327             (not $tagInfo or $$tagInfo{LongBinary} or $warned) and not $$et{OPTIONS}{IgnoreMinorErrors})
6328             {
6329 0 0       0 $et->WarnOnce('Not decoding some large array(s). Ignore minor errors to decode', 2) unless $warned;
6330 0 0       0 next if $$et{TAGS_FROM_FILE}; # don't generate bogus value when copying tags
6331 0         0 $val = "(large array of $count $formatStr values)";
6332             } else {
6333             # convert according to specified format
6334 14465         41071 $val = ReadValue($valueDataPt,$valuePtr,$formatStr,$count,$readSize,\$rational);
6335             # re-code if necessary
6336 14465 0 33     37279 $val = $et->Decode($val, $strEnc) if $strEnc and $formatStr eq 'string' and defined $val;
      33        
6337             }
6338             }
6339              
6340 14465 100       28672 if ($verbose) {
6341 345         579 my $tval = $val;
6342             # also show as a rational
6343 345 100       791 $tval .= " ($rational)" if defined $rational;
6344 345 50       684 if ($htmlDump) {
6345 0         0 my ($tagName, $colName);
6346 0 0 0     0 if ($tagID == 0x927c and $dirName eq 'ExifIFD') {
    0          
6347 0         0 $tagName = 'MakerNotes';
6348             } elsif ($tagInfo) {
6349 0         0 $tagName = $$tagInfo{Name};
6350             } else {
6351 0         0 $tagName = sprintf("Tag 0x%.4x",$tagID);
6352             }
6353 0         0 my $dname = sprintf("${dir}-%.2d", $index);
6354             # build our tool tip
6355 0 0       0 $size < 0 and $size = $count * $formatSize[$format];
6356 0         0 my $fstr = "$formatName[$format]\[$count]";
6357 0 0 0     0 $fstr = "$origFormStr read as $fstr" if $origFormStr and $origFormStr ne $fstr;
6358 0 0       0 $fstr .= ' <-- WRONG' if $wrongFormat;
6359 0         0 my $tip = sprintf("Tag ID: 0x%.4x\n", $tagID) .
6360             "Format: $fstr\nSize: $size bytes\n";
6361 0 0       0 if ($size > 4) {
6362 0         0 my $offPt = Get32u($dataPt,$entry+8);
6363             # (test this with ../pics/{CanonEOS-1D_XMarkIII.hif,PanasonicDC-G9.rw2})
6364 0   0     0 my $actPt = $valuePtr + $valueDataPos + $base - ($$et{EXIF_POS} || 0) + ($$et{BASE_FUDGE} || 0);
      0        
6365 0         0 $tip .= sprintf("Value offset: 0x%.4x\n", $offPt);
6366             # highlight tag name (red for bad size)
6367 0 0 0     0 my $style = ($bad or not defined $tval) ? 'V' : 'H';
6368 0 0       0 if ($actPt != $offPt) {
6369 0         0 $tip .= sprintf("Actual offset: 0x%.4x\n", $actPt);
6370 0 0       0 my $sign = $actPt < $offPt ? '-' : '';
6371 0         0 $tip .= sprintf("Offset base: ${sign}0x%.4x\n", abs($actPt - $offPt));
6372 0 0       0 $style = 'F' if $style eq 'H'; # purple for different offsets
6373             }
6374 0 0 0     0 if ($$et{EXIF_POS} and not $$et{BASE_FUDGE}) {
6375             $tip .= sprintf("File offset: 0x%.4x\n", $actPt + $$et{EXIF_POS})
6376 0         0 }
6377 0         0 $colName = "$tagName";
6378 0 0       0 $colName .= ' (odd)' if $offPt & 0x01;
6379             } else {
6380 0         0 $colName = $tagName;
6381             }
6382 0 0       0 $colName .= ' (err)' if $wrongFormat;
6383 0 0 0     0 $colName .= ' (seq)' if $tagID <= $lastID and not $inMakerNotes;
6384 0         0 $lastID = $tagID;
6385 0 0       0 if (not defined $tval) {
6386 0         0 $tval = '';
6387             } else {
6388 0 0       0 $tval = substr($tval,0,28) . '[...]' if length($tval) > 32;
6389 0 0 0     0 if ($formatStr =~ /^(string|undef|binary)/) {
    0          
6390             # translate non-printable characters
6391 0         0 $tval =~ tr/\x00-\x1f\x7f-\xff/./;
6392             } elsif ($tagInfo and Image::ExifTool::IsInt($tval)) {
6393 0 0 0     0 if ($$tagInfo{IsOffset} or $$tagInfo{SubIFD}) {
    0          
6394 0         0 $tval = sprintf('0x%.4x', $tval);
6395 0   0     0 my $actPt = $val + $base - ($$et{EXIF_POS} || 0) + ($$et{BASE_FUDGE} || 0);
      0        
6396 0 0       0 if ($actPt != $val) {
6397 0         0 $tval .= sprintf("\nActual offset: 0x%.4x", $actPt);
6398 0 0       0 my $sign = $actPt < $val ? '-' : '';
6399 0         0 $tval .= sprintf("\nOffset base: ${sign}0x%.4x", abs($actPt - $val));
6400             }
6401             } elsif ($$tagInfo{PrintHex}) {
6402 0         0 $tval = sprintf('0x%x', $tval);
6403             }
6404             }
6405             }
6406 0         0 $tip .= "Value: $tval";
6407 0         0 my $id = $offName;
6408 0         0 my $sid;
6409 0 0 0     0 ($subOffName, $id, $sid) = NextOffsetName($et, $offName) if $tagInfo and $$tagInfo{SubIFD};
6410 0         0 $et->HDump($entry+$dataPos+$base, 12, "$dname $colName", $tip, 1, $id);
6411 0 0       0 next if $valueDataLen < 0; # don't process bad pointer entry
6412 0 0       0 if ($size > 4) {
6413 0         0 my $exifDumpPos = $valuePtr + $valueDataPos + $base;
6414 0         0 my $flag = 0;
6415 0 0       0 if ($subdir) {
6416 0 0       0 if ($$tagInfo{MakerNotes}) {
    0          
6417 0         0 $flag = 0x04;
6418             } elsif ($$tagInfo{NestedHtmlDump}) {
6419 0 0       0 $flag = $$tagInfo{NestedHtmlDump} == 2 ? 0x10 : 0x04;
6420             }
6421             }
6422             # add value data block (underlining maker notes data)
6423 0         0 $et->HDump($exifDumpPos,$size,"$tagName value",'SAME', $flag, $sid);
6424             }
6425             } else {
6426 345 50 66     854 if ($tagID <= $lastID and not $inMakerNotes) {
6427 0 0       0 my $str = $tagInfo ? ' '.$$tagInfo{Name} : '';
6428 0 0       0 if ($tagID == $lastID) {
6429 0         0 $et->Warn(sprintf('Duplicate tag 0x%.4x%s in %s', $tagID, $str, $dirName));
6430             } else {
6431 0         0 $et->Warn(sprintf('Tag ID 0x%.4x%s out of sequence in %s', $tagID, $str, $dirName));
6432             }
6433             }
6434 345         559 $lastID = $tagID;
6435 345 100       704 if ($verbose > 0) {
6436 257         462 my $fstr = $formatName[$format];
6437 257 100       523 $fstr = "$origFormStr read as $fstr" if $origFormStr;
6438 257         995 $et->VerboseInfo($tagID, $tagInfo,
6439             Table => $tagTablePtr,
6440             Index => $index,
6441             Value => $tval,
6442             DataPt => $valueDataPt,
6443             DataPos => $valueDataPos + $base,
6444             Size => $size,
6445             Start => $valuePtr,
6446             Format => $fstr,
6447             Count => $count,
6448             );
6449             }
6450             }
6451 345 100 66     1355 next if not $tagInfo or $wrongFormat;
6452             }
6453 14462 50       29222 next unless defined $val;
6454             #..............................................................................
6455             # Handle SubDirectory tag types
6456             #
6457 14462 100       27036 if ($subdir) {
6458             # don't process empty subdirectories
6459 1247 50       3572 unless ($size) {
6460 0 0 0     0 unless ($$tagInfo{MakerNotes} or $inMakerNotes) {
6461 0         0 $et->Warn("Empty $$tagInfo{Name} data", 1);
6462             }
6463 0         0 next;
6464             }
6465 1247         2709 my (@values, $newTagTable, $dirNum, $newByteOrder, $invalid);
6466 1247         3193 my $tagStr = $$tagInfo{Name};
6467 1247 100       3452 if ($$subdir{MaxSubdirs}) {
6468 144         753 @values = split ' ', $val;
6469             # limit the number of subdirectories we parse
6470 144         492 my $over = @values - $$subdir{MaxSubdirs};
6471 144 50       693 if ($over > 0) {
6472 0         0 $et->Warn("Ignoring $over $tagStr directories");
6473 0         0 splice @values, $$subdir{MaxSubdirs};
6474             }
6475 144         578 $val = shift @values;
6476             }
6477 1247 100       3115 if ($$subdir{TagTable}) {
6478 902         3033 $newTagTable = GetTagTable($$subdir{TagTable});
6479 902 50       2753 $newTagTable or warn("Unknown tag table $$subdir{TagTable}"), next;
6480             } else {
6481 345         745 $newTagTable = $tagTablePtr; # use existing table
6482             }
6483             # loop through all sub-directories specified by this tag
6484 1247         2771 for ($dirNum=0; ; ++$dirNum) {
6485 1253         2034 my $subdirBase = $base;
6486 1253         2135 my $subdirDataPt = $valueDataPt;
6487 1253         1963 my $subdirDataPos = $valueDataPos;
6488 1253         1980 my $subdirDataLen = $valueDataLen;
6489 1253         2121 my $subdirStart = $valuePtr;
6490 1253 100       3312 if (defined $$subdir{Start}) {
6491             # set local $valuePtr relative to file $base for eval
6492 445         952 my $valuePtr = $subdirStart + $subdirDataPos;
6493             #### eval Start ($valuePtr, $val)
6494 445         26355 my $newStart = eval($$subdir{Start});
6495 445 50       2819 unless (Image::ExifTool::IsInt($newStart)) {
6496 0         0 $et->Warn("Bad value for $tagStr");
6497 0         0 last;
6498             }
6499             # convert back to relative to $subdirDataPt
6500 445         1435 $newStart -= $subdirDataPos;
6501             # adjust directory size if necessary
6502 445 50 66     1777 unless ($$tagInfo{SubIFD} or $$subdir{BadOffset}) {
6503 51         136 $size -= $newStart - $subdirStart;
6504             }
6505 445         1089 $subdirStart = $newStart;
6506             }
6507             # this is a pain, but some maker notes are always a specific
6508             # byte order, regardless of the byte order of the file
6509 1253         3379 my $oldByteOrder = GetByteOrder();
6510 1253         2764 $newByteOrder = $$subdir{ByteOrder};
6511 1253 100       2783 if ($newByteOrder) {
6512 171 100       1554 if ($newByteOrder =~ /^Little/i) {
    100          
    50          
    50          
6513 31         75 $newByteOrder = 'II';
6514             } elsif ($newByteOrder =~ /^Big/i) {
6515 29         77 $newByteOrder = 'MM';
6516             } elsif ($$subdir{OffsetPt}) {
6517 0         0 undef $newByteOrder;
6518 0         0 warn "Can't have variable byte ordering for SubDirectories using OffsetPt";
6519 0         0 last;
6520             } elsif ($subdirStart + 2 <= $subdirDataLen) {
6521             # attempt to determine the byte ordering by checking
6522             # the number of directory entries. This is an int16u
6523             # that should be a reasonable value.
6524 111         432 my $num = Get16u($subdirDataPt, $subdirStart);
6525 111 100 100     755 if ($num & 0xff00 and ($num>>8) > ($num&0xff)) {
6526             # This looks wrong, we shouldn't have this many entries
6527 4         23 my %otherOrder = ( II=>'MM', MM=>'II' );
6528 4         18 $newByteOrder = $otherOrder{$oldByteOrder};
6529             } else {
6530 107         271 $newByteOrder = $oldByteOrder;
6531             }
6532             }
6533             } else {
6534 1082         2001 $newByteOrder = $oldByteOrder;
6535             }
6536             # set base offset if necessary
6537 1253 100       3261 if ($$subdir{Base}) {
6538             # calculate subdirectory start relative to $base for eval
6539 35         95 my $start = $subdirStart + $subdirDataPos;
6540             #### eval Base ($start,$base)
6541 35         2132 $subdirBase = eval($$subdir{Base}) + $base;
6542             }
6543             # add offset to the start of the directory if necessary
6544 1253 100       3253 if ($$subdir{OffsetPt}) {
6545             #### eval OffsetPt ($valuePtr)
6546 24         1079 my $pos = eval $$subdir{OffsetPt};
6547 24 50       208 if ($pos + 4 > $subdirDataLen) {
6548 0         0 $et->Warn("Bad $tagStr OffsetPt");
6549 0         0 last;
6550             }
6551 24         106 SetByteOrder($newByteOrder);
6552 24         136 $subdirStart += Get32u($subdirDataPt, $pos);
6553 24         93 SetByteOrder($oldByteOrder);
6554             }
6555 1253 100 100     5834 if ($subdirStart < 0 or $subdirStart + 2 > $subdirDataLen) {
6556             # convert $subdirStart back to a file offset
6557 31 50       140 if ($raf) {
6558             # reset SubDirectory buffer (we will load it later)
6559 31         72 my $buff = '';
6560 31         77 $subdirDataPt = \$buff;
6561 31         75 $subdirDataLen = $size = length $buff;
6562             } else {
6563 0         0 my $msg = "Bad $tagStr SubDirectory start";
6564 0 0       0 if ($verbose > 0) {
6565 0 0       0 if ($subdirStart < 0) {
6566 0         0 $msg .= " (directory start $subdirStart is before EXIF start)";
6567             } else {
6568 0         0 my $end = $subdirStart + $size;
6569 0         0 $msg .= " (directory end is $end but EXIF size is only $subdirDataLen)";
6570             }
6571             }
6572 0         0 $et->Warn($msg, $inMakerNotes);
6573 0         0 last;
6574             }
6575             }
6576              
6577             # must update subdirDataPos if $base changes for this subdirectory
6578 1253         2667 $subdirDataPos += $base - $subdirBase;
6579              
6580             # build information hash for new directory
6581             my %subdirInfo = (
6582             Name => $tagStr,
6583             Base => $subdirBase,
6584             DataPt => $subdirDataPt,
6585             DataPos => $subdirDataPos,
6586             DataLen => $subdirDataLen,
6587             DirStart => $subdirStart,
6588             DirLen => $size,
6589             RAF => $raf,
6590             Parent => $dirName,
6591             DirName => $$subdir{DirName},
6592             FixBase => $$subdir{FixBase},
6593             FixOffsets => $$subdir{FixOffsets},
6594             EntryBased => $$subdir{EntryBased},
6595             TagInfo => $tagInfo,
6596             SubIFD => $$tagInfo{SubIFD},
6597 1253         14978 Subdir => $subdir,
6598             OffsetName => $subOffName,
6599             );
6600             # (remember: some cameras incorrectly write maker notes in IFD0)
6601 1253 100       3761 if ($$tagInfo{MakerNotes}) {
6602             # don't parse makernotes if FastScan > 1
6603 130         695 my $fast = $et->Options('FastScan');
6604 130 100 66     752 last if $fast and $fast > 1;
6605 129         491 $subdirInfo{MakerNoteAddr} = $valuePtr + $valueDataPos + $base;
6606 129 100       556 $subdirInfo{NoFixBase} = 1 if defined $$subdir{Base};
6607             }
6608             # set directory IFD name from group name of family 1 if it exists,
6609             # unless the tag is writable as a block in which case group 1 may
6610             # have been set automatically
6611 1252 100 100     4708 if ($$tagInfo{Groups} and not $$tagInfo{Writable}) {
6612 394         1198 $subdirInfo{DirName} = $$tagInfo{Groups}{1};
6613             # number multiple subdirectories
6614 394 100       1198 $subdirInfo{DirName} =~ s/\d*$/$dirNum/ if $dirNum;
6615             }
6616 1252         4074 SetByteOrder($newByteOrder); # set byte order for this subdir
6617             # validate the subdirectory if necessary
6618 1252         2437 my $dirData = $subdirDataPt; # set data pointer to be used in eval
6619             #### eval Validate ($val, $dirData, $subdirStart, $size)
6620 1252         2281 my $ok = 0;
6621 1252 50 66     30526 if (defined $$subdir{Validate} and not eval $$subdir{Validate}) {
6622 0         0 $et->Warn("Invalid $tagStr data", $inMakerNotes);
6623 0         0 $invalid = 1;
6624             } else {
6625 1252 100 100     4891 if (not $subdirInfo{DirName} and $inMakerNotes) {
6626 622         1507 $subdirInfo{DirName} = $$tagInfo{Name};
6627             }
6628             # process the subdirectory
6629 1252         7071 $ok = $et->ProcessDirectory(\%subdirInfo, $newTagTable, $$subdir{ProcessProc});
6630             }
6631             # print debugging information if there were errors
6632 1252 50 66     6046 if (not $ok and $verbose > 1 and $subdirStart != $valuePtr) {
      33        
6633 0         0 my $out = $et->Options('TextOut');
6634 0         0 printf $out "%s (SubDirectory start = 0x%x)\n", $$et{INDENT}, $subdirStart;
6635             }
6636 1252         3996 SetByteOrder($oldByteOrder); # restore original byte swapping
6637              
6638 1252 100       8362 @values or last;
6639 6         36 $val = shift @values; # continue with next subdir
6640             }
6641 1247         4470 my $doMaker = $et->Options('MakerNotes');
6642 1247 100 100     11268 next unless $doMaker or $$et{REQ_TAG_LOOKUP}{lc($tagStr)} or $$tagInfo{BlockExtract};
      100        
6643             # extract as a block if specified
6644 204 100       708 if ($$tagInfo{MakerNotes}) {
6645             # save maker note byte order (if it was significant and valid)
6646 20 50 33     195 if ($$subdir{ByteOrder} and not $invalid) {
6647             $$et{MAKER_NOTE_BYTE_ORDER} =
6648             defined ($$et{UnknownByteOrder}) ?
6649 20 100       147 $$et{UnknownByteOrder} : $newByteOrder;
6650             }
6651 20 50 33     234 if ($doMaker and $doMaker eq '2') {
    50 33        
6652             # extract maker notes without rebuilding (no fixup information)
6653 0         0 delete $$et{MAKER_NOTE_FIXUP};
6654             } elsif (not $$tagInfo{NotIFD} or $$tagInfo{IsPhaseOne}) {
6655             # this is a pain, but we must rebuild EXIF-type maker notes to
6656             # include all the value data if data was outside the maker notes
6657             my %makerDirInfo = (
6658             Name => $tagStr,
6659             Base => $base,
6660             DataPt => $valueDataPt,
6661             DataPos => $valueDataPos,
6662             DataLen => $valueDataLen,
6663             DirStart => $valuePtr,
6664             DirLen => $size,
6665             RAF => $raf,
6666             Parent => $dirName,
6667             DirName => 'MakerNotes',
6668             FixOffsets => $$subdir{FixOffsets},
6669 20         268 TagInfo => $tagInfo,
6670             );
6671 20         70 my $val2;
6672 20 50       91 if ($$tagInfo{IsPhaseOne}) {
6673 0         0 $$et{DropTags} = 1;
6674 0         0 $val2 = Image::ExifTool::PhaseOne::WritePhaseOne($et, \%makerDirInfo, $newTagTable);
6675 0         0 delete $$et{DropTags};
6676             } else {
6677 20 100       93 $makerDirInfo{FixBase} = 1 if $$subdir{FixBase};
6678             # rebuild maker notes (creates $$et{MAKER_NOTE_FIXUP})
6679 20         175 $val2 = RebuildMakerNotes($et, \%makerDirInfo, $newTagTable);
6680             }
6681 20 50       94 if (defined $val2) {
    0          
6682 20         159 $val = $val2;
6683             } elsif ($size > 4) {
6684 0         0 $et->Warn('Error rebuilding maker notes (may be corrupt)');
6685             }
6686             }
6687             } else {
6688             # extract this directory as a block if specified
6689 184 100       1314 next unless $$tagInfo{Writable};
6690             }
6691             }
6692             #..............................................................................
6693             # convert to absolute offsets if this tag is an offset
6694             #### eval IsOffset ($val, $et)
6695 13251 100 100     42850 if ($$tagInfo{IsOffset} and eval $$tagInfo{IsOffset}) {
6696 224 100       1437 my $offsetBase = $$tagInfo{IsOffset} eq '2' ? $firstBase : $base;
6697 224         643 $offsetBase += $$et{BASE};
6698             # handle offsets which use a wrong base (Minolta A200)
6699 224 50       835 if ($$tagInfo{WrongBase}) {
6700 0         0 my $self = $et;
6701             #### eval WrongBase ($self)
6702 0   0     0 $offsetBase += eval $$tagInfo{WrongBase} || 0;
6703             }
6704 224         1105 my @vals = split(' ',$val);
6705 224         704 foreach $val (@vals) {
6706 224         906 $val += $offsetBase;
6707             }
6708 224         915 $val = join(' ', @vals);
6709             }
6710 13251 100       24375 if ($validate) {
6711 70 100 66     264 if ($$tagInfo{OffsetPair}) {
    100          
6712 10         43 $offsetInfo{$tagID} = [ $tagInfo, $val ];
6713             } elsif ($saveForValidate{$tagID} and $isExif) {
6714 10         26 $offsetInfo{$tagID} = $val;
6715             }
6716             }
6717             # save the value of this tag
6718 13251         37538 $tagKey = $et->FoundTag($tagInfo, $val);
6719 13251 100       30988 if (defined $tagKey) {
6720             # set the group 1 name for tags in specified tables
6721 13240 100       45268 $et->SetGroup($tagKey, $dirName) if $$tagTablePtr{SET_GROUP1};
6722             # save original components of rational numbers (used when copying)
6723 13240 100       30815 $$et{RATIONAL}{$tagKey} = $rational if defined $rational;
6724 13240 50       44283 $$et{TAG_EXTRA}{$tagKey}{G6} = $saveFormat if $saveFormat;
6725             }
6726             }
6727              
6728             # validate image data offsets for this IFD
6729 1056 100 100     3702 if ($validate and %offsetInfo) {
6730 5         21 Image::ExifTool::Validate::ValidateOffsetInfo($et, \%offsetInfo, $$dirInfo{DirName}, $inMakerNotes)
6731             }
6732              
6733             # scan for subsequent IFD's if specified
6734 1056 100 66     5687 if ($$dirInfo{Multi} and $bytesFromEnd >= 4) {
    100          
6735             # use same directory information for trailing directory,
6736             # but change the start location (ProcessDirectory will
6737             # test to make sure we don't reprocess the same dir twice)
6738 292         2992 my %newDirInfo = %$dirInfo;
6739 292         1172 $newDirInfo{Multi} = 0; # prevent recursion
6740 292         801 $newDirInfo{OffsetName} = $nextOffName;
6741 292         2115 $$et{INDENT} =~ s/..$//; # keep indent the same
6742 292         720 for (;;) {
6743 461 100       1552 my $offset = Get32u($dataPt, $dirEnd) or last;
6744 169         559 $newDirInfo{DirStart} = $offset - $dataPos;
6745             # increment IFD number
6746 169 50       1570 my $ifdNum = $newDirInfo{DirName} =~ s/(\d+)$// ? $1 : 0;
6747 169         625 $newDirInfo{DirName} .= $ifdNum + 1;
6748             # must validate SubIFD1 because the nextIFD pointer is invalid for some RAW formats
6749 169 50 33     919 if ($newDirInfo{DirName} ne 'SubIFD1' or ValidateIFD(\%newDirInfo)) {
    0 0        
6750 169         352 my $cur = pop @{$$et{PATH}};
  169         552  
6751 169 50       952 $et->ProcessDirectory(\%newDirInfo, $tagTablePtr) or $success = 0;
6752 169         535 push @{$$et{PATH}}, $cur;
  169         599  
6753 169 50 33     1137 if ($success and $newDirInfo{BytesFromEnd} >= 4) {
6754 169         442 $dataPt = $newDirInfo{DataPt};
6755 169         392 $dataPos = $newDirInfo{DataPos};
6756 169         376 $dirEnd = $newDirInfo{DirEnd};
6757 169         429 next;
6758             }
6759             } elsif ($verbose or $$et{TIFF_TYPE} eq 'TIFF') {
6760 0         0 $et->Warn('Ignored bad IFD linked from SubIFD');
6761             }
6762 0         0 last;
6763             }
6764             } elsif (defined $$dirInfo{Multi}) {
6765             # return necessary parameters for parsing next IFD
6766 170         1054 $$dirInfo{DirEnd} = $dirEnd;
6767 170         635 $$dirInfo{OffsetName} = $nextOffName;
6768 170         464 $$dirInfo{BytesFromEnd} = $bytesFromEnd;
6769             }
6770 1056         4936 return $success;
6771             }
6772              
6773             1; # end
6774              
6775             __END__