source: main/trunk/greenstone2/perllib/cpan/Image/ExifTool/Exif.pm@ 34921

Last change on this file since 34921 was 34921, checked in by anupama, 3 years ago

Committing the improvements to EmbeddedMetaPlugin's processing of Keywords vs other metadata fields. Keywords were literally stored as arrays of words rather than phrases in PDFs (at least in Diego's sample PDF), whereas other meta fields like Subjects and Creators stored them as arrays of phrases. To get both to work, Kathy updated EXIF to a newer version, to retrieve the actual EXIF values stored in the PDF. And Kathy and Dr Bainbridge came up with a new option that I added called apply_join_before_split_to_metafields that's a regex which can list the metadata fields to apply the join_before_split to and whcih previously always got applied to all metadata fields. Now it's applied to any *Keywords metafields by default, as that's the metafield we have experience of that behaves differently to the others, as it stores by word instead of phrases. Tested on Diego's sample PDF. Diego has double-checked it to works on his sample PDF too, setting the split char to ; and turning on the join_before_split and leaving apply_join_before_split_to_metafields at its default of .*Keywords. File changes are strings.properties for the tooltip, the plugin introducing the option and working with it and Kathy's EXIF updates affecting cpan/File and cpan/Image.

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