File Coverage

blib/lib/Image/ExifTool/Exif.pm
Criterion Covered Total %
statement 632 1002 63.0
branch 394 816 48.2
condition 263 592 44.4
subroutine 30 33 90.9
pod 0 28 0.0
total 1319 2471 53.3


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