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   1081 use strict;
  113         464  
  113         7563  
54 113         15374 use vars qw($VERSION $AUTOLOAD @formatSize @formatName %formatNumber %intFormat
55             %lightSource %flash %compression %photometricInterpretation %orientation
56 113     113   715 %subfileType %saveForValidate);
  113         291  
57 113     113   841 use Image::ExifTool qw(:DataAccess :Utils);
  113         424  
  113         34024  
58 113     113   103248 use Image::ExifTool::MakerNotes;
  113         655  
  113         3412432  
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 2642 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   451 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 1067 my ($et, $comp) = @_;
5318 279 100 66     6835 if ($$et{FILE_TYPE} eq 'TIFF' and not $$et{IdentifiedRawFile}) {
5319 71 100 66     2543 if ($compression{$comp} and $compression{$comp} =~ /^\w+ ([A-Z]{3}) Compressed$/) {
5320 3         26 $et->OverrideFileType($$et{TIFF_TYPE} = $1);
5321 3         95 $$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 323 local $_;
5333             # do validity checks on arguments
5334 129 50       623 return undef unless @_ >= 3;
5335 129         426 foreach (@_) {
5336 387 50 66     4392 return undef unless $_ and /([+-]?(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?)/ and $1 > 0;
      66        
5337 382         2773 $_ = $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         2002 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 562     562 0 1456 my $et = shift;
5359 562         1212 my $res = $_[7]; # save resolution units (in case they have been converted to string)
5360 562         1038 my $sensXY = $_[4];
5361 562         2411 Image::ExifTool::ToFloat(@_);
5362 562         1453 my $focal = shift;
5363 562         993 my $foc35 = shift;
5364              
5365 562 100 100     3105 return $foc35 / $focal if $focal and $foc35;
5366              
5367 533   50     2304 my $digz = shift || 1;
5368 533         951 my $diag = shift;
5369 533         897 my $sens = shift;
5370             # calculate Canon sensor size using a dedicated algorithm
5371 533 100       2104 if ($$et{Make} eq 'Canon') {
5372 54         8489 require Image::ExifTool::Canon;
5373 54         407 my $canonDiag = Image::ExifTool::Canon::CalcSensorDiag($et);
5374 54 100       204 $diag = $canonDiag if $canonDiag;
5375             }
5376 533 100 66     1862 unless ($diag and Image::ExifTool::IsFloat($diag)) {
5377 497 50 33     1744 if ($sens and $sensXY =~ / (\d+(\.?\d*)?)$/) {
5378 0         0 $diag = sqrt($sens * $sens + $1 * $1);
5379             } else {
5380 497         904 undef $diag;
5381 497         811 my $xsize = shift;
5382 497         807 my $ysize = shift;
5383 497 100 66     1562 if ($xsize and $ysize) {
5384             # validate by checking aspect ratio because FocalPlaneX/YSize is not reliable
5385 17         40 my $a = $xsize / $ysize;
5386 17 50 66     105 if (abs($a-1.3333) < .1 or abs($a-1.5) < .1) {
5387 17         55 $diag = sqrt($xsize * $xsize + $ysize * $ysize);
5388             }
5389             }
5390             }
5391 497 100       1443 unless ($diag) {
5392             # get number of mm in units (assume inches unless otherwise specified)
5393 480         3403 my %lkup = ( 3=>10, 4=>1, 5=>0.001 , cm=>10, mm=>1, um=>0.001 );
5394 480   100     4373 my $units = $lkup{ shift() || $res || '' } || 25.4;
5395 480   100     7035 my $x_res = shift || return undef;
5396 122   66     332 my $y_res = shift || $x_res;
5397 122 50 33     435 Image::ExifTool::IsFloat($x_res) and $x_res != 0 or return undef;
5398 122 50 33     342 Image::ExifTool::IsFloat($y_res) and $y_res != 0 or return undef;
5399 122         281 my ($w, $h);
5400 122         217 for (;;) {
5401 124 50       381 @_ < 2 and return undef;
5402 124         267 $w = shift;
5403 124         199 $h = shift;
5404 124 100 66     552 next unless $w and $h;
5405 122         355 my $a = $w / $h;
5406 122 50 33     626 last if $a > 0.5 and $a < 2; # stop if we get a reasonable value
5407             }
5408             # calculate focal plane size in mm
5409 122         275 $w *= $units / $x_res;
5410 122         281 $h *= $units / $y_res;
5411 122         365 $diag = sqrt($w*$w+$h*$h);
5412             # make sure size is reasonable
5413 122 100 100     1790 return undef unless $diag > 1 and $diag < 100;
5414             }
5415             }
5416 59         757 return sqrt(36*36+24*24) * $digz / $diag;
5417             }
5418              
5419             #------------------------------------------------------------------------------
5420             # Print exposure compensation fraction
5421             sub PrintFraction($)
5422             {
5423 296     296 0 885 my $val = shift;
5424 296         582 my $str;
5425 296 50       1033 if (defined $val) {
5426 296         985 $val *= 1.00001; # avoid round-off errors
5427 296 100       1124 if (not $val) {
    100          
    50          
    100          
5428 268         727 $str = '0';
5429             } elsif (int($val)/$val > 0.999) {
5430 5         25 $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 10         48 $str = sprintf("%+d/3", int($val * 3));
5435             } else {
5436 13         96 $str = sprintf("%+.3g", $val);
5437             }
5438             }
5439 296         2648 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 3112 my $val = shift;
5447 1165 100       4801 if ($val =~ m{([-+]?\d+)/(\d+)}) {
5448 428 0       2329 $val = $2 ? $1 / $2 : ($1 ? 'inf' : 'undef');
    50          
5449             }
5450 1165         11198 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 471 my ($et, $val, $asciiFlex, $tag) = @_;
5462 96 100       469 return $val if length($val) < 8;
5463 95         360 my $id = substr($val, 0, 8);
5464 95         359 my $str = substr($val, 8);
5465 95         222 my $type;
5466              
5467 95         283 delete $$et{WrongByteOrder};
5468 95 50 66     548 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       734 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         520 $str =~ s/\0.*//s;
5478             # allow ASCII text to contain any other specified encoding
5479 95 100 66     709 if ($asciiFlex and $asciiFlex eq '1') {
5480 94         547 my $enc = $et->Options('CharsetEXIF');
5481 94 50       328 $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     451 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         306 $str =~ s/ +$//; # trim trailing blanks
5505 95         1275 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 535 my ($val, $inv, $conv) = @_;
5536 199 100       910 return $val if $inv;
5537 52 50       148 if ($val > 0) {
5538 52 50       141 if ($val > 0xfff0) { # a negative value in disguise?
5539 0         0 $val = $val - 0x10000;
5540             } else {
5541 52         191 $val = "+$val";
5542             }
5543             }
5544 52         230 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 193 my $val = shift;
5555 67         244 my $isFloat = Image::ExifTool::IsFloat($val);
5556             # normal is a value of zero
5557 67 100 100     761 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     380 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     539 return 2 if $val =~ /\bh/i or $isFloat;
5562 2         17 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 246 my $blue = shift;
5584 90         215 my ($i, $val, $levels);
5585 90         643 for ($i=0; $i<@rggbLookup; ++$i) {
5586 232 100       698 $levels = shift or next;
5587 86         331 my @levels = split ' ', $levels;
5588 86 50       281 next if @levels < 2;
5589 86         283 my $lookup = $rggbLookup[$i];
5590 86         237 my $g = $$lookup[1]; # get green level or index
5591 86 100       293 if ($g < 4) {
    50          
5592 78 50       230 next if @levels < 3;
5593 78 50       512 $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         418 $val = $levels[$$lookup[$blue * 3]] / $g;
5598 86         267 last;
5599             }
5600 90 50 33     300 $val = $_[0] / $_[1] if not defined $val and ($_[0] and $_[1]);
      66        
5601 90         912 return $val;
5602             }
5603              
5604             #------------------------------------------------------------------------------
5605             # Print exposure time as a fraction
5606             sub PrintExposureTime($)
5607             {
5608 575     575 0 1444 my $secs = shift;
5609 575 50       2002 return $secs unless Image::ExifTool::IsFloat($secs);
5610 575 100 100     3876 if ($secs < 0.25001 and $secs > 0) {
5611 442         5768 return sprintf("1/%d",int(0.5 + 1/$secs));
5612             }
5613 133         924 $_ = sprintf("%.1f",$secs);
5614 133         705 s/\.0$//;
5615 133         1213 return $_;
5616             }
5617              
5618             #------------------------------------------------------------------------------
5619             # Print FNumber
5620             sub PrintFNumber($)
5621             {
5622 347     347 0 971 my $val = shift;
5623 347 50 33     1337 if (Image::ExifTool::IsFloat($val) and $val > 0) {
5624             # round to 1 decimal place, or 2 for values < 1.0
5625 347 100       2813 $val = sprintf(($val<1 ? "%.2f" : "%.1f"), $val);
5626             }
5627 347         3264 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 20 my ($self, $val) = @_;
5637             # some panasonic cameras (SV-AS3, SV-AS30) write this in ascii (very odd)
5638 5 50       32 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       19 return $val unless length($val) >= 4;
5643 5 100       18 my @a = unpack(GetByteOrder() eq 'II' ? 'v2C*' : 'n2C*', $val);
5644 5         17 my $end = 2 + $a[0] * $a[1];
5645 5 100       29 if ($end > @a) {
5646             # try swapping byte order (I have seen this order different than in EXIF)
5647 2         12 my ($x, $y) = unpack('n2',pack('v2',$a[0],$a[1]));
5648 2 50       11 if (@a < 2 + $x * $y) {
5649 0         0 $self->Warn('Invalid CFAPattern', 1);
5650             } else {
5651 2         5 ($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         85 return "@a";
5657             }
5658              
5659             #------------------------------------------------------------------------------
5660             # Print CFA Pattern
5661             sub PrintCFAPattern($)
5662             {
5663 6     6 0 16 my $val = shift;
5664 6         26 my @a = split ' ', $val;
5665 6 50       26 return '' unless @a >= 2;
5666 6 50 33     43 return '' unless $a[0] and $a[1];
5667 6         23 my $end = 2 + $a[0] * $a[1];
5668 6 50       44 return '' if $end > @a;
5669 6         34 my @cfaColor = qw(Red Green Blue Cyan Magenta Yellow White);
5670 6         23 my ($pos, $rtnVal) = (2, '[');
5671 6         13 for (;;) {
5672 24   50     258 $rtnVal .= $cfaColor[$a[$pos]] || 'Unknown';
5673 24 100       77 last if ++$pos >= $end;
5674 18 100       60 ($pos - 2) % $a[1] and $rtnVal .= ',', next;
5675 6         13 $rtnVal .= '][';
5676             }
5677 6         79 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 12     12 0 40 my $val = shift;
5708 12         60 my @vals = split ' ', $val;
5709 12 50       45 return $val unless @vals == 4;
5710 12         29 my $c = 0;
5711 12         35 foreach (@vals) {
5712 48 100       113 Image::ExifTool::IsFloat($_) and ++$c, next;
5713 4 50       13 $_ eq 'inf' and $_ = '?', ++$c, next;
5714 4 50       14 $_ eq 'undef' and $_ = '?', ++$c, next;
5715             }
5716 12 50       44 return $val unless $c == 4;
5717 12         28 $val = $vals[0];
5718             # (the Pentax Q writes zero for upper value of fixed-focal-length lenses)
5719 12 100 66     93 $val .= "-$vals[1]" if $vals[1] and $vals[1] ne $vals[0];
5720 12         30 $val .= "mm f/$vals[2]";
5721 12 100 100     73 $val .= "-$vals[3]" if $vals[3] and $vals[3] ne $vals[2];
5722 12         51 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 118 my ($lens, $unk) = @_;
5733             # extract focal length and aperture ranges for this lens
5734 53         101 my $pat = '\\d+(?:\\.\\d+)?';
5735 53 100       154 $pat .= '|\\?' if $unk;
5736 53 100       2028 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         182 my @a = ($1, $2, $3, $4);
5739 33 50       75 $a[1] or $a[1] = $a[0];
5740 33 50       83 $a[3] or $a[3] = $a[2];
5741 33 100       73 if ($unk) {
5742 1         2 local $_;
5743 1   50     6 $_ eq '?' and $_ = 'undef' foreach @a;
5744             }
5745 33         194 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 13 my ($try, $lensModel) = @_;
5755 4 50 33     26 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 256 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       171 return undef unless defined $lensType;
5793             # get print conversion hash if necessary
5794 36 50       265 $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       183 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         92 my ($sf0, $lf0, $sa0, $la0);
5810 36 50       136 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       112 $maxAperture = $maxApertureValue unless $maxAperture;
5816 36 100 100     214 if ($lensFocalRange and $lensFocalRange =~ /^(\d+)(?: (?:to )?(\d+))?$/) {
5817 1   33     9 ($shortFocal, $longFocal) = ($1, $2 || $1);
5818             }
5819 36 50 66     545 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         2428 require Image::ExifTool::Canon;
5864 30         203 return Image::ExifTool::Canon::PrintLensID($printConv, $lensType,
5865             $shortFocal, $longFocal, $maxAperture, $lensModel);
5866             }
5867 6         19 my $lens = $$printConv{$lensType};
5868 6 100 33     41 return ($lensModel || $lensTypePrt) unless $lens;
5869 5 100       55 return $lens unless $$printConv{"$lensType.1"};
5870 4         30 $lens =~ s/ or .*//s; # remove everything after "or"
5871             # make list of all possible matching lenses
5872 4         16 my @lenses = ( $lens );
5873 4         9 my $i;
5874 4         24 for ($i=1; $$printConv{"$lensType.$i"}; ++$i) {
5875 28         93 push @lenses, $$printConv{"$lensType.$i"};
5876             }
5877             # attempt to determine actual lens
5878 4         11 my (@matches, @best, @user, $diff);
5879 4         10 foreach $lens (@lenses) {
5880 32 50       82 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         86 my ($sf, $lf, $sa, $la) = GetLensInfo($lens);
5886 32 100       77 next unless $sf;
5887             # check against LensSpec parameters if available
5888 28 50       60 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       74 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       57 if ($focalLength) {
5908 28 100       130 next if $focalLength < $sf - 0.5;
5909 9 100       31 next if $focalLength > $lf + 0.5;
5910             }
5911 8 50       21 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       36 next if $maxAperture < $sa - 0.15; # (0.15 is arbitrary)
5918 3 50       13 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     38 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         7 $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         9 my $d = abs($maxAperture - $aa);
5935 3 50       11 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         5 $diff = $d;
5940 3         7 push @best, $lens;
5941             }
5942 3         11 push @matches, $lens;
5943             }
5944             # return the user-defined lens if it exists
5945 4 50       17 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       19 @best = @matches unless @best;
5958 4 100       18 if (@best) {
5959 3         15 MatchLensModel(\@best, $lensModel);
5960 3         125 return join(' or ', @best);
5961             }
5962 1         5 $lens = $$printConv{$lensType};
5963 1 50 33     6 return $lensModel if $lensModel and $lens =~ / or /; # (eg. Sony NEX-5N)
5964 1         40 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 70     70 0 225 my $date = shift;
5976 70         280 $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 70         1393 $date =~ s/(\d{4})[^\d]*(\d{2})[^\d]*(\d{2})$/$1:$2:$3/;
5980 70         716 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 8     8 0 23 my $time = shift;
5993 8         23 $time =~ tr/ /:/; # use ':' (not ' ') as a separator
5994 8         24 $time =~ s/\0$//; # remove any null terminator
5995             # add separators if they don't exist
5996 8         85 $time =~ s/^(\d{2})(\d{2})(\d{2})/$1:$2:$3/;
5997 8         72 $time =~ s/([+-]\d{2})(\d{2})\s*$/$1:$2/; # to timezone too
5998 8         126 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 19 my ($entries, $dataPt) = @_;
6009 4         9 my ($rtnVal, $tag, $offsetPos);
6010              
6011 4         10 my $num = scalar keys %$entries;
6012 4         20 my $ifdBuff = GetByteOrder() . Set16u(42) . Set32u(8) . Set16u($num);
6013 4         12 my $valBuff = '';
6014 4         21 my $tagTablePtr = GetTagTable('Image::ExifTool::Exif::Main');
6015 4         44 foreach $tag (sort { $a <=> $b } keys %$entries) {
  173         237  
6016 60         121 my $tagInfo = $$tagTablePtr{$tag};
6017 60 100       160 my $fmt = ref $tagInfo eq 'HASH' ? $$tagInfo{Writable} : 'int32u';
6018 60 50       118 return undef unless defined $fmt;
6019 60         164 my $val = Image::ExifTool::WriteValue($$entries{$tag}, $fmt, -1);
6020 60 50       138 return undef unless defined $val;
6021 60         113 my $format = $formatNumber{$fmt};
6022 60         131 $ifdBuff .= Set16u($tag) . Set16u($format) . Set32u(length($val)/$formatSize[$format]);
6023 60 100       120 $offsetPos = length($ifdBuff) if $tag == 0x111; # (remember StripOffsets position)
6024 60 100       98 if (length $val > 4) {
6025 12         36 $ifdBuff .= Set32u(10 + 12 * $num + 4 + length($valBuff));
6026 12         46 $valBuff .= $val;
6027             } else {
6028 48 100       110 $val .= "\0" x (4 - length($val)) if length $val < 4;
6029 48         85 $ifdBuff .= $val;
6030             }
6031             }
6032 4         14 $ifdBuff .= "\0\0\0\0"; # (no IFD1)
6033 4 50       13 return undef unless $offsetPos;
6034 4         18 Set32u(length($ifdBuff) + length($valBuff), \$ifdBuff, $offsetPos);
6035 4         32 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         34 my $et = $_[0];
6048 14         50 my $value = $$et{VALUE};
6049 14         35 my ($i, $j, $rtn, $grp0, $grp1);
6050 14 50       102 return undef if $$et{FILE_TYPE} eq 'RWZ';
6051             SubFile:
6052 14         43 for ($i=0; ; ++$i) {
6053 40 100       146 my $key = 'SubfileType' . ($i ? " ($i)" : '');
6054 40 100       145 last unless defined $$value{$key};
6055 26 100       97 next unless $$value{$key} == 1; # (reduced-resolution image)
6056 12         52 my $grp = $et->GetGroup($key, 1);
6057 12         66 my $cmp = $et->FindValue('Compression', $grp);
6058 12 100       43 next unless $cmp == 1; # (no compression)
6059 6         38 my %vals = (Compression=>$cmp, PlanarConfiguration=>1, Orientation=>1);
6060 6         19 foreach (qw(ImageWidth ImageHeight BitsPerSample PhotometricInterpretation
6061             StripOffsets SamplesPerPixel RowsPerStrip StripByteCounts
6062             PlanarConfiguration Orientation))
6063             {
6064 60         145 my $val = $et->FindValue($_, $grp);
6065 60 100       203 defined $val and $vals{$_} = $val, next;
6066 2 50       10 next SubFile unless defined $vals{$_};
6067             }
6068 6         26 my ($w, $h) = @vals{'ImageWidth', 'ImageHeight'};
6069 6         28 my @bits = split ' ', $vals{BitsPerSample};
6070 6         31 my $rowBytes = 0;
6071 6         47 $rowBytes += $w * int(($_+7)/8) foreach @bits;
6072 6         13 my $dat = '';
6073 6         19 my @off = split ' ', $vals{StripOffsets};
6074 6         24 my @len = split ' ', $vals{StripByteCounts};
6075             # read the image data
6076 6         30 for ($j=0; $j<@off; ++$j) {
6077 6 100       37 next SubFile unless $len[$j] == $rowBytes * $vals{RowsPerStrip};
6078 4         24 my $tmp = $et->ExtractBinary($off[$j], $len[$j]);
6079 4 50       30 next SubFile unless defined $tmp;
6080 4         33 $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         103 0x128 => 2, # ResolutionUnit = 2
6099             );
6100 4         21 my $img = GenerateTIFF(\%entries, \$dat);
6101              
6102 4 50 33     38 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         9 $rtn = \$img;
6108 4         26 ($grp0, $grp1) = $et->GetGroup($key);
6109             }
6110             }
6111 14 50       58 return $rtn unless wantarray;
6112 14         468 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 858 my ($et, $offset, $len, $tag) = @_;
6124 209         713 my $dataPt = \$$et{EXIF_DATA};
6125 209         586 my $dataPos = $$et{EXIF_POS};
6126 209         458 my $image;
6127              
6128             # no image if length is zero, and don't try to extract binary from XMP file
6129 209 100 100     2080 return undef if not $len or $$et{FILE_TYPE} eq 'XMP';
6130              
6131             # take data from EXIF block if possible
6132 148 100 66     1405 if (defined $dataPos and $offset>=$dataPos and $offset+$len<=$dataPos+length($$dataPt)) {
      100        
6133 106         481 $image = substr($$dataPt, $offset-$dataPos, $len);
6134             } else {
6135 42         295 $image = $et->ExtractBinary($offset, $len, $tag);
6136 42 100       159 return undef unless defined $image;
6137             # patch for incorrect ThumbnailOffset in some Sony DSLR-A100 ARW images
6138 37 0 66     342 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         982 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 41 my ($tagID, $tagInfo) = @_;
6160 20 100       57 my $tagName = $tagInfo ? ' '.$$tagInfo{Name} : '';
6161 20         92 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 object reference
6182             # 1) Reference to directory information hash
6183             # 2) Pointer to tag table for this directory
6184             # Returns: 1 on success, otherwise returns 0 and sets a Warning
6185             sub ProcessExif($$$)
6186             {
6187 1124     1124 0 3036 my ($et, $dirInfo, $tagTablePtr) = @_;
6188 1124         2744 my $dataPt = $$dirInfo{DataPt};
6189 1124   100     5119 my $dataPos = $$dirInfo{DataPos} || 0;
6190 1124         2434 my $dataLen = $$dirInfo{DataLen};
6191 1124   100     3748 my $dirStart = $$dirInfo{DirStart} || 0;
6192 1124   100     3561 my $dirLen = $$dirInfo{DirLen} || $dataLen - $dirStart;
6193 1124         2395 my $dirName = $$dirInfo{DirName};
6194 1124   100     3573 my $base = $$dirInfo{Base} || 0;
6195 1124         2044 my $firstBase = $base;
6196 1124         2316 my $raf = $$dirInfo{RAF};
6197 1124         2293 my ($verbose,$validate,$saveFormat,$saveBin) = @{$$et{OPTIONS}}{qw(Verbose Validate SaveFormat SaveBin)};
  1124         5119  
6198 1124         2780 my $htmlDump = $$et{HTML_DUMP};
6199 1124         2158 my $success = 1;
6200 1124         2407 my ($tagKey, $dirSize, $makerAddr, $strEnc, %offsetInfo, $offName, $nextOffName, $doHash);
6201 1124         4155 my $inMakerNotes = $$tagTablePtr{GROUPS}{0} eq 'MakerNotes';
6202 1124         4109 my $isExif = ($tagTablePtr eq \%Image::ExifTool::Exif::Main);
6203              
6204             # warn for incorrect maker notes in CR3 files
6205 1124 100       3560 if ($dirName eq 'MakerNotes') {
6206 94 50 66     487 if ($$et{FileType} eq 'CR3' and $$dirInfo{Parent} and $$dirInfo{Parent} eq 'ExifIFD') {
      33        
6207 0         0 $et->Warn("MakerNotes shouldn't exist ExifIFD of CR3 image", 1);
6208             }
6209 94 100 100     995 if ($$dirInfo{TagInfo} and $$dirInfo{TagInfo}{MakerNotes} and
      66        
      66        
6210             $$et{ExifByteOrder} and $$et{ExifByteOrder} ne GetByteOrder())
6211             {
6212 2         6 $et->FoundTag(MakerNoteByteOrder => GetByteOrder());
6213             }
6214             }
6215             # set flag to calculate image data hash if requested
6216             $doHash = 1 if $$et{ImageDataHash} and (($$et{FILE_TYPE} eq 'TIFF' and not $base and not $inMakerNotes) or
6217 1124 0 0     3633 ($$et{FILE_TYPE} eq 'RAF' and $dirName eq 'FujiIFD'));
      33        
6218              
6219             # set encoding to assume for strings
6220 1124 100       6039 $strEnc = $et->Options('CharsetEXIF') if $$tagTablePtr{GROUPS}{0} eq 'EXIF';
6221              
6222             # ignore non-standard EXIF while in strict MWG compatibility mode
6223 1124 50 100     6320 if (($validate or $Image::ExifTool::MWG::strict) and $dirName eq 'IFD0' and
      100        
      66        
      66        
6224             $isExif and $$et{FILE_TYPE} =~ /^(JPEG|TIFF|PSD)$/)
6225             {
6226 7         27 my $path = $et->MetadataPath();
6227 7 50       51 if ($path =~ /^(JPEG-APP1-IFD0|TIFF-IFD0|PSD-EXIFInfo-IFD0)$/) {
6228 7 50       27 unless ($$et{DOC_NUM}) {
6229 7 50       22 $et->Warn("Duplicate EXIF at $path") if $$et{HasExif};
6230 7         22 $$et{HasExif} = 1;
6231             }
6232             } else {
6233 0 0       0 if ($Image::ExifTool::MWG::strict) {
6234 0         0 $et->Warn("Ignored non-standard EXIF at $path");
6235 0         0 return 0;
6236             } else {
6237 0         0 $et->Warn("Non-standard EXIF at $path", 1);
6238             }
6239             }
6240             }
6241             # mix htmlDump and Validate into verbose so we can test for all at once
6242 1124 50       3131 $verbose = -1 if $htmlDump;
6243 1124 100 66     3361 $verbose = -2 if $validate and not $verbose;
6244 1124 100       3187 $dirName eq 'EXIF' and $dirName = $$dirInfo{DirName} = 'IFD0';
6245 1124 100 100     8542 $$dirInfo{Multi} = 1 if $dirName =~ /^(IFD0|SubIFD)$/ and not defined $$dirInfo{Multi};
6246             # get a more descriptive name for MakerNote sub-directories
6247 1124         2962 my $dir = $$dirInfo{Name};
6248 1124 100 100     5940 $dir = $dirName unless $dir and $inMakerNotes and $dir !~ /^MakerNote/;
      100        
6249              
6250 1124         2545 my ($numEntries, $dirEnd);
6251 1124 100 100     5580 if ($dirStart >= 0 and $dirStart <= $dataLen-2) {
6252             # make sure data is large enough (patches bug in Olympus subdirectory lengths)
6253 1048         3821 $numEntries = Get16u($dataPt, $dirStart);
6254 1048         2717 $dirSize = 2 + 12 * $numEntries;
6255 1048         2120 $dirEnd = $dirStart + $dirSize;
6256 1048 100       3069 if ($dirSize > $dirLen) {
6257 394 50 66     4235 if (($verbose > 0 or $validate) and not $$dirInfo{SubIFD}) {
      66        
6258 0         0 my $short = $dirSize - $dirLen;
6259 0         0 $$et{INDENT} =~ s/..$//; # keep indent the same
6260 0         0 $et->Warn("Short directory size for $dir (missing $short bytes)");
6261 0         0 $$et{INDENT} .= '| ';
6262             }
6263 394 50       1348 undef $dirSize if $dirEnd > $dataLen; # read from file if necessary
6264             }
6265             }
6266             # read IFD from file if necessary
6267 1124 100       3374 unless ($dirSize) {
6268 76         177 $success = 0;
6269 76 50       271 if ($raf) {
6270             # read the count of entries in this IFD
6271 76         208 my $offset = $dirStart + $dataPos;
6272 76         187 my ($buff, $buf2);
6273 76 50 33     411 if ($raf->Seek($offset + $base, 0) and $raf->Read($buff,2) == 2) {
6274 76         342 my $len = 12 * Get16u(\$buff,0);
6275             # also read next IFD pointer if available
6276 76 50       296 if ($raf->Read($buf2, $len+4) >= $len) {
6277 76         281 $buff .= $buf2;
6278 76         275 $dataPt = $$dirInfo{DataPt} = \$buff;
6279 76         197 $dataPos = $$dirInfo{DataPos} = $offset;
6280 76         222 $dataLen = $$dirInfo{DataLen} = length $buff;
6281 76         183 $dirStart = $$dirInfo{DirStart} = 0;
6282 76         205 $dirLen = $$dirInfo{DirLen} = length $buff;
6283 76         201 $success = 1;
6284             }
6285             }
6286             }
6287 76 50       286 if ($success) {
6288 76         255 $numEntries = Get16u($dataPt, $dirStart);
6289             } else {
6290 0         0 $et->Warn("Bad $dir directory", $inMakerNotes);
6291 0 0 0     0 return 0 unless $inMakerNotes and $dirLen >= 14 and $dirStart >= 0 and
      0        
      0        
6292             $dirStart + $dirLen <= length($$dataPt);
6293 0         0 $dirSize = $dirLen;
6294 0         0 $numEntries = int(($dirSize - 2) / 12); # read what we can
6295 0         0 Set16u($numEntries, $dataPt, $dirStart);
6296             }
6297 76         236 $dirSize = 2 + 12 * $numEntries;
6298 76         170 $dirEnd = $dirStart + $dirSize;
6299             }
6300 1124 100       3161 $verbose > 0 and $et->VerboseDir($dirName, $numEntries, undef, GetByteOrder());
6301 1124         2754 my $bytesFromEnd = $dataLen - $dirEnd;
6302 1124 100       3115 if ($bytesFromEnd < 4) {
6303 4 50 33     35 unless ($bytesFromEnd==2 or $bytesFromEnd==0) {
6304 0         0 $et->Warn("Illegal $dir directory size ($numEntries entries)");
6305 0         0 return 0;
6306             }
6307             }
6308             # fix base offset for maker notes if necessary
6309 1124 100       3475 if (defined $$dirInfo{MakerNoteAddr}) {
6310 134         407 $makerAddr = $$dirInfo{MakerNoteAddr};
6311 134         483 delete $$dirInfo{MakerNoteAddr};
6312 134 100       813 if (Image::ExifTool::MakerNotes::FixBase($et, $dirInfo)) {
6313 3         8 $base = $$dirInfo{Base};
6314 3         7 $dataPos = $$dirInfo{DataPos};
6315             }
6316             }
6317 1124 50       3099 if ($htmlDump) {
6318 0         0 $offName = $$dirInfo{OffsetName};
6319 0 0 0     0 my $longName = $dir eq 'MakerNotes' ? ($$dirInfo{Name} || $dir) : $dir;
6320 0 0       0 if (defined $makerAddr) {
6321 0         0 my $hdrLen = $dirStart + $dataPos + $base - $makerAddr;
6322 0 0       0 $et->HDump($makerAddr, $hdrLen, "MakerNotes header", $longName) if $hdrLen > 0;
6323             }
6324 0 0       0 unless ($$dirInfo{NoDumpEntryCount}) {
6325 0         0 $et->HDump($dirStart + $dataPos + $base, 2, "$longName entries",
6326             "Entry count: $numEntries", undef, $offName);
6327             }
6328 0         0 my $tip;
6329 0         0 my $id = $offName;
6330 0 0       0 if ($bytesFromEnd >= 4) {
6331 0 0       0 my $nxt = ($dir =~ /^(.*?)(\d+)$/) ? $1 . ($2 + 1) : 'Next IFD';
6332 0         0 my $off = Get32u($dataPt, $dirEnd);
6333 0         0 $tip = sprintf("$nxt offset: 0x%.4x", $off);
6334 0 0       0 ($nextOffName, $id) = NextOffsetName($et, $offName) if $off;
6335             }
6336 0         0 $et->HDump($dirEnd + $dataPos + $base, 4, "Next IFD", $tip, 0, $id);
6337             }
6338              
6339             # patch for Canon EOS 40D firmware 1.0.4 bug (incorrect directory counts)
6340             # (must do this before parsing directory or CameraSettings offset will be suspicious)
6341 1124 50 66     5790 if ($inMakerNotes and $$et{Model} eq 'Canon EOS 40D' and $numEntries) {
      33        
6342 0         0 my $entry = $dirStart + 2 + 12 * ($numEntries - 1);
6343 0         0 my $fmt = Get16u($dataPt, $entry + 2);
6344 0 0 0     0 if ($fmt < 1 or $fmt > 13) {
6345 0         0 $et->HDump($entry+$dataPos+$base,12,"[invalid IFD entry]",
6346             "Bad format type: $fmt", 1, $offName);
6347             # adjust the number of directory entries
6348 0         0 --$numEntries;
6349 0         0 $dirEnd -= 12;
6350             }
6351             }
6352              
6353             # make sure that Compression and SubfileType are defined for this IFD (for Condition's)
6354 1124         3827 $$et{Compression} = $$et{SubfileType} = '';
6355              
6356             # loop through all entries in an EXIF directory (IFD)
6357 1124         2608 my ($index, $valEnd, $offList, $offHash, $mapFmt, @valPos);
6358 1124 100       3534 $mapFmt = $$tagTablePtr{VARS}{MAP_FORMAT} if $$tagTablePtr{VARS};
6359              
6360 1124         2830 my ($warnCount, $lastID) = (0, -1);
6361 1124         3500 for ($index=0; $index<$numEntries; ++$index) {
6362 15784 50       34050 if ($warnCount > 10) {
6363 0 0       0 $et->Warn("Too many warnings -- $dir parsing aborted", 2) and return 0;
6364             }
6365 15784         30539 my $entry = $dirStart + 2 + 12 * $index;
6366 15784         39797 my $tagID = Get16u($dataPt, $entry);
6367 15784         36378 my $format = Get16u($dataPt, $entry+2);
6368 15784         42475 my $count = Get32u($dataPt, $entry+4);
6369             # (Apple uses the BigTIFF format code 16 in the maker notes of their ProRaw DNG files)
6370 15784 0 33     66957 if (($format < 1 or $format > 13) and $format != 129 and not ($format == 16 and $$et{Make} eq 'Apple' and $inMakerNotes)) {
      33        
      0        
      33        
6371 0 0 0     0 if ($mapFmt and $$mapFmt{$format}) {
6372 0         0 $format = $$mapFmt{$format};
6373             } else {
6374 0         0 $et->HDump($entry+$dataPos+$base,12,"[invalid IFD entry]",
6375             "Bad format type: $format", 1, $offName);
6376             # warn unless the IFD was just padded with zeros
6377 0 0 0     0 if ($format or $validate) {
6378 0         0 $et->Warn("Bad format ($format) for $dir entry $index", $inMakerNotes);
6379 0         0 ++$warnCount;
6380             }
6381             # assume corrupted IFD if this is our first entry (except Sony ILCE which have an empty first entry)
6382 0 0 0     0 next if $index or $$et{Model} =~ /^ILCE/;
6383             # $et->Warn(sprintf('Format code 0x%x encountered -- Possibly corrupted IFD'));
6384 0         0 return 0;
6385             }
6386             }
6387 15784         38178 my $formatStr = $formatName[$format]; # get name of this format
6388 15784         24091 my $valueDataPt = $dataPt;
6389 15784         22555 my $valueDataPos = $dataPos;
6390 15784         21943 my $valueDataLen = $dataLen;
6391 15784         26671 my $valuePtr = $entry + 8; # pointer to value within $$dataPt
6392 15784         47934 my $tagInfo = $et->GetTagInfo($tagTablePtr, $tagID);
6393 15784         28447 my ($origFormStr, $bad, $rational, $binVal, $subOffName);
6394             # save the EXIF format codes if requested
6395 15784 50       32485 $$et{SaveFormat}{$saveFormat = $formatStr} = 1 if $saveFormat;
6396             # hack to patch incorrect count in Kodak SubIFD3 tags
6397 15784 50 100     74740 if ($count < 2 and ref $$tagTablePtr{$tagID} eq 'HASH' and $$tagTablePtr{$tagID}{FixCount}) {
      66        
6398 0 0       0 $offList or ($offList, $offHash) = GetOffList($dataPt, $dirStart, $dataPos,
6399             $numEntries, $tagTablePtr);
6400 0         0 my $i = $$offHash{Get32u($dataPt, $valuePtr)};
6401 0 0 0     0 if (defined $i and $i < $#$offList) {
6402 0         0 my $oldCount = $count;
6403 0         0 $count = int(($$offList[$i+1] - $$offList[$i]) / $formatSize[$format]);
6404 0 0       0 $origFormStr = $formatName[$format] . '[' . $oldCount . ']' if $oldCount != $count;
6405             }
6406             }
6407 15784 100 100     36424 $validate and not $inMakerNotes and Image::ExifTool::Validate::ValidateExif(
6408             $et, $tagTablePtr, $tagID, $tagInfo, $lastID, $dir, $count, $formatStr);
6409 15784         32722 my $size = $count * $formatSize[$format];
6410 15784         25069 my $readSize = $size;
6411 15784 100       33522 if ($size > 4) {
6412 7121 50 0     16452 if ($size > 0x7fffffff and (not $tagInfo or not $$tagInfo{ReadFromRAF})) {
      33        
6413 0         0 $et->Warn(sprintf("Invalid size (%u) for %s %s",$size,$dir,TagName($tagID,$tagInfo)), $inMakerNotes);
6414 0         0 ++$warnCount;
6415 0         0 next;
6416             }
6417 7121         16691 $valuePtr = Get32u($dataPt, $valuePtr);
6418 7121 100 100     19304 if ($validate and not $inMakerNotes) {
6419 20         58 my $tagName = TagName($tagID, $tagInfo);
6420 20 50       61 $et->Warn("Odd offset for $dir $tagName", 1) if $valuePtr & 0x01;
6421 20 50 33     143 if ($valuePtr < 8 || ($valuePtr + $size > length($$dataPt) and
      33        
6422             $valuePtr + $size > $$et{VALUE}{FileSize}))
6423             {
6424 0         0 $et->Warn("Invalid offset for $dir $tagName");
6425 0         0 ++$warnCount;
6426 0         0 next;
6427             }
6428 20 50 33     79 if ($valuePtr + $size > $dirStart + $dataPos and $valuePtr < $dirEnd + $dataPos + 4) {
6429 0         0 $et->Warn("Value for $dir $tagName overlaps IFD");
6430             }
6431 20         36 foreach (@valPos) {
6432 81 50 33     255 next if $$_[0] >= $valuePtr + $size or $$_[0] + $$_[1] <= $valuePtr;
6433 0         0 $et->Warn("Value for $dir $tagName overlaps $$_[2]");
6434             }
6435 20         62 push @valPos, [ $valuePtr, $size, $tagName ];
6436             }
6437             # fix valuePtr if necessary
6438 7121 50       18082 if ($$dirInfo{FixOffsets}) {
6439 0         0 my $wFlag;
6440 0 0       0 $valEnd or $valEnd = $dataPos + $dirEnd + 4;
6441             #### eval FixOffsets ($valuePtr, $valEnd, $size, $tagID, $wFlag)
6442 0         0 eval $$dirInfo{FixOffsets};
6443             }
6444 7121         10983 my $suspect;
6445             # offset shouldn't point into TIFF header
6446 7121 100 66     17008 $valuePtr < 8 and not $$dirInfo{ZeroOffsetOK} and $suspect = $warnCount;
6447             # convert offset to pointer in $$dataPt
6448 7121 100 66     41279 if ($$dirInfo{EntryBased} or (ref $$tagTablePtr{$tagID} eq 'HASH' and
      66        
6449             $$tagTablePtr{$tagID}{EntryBased}))
6450             {
6451 10         15 $valuePtr += $entry;
6452             } else {
6453 7111         13012 $valuePtr -= $dataPos;
6454             }
6455             # value shouldn't overlap our directory
6456 7121 50 66     17446 $suspect = $warnCount if $valuePtr < $dirEnd and $valuePtr+$size > $dirStart;
6457             # load value from file if necessary
6458 7121 100 100     25732 if ($valuePtr < 0 or $valuePtr+$size > $dataLen) {
6459             # get value by seeking in file if we are allowed
6460 449         839 my $buff;
6461 449 50       1188 if ($raf) {
6462             # avoid loading large binary data unless necessary
6463 449         1317 while ($size > BINARY_DATA_LIMIT) {
6464 0 0       0 if ($tagInfo) {
6465             # make large unknown blocks binary data
6466 0 0       0 $$tagInfo{Binary} = 1 if $$tagInfo{Unknown};
6467 0 0       0 last unless $$tagInfo{Binary}; # must read non-binary data
6468 0 0       0 last if $$tagInfo{SubDirectory};
6469 0         0 my $lcTag = lc($$tagInfo{Name});
6470 0 0 0     0 if ($$et{OPTIONS}{Binary} and
6471             not $$et{EXCL_TAG_LOOKUP}{$lcTag})
6472             {
6473             # read binary data if specified unless tagsFromFile won't use it
6474 0 0 0     0 last unless $$et{TAGS_FROM_FILE} and $$tagInfo{Protected};
6475             }
6476             # must read if tag is specified by name
6477 0 0       0 last if $$et{REQ_TAG_LOOKUP}{$lcTag};
6478             } else {
6479             # must read value if needed for a condition
6480 0 0       0 last if defined $tagInfo;
6481             }
6482             # (note: changing the value without changing $size will cause
6483             # a warning in the verbose output, but we need to maintain the
6484             # proper size for the htmlDump, so we can't change this)
6485 0         0 $buff = "Binary data $size bytes";
6486 0         0 $readSize = length $buff;
6487 0         0 last;
6488             }
6489             # read from file if necessary
6490 449 50       1145 unless (defined $buff) {
6491 449         877 my ($wrn, $truncOK);
6492 449   66     1789 my $readFromRAF = ($tagInfo and $$tagInfo{ReadFromRAF});
6493 449 50 33     2547 if (not $raf->Seek($base + $valuePtr + $dataPos, 0)) {
    50 33        
    50          
    50          
6494 0         0 $wrn = "Invalid offset for $dir entry $index";
6495             } elsif ($readFromRAF and $size > BINARY_DATA_LIMIT and
6496             not $$et{REQ_TAG_LOOKUP}{lc $$tagInfo{Name}})
6497             {
6498 0         0 $buff = "$$tagInfo{Name} data $size bytes";
6499 0         0 $readSize = length $buff;
6500             } elsif ($raf->Read($buff,$size) != $size) {
6501 0         0 $wrn = sprintf("Error reading value for $dir entry $index, ID 0x%.4x", $tagID);
6502 0 0 0     0 if ($tagInfo and not $$tagInfo{Unknown}) {
6503 0         0 $wrn .= " $$tagInfo{Name}";
6504 0         0 $truncOK = $$tagInfo{TruncateOK};
6505             }
6506             } elsif ($readFromRAF) {
6507             # seek back to the start of the value
6508 0         0 $raf->Seek($base + $valuePtr + $dataPos, 0);
6509             }
6510 449 50       1367 if ($wrn) {
6511 0   0     0 $et->Warn($wrn, $inMakerNotes || $truncOK);
6512 0 0 0     0 return 0 unless $inMakerNotes or $htmlDump or $truncOK;
      0        
6513 0         0 ++$warnCount;
6514 0 0       0 $buff = '' unless defined $buff;
6515 0         0 $readSize = length $buff;
6516 0 0       0 $bad = 1 unless $truncOK;
6517             }
6518             }
6519 449         799 $valueDataLen = length $buff;
6520 449         818 $valueDataPt = \$buff;
6521 449         2608 $valueDataPos = $valuePtr + $dataPos;
6522 449         833 $valuePtr = 0;
6523             } else {
6524 0         0 my ($tagStr, $tmpInfo, $leicaTrailer);
6525 0 0       0 if ($tagInfo) {
    0          
6526 0         0 $tagStr = $$tagInfo{Name};
6527 0         0 $leicaTrailer = $$tagInfo{LeicaTrailer};
6528             } elsif (defined $tagInfo) {
6529 0         0 $tmpInfo = $et->GetTagInfo($tagTablePtr, $tagID, \ '', $formatStr, $count);
6530 0 0       0 if ($tmpInfo) {
6531 0         0 $tagStr = $$tmpInfo{Name};
6532 0         0 $leicaTrailer = $$tmpInfo{LeicaTrailer};
6533             }
6534             }
6535 0 0 0     0 if ($tagInfo and $$tagInfo{ChangeBase}) {
6536             # adjust base offset for this tag only
6537             #### eval ChangeBase ($dirStart,$dataPos)
6538 0         0 my $newBase = eval $$tagInfo{ChangeBase};
6539 0         0 $valuePtr += $newBase;
6540             }
6541 0 0       0 $tagStr or $tagStr = sprintf("tag 0x%.4x",$tagID);
6542             # allow PreviewImage to run outside EXIF data
6543 0 0 0     0 if ($tagStr eq 'PreviewImage' and $$et{RAF}) {
    0 0        
6544 0         0 my $pos = $$et{RAF}->Tell();
6545 0         0 $buff = $et->ExtractBinary($base + $valuePtr + $dataPos, $size, 'PreviewImage');
6546 0         0 $$et{RAF}->Seek($pos, 0);
6547 0         0 $valueDataPt = \$buff;
6548 0         0 $valueDataPos = $valuePtr + $dataPos;
6549 0         0 $valueDataLen = $size;
6550 0         0 $valuePtr = 0;
6551             } elsif ($leicaTrailer and $$et{RAF}) {
6552 0 0       0 if ($verbose > 0) {
6553 0         0 $et->VPrint(0, "$$et{INDENT}$index) $tagStr --> (outside APP1 segment)\n");
6554             }
6555 0 0       0 if ($et->Options('FastScan')) {
6556 0         0 $et->Warn('Ignored Leica MakerNote trailer');
6557             } else {
6558 0         0 require Image::ExifTool::Fixup;
6559             $$et{LeicaTrailer} = {
6560 0   0     0 TagInfo => $tagInfo || $tmpInfo,
6561             Offset => $base + $valuePtr + $dataPos,
6562             Size => $size,
6563             Fixup => Image::ExifTool::Fixup->new,
6564             };
6565             }
6566             } else {
6567 0         0 $et->Warn("Bad offset for $dir $tagStr", $inMakerNotes);
6568 0         0 ++$warnCount;
6569             }
6570 0 0       0 unless (defined $buff) {
6571 0         0 $valueDataPt = '';
6572 0         0 $valueDataPos = $valuePtr + $dataPos;
6573 0         0 $valueDataLen = 0;
6574 0         0 $valuePtr = 0;
6575 0         0 $bad = 1;
6576             }
6577             }
6578             }
6579             # warn about suspect offsets if they didn't already cause another warning
6580 7121 100 66     18150 if (defined $suspect and $suspect == $warnCount) {
6581 2 50       6 my $tagStr = $tagInfo ? $$tagInfo{Name} : sprintf('tag 0x%.4x', $tagID);
6582 2 50       13 if ($et->Warn("Suspicious $dir offset for $tagStr", $inMakerNotes)) {
6583 2         2 ++$warnCount;
6584 2 50       16 next unless $verbose;
6585             }
6586             }
6587             }
6588             # treat single unknown byte as int8u
6589 15782 100 100     44539 $formatStr = 'int8u' if $format == 7 and $count == 1;
6590              
6591 15782         29747 my ($val, $subdir, $wrongFormat);
6592 15782 50 33     36636 if ($tagID > 0xf000 and $isExif) {
6593 0         0 my $oldInfo = $$tagTablePtr{$tagID};
6594 0 0 0     0 if ((not $oldInfo or (ref $oldInfo eq 'HASH' and $$oldInfo{Condition} and
      0        
6595             not $$oldInfo{PSRaw})) and not $bad)
6596             {
6597             # handle special case of Photoshop RAW tags (0xfde8-0xfe58)
6598             # --> generate tags from the value if possible
6599 0         0 $val = ReadValue($valueDataPt,$valuePtr,$formatStr,$count,$readSize);
6600 0 0 0     0 if (defined $val and $val =~ /(.*): (.*)/) {
6601 0         0 my $tag = $1;
6602 0         0 $val = $2;
6603 0         0 $tag =~ s/'s//; # remove 's (so "Owner's Name" becomes "OwnerName")
6604 0         0 $tag =~ tr/a-zA-Z0-9_//cd; # remove unknown characters
6605 0 0       0 if ($tag) {
6606 0         0 $tagInfo = {
6607             Name => $tag,
6608             Condition => '$$self{TIFF_TYPE} ne "DCR"',
6609             ValueConv => '$_=$val;s/^.*: //;$_', # remove descr
6610             PSRaw => 1, # (just as flag to avoid adding this again)
6611             };
6612 0         0 AddTagToTable($tagTablePtr, $tagID, $tagInfo);
6613             # generate conditional list if a conditional tag already existed
6614 0 0       0 $$tagTablePtr{$tagID} = [ $oldInfo, $tagInfo ] if $oldInfo;
6615             }
6616             }
6617             }
6618             }
6619 15782 100 100     53421 if (defined $tagInfo and not $tagInfo) {
6620 406 50       1419 if ($bad) {
6621 0         0 undef $tagInfo;
6622             } else {
6623             # GetTagInfo() required the value for a Condition
6624 406 100       2153 my $tmpVal = substr($$valueDataPt, $valuePtr, $readSize < 128 ? $readSize : 128);
6625             # (use original format name in this call -- $formatStr may have been changed to int8u)
6626 406         3677 $tagInfo = $et->GetTagInfo($tagTablePtr, $tagID, \$tmpVal,
6627             $formatName[$format], $count);
6628             }
6629             }
6630             # make sure we are handling the 'ifd' format properly
6631 15782 50 66     58077 if (($format == 13 or $format == 18) and (not $tagInfo or not $$tagInfo{SubIFD})) {
      33        
      66        
6632 0         0 my $str = sprintf('%s tag 0x%.4x IFD format not handled', $dirName, $tagID);
6633 0         0 $et->Warn($str, $inMakerNotes);
6634             }
6635 15782 100       30579 if (defined $tagInfo) {
6636 15200         32317 my $readFormat = $$tagInfo{Format};
6637 15200         27580 $subdir = $$tagInfo{SubDirectory};
6638             # unless otherwise specified, all SubDirectory data except
6639             # EXIF SubIFD offsets should be unformatted
6640 15200 100 100     40482 $readFormat = 'undef' if $subdir and not $$tagInfo{SubIFD} and not $readFormat;
      100        
6641             # override EXIF format if specified
6642 15200 100       29845 if ($readFormat) {
6643 1842         3679 $formatStr = $readFormat;
6644 1842         5320 my $newNum = $formatNumber{$formatStr};
6645 1842 100 66     7763 if ($newNum and $newNum != $format) {
6646 1000         2958 $origFormStr = $formatName[$format] . '[' . $count . ']';
6647 1000         1760 $format = $newNum;
6648 1000 50       3115 $size = $readSize = $$tagInfo{FixedSize} if $$tagInfo{FixedSize};
6649             # adjust number of items for new format size
6650 1000         3457 $count = int($size / $formatSize[$format]);
6651             }
6652             }
6653             # verify that offset-type values are integral
6654 15200 50 100     67258 if (($$tagInfo{IsOffset} or $$tagInfo{SubIFD}) and not $intFormat{$formatStr}) {
      66        
6655 0         0 $et->Warn(sprintf('Wrong format (%s) for %s 0x%.4x %s',$formatStr,$dir,$tagID,$$tagInfo{Name}));
6656 0 0       0 if ($validate) {
6657 0         0 $$et{WrongFormat}{"$dir:$$tagInfo{Name}"} = 1;
6658 0         0 $offsetInfo{$tagID} = [ $tagInfo, '' ];
6659             }
6660 0 0       0 next unless $verbose;
6661 0         0 $wrongFormat = 1;
6662             }
6663             } else {
6664 582 100       2777 next unless $verbose;
6665             }
6666 15203 50       33609 unless ($bad) {
6667             # limit maximum length of data to reformat
6668             # (avoids long delays when processing some corrupted files)
6669 15203         24712 my $warned;
6670 15203 50 33     34382 if ($count > 100000 and $formatStr !~ /^(undef|string|binary)$/) {
6671 0 0       0 my $tagName = $tagInfo ? $$tagInfo{Name} : sprintf('tag 0x%.4x', $tagID);
6672             # (count of 196608 is typical for ColorMap)
6673 0 0 0     0 if ($tagName ne 'TransferFunction' or $count != 196608) {
6674 0 0       0 my $minor = $count > 2000000 ? 0 : 2;
6675 0 0       0 if ($et->Warn("Ignoring $dirName $tagName with excessive count", $minor)) {
6676 0 0       0 next unless $$et{OPTIONS}{HtmlDump};
6677 0         0 $warned = 1;
6678             }
6679             }
6680             }
6681 15203 50 100     44441 if ($count > 500 and $formatStr !~ /^(undef|string|binary)$/ and
      33        
      66        
      33        
6682             (not $tagInfo or $$tagInfo{LongBinary} or $warned) and not $$et{OPTIONS}{IgnoreMinorErrors})
6683             {
6684 0 0       0 $et->Warn('Not decoding some large array(s). Ignore minor errors to decode', 2) unless $warned;
6685 0 0       0 next if $$et{TAGS_FROM_FILE}; # don't generate bogus value when copying tags
6686 0         0 $val = "(large array of $count $formatStr values)";
6687             } else {
6688             # convert according to specified format
6689 15203         48612 $val = ReadValue($valueDataPt,$valuePtr,$formatStr,$count,$readSize,\$rational);
6690 15203 50       33741 $binVal = substr($$valueDataPt,$valuePtr,$readSize) if $saveBin;
6691             # re-code if necessary
6692 15203 50       34663 if (defined $val) {
6693 15203 50 33     56006 if ($formatStr eq 'utf8') {
    50          
6694 0         0 $val = $et->Decode($val, 'UTF8');
6695             } elsif ($strEnc and $formatStr eq 'string') {
6696 0         0 $val = $et->Decode($val, $strEnc);
6697             }
6698             }
6699             }
6700             }
6701              
6702 15203 100       32404 if ($verbose) {
6703 345         760 my $tval = $val;
6704             # also show as a rational
6705 345 100       881 $tval .= " ($rational)" if defined $rational;
6706 345 50       762 if ($htmlDump) {
6707 0         0 my ($tagName, $colName);
6708 0 0 0     0 if ($tagInfo) {
    0          
6709 0         0 $tagName = $$tagInfo{Name};
6710             } elsif ($tagID == 0x927c and $dirName eq 'ExifIFD') {
6711 0         0 $tagName = 'MakerNotes';
6712             } else {
6713 0         0 $tagName = sprintf("Tag 0x%.4x",$tagID);
6714             }
6715 0         0 my $dname = sprintf("${dir}-%.2d", $index);
6716             # build our tool tip
6717 0 0       0 $size < 0 and $size = $count * $formatSize[$format];
6718 0         0 my $fstr = "$formatName[$format]\[$count]";
6719 0 0 0     0 $fstr = "$origFormStr read as $fstr" if $origFormStr and $origFormStr ne $fstr;
6720 0 0       0 $fstr .= ' <-- WRONG' if $wrongFormat;
6721 0         0 my $tip = sprintf("Tag ID: 0x%.4x\n", $tagID) .
6722             "Format: $fstr\nSize: $size bytes\n";
6723 0 0       0 if ($size > 4) {
6724 0         0 my $offPt = Get32u($dataPt,$entry+8);
6725             # (test this with ../pics/{CanonEOS-1D_XMarkIII.hif,PanasonicDC-G9.rw2,*.raf})
6726             my $actPt = $valuePtr + $valueDataPos + $base - ($$et{EXIF_POS} || 0) +
6727 0   0     0 ($$et{BASE_FUDGE} || $$et{BASE} || 0);
      0        
6728 0         0 $tip .= sprintf("Value offset: 0x%.4x\n", $offPt);
6729             # highlight tag name (red for bad size)
6730 0 0 0     0 my $style = ($bad or not defined $tval) ? 'V' : 'H';
6731 0 0       0 if ($actPt != $offPt) {
6732 0         0 $tip .= sprintf("Actual offset: 0x%.4x\n", $actPt);
6733 0 0       0 my $sign = $actPt < $offPt ? '-' : '';
6734 0         0 $tip .= sprintf("Offset base: ${sign}0x%.4x\n", abs($actPt - $offPt));
6735 0 0       0 $style = 'F' if $style eq 'H'; # purple for different offsets
6736             }
6737 0 0 0     0 if ($$et{EXIF_POS} and not $$et{BASE_FUDGE}) {
6738             $tip .= sprintf("File offset: 0x%.4x\n", $actPt + $$et{EXIF_POS})
6739 0         0 }
6740 0         0 $colName = "$tagName";
6741 0 0       0 $colName .= ' (odd)' if $offPt & 0x01;
6742             } else {
6743 0         0 $colName = $tagName;
6744             }
6745 0 0       0 $colName .= ' (err)' if $wrongFormat;
6746 0 0 0     0 $colName .= ' (seq)' if $tagID <= $lastID and not $inMakerNotes;
6747 0         0 $lastID = $tagID;
6748 0 0       0 if (not defined $tval) {
6749 0         0 $tval = '';
6750             } else {
6751 0 0       0 $tval = substr($tval,0,28) . '[...]' if length($tval) > 32;
6752 0 0 0     0 if ($formatStr =~ /^(string|undef|binary)/) {
    0          
6753             # translate non-printable characters
6754 0         0 $tval =~ tr/\x00-\x1f\x7f-\xff/./;
6755             } elsif ($tagInfo and Image::ExifTool::IsInt($tval)) {
6756 0 0 0     0 if ($$tagInfo{IsOffset} or $$tagInfo{SubIFD}) {
    0          
6757 0         0 $tval = sprintf('0x%.4x', $tval);
6758 0   0     0 my $actPt = $val + $base - ($$et{EXIF_POS} || 0) + ($$et{BASE_FUDGE} || $$et{BASE} || 0);
      0        
6759 0 0       0 if ($actPt != $val) {
6760 0         0 $tval .= sprintf("\nActual offset: 0x%.4x", $actPt);
6761 0 0       0 my $sign = $actPt < $val ? '-' : '';
6762 0         0 $tval .= sprintf("\nOffset base: ${sign}0x%.4x", abs($actPt - $val));
6763             }
6764 0 0 0     0 if ($$et{EXIF_POS} and not $$et{BASE_FUDGE}) {
6765             $tip .= sprintf("File offset: 0x%.4x\n", $actPt + $$et{EXIF_POS})
6766 0         0 }
6767             } elsif ($$tagInfo{PrintHex}) {
6768 0         0 $tval = sprintf('0x%x', $tval);
6769             }
6770             }
6771             }
6772 0         0 $tip .= "Value: $tval";
6773 0         0 my $id = $offName;
6774 0         0 my $sid;
6775 0 0 0     0 ($subOffName, $id, $sid) = NextOffsetName($et, $offName) if $tagInfo and $$tagInfo{SubIFD};
6776 0         0 $et->HDump($entry+$dataPos+$base, 12, "$dname $colName", $tip, 1, $id);
6777 0 0       0 next if $valueDataLen < 0; # don't process bad pointer entry
6778 0 0       0 if ($size > 4) {
6779 0         0 my $exifDumpPos = $valuePtr + $valueDataPos + $base;
6780 0         0 my $flag = 0;
6781 0 0       0 if ($subdir) {
6782 0 0       0 if ($$tagInfo{MakerNotes}) {
    0          
6783 0         0 $flag = 0x04;
6784             } elsif ($$tagInfo{NestedHtmlDump}) {
6785 0 0       0 $flag = $$tagInfo{NestedHtmlDump} == 2 ? 0x10 : 0x04;
6786             }
6787             }
6788             # add value data block (underlining maker notes data)
6789 0         0 $et->HDump($exifDumpPos,$size,"$tagName value",'SAME', $flag, $sid);
6790 0 0 0     0 if ($subdir and $$tagInfo{MakerNotes} and $$tagInfo{NotIFD}) {
      0        
6791 0         0 $et->HDump($exifDumpPos,$size,"$tagName value",undef,undef,$$dirInfo{OffsetName});
6792             }
6793             }
6794             } else {
6795 345 50 66     1129 if ($tagID <= $lastID and not $inMakerNotes) {
6796 0 0       0 my $str = $tagInfo ? ' '.$$tagInfo{Name} : '';
6797 0 0       0 if ($tagID == $lastID) {
6798 0         0 $et->Warn(sprintf('Duplicate tag 0x%.4x%s in %s', $tagID, $str, $dirName));
6799             } else {
6800 0         0 $et->Warn(sprintf('Tag ID 0x%.4x%s out of sequence in %s', $tagID, $str, $dirName));
6801             }
6802             }
6803 345         595 $lastID = $tagID;
6804 345 100       806 if ($verbose > 0) {
6805 257         606 my $fstr = $formatName[$format];
6806 257 100       507 $fstr = "$origFormStr read as $fstr" if $origFormStr;
6807 257         1353 $et->VerboseInfo($tagID, $tagInfo,
6808             Table => $tagTablePtr,
6809             Index => $index,
6810             Value => $tval,
6811             DataPt => $valueDataPt,
6812             DataPos => $valueDataPos + $base,
6813             Size => $size,
6814             Start => $valuePtr,
6815             Format => $fstr,
6816             Count => $count,
6817             );
6818             }
6819             }
6820 345 100 66     1728 next if not $tagInfo or $wrongFormat;
6821             }
6822 15200 50       31012 next unless defined $val;
6823             #..............................................................................
6824             # Handle SubDirectory tag types
6825             #
6826 15200 100       33208 if ($subdir) {
6827             # don't process empty subdirectories
6828 1329 50       3645 unless ($size) {
6829 0 0 0     0 unless ($$tagInfo{MakerNotes} or $inMakerNotes) {
6830 0         0 $et->Warn("Empty $$tagInfo{Name} data", 1);
6831             }
6832 0         0 next;
6833             }
6834 1329         2929 my (@values, $newTagTable, $dirNum, $newByteOrder, $invalid);
6835 1329         3967 my $tagStr = $$tagInfo{Name};
6836 1329 100       4126 if ($$subdir{MaxSubdirs}) {
6837 161         816 @values = split ' ', $val;
6838             # limit the number of subdirectories we parse
6839 161         585 my $over = @values - $$subdir{MaxSubdirs};
6840 161 50       631 if ($over > 0) {
6841 0         0 $et->Warn("Ignoring $over $tagStr directories");
6842 0         0 splice @values, $$subdir{MaxSubdirs};
6843             }
6844 161         487 $val = shift @values;
6845             }
6846 1329 100       3829 if ($$subdir{TagTable}) {
6847 958         4282 $newTagTable = GetTagTable($$subdir{TagTable});
6848 958 50       3309 $newTagTable or warn("Unknown tag table $$subdir{TagTable}"), next;
6849             } else {
6850 371         847 $newTagTable = $tagTablePtr; # use existing table
6851             }
6852             # loop through all sub-directories specified by this tag
6853 1329         3036 for ($dirNum=0; ; ++$dirNum) {
6854 1335         2391 my $subdirBase = $base;
6855 1335         2356 my $subdirDataPt = $valueDataPt;
6856 1335         2473 my $subdirDataPos = $valueDataPos;
6857 1335         2569 my $subdirDataLen = $valueDataLen;
6858 1335         2578 my $subdirStart = $valuePtr;
6859 1335 100       4264 if (defined $$subdir{Start}) {
6860             # set local $valuePtr relative to file $base for eval
6861 476         1193 my $valuePtr = $subdirStart + $subdirDataPos;
6862             #### eval Start ($valuePtr, $val)
6863 476         38030 my $newStart = eval($$subdir{Start});
6864 476 50       3569 unless (Image::ExifTool::IsInt($newStart)) {
6865 0         0 $et->Warn("Bad value for $tagStr");
6866 0         0 last;
6867             }
6868             # convert back to relative to $subdirDataPt
6869 476         1356 $newStart -= $subdirDataPos;
6870             # adjust directory size if necessary
6871 476 50 66     2147 unless ($$tagInfo{SubIFD} or $$subdir{BadOffset}) {
6872 51         148 $size -= $newStart - $subdirStart;
6873             }
6874 476         1097 $subdirStart = $newStart;
6875             }
6876             # this is a pain, but some maker notes are always a specific
6877             # byte order, regardless of the byte order of the file
6878 1335         4612 my $oldByteOrder = GetByteOrder();
6879 1335         3341 $newByteOrder = $$subdir{ByteOrder};
6880 1335 100       3744 if ($newByteOrder) {
6881 186 100       1782 if ($newByteOrder =~ /^Little/i) {
    100          
    50          
    50          
6882 38         105 $newByteOrder = 'II';
6883             } elsif ($newByteOrder =~ /^Big/i) {
6884 29         73 $newByteOrder = 'MM';
6885             } elsif ($$subdir{OffsetPt}) {
6886 0         0 undef $newByteOrder;
6887 0         0 warn "Can't have variable byte ordering for SubDirectories using OffsetPt";
6888 0         0 last;
6889             } elsif ($subdirStart + 2 <= $subdirDataLen) {
6890             # attempt to determine the byte ordering by checking
6891             # the number of directory entries. This is an int16u
6892             # that should be a reasonable value.
6893 119         495 my $num = Get16u($subdirDataPt, $subdirStart);
6894 119 100 100     694 if ($num & 0xff00 and ($num>>8) > ($num&0xff)) {
6895             # This looks wrong, we shouldn't have this many entries
6896 4         22 my %otherOrder = ( II=>'MM', MM=>'II' );
6897 4         13 $newByteOrder = $otherOrder{$oldByteOrder};
6898             } else {
6899 115         320 $newByteOrder = $oldByteOrder;
6900             }
6901             }
6902             } else {
6903 1149         2428 $newByteOrder = $oldByteOrder;
6904             }
6905             # set base offset if necessary
6906 1335 100       4027 if ($$subdir{Base}) {
6907             # calculate subdirectory start relative to $base for eval
6908 38         94 my $start = $subdirStart + $subdirDataPos;
6909             #### eval Base ($start,$base)
6910 38         2375 $subdirBase = eval($$subdir{Base}) + $base;
6911             }
6912             # add offset to the start of the directory if necessary
6913 1335 100       4371 if ($$subdir{OffsetPt}) {
6914             #### eval OffsetPt ($valuePtr)
6915 27         1444 my $pos = eval $$subdir{OffsetPt};
6916 27 50       152 if ($pos + 4 > $subdirDataLen) {
6917 0         0 $et->Warn("Bad $tagStr OffsetPt");
6918 0         0 last;
6919             }
6920 27         133 SetByteOrder($newByteOrder);
6921 27         100 $subdirStart += Get32u($subdirDataPt, $pos);
6922 27         86 SetByteOrder($oldByteOrder);
6923             }
6924 1335 100 100     6913 if ($subdirStart < 0 or $subdirStart + 2 > $subdirDataLen) {
6925             # convert $subdirStart back to a file offset
6926 31 50       164 if ($raf) {
6927             # reset SubDirectory buffer (we will load it later)
6928 31         98 my $buff = '';
6929 31         82 $subdirDataPt = \$buff;
6930 31         89 $subdirDataLen = $size = length $buff;
6931             } else {
6932 0         0 my $msg = "Bad $tagStr SubDirectory start";
6933 0 0       0 if ($verbose > 0) {
6934 0 0       0 if ($subdirStart < 0) {
6935 0         0 $msg .= " (directory start $subdirStart is before EXIF start)";
6936             } else {
6937 0         0 my $end = $subdirStart + $size;
6938 0         0 $msg .= " (directory end is $end but EXIF size is only $subdirDataLen)";
6939             }
6940             }
6941 0         0 $et->Warn($msg, $inMakerNotes);
6942 0         0 last;
6943             }
6944             }
6945              
6946             # must update subdirDataPos if $base changes for this subdirectory
6947 1335         2990 $subdirDataPos += $base - $subdirBase;
6948              
6949             # build information hash for new directory
6950             my %subdirInfo = (
6951             Name => $tagStr,
6952             Base => $subdirBase,
6953             DataPt => $subdirDataPt,
6954             DataPos => $subdirDataPos,
6955             DataLen => $subdirDataLen,
6956             DirStart => $subdirStart,
6957             DirLen => $size,
6958             RAF => $raf,
6959             Parent => $dirName,
6960             DirName => $$subdir{DirName},
6961             FixBase => $$subdir{FixBase},
6962             FixOffsets => $$subdir{FixOffsets},
6963             EntryBased => $$subdir{EntryBased},
6964             TagInfo => $tagInfo,
6965             SubIFD => $$tagInfo{SubIFD},
6966 1335         22261 Subdir => $subdir,
6967             OffsetName => $subOffName,
6968             );
6969             # (remember: some cameras incorrectly write maker notes in IFD0)
6970 1335 100       4511 if ($$tagInfo{MakerNotes}) {
6971             # don't parse makernotes if FastScan > 1
6972 141         848 my $fast = $et->Options('FastScan');
6973 141 100 66     693 last if $fast and $fast > 1;
6974 140         555 $subdirInfo{MakerNoteAddr} = $valuePtr + $valueDataPos + $base;
6975 140 100       659 $subdirInfo{NoFixBase} = 1 if defined $$subdir{Base};
6976             }
6977             # set directory IFD name from group name of family 1 if it exists,
6978             # unless the tag is writable as a block in which case group 1 may
6979             # have been set automatically
6980 1334 100 100     5814 if ($$tagInfo{Groups} and not $$tagInfo{Writable}) {
6981 425         1452 $subdirInfo{DirName} = $$tagInfo{Groups}{1};
6982             # number multiple subdirectories
6983 425 100       1375 $subdirInfo{DirName} =~ s/\d*$/$dirNum/ if $dirNum;
6984             }
6985 1334         4863 SetByteOrder($newByteOrder); # set byte order for this subdir
6986             # validate the subdirectory if necessary
6987 1334         2648 my $dirData = $subdirDataPt; # set data pointer to be used in eval
6988             #### eval Validate ($val, $dirData, $subdirStart, $size)
6989 1334         3061 my $ok = 0;
6990 1334 50 66     44899 if (defined $$subdir{Validate} and not eval $$subdir{Validate}) {
6991 0         0 $et->Warn("Invalid $tagStr data", $inMakerNotes);
6992 0         0 $invalid = 1;
6993             } else {
6994 1334 100 100     6890 if (not $subdirInfo{DirName} and $inMakerNotes) {
6995 662         2278 $subdirInfo{DirName} = $$tagInfo{Name};
6996             }
6997             # process the subdirectory
6998 1334         9674 $ok = $et->ProcessDirectory(\%subdirInfo, $newTagTable, $$subdir{ProcessProc});
6999             }
7000             # print debugging information if there were errors
7001 1334 50 66     8409 if (not $ok and $verbose > 1 and $subdirStart != $valuePtr) {
      33        
7002 0         0 my $out = $et->Options('TextOut');
7003 0         0 printf $out "%s (SubDirectory start = 0x%x)\n", $$et{INDENT}, $subdirStart;
7004             }
7005 1334         4443 SetByteOrder($oldByteOrder); # restore original byte swapping
7006              
7007 1334 100       12041 @values or last;
7008 6         57 $val = shift @values; # continue with next subdir
7009             }
7010 1329         5949 my $doMaker = $et->Options('MakerNotes');
7011 1329 100 100     15769 next unless $doMaker or $$et{REQ_TAG_LOOKUP}{lc($tagStr)} or $$tagInfo{BlockExtract};
      100        
7012             # extract as a block if specified
7013 223 100       967 if ($$tagInfo{MakerNotes}) {
7014             # save maker note byte order (if it was significant and valid)
7015 23 50 33     239 if ($$subdir{ByteOrder} and not $invalid) {
7016             $$et{MAKER_NOTE_BYTE_ORDER} =
7017             defined ($$et{UnknownByteOrder}) ?
7018 23 100       162 $$et{UnknownByteOrder} : $newByteOrder;
7019             }
7020 23 50 33     301 if ($doMaker and $doMaker eq '2') {
    50 33        
7021             # extract maker notes without rebuilding (no fixup information)
7022 0         0 delete $$et{MAKER_NOTE_FIXUP};
7023             } elsif (not $$tagInfo{NotIFD} or $$tagInfo{IsPhaseOne}) {
7024             # this is a pain, but we must rebuild EXIF-type maker notes to
7025             # include all the value data if data was outside the maker notes
7026             my %makerDirInfo = (
7027             Name => $tagStr,
7028             Base => $base,
7029             DataPt => $valueDataPt,
7030             DataPos => $valueDataPos,
7031             DataLen => $valueDataLen,
7032             DirStart => $valuePtr,
7033             DirLen => $size,
7034             RAF => $raf,
7035             Parent => $dirName,
7036             DirName => 'MakerNotes',
7037             FixOffsets => $$subdir{FixOffsets},
7038 23         293 TagInfo => $tagInfo,
7039             );
7040 23         61 my $val2;
7041 23 50       132 if ($$tagInfo{IsPhaseOne}) {
7042 0         0 $$et{DropTags} = 1;
7043 0         0 $val2 = Image::ExifTool::PhaseOne::WritePhaseOne($et, \%makerDirInfo, $newTagTable);
7044 0         0 delete $$et{DropTags};
7045             } else {
7046 23 100       122 $makerDirInfo{FixBase} = 1 if $$subdir{FixBase};
7047             # rebuild maker notes (creates $$et{MAKER_NOTE_FIXUP})
7048 23         300 $val2 = RebuildMakerNotes($et, \%makerDirInfo, $newTagTable);
7049             }
7050 23 50       111 if (defined $val2) {
    0          
7051 23         237 $val = $val2;
7052             } elsif ($size > 4) {
7053 0         0 $et->Warn('Error rebuilding maker notes (may be corrupt)');
7054             }
7055             }
7056             } else {
7057             # extract this directory as a block if specified
7058 200 100       1391 next unless $$tagInfo{Writable};
7059             }
7060             }
7061             #..............................................................................
7062             # convert to absolute offsets if this tag is an offset
7063             #### eval IsOffset ($val, $et)
7064 13910 100 100     53691 if ($$tagInfo{IsOffset} and eval $$tagInfo{IsOffset}) {
7065 233 100       1148 my $offsetBase = $$tagInfo{IsOffset} eq '2' ? $firstBase : $base;
7066 233         726 $offsetBase += $$et{BASE};
7067             # handle offsets which use a wrong base (Minolta A200)
7068 233 50       906 if ($$tagInfo{WrongBase}) {
7069 0         0 my $self = $et;
7070             #### eval WrongBase ($self)
7071 0   0     0 $offsetBase += eval $$tagInfo{WrongBase} || 0;
7072             }
7073 233         858 my @vals = split(' ',$val);
7074 233         688 foreach $val (@vals) {
7075 233         999 $val += $offsetBase;
7076             }
7077 233         944 $val = join(' ', @vals);
7078             }
7079 13910 100 66     47197 if ($validate or $doHash) {
7080 70 100 66     319 if ($$tagInfo{OffsetPair}) {
    100          
7081 10         53 $offsetInfo{$tagID} = [ $tagInfo, $val ];
7082             } elsif ($saveForValidate{$tagID} and $isExif) {
7083 10         36 $offsetInfo{$tagID} = $val;
7084             }
7085             }
7086             # save the value of this tag
7087 13910         48038 $tagKey = $et->FoundTag($tagInfo, $val);
7088 13910 100       35211 if (defined $tagKey) {
7089             # set the group 1 name for tags in specified tables
7090 13899 100       54325 $et->SetGroup($tagKey, $dirName) if $$tagTablePtr{SET_GROUP1};
7091             # save original components of rational numbers (used when copying)
7092 13899 100       34343 $$et{TAG_EXTRA}{$tagKey}{Rational} = $rational if defined $rational;
7093 13899 50       28460 $$et{TAG_EXTRA}{$tagKey}{BinVal} = $binVal if defined $binVal;
7094 13899 50       26625 $$et{TAG_EXTRA}{$tagKey}{G6} = $saveFormat if $saveFormat;
7095 13899 100       63107 if ($$et{MAKER_NOTE_FIXUP}) {
7096 19         78 $$et{TAG_EXTRA}{$tagKey}{Fixup} = $$et{MAKER_NOTE_FIXUP};
7097 19         152 delete $$et{MAKER_NOTE_FIXUP};
7098             }
7099             }
7100             }
7101              
7102 1124 100       3677 if (%offsetInfo) {
7103             # calculate image data hash if requested
7104 5 50       21 AddImageDataHash($et, $dirInfo, \%offsetInfo) if $doHash;
7105             # validate image data offsets for this IFD (note: modifies %offsetInfo)
7106 5 50       39 Image::ExifTool::Validate::ValidateOffsetInfo($et, \%offsetInfo, $dirName, $inMakerNotes) if $validate;
7107             }
7108              
7109             # scan for subsequent IFD's if specified
7110 1124 100 66     6486 if ($$dirInfo{Multi} and $bytesFromEnd >= 4) {
    100          
7111             # use same directory information for trailing directory,
7112             # but change the start location (ProcessDirectory will
7113             # test to make sure we don't reprocess the same dir twice)
7114 309         3871 my %newDirInfo = %$dirInfo;
7115 309         1319 $newDirInfo{Multi} = 0; # prevent recursion
7116 309         969 $newDirInfo{OffsetName} = $nextOffName;
7117 309         2304 $$et{INDENT} =~ s/..$//; # keep indent the same
7118 309         709 for (;;) {
7119 487 100       1748 my $offset = Get32u($dataPt, $dirEnd) or last;
7120 178         572 $newDirInfo{DirStart} = $offset - $dataPos;
7121             # increment IFD number
7122 178 50       2002 my $ifdNum = $newDirInfo{DirName} =~ s/(\d+)$// ? $1 : 0;
7123 178         711 $newDirInfo{DirName} .= $ifdNum + 1;
7124             # must validate SubIFD1 because the nextIFD pointer is invalid for some RAW formats
7125 178 50 33     1014 if ($newDirInfo{DirName} ne 'SubIFD1' or ValidateIFD(\%newDirInfo)) {
    0 0        
7126 178         335 my $cur = pop @{$$et{PATH}};
  178         708  
7127 178 50       1038 $et->ProcessDirectory(\%newDirInfo, $tagTablePtr) or $success = 0;
7128 178         412 push @{$$et{PATH}}, $cur;
  178         659  
7129 178 50 33     1145 if ($success and $newDirInfo{BytesFromEnd} >= 4) {
7130 178         779 $dataPt = $newDirInfo{DataPt};
7131 178         476 $dataPos = $newDirInfo{DataPos};
7132 178         423 $dirEnd = $newDirInfo{DirEnd};
7133 178         523 next;
7134             }
7135             } elsif ($verbose or $$et{TIFF_TYPE} eq 'TIFF') {
7136 0         0 $et->Warn('Ignored bad IFD linked from SubIFD');
7137             }
7138 0         0 last;
7139             }
7140             } elsif (defined $$dirInfo{Multi}) {
7141             # return necessary parameters for parsing next IFD
7142 179         671 $$dirInfo{DirEnd} = $dirEnd;
7143 179         1114 $$dirInfo{OffsetName} = $nextOffName;
7144 179         757 $$dirInfo{BytesFromEnd} = $bytesFromEnd;
7145             }
7146 1124         6234 return $success;
7147             }
7148              
7149             1; # end
7150              
7151             __END__