File Coverage

blib/lib/Image/ExifTool/Exif.pm
Criterion Covered Total %
statement 622 983 63.2
branch 383 792 48.3
condition 253 559 45.2
subroutine 30 33 90.9
pod 0 28 0.0
total 1288 2395 53.7


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