File Coverage

blib/lib/Image/MetaData/JPEG/data/Tables.pm
Criterion Covered Total %
statement 41 41 100.0
branch 8 12 66.6
condition n/a
subroutine 8 8 100.0
pod 0 4 0.0
total 57 65 87.6


line stmt bran cond sub pod time code
1             ###########################################################
2             # A Perl package for showing/modifying JPEG (meta)data. #
3             # Copyright (C) 2004,2005,2006 Stefano Bettelli #
4             # See the COPYING and LICENSE files for license terms. #
5             ###########################################################
6             package Image::MetaData::JPEG::data::Tables;
7 16     16   163347 use Exporter 'import';
  16         24  
  16         530  
8 16     16   62 use strict;
  16         22  
  16         374  
9 16     16   59 use warnings;
  16         26  
  16         329  
10 16     16   7503 no integer;
  16         122  
  16         57  
11              
12             #============================================================================#
13             #============================================================================#
14             #============================================================================#
15             # This section defines the export policy of this module; no variable or #
16             # method is exported by default. Everything is exportable via %EXPORT_TAGS. #
17             #----------------------------------------------------------------------------#
18             our @ISA = qw(Exporter); #
19             our @EXPORT = qw(); #
20             our @EXPORT_OK = qw(); #
21             our %EXPORT_TAGS = #
22             (RecordTypes => [qw($NIBBLES $BYTE $ASCII $SHORT $LONG $RATIONAL), #
23             qw($SBYTE $UNDEF $SSHORT $SLONG $SRATIONAL $FLOAT), #
24             qw($DOUBLE $REFERENCE)], #
25             RecordProps => [qw(@JPEG_RECORD_TYPE_NAME @JPEG_RECORD_TYPE_LENGTH), #
26             qw(@JPEG_RECORD_TYPE_CATEGORY @JPEG_RECORD_TYPE_SIGN)], #
27             Endianness => [qw($NATIVE_ENDIANNESS $BIG_ENDIAN $LITTLE_ENDIAN)], #
28             JPEGgrammar => [qw($JPEG_PUNCTUATION %JPEG_MARKER $JPEG_SEG_MAX_LEN)], #
29             TagsAPP0 => [qw($APP0_JFIF_TAG $APP0_JFXX_TAG $APP0_JFXX_JPG), #
30             qw($APP0_JFXX_1B $APP0_JFXX_3B $APP0_JFXX_PAL)], #
31             TagsAPP1_Exif=>[qw($APP1_TH_JPEG $APP1_TH_TIFF $APP1_TH_TYPE), #
32             qw($APP1_EXIF_TAG $THJPEG_OFFSET $THJPEG_LENGTH), #
33             qw($APP1_TIFF_SIG $THTIFF_OFFSET $THTIFF_LENGTH), #
34             qw(%IFD_SUBDIRS $HASH_MAKERNOTES $MAKERNOTE_TAG)], #
35             TagsAPP1_XMP=> [qw($APP1_XMP_TAG $APP1_XMP_XPACKET_BEGIN), #
36             qw($APP1_XMP_XPACKET_ID $APP1_XMP_META_NS), #
37             qw($APP1_XMP_OUTER_RDF_NS)], #
38             TagsAPP2 => [qw($APP2_FPXR_TAG $APP2_ICC_TAG)], #
39             TagsAPP3 => [qw($APP3_EXIF_TAG %IFD_SUBDIRS)], #
40             TagsAPP13 => [qw($APP13_PHOTOSHOP_IPTC $APP13_PHOTOSHOP_IDS), #
41             qw($APP13_PHOTOSHOP_TYPE $APP13_IPTC_TAGMARKER), #
42             qw($APP13_PHOTOSHOP_DIRNAME $APP13_IPTC_DIRNAME)], #
43             TagsAPP14 => [qw($APP14_PHOTOSHOP_IDENTIFIER)], #
44             Lookups => [qw(&JPEG_lookup)], ); #
45             #----------------------------------------------------------------------------#
46             Exporter::export_ok_tags( #
47             qw(RecordTypes RecordProps Endianness JPEGgrammar), #
48             qw(TagsAPP0 TagsAPP1_Exif TagsAPP1_XMP), #
49             qw(TagsAPP2 TagsAPP3 TagsAPP13 TagsAPP14 Lookups)); #
50             #============================================================================#
51             #============================================================================#
52             #============================================================================#
53             # Constants for the grammar of a JPEG files. You can find here everything #
54             # about segment markers as well as the JPEG puncutation mark. The maximum #
55             # length of the data area of a standard JPEG segment is determined by the #
56             # fact that the segment lenght must be written to a two bytes field (inclu- #
57             # ding the two bytes themselves (so, it is 2^16 - 3). #
58             #----------------------------------------------------------------------------#
59             our $JPEG_SEG_MAX_LEN = 2**16 - 3; # data area max length for a std segment #
60             our $JPEG_PUNCTUATION = 0xff; # constant prefixed to every JPEG marker #
61             our %JPEG_MARKER = # non-repetitive JPEG markers #
62             (TEM => 0x01, # for TEMporary private use in arithmetic coding #
63             DHT => 0xc4, # Define Huffman Table(s) #
64             JPG => 0xc8, # reserved for JPEG extensions #
65             DAC => 0xcc, # Define Arithmetic Coding Conditioning(s) #
66             SOI => 0xd8, # Start Of Image #
67             EOI => 0xd9, # End Of Image #
68             SOS => 0xda, # Start Of Scan #
69             DQT => 0xdb, # Define Quantization Table(s) #
70             DNL => 0xdc, # Define Number of Lines #
71             DRI => 0xdd, # Define Restart Interval #
72             DHP => 0xde, # Define Hierarchical Progression #
73             EXP => 0xdf, # EXPand reference component(s) #
74             COM => 0xfe); # COMment block #
75             #----------------------------------------------------------------------------#
76             # markers 0x02 --> 0xbf are REServed for future uses #
77             for (0x02..0xbf) { $JPEG_MARKER{sprintf "res%02x", $_} = $_; } #
78             # some markers in 0xc0 --> 0xcf correspond to Start-Of-Frame typologies #
79             for (0xc0..0xc3, 0xc5..0xc7, 0xc9..0xcb, #
80             0xcd..0xcf) { $JPEG_MARKER{sprintf "SOF_%d", $_ - 0xc0} = $_; } #
81             # markers 0xd0 --> 0xd7 correspond to ReSTart with module 8 count #
82             for (0xd0..0xd7) { $JPEG_MARKER{sprintf "RST%d", $_ - 0xd0} = $_; } #
83             # markers 0xe0 --> 0xef are the APPlication markers #
84             for (0xe0..0xef) { $JPEG_MARKER{sprintf "APP%d", $_ - 0xe0} = $_; } #
85             # markers 0xf0 --> 0xfd are reserved for JPEG extensions #
86             for (0xf0..0xfd) { $JPEG_MARKER{sprintf "JPG%d", $_ - 0xf0} = $_; } #
87             #============================================================================#
88             #============================================================================#
89             #============================================================================#
90             # Functions for generating arrays (arg0=hashref, arg1=index) or references #
91             # to lookup tables [hashes] (arg0=hashref,arg1=index) from hashes; it is #
92             # assumed that the general hash they work on has array references as values. #
93             #----------------------------------------------------------------------------#
94 608     608 0 476 sub generate_lookup { my %a=map { $_ => $_[0]{$_}[$_[1]] } keys %{$_[0]}; \%a};
  43840         62831  
  608         4690  
  608         5743  
95 64     64 0 70 sub generate_array { map { $_[0]{$_}[$_[1]] } (0..(-1+scalar keys %{$_[0]}))};
  896         1210  
  64         161  
96             #============================================================================#
97             #============================================================================#
98             #============================================================================#
99             # Various lists for JPEG record names, lengths, categories and signs; see #
100             # Image::MetaData::JPEG::Record class for further details. The general hash #
101             # is private to this file, the other arrays are exported if so requested. #
102             #----------------------------------------------------------------------------#
103             # I gave up trying to calculate the length of a reference. This is probably #
104             # allocation dependent ... I use 0 here, meaning the length is variable. #
105             #----------------------------------------------------------------------------#
106             my $RECORD_TYPE_GENERAL = #
107             {(our $NIBBLES = 0) => [ 'NIBBLES' , 1, 'I', 'N' ], #
108             (our $BYTE = 1) => [ 'BYTE' , 1, 'I', 'N' ], #
109             (our $ASCII = 2) => [ 'ASCII' , 0, 'S', 'N' ], #
110             (our $SHORT = 3) => [ 'SHORT' , 2, 'I', 'N' ], #
111             (our $LONG = 4) => [ 'LONG' , 4, 'I', 'N' ], #
112             (our $RATIONAL = 5) => [ 'RATIONAL' , 8, 'R', 'N' ], #
113             (our $SBYTE = 6) => [ 'SBYTE' , 1, 'I', 'Y' ], #
114             (our $UNDEF = 7) => [ 'UNDEF' , 0, 'S', 'N' ], #
115             (our $SSHORT = 8) => [ 'SSHORT' , 2, 'I', 'Y' ], #
116             (our $SLONG = 9) => [ 'SLONG' , 4, 'I', 'Y' ], #
117             (our $SRATIONAL = 10) => [ 'SRATIONAL' , 8, 'R', 'Y' ], #
118             (our $FLOAT = 11) => [ 'FLOAT' , 4, 'F', 'N' ], #
119             (our $DOUBLE = 12) => [ 'DOUBLE' , 8, 'F', 'N' ], #
120             (our $REFERENCE = 13) => [ 'REFERENCE' , 0, 'p', 'N' ], }; #
121             #----------------------------------------------------------------------------#
122             our @JPEG_RECORD_TYPE_NAME = generate_array($RECORD_TYPE_GENERAL, 0); #
123             our @JPEG_RECORD_TYPE_LENGTH = generate_array($RECORD_TYPE_GENERAL, 1); #
124             our @JPEG_RECORD_TYPE_CATEGORY = generate_array($RECORD_TYPE_GENERAL, 2); #
125             our @JPEG_RECORD_TYPE_SIGN = generate_array($RECORD_TYPE_GENERAL, 3); #
126             #============================================================================#
127             #============================================================================#
128             #============================================================================#
129             # These tags are related to endianness. The endianness of the current #
130             # machine is detected every time with a simple procedure. #
131             #----------------------------------------------------------------------------#
132             my ($__short, $__byte1, $__byte2) = unpack "SCC", "\111\333" x 2; #
133             our $BIG_ENDIAN = 'MM'; #
134             our $LITTLE_ENDIAN = 'II'; #
135             our $NATIVE_ENDIANNESS = $__byte2 + ($__byte1<<8) == $__short ? $BIG_ENDIAN #
136             : $__byte1 + ($__byte2<<8) == $__short ? $LITTLE_ENDIAN : undef; #
137             #----------------------------------------------------------------------------#
138             # various interesting constants which are not tags (mostly record values); #
139             #----------------------------------------------------------------------------#
140             our $APP0_JFIF_TAG = "JFIF\000"; #
141             our $APP0_JFXX_TAG = "JFXX\000"; #
142             our $APP0_JFXX_JPG = 0x10; #
143             our $APP0_JFXX_1B = 0x11; #
144             our $APP0_JFXX_3B = 0x13; #
145             our $APP0_JFXX_PAL = 768; #
146             our $APP1_EXIF_TAG = "Exif\000\000"; #
147             our $APP1_XMP_TAG = "http://ns.adobe.com/xap/1.0/\000"; #
148             our $APP1_XMP_XPACKET_ID = 'W5M0MpCehiHzreSzNTczkc9d'; #
149             our $APP1_XMP_XPACKET_BEGIN = "\x{FEFF}"; #
150             our $APP1_XMP_META_NS = 'adobe:ns:meta/'; #
151             our $APP1_XMP_OUTER_RDF_NS ='http://www.w3.org/1999/02/22-rdf-syntax-ns#';
152             our $APP1_TIFF_SIG = 42; #
153             our $APP1_TH_TIFF = 1; #
154             our $APP1_TH_JPEG = 6; #
155             our $APP2_FPXR_TAG = "FPXR\000"; #
156             our $APP2_ICC_TAG = "ICC_PROFILE\000"; #
157             our $APP3_EXIF_TAG = "Meta\000\000"; #
158             our $APP13_PHOTOSHOP_IDS = ["Photoshop 3.0\000",'Adobe_Photoshop2.5:'];
159             our $APP13_PHOTOSHOP_TYPE = ['8BIM', '8BPS', 'PHUT']; #
160             our $APP13_PHOTOSHOP_IPTC = 0x0404; #
161             our $APP13_PHOTOSHOP_DIRNAME = 'Photoshop_RECORDS'; #
162             our $APP13_IPTC_TAGMARKER = 0x1c; #
163             our $APP13_IPTC_DIRNAME = 'IPTC_RECORD'; #
164             our $APP14_PHOTOSHOP_IDENTIFIER = 'Adobe'; #
165             #============================================================================#
166             #============================================================================#
167             #============================================================================#
168             # The following lines contain a list of general-purpose regular expressions, #
169             # which are used by the IFD, GPS ... and other sections. The only reason for #
170             # them being here is to avoid to do errors more than once ... #
171             #----------------------------------------------------------------------------#
172             my $re_integer = '\d+'; # a generic integer number #
173             my $re_signed = join('', '-?', $re_integer); # a generic signed integer num #
174             my $re_float = '[+-]?\d+(|.\d+)'; # a generic floating point #
175             my $re_Cstring = '.*\000'; # a null-terminated string #
176             my $re_yr18 = '(18|19|20)\d\d'; # YYYY (from 1800AD only ...) #
177             my $re_year = '\d{4}'; # YYYY (from 0AD on) #
178             my $re_month = '(0[1-9]|1[0-2])'; # MM (month in 1-12) #
179             my $re_day = '(0[1-9]|[12]\d|3[01])'; # DD (day in 1-31) #
180             my $re_hour = '([01]\d|2[0-3])'; # HH (hour in 0-23) #
181             my $re_minute = '[0-5]\d'; # MM (minute in 0-59) #
182             my $re_second = $re_minute; # SS (seconds like minutes) #
183             my $re_zone = join('', $re_hour, $re_minute); # HHMM #
184             my $re_dt18 = join('', $re_yr18, $re_month, $re_day); # YYYYMMDD #
185             my $re_date = join('', $re_year, $re_month, $re_day); # YYYYMMDD #
186             my $re_time = join('', $re_hour, $re_minute, $re_second); # HHMMSS #
187             my $re_dt18_cl = join(':', $re_yr18, $re_month, $re_day); # YYYY:MM:DD #
188             my $re_date_cl = join(':', $re_year, $re_month, $re_day); # YYYY:MM:DD #
189             my $re_time_cl = join(':', $re_hour, $re_minute, $re_second); # HH:MM:SS #
190             #============================================================================#
191             #============================================================================#
192             #============================================================================#
193             # Root level records for an Exif APP1 segment; we could avoid writing them #
194             # down here, but this makes syntax checks easier. Also, mandatory tags are #
195             # here just for reference, since I think they are already present, hence #
196             # never used. See the tables for IFD0 and IFD1 for further details. #
197             #--- Mandatory records for IFD0 and IFD1 (not calculated) -------------------#
198             my $HASH_APP1_ROOT_MANDATORY = {'Identifier' => $APP1_EXIF_TAG, #
199             'Endianness' => $BIG_ENDIAN, #
200             'Signature' => $APP1_TIFF_SIG, }; #
201             #--- Legal records' list ----------------------------------------------------#
202             my $HASH_APP1_ROOT_GENERAL = #
203             {'Identifier' => ['Idx-1', $ASCII, 6, $APP1_EXIF_TAG, 'B' ], #
204             'Endianness' => ['Idx-2', $UNDEF, 2, "($BIG_ENDIAN|$LITTLE_ENDIAN)" ], #
205             'Signature' => ['Idx-3', $SHORT, 1, $APP1_TIFF_SIG, 'B' ], #
206             'ThumbnailData' => ['Idx-4', $UNDEF, undef, '.*', 'T' ], }; #
207             #============================================================================#
208             #============================================================================#
209             #============================================================================#
210             # Most tags in the following three lists are the same for IFD0 and IFD1, #
211             # only the support level changes (some of them, indeed, must be present in #
212             # both directories). See the relevant sections in the Image::MetaData::JPEG #
213             # module perldoc page for further details on the %$HASH_APP1_IFD01_* hashes: #
214             # MAIN --> "Canonical Exif 2.2 and TIFF 6.0 tags for IFD0 and IFD1"; #
215             # ADDITIONAL --> "Additional TIFF 6.0 tags not in Exif 2.2 for IFD0"; #
216             # COMPANIES --> "Exif tags assigned to companies for IFD0 and IFD1". #
217             #----------------------------------------------------------------------------#
218             # The meaning of pseudo-regular-expressions is the following: #
219             # - 'calculated': these tags must not be set by the final user (they are #
220             # created, if necessary, by the module itself [this is more reliable]). #
221             # - 'obsoleted': this means that the corresponding tag is no more allowed. #
222             # Some tags do not have a fixed type (for instance, they can be $SHORT or #
223             # $LONG): in these cases, the most general type was chosen. Remember that #
224             # some tags in the main hash table are mandatory. #
225             #----------------------------------------------------------------------------#
226             # Hash keys are numeric tags, here written in hexadecimal base. #
227             # Fields: 0 -> name, 1 -> type, 2 -> count, 3 -> matching regular expression #
228             # 4 -> (optional) this tag can be set only together with the thumbnail #
229             #----------------------------------------------------------------------------#
230             my $IFD_integer = $re_integer; # a generic integer number #
231             my $IFD_signed = $re_signed; # a generic signed integer num #
232             my $IFD_float = $re_float; # a generic floating point #
233             my $IFD_Cstring = $re_Cstring; # a null-terminated string #
234             my $IFD_dt_full = $re_dt18_cl.' '.$re_time_cl; # YYYY:MM:DD HH:MM:SS #
235             my $IFD_datetime = '('.$IFD_dt_full.'| : : : : |\s{19})\000'; #
236             #--- Special screen rules for IFD0 and IFD1 ---------------------------------#
237             # a YCbCrSubSampling tag indicates the ratio of chrominance components. Its #
238             # value can be only [2,1] (for YCbCr 4:2:2) or [2,2] (for YCbCr 4:2:0). #
239             my $SSR_YCCsampl = sub { die unless $_[0] == 2 && $_[1] =~ /1|2/; }; #
240             #--- Mandatory records for IFD0 and IFD1 (not calculated) -------------------#
241             my $HASH_APP1_IFD01_MANDATORY = {'XResolution' => [72, 1], #
242             'YResolution' => [72, 1], #
243             'ResolutionUnit' => 2, }; #
244             my $HASH_APP1_IFD0_MANDATORY = {%$HASH_APP1_IFD01_MANDATORY, #
245             'YCbCrPositioning' => 1, }; #
246             my $HASH_APP1_IFD1_MANDATORY = {%$HASH_APP1_IFD01_MANDATORY, #
247             'YCbCrSubSampling' => [2, 1], #
248             'PhotometricInterpretation' => 2, #
249             'PlanarConfiguration' => 1, }; #
250             #--- Legal records' list ----------------------------------------------------#
251             my $HASH_APP1_IFD01_MAIN = #
252             {0x0100 => ['ImageWidth', $LONG, 1, $IFD_integer, 'T'], #
253             0x0101 => ['ImageLength', $LONG, 1, $IFD_integer, 'T'], #
254             0x0102 => ['BitsPerSample', $SHORT, 3, '8', 'T'], #
255             0x0103 => ['Compression', $SHORT, 1, '[16]', 'T'], #
256             0x0106 => ['PhotometricInterpretation', $SHORT, 1, '[26]', ], #
257             0x010e => ['ImageDescription', $ASCII, undef, $IFD_Cstring ], #
258             0x010f => ['Make', $ASCII, undef, $IFD_Cstring ], #
259             0x0110 => ['Model', $ASCII, undef, $IFD_Cstring ], #
260             0x0111 => ['StripOffsets', $LONG, undef, 'calculated' ], #
261             0x0112 => ['Orientation', $SHORT, 1, '[1-8]' ], #
262             0x0115 => ['SamplesPerPixel', $SHORT, 1, '3', 'T'], #
263             0x0116 => ['RowsPerStrip', $LONG, 1, $IFD_integer, 'T'], #
264             0x0117 => ['StripByteCounts', $LONG, undef, $IFD_integer, 'T'], #
265             0x011a => ['XResolution', $RATIONAL, 1, $IFD_integer ], #
266             0x011b => ['YResolution', $RATIONAL, 1, $IFD_integer ], #
267             0x011c => ['PlanarConfiguration', $SHORT, 1, '[12]' ], #
268             0x0128 => ['ResolutionUnit', $SHORT, 1, '[23]' ], #
269             0x012d => ['TransferFunction', $SHORT, 768, $IFD_integer ], #
270             0x0131 => ['Software', $ASCII, undef, $IFD_Cstring ], #
271             0x0132 => ['DateTime', $ASCII, 20, $IFD_datetime ], #
272             0x013b => ['Artist', $ASCII, undef, $IFD_Cstring ], #
273             0x013e => ['WhitePoint', $RATIONAL, 2, $IFD_integer ], #
274             0x013f => ['PrimaryChromaticities', $RATIONAL, 6, $IFD_integer ], #
275             0x0201 => ['JPEGInterchangeFormat', $LONG, 1, 'calculated' ], #
276             0x0202 => ['JPEGInterchangeFormatLength',$LONG, 1, $IFD_integer, 'T'], #
277             0x0211 => ['YCbCrCoefficients', $RATIONAL, 3, $IFD_integer ], #
278             0x0212 => ['YCbCrSubSampling', $SHORT, 2, $SSR_YCCsampl ], #
279             0x0213 => ['YCbCrPositioning', $SHORT, 1, '[12]' ], #
280             0x0214 => ['ReferenceBlackWhite', $RATIONAL, 6, $IFD_integer ], #
281             0x8298 => ['Copyright', $ASCII, undef, $IFD_Cstring ], #
282             0x8769 => ['ExifOffset', $LONG, 1, 'calculated' ], #
283             0x8825 => ['GPSInfo', $LONG, 1, 'calculated' ], }; #
284             #----------------------------------------------------------------------------#
285             my $HASH_APP1_IFD01_ADDITIONAL = #
286             {0x00fe => ['NewSubfileType', $LONG, 1, $IFD_integer ], #
287             0x00ff => ['SubFileType', $SHORT, 1, $IFD_integer ], #
288             0x0107 => ['Thresholding', $SHORT, 1, $IFD_integer ], #
289             0x0108 => ['CellWidth', $SHORT, 1, $IFD_integer ], #
290             0x0109 => ['CellLength', $SHORT, 1, $IFD_integer ], #
291             0x010a => ['FillOrder', $SHORT, 1, $IFD_integer ], #
292             0x010d => ['DocumentName', $ASCII, undef, $IFD_Cstring ], #
293             0x0118 => ['MinSampleValue', $SHORT, undef, $IFD_integer ], #
294             0x0119 => ['MaxSampleValue', $SHORT, undef, $IFD_integer ], #
295             0x011d => ['PageName', $ASCII, undef, $IFD_Cstring ], #
296             0x011e => ['XPosition', $RATIONAL, 1, $IFD_integer ], #
297             0x011f => ['YPosition', $RATIONAL, 1, $IFD_integer ], #
298             0x0120 => ['FreeOffsets', $LONG, undef, $IFD_integer ], #
299             0x0121 => ['FreeByteCounts', $LONG, undef, $IFD_integer ], #
300             0x0122 => ['GrayResponseUnit', $SHORT, 1, $IFD_integer ], #
301             0x0123 => ['GrayResponseCurve', $SHORT, undef, $IFD_integer ], #
302             0x0124 => ['T4Options', $LONG, 1, $IFD_integer ], #
303             0x0125 => ['T6Options', $LONG, 1, $IFD_integer ], #
304             0x0129 => ['PageNumber', $SHORT, 2, $IFD_integer ], #
305             0x012c => ['ColorResponseUnit', $SHORT, 1, 'invalid' ], #
306             0x013c => ['HostComputer', $ASCII, undef, $IFD_Cstring ], #
307             0x013d => ['Predictor', $SHORT, 1, $IFD_integer ], #
308             0x0140 => ['Colormap', $SHORT, undef, $IFD_integer ], #
309             0x0141 => ['HalftoneHints', $SHORT, 2, $IFD_integer ], #
310             0x0142 => ['TileWidth', $LONG, 1, $IFD_integer ], #
311             0x0143 => ['TileLength', $LONG, 1, $IFD_integer ], #
312             0x0144 => ['TileOffsets', $LONG, undef, $IFD_integer ], #
313             0x0145 => ['TileByteCounts', $LONG, undef, $IFD_integer ], #
314             0x0146 => ['BadFaxLines', $LONG, 1, $IFD_integer ], #
315             0x0147 => ['CleanFaxData', $SHORT, 1, $IFD_integer ], #
316             0x0148 => ['ConsecutiveBadFaxLines', $LONG, 1, $IFD_integer ], #
317             0x014a => ['SubIFD', $LONG, undef, $IFD_integer ], #
318             0x014c => ['InkSet', $SHORT, 1, $IFD_integer ], #
319             0x014d => ['InkNames', $ASCII, undef, $IFD_Cstring ], #
320             0x014e => ['NumberOfInks', $SHORT, 1, $IFD_integer ], #
321             0x0150 => ['DotRange', $SHORT, undef, $IFD_integer ], #
322             0x0151 => ['TargetPrinter', $ASCII, undef, $IFD_Cstring ], #
323             0x0152 => ['ExtraSamples', $SHORT, undef, $IFD_integer ], #
324             0x0153 => ['SampleFormats', $SHORT, undef, $IFD_integer ], #
325             0x0154 => ['SMinSampleValue', $UNDEF, undef, '.*' ], #
326             0x0155 => ['SMaxSampleValue', $UNDEF, undef, '.*' ], #
327             0x0156 => ['TransferRange', $SHORT, 6, $IFD_integer ], #
328             0x0157 => ['ClipPath', $BYTE, undef, $IFD_integer ], #
329             0x0158 => ['XClipPathUnits', $DOUBLE, 1, $IFD_float ], #
330             0x0159 => ['YClipPathUnits', $DOUBLE, 1, $IFD_float ], #
331             0x015a => ['Indexed', $SHORT, 1, $IFD_integer ], #
332             0x015b => ['JPEGTables', undef, undef, 'invalid' ], #
333             0x015f => ['OPIProxy', $SHORT, 1, $IFD_integer ], #
334             0x0200 => ['JPEGProc', $SHORT, 1, 'invalid' ], #
335             0x0203 => ['JPEGRestartInterval', $SHORT, 1, 'invalid' ], #
336             0x0205 => ['JPEGLosslessPredictors', $SHORT, undef, 'invalid' ], #
337             0x0206 => ['JPEGPointTransforms', $SHORT, undef, 'invalid' ], #
338             0x0207 => ['JPEGQTables', $LONG, undef, 'invalid' ], #
339             0x0208 => ['JPEGDCTables', $LONG, undef, 'invalid' ], #
340             0x0209 => ['JPEGACTables', $LONG, undef, 'invalid' ], #
341             0x02bc => ['XML_Packet', $BYTE, undef, $IFD_integer ], }; #
342             #----------------------------------------------------------------------------#
343             # The following company-related fields are marked as invalid because they #
344             # are present also in the SubIFD section (with different numerical values) #
345             # and I don't want the two entries to collide when setting IMAGE_DATA: #
346             # 'FlashEnergy', 'SpatialFrequencyResponse', FocalPlane[XY]Resolution', #
347             # 'FocalPlaneResolutionUnit', 'ExposureIndex', 'SensingMethod', 'CFAPattern' #
348             #----------------------------------------------------------------------------#
349             my $HASH_APP1_IFD01_COMPANIES = #
350             {0x800d => ['ImageID', $ASCII, undef, $IFD_Cstring ], #
351             0x80b9 => ['RefPts', undef, undef, 'invalid' ], #
352             0x80ba => ['RegionTackPoint', undef, undef, 'invalid' ], #
353             0x80bb => ['RegionWarpCorners', undef, undef, 'invalid' ], #
354             0x80bc => ['RegionAffine', undef, undef, 'invalid' ], #
355             0x80e3 => ['Matteing', $SHORT, 1, 'obsoleted' ], #
356             0x80e4 => ['DataType', $SHORT, 1, 'obsoleted' ], #
357             0x80e5 => ['ImageDepth', $LONG, 1, $IFD_integer ], #
358             0x80e6 => ['TileDepth', $LONG, 1, $IFD_integer ], #
359             0x8214 => ['ImageFullWidth', $LONG, 1, $IFD_integer ], #
360             0x8215 => ['ImageFullLength', $LONG, 1, $IFD_integer ], #
361             0x8216 => ['TextureFormat', $ASCII, undef, $IFD_Cstring ], #
362             0x8217 => ['WrapModes', $ASCII, undef, $IFD_Cstring ], #
363             0x8218 => ['FovCot', $FLOAT, 1, $IFD_float ], #
364             0x8219 => ['MatrixWorldToScreen', $FLOAT, 16, $IFD_float ], #
365             0x821a => ['MatrixWorldToCamera', $FLOAT, 16, $IFD_float ], #
366             0x827d => ['WriterSerialNumber', undef, undef, 'invalid' ], #
367             0x828d => ['CFARepeatPatternDim', $SHORT, 2, $IFD_integer ], #
368             0x828e => ['CFAPattern', $BYTE, undef, 'invalid' ], #
369             0x828f => ['BatteryLevel', $ASCII, undef, $IFD_Cstring ], #
370             0x830e => ['ModelPixelScaleTag', $DOUBLE, 3, $IFD_float ], #
371             0x83bb => ['IPTC/NAA', $ASCII, undef, $IFD_Cstring ], #
372             0x8480 => ['IntergraphMatrixTag', $DOUBLE, 16, 'obsoleted' ], #
373             0x8482 => ['ModelTiepointTag', $DOUBLE,undef, $IFD_float ], #
374             0x84e0 => ['Site', $ASCII, undef, $IFD_Cstring ], #
375             0x84e1 => ['ColorSequence', $ASCII, undef, $IFD_Cstring ], #
376             0x84e2 => ['IT8Header', $ASCII, undef, $IFD_Cstring ], #
377             0x84e3 => ['RasterPadding', $SHORT, 1, $IFD_integer ], #
378             0x84e4 => ['BitsPerRunLength', $SHORT, 1, $IFD_integer ], #
379             0x84e5 => ['BitsPerExtendedRunLength', $SHORT, 1, $IFD_integer ], #
380             0x84e6 => ['ColorTable', $BYTE, undef, $IFD_integer ], #
381             0x84e7 => ['ImageColorIndicator', $BYTE, 1, $IFD_integer ], #
382             0x84e8 => ['BackgroundColorIndicator', $BYTE, 1, $IFD_integer ], #
383             0x84e9 => ['ImageColorValue', $BYTE, 1, $IFD_integer ], #
384             0x84ea => ['BackgroundColorValue', $BYTE, 1, $IFD_integer ], #
385             0x84eb => ['PixelIntensityRange', $BYTE, 2, $IFD_integer ], #
386             0x84ec => ['TransparencyIndicator', $BYTE, 1, $IFD_integer ], #
387             0x84ed => ['ColorCharacterization', $ASCII, undef, $IFD_Cstring ], #
388             0x84ee => ['HCUsage', $LONG, 1, $IFD_integer ], #
389             0x84ef => ['TrapIndicator', $BYTE, 1, $IFD_integer ], #
390             0x84f0 => ['CMYKEquivalent', $SHORT, undef, $IFD_integer ], #
391             0x84f1 => ['Reserved_TIFF_IT_1', undef, undef, 'invalid' ], #
392             0x84f2 => ['Reserved_TIFF_IT_2', undef, undef, 'invalid' ], #
393             0x84f3 => ['Reserved_TIFF_IT_3', undef, undef, 'invalid' ], #
394             0x85b8 => ['FrameCount', $LONG, 1, $IFD_integer ], #
395             0x85d8 => ['ModelTransformationTag', $DOUBLE, 16, $IFD_float ], #
396             0x8649 => ['PhotoshopImageResources', $BYTE, undef, $IFD_integer ], #
397             0x8773 => ['ICCProfile', undef, undef, 'invalid' ], #
398             0x87af => ['GeoKeyDirectoryTag', $SHORT, undef, $IFD_integer ], #
399             0x87b0 => ['GeoDoubleParamsTag', $DOUBLE,undef, $IFD_float ], #
400             0x87b1 => ['GeoAsciiParamsTag', $ASCII, undef, $IFD_Cstring ], #
401             0x87be => ['JBIG_Options', undef, undef, 'invalid' ], #
402             0x8829 => ['Interlace', $SHORT, 1, $IFD_integer ], #
403             0x882a => ['TimeZoneOffset', $SSHORT,undef, $IFD_signed ], #
404             0x882b => ['SelfTimerMode', $SHORT, 1, $IFD_integer ], #
405             0x885c => ['FaxRecvParams', $LONG, 1, $IFD_integer ], #
406             0x885d => ['FaxSubAddress', $ASCII, undef, $IFD_Cstring ], #
407             0x885e => ['FaxRecvTime', $LONG, 1, $IFD_integer ], #
408             0x8871 => ['FedExEDR', undef, undef, 'invalid' ], #
409             0x920b => ['FlashEnergy', $RATIONAL,undef,'invalid' ], #
410             0x920c => ['SpatialFrequencyResponse', undef, undef, 'invalid' ], #
411             0x920d => ['Noise', undef, undef, 'invalid' ], #
412             0x920e => ['FocalPlaneXResolution', $RATIONAL, 1, 'invalid' ], #
413             0x920f => ['FocalPlaneYResolution', $RATIONAL, 1, 'invalid' ], #
414             0x9210 => ['FocalPlaneResolutionUnit', $SHORT, 1, 'invalid' ], #
415             0x9211 => ['ImageNumber', $LONG, 1, $IFD_integer ], #
416             0x9212 => ['SecurityClassification', $ASCII, undef, $IFD_Cstring ], #
417             0x9213 => ['ImageHistory', $ASCII, undef, $IFD_Cstring ], #
418             0x9215 => ['ExposureIndex', $RATIONAL,undef,'invalid' ], #
419             0x9216 => ['TIFF/EPStandardID', $BYTE, 4, $IFD_integer ], #
420             0x9217 => ['SensingMethod', $SHORT, 1, 'invalid' ], #
421             0x923f => ['StoNits', $DOUBLE, 1, $IFD_float ], #
422             0x935c => ['ImageSourceData', undef, undef, 'invalid' ], #
423             0xc4a5 => ['PrintIM_Data', undef, undef, 'invalid' ], #
424             0xc44f => ['PhotoshopAnnotations', undef, undef, 'invalid' ], #
425             0xffff => ['DCSHueShiftValues', undef, undef, 'invalid' ], }; #
426             #----------------------------------------------------------------------------#
427             my $HASH_APP1_IFD01_GENERAL = {}; #
428             @$HASH_APP1_IFD01_GENERAL{keys %$_} = #
429             values %$_ for ($HASH_APP1_IFD01_MAIN, #
430             $HASH_APP1_IFD01_ADDITIONAL, #
431             $HASH_APP1_IFD01_COMPANIES); #
432             #============================================================================#
433             #============================================================================#
434             #============================================================================#
435             # See the "Exif tags for the 0th IFD Exif private subdirectory" section in #
436             # the Image::MetaData::JPEG module perldoc page for further details (private #
437             # EXIF region in IFD0, also known as SubIFD). #
438             #----------------------------------------------------------------------------#
439             # Hash keys are numeric tags, here written in hexadecimal base. #
440             # Fields: 0 -> name, 1 -> type, 2 -> count, 3 -> matching regular #
441             # Mandatory records: ExifVersion, ComponentsConfiguration, FlashpixVersion, #
442             # ColorSpace, PixelXDimension and PixelYDimension. #
443             #----------------------------------------------------------------------------#
444             my $IFD_subsecs = '\d*\s*\000'; # a fraction of a second #
445             my $IFD_Ustring = '(ASCII\000{3}|JIS\000{5}|Unicode\000|\000{8}).*'; #
446             my $IFD_DOSfile = '\w{8}\.\w{3}\000'; # a DOS filename (8+3) #
447             my $IFD_lightsrc = '([0-49]|1[0-57-9]|2[0-4]|255)'; # possible light sources #
448             my $IFD_flash = '([01579]|1[356]|2[459]|3[12]|6[59]|7[1379]|89|9[35])'; #
449             my $IFD_hexstr = '[0-9a-fA-F]+\000+'; # hexadecimal ASCII str #
450             my $IFD_Exifver = '0(100|110|200|210|220|221)'; # known Exif versions #
451             my $IFD_setdesc = '.{4}(\376\377(.{2})*\000\000)*'; # for DeviceSettingDesc.#
452             my $IFD_compconf = '(\004\005\006|\001\002\003)\000';# for ComponentsConfig. #
453             #--- Special screen rules ---------------------------------------------------#
454             # a SubjectArea tag indicates the location and area of the main subject. The #
455             # tag can contain 2, 3 or 4 integer numbers (see Exif 2.2 for their meaning) #
456             my $SSR_subjectarea = sub { die if scalar @_ < 2 || scalar @_ > 4; #
457             die if grep { ! /^\d+$/ } @_; }; #
458             # a CFAPattern tag indicates a color filter array. The first four bytes are #
459             # two shorts giving the horizontal (m) and vertical (n) repeat pixel units. #
460             # Then, m x n bytes follow, with the actual filter values (in the range 0-6).#
461             my $SSR_cfapattern = sub { my ($x, $y) = unpack 'nn', $_[0]; #
462             die if length $_[0] != 4+$x*$y; #
463             die if $_[0] !~ /^.{4}[0-6]*$/; }; #
464             #--- Mandatory records ------------------------------------------------------#
465             my $HASH_APP1_SUBIFD_MANDATORY = {'ExifVersion' => '0220', #
466             'ComponentsConfiguration' => "\001\002\003\000", #
467             'FlashpixVersion' => '0100', #
468             'ColorSpace' => 1, #
469             'PixelXDimension' => 0, # global info #
470             'PixelYDimension' => 0 }; # needed here! #
471             #--- Legal records' list ----------------------------------------------------#
472             my $HASH_APP1_SUBIFD_GENERAL = #
473             {0x829a => ['ExposureTime', $RATIONAL, 1, $IFD_integer ], #
474             0x829d => ['FNumber', $RATIONAL, 1, $IFD_integer ], #
475             0x8822 => ['ExposureProgram', $SHORT, 1, '[0-8]' ], #
476             0x8824 => ['SpectralSensitivity', $ASCII, undef, $IFD_Cstring ], #
477             0x8827 => ['ISOSpeedRatings', $SHORT, undef, $IFD_integer ], #
478             0x8828 => ['OECF', $UNDEF, undef, '.*' ], #
479             0x9000 => ['ExifVersion', $UNDEF, 4, $IFD_Exifver ], #
480             0x9003 => ['DateTimeOriginal', $ASCII, 20, $IFD_datetime ], #
481             0x9004 => ['DateTimeDigitized', $ASCII, 20, $IFD_datetime ], #
482             0x9101 => ['ComponentsConfiguration', $UNDEF, 4, $IFD_compconf ], #
483             0x9102 => ['CompressedBitsPerPixel', $RATIONAL, 1, $IFD_integer ], #
484             0x9201 => ['ShutterSpeedValue', $SRATIONAL, 1, $IFD_signed ], #
485             0x9202 => ['ApertureValue', $RATIONAL, 1, $IFD_integer ], #
486             0x9203 => ['BrightnessValue', $SRATIONAL, 1, $IFD_signed ], #
487             0x9204 => ['ExposureBiasValue', $SRATIONAL, 1, $IFD_signed ], #
488             0x9205 => ['MaxApertureValue', $RATIONAL, 1, $IFD_integer ], #
489             0x9206 => ['SubjectDistance', $RATIONAL, 1, $IFD_integer ], #
490             0x9207 => ['MeteringMode', $SHORT, 1, '([0-6]|255)' ], #
491             0x9208 => ['LightSource', $SHORT, 1, $IFD_lightsrc ], #
492             0x9209 => ['Flash', $SHORT, 1, $IFD_flash ], #
493             0x920a => ['FocalLength', $RATIONAL, 1, $IFD_integer ], #
494             0x9214 => ['SubjectArea', $SHORT, undef, $SSR_subjectarea], #
495             0x927c => ['MakerNote', $UNDEF, undef, 'invalid' ], #
496             0x9286 => ['UserComment', $UNDEF, undef, $IFD_Ustring ], #
497             0x9290 => ['SubSecTime', $ASCII, undef, $IFD_subsecs ], #
498             0x9291 => ['SubSecTimeOriginal', $ASCII, undef, $IFD_subsecs ], #
499             0x9292 => ['SubSecTimeDigitized', $ASCII, undef, $IFD_subsecs ], #
500             0xa000 => ['FlashpixVersion', $UNDEF, 4, '0100' ], #
501             0xa001 => ['ColorSpace', $SHORT, 1, '(1|65535)' ], #
502             0xa002 => ['PixelXDimension', $LONG, 1, $IFD_integer ], #
503             0xa003 => ['PixelYDimension', $LONG, 1, $IFD_integer ], #
504             0xa004 => ['RelatedSoundFile', $ASCII, 13, $IFD_DOSfile ], #
505             0xa005 => ['InteroperabilityOffset', $LONG, 1, 'calculated' ], #
506             0xa20b => ['FlashEnergy', $RATIONAL, 1, $IFD_integer ], #
507             0xa20c => ['SpatialFrequencyResponse', $UNDEF, undef, '.*' ], #
508             0xa20e => ['FocalPlaneXResolution', $RATIONAL, 1, $IFD_integer ], #
509             0xa20f => ['FocalPlaneYResolution', $RATIONAL, 1, $IFD_integer ], #
510             0xa210 => ['FocalPlaneResolutionUnit', $SHORT, 1, '[23]' ], #
511             0xa214 => ['SubjectLocation', $SHORT, 2, $IFD_integer ], #
512             0xa215 => ['ExposureIndex', $RATIONAL, 1, $IFD_integer ], #
513             0xa217 => ['SensingMethod', $SHORT, 1, '[1-578]' ], #
514             0xa300 => ['FileSource', $UNDEF, 1, '\003' ], #
515             0xa301 => ['SceneType', $UNDEF, 1, '\001' ], #
516             0xa302 => ['CFAPattern', $UNDEF, undef, $SSR_cfapattern ], #
517             0xa401 => ['CustomRendered', $SHORT, 1, '[01]' ], #
518             0xa402 => ['ExposureMode', $SHORT, 1, '[012]' ], #
519             0xa403 => ['WhiteBalance', $SHORT, 1, '[01]' ], #
520             0xa404 => ['DigitalZoomRatio', $RATIONAL, 1, $IFD_integer ], #
521             0xa405 => ['FocalLengthIn35mmFilm', $SHORT, 1, $IFD_integer ], #
522             0xa406 => ['SceneCaptureType', $SHORT, 1, '[0-3]' ], #
523             0xa407 => ['GainControl', $SHORT, 1, '[0-4]' ], #
524             0xa408 => ['Contrast', $SHORT, 1, '[0-2]' ], #
525             0xa409 => ['Saturation', $SHORT, 1, '[0-2]' ], #
526             0xa40a => ['Sharpness', $SHORT, 1, '[0-2]' ], #
527             0xa40b => ['DeviceSettingDescription', $UNDEF, undef, $IFD_setdesc ], #
528             0xa40c => ['SubjectDistanceRange', $SHORT, 1, '[0-3]' ], #
529             0xa420 => ['ImageUniqueID', $ASCII, 33, $IFD_hexstr ], #
530             # --- From Photoshop >= 7.0 treatment of raw camera files (undocumented) --- #
531             0xfde8 => ['_OwnerName', $ASCII, undef, "Owner'".'s Name: .*\000' ], #
532             0xfde9 => ['_SerialNumber', $ASCII, undef, 'Serial Number: .*\000' ], #
533             0xfdea => ['_Lens', $ASCII, undef, 'Lens: .*\000' ], #
534             0xfe4c => ['_RawFile', $ASCII, undef, 'Raw File: .*\000' ], #
535             0xfe4d => ['_Converter', $ASCII, undef, 'Converter: .*\000' ], #
536             0xfe4e => ['_WhiteBalance', $ASCII, undef, 'White Balance: .*\000' ], #
537             0xfe51 => ['_Exposure', $ASCII, undef, 'Exposure: .*\000' ], #
538             0xfe52 => ['_Shadows', $ASCII, undef, 'Shadows: .*\000' ], #
539             0xfe53 => ['_Brightness', $ASCII, undef, 'Brightness: .*\000' ], #
540             0xfe54 => ['_Contrast', $ASCII, undef, 'Contrast: .*\000' ], #
541             0xfe55 => ['_Saturation', $ASCII, undef, 'Saturation: .*\000' ], #
542             0xfe56 => ['_Sharpness', $ASCII, undef, 'Sharpness: .*\000' ], #
543             0xfe57 => ['_Smoothness', $ASCII, undef, 'Smoothness: .*\000' ], #
544             0xfe58 => ['_MoireFilter', $ASCII, undef, 'Moire Filter: .*\000' ], }; #
545             #============================================================================#
546             #============================================================================#
547             #============================================================================#
548             # See the "EXIF tags for the 0th IFD Interoperability subdirectory" section #
549             # in the Image::MetaData::JPEG module perldoc page for further details. #
550             # Mandatory records: InteroperabilityIndex and InteroperabilityVersion #
551             #----------------------------------------------------------------------------#
552             # Hash keys are numeric tags, here written in hexadecimal base. #
553             # Fields: 0 -> name, 1 -> type, 2 -> count, 3 -> matching regular #
554             #--- Mandatory records ------------------------------------------------------#
555             my $HASH_INTEROP_MANDATORY = {'InteroperabilityVersion' => '0100', #
556             'InteroperabilityIndex' => 'R98' }; #
557             #--- Legal records' list ----------------------------------------------------#
558             my $HASH_INTEROP_GENERAL = #
559             {0x0001 => ['InteroperabilityIndex', $ASCII, 4, 'R98\000' ], #
560             0x0002 => ['InteroperabilityVersion', $UNDEF, 4, '[0-9]{4}' ], #
561             0x1000 => ['RelatedImageFileFormat', $ASCII, undef, $IFD_Cstring ], #
562             0x1001 => ['RelatedImageWidth', $LONG, 1, '[0-9]*' ], #
563             0x1002 => ['RelatedImageLength', $LONG, 1, '[0-9]*' ], }; #
564             #============================================================================#
565             #============================================================================#
566             #============================================================================#
567             # See the "EXIF tags for the 0th IFD GPS subdirectory" section in the #
568             # Image::MetaData::JPEG module perldoc page for further details on GPS data. #
569             # Mandatory records: only GPSVersionID #
570             #----------------------------------------------------------------------------#
571             # Hash keys are numeric tags, here written in hexadecimal base. #
572             # Fields: 0 -> name, 1 -> type, 2 -> count, 3 -> matching regular #
573             #----------------------------------------------------------------------------#
574             my $GPS_re_Cstring = $re_Cstring; # a null terminated string #
575             my $GPS_re_date = $re_dt18_cl . '\000'; # YYYY:MM:DD null terminated #
576             my $GPS_re_number = $re_integer; # a generic integer number #
577             my $GPS_re_NS = '[NS]\000'; # latitude reference #
578             my $GPS_re_EW = '[EW]\000'; # longitude reference #
579             my $GPS_re_spdsref = '[KMN]\000'; # speed or distance reference #
580             my $GPS_re_direref = '[TM]\000'; # directin reference #
581             my $GPS_re_string = '[AJU\000].*'; # GPS "undefined" strings #
582             #--- Special screen rules ---------------------------------------------------#
583             # a direction is a rational number in [0.00, 359.99] (we should also test #
584             # explicitely that the numerator and the denominator are not negative). #
585             my $SSR_direction = sub { die if grep { $_ < 0 } @_; #
586             my $dire = $_[0]/$_[1]; die if $dire >= 360; #
587             die unless $dire =~ /^\d+(\.\d{1,2})?$/; }; #
588             # a "triplet" corresponds to three rationals for units, minutes (< 60) and #
589             # seconds (< 60). The 1st argument must be a limit on units (helper rule). #
590             my $SSR_triplet = sub { my $limit = shift; die if grep { $_ < 0 } @_; #
591             my ($dd,$mm,$ss) = map {$_[$_]/$_[1+$_]} (0,2,4); #
592             die unless $mm < 60 && $ss < 60 && $dd <= $limit; #
593             die unless ($dd + $mm /60 + $ss/360) <= $limit;}; #
594             # a latitude or a longitude is stored as a sequence of three rationals nums #
595             # (degrees, minutes and seconds) with degrees<=90 or 180 (see $SSR_triplet). #
596             my $SSR_latitude = sub { &$SSR_triplet( 90, @_); }; #
597             my $SSR_longitude = sub { &$SSR_triplet(180, @_); }; #
598             # a time stamp is stored as three rationals (hours, minutes and seconds); in #
599             # this case hours must be <= 24 (see $SSR_triplet for further details). #
600             my $SSR_stupidtime = sub { &$SSR_triplet(24, @_); }; #
601             #--- Mandatory records ------------------------------------------------------#
602             my $HASH_GPS_MANDATORY = {'GPSVersionID' => [2,2,0,0]}; #
603             #--- Legal records' list ----------------------------------------------------#
604             my $HASH_GPS_GENERAL = #
605             {0x00 => ['GPSVersionID', $BYTE, 4, '.' ], #
606             0x01 => ['GPSLatitudeRef', $ASCII, 2, $GPS_re_NS ], #
607             0x02 => ['GPSLatitude', $RATIONAL, 3, $SSR_latitude ], #
608             0x03 => ['GPSLongitudeRef', $ASCII, 2, $GPS_re_EW ], #
609             0x04 => ['GPSLongitude', $RATIONAL, 3, $SSR_longitude ], #
610             0x05 => ['GPSAltitudeRef', $BYTE, 1, '[01]' ], #
611             0x06 => ['GPSAltitude', $RATIONAL, 1, $GPS_re_number ], #
612             0x07 => ['GPSTimeStamp', $RATIONAL, 3, $SSR_stupidtime ], #
613             0x08 => ['GPSSatellites', $ASCII, undef, $GPS_re_Cstring ], #
614             0x09 => ['GPSStatus', $ASCII, 2, '[AV]\000' ], #
615             0x0a => ['GPSMeasureMode', $ASCII, 2, '[23]\000' ], #
616             0x0b => ['GPSDOP', $RATIONAL, 1, $GPS_re_number ], #
617             0x0c => ['GPSSpeedRef', $ASCII, 2, $GPS_re_spdsref ], #
618             0x0d => ['GPSSpeed', $RATIONAL, 1, $GPS_re_number ], #
619             0x0e => ['GPSTrackRef', $ASCII, 2, $GPS_re_direref ], #
620             0x0f => ['GPSTrack', $RATIONAL, 1, $SSR_direction ], #
621             0x10 => ['GPSImgDirectionRef', $ASCII, 2, $GPS_re_direref ], #
622             0x11 => ['GPSImgDirection', $RATIONAL, 1, $SSR_direction ], #
623             0x12 => ['GPSMapDatum', $ASCII, undef, $GPS_re_Cstring ], #
624             0x13 => ['GPSDestLatitudeRef', $ASCII, 2, $GPS_re_NS ], #
625             0x14 => ['GPSDestLatitude', $RATIONAL, 3, $SSR_latitude ], #
626             0x15 => ['GPSDestLongitudeRef', $ASCII, 2, $GPS_re_EW ], #
627             0x16 => ['GPSDestLongitude', $RATIONAL, 3, $SSR_longitude ], #
628             0x17 => ['GPSDestBearingRef', $ASCII, 2, $GPS_re_direref ], #
629             0x18 => ['GPSDestBearing', $RATIONAL, 1, $SSR_direction ], #
630             0x19 => ['GPSDestDistanceRef', $ASCII, 2, $GPS_re_spdsref ], #
631             0x1a => ['GPSDestDistance', $RATIONAL, 1, $GPS_re_number ], #
632             0x1b => ['GPSProcessingMethod', $UNDEF, undef, $GPS_re_string ], #
633             0x1c => ['GPSAreaInformation', $UNDEF, undef, $GPS_re_string ], #
634             0x1d => ['GPSDateStamp', $ASCII, 11, $GPS_re_date ], #
635             0x1e => ['GPSDifferential', $SHORT, 1, '[01]' ],}; #
636             #============================================================================#
637              
638             # Tags used for ICC data in APP2 (they are 4 bytes strings, so
639             # I prefer to write the string and then convert it).
640 592     592 0 522 sub str2hex { my $z = 0; ($z *= 256) += $_ for unpack "CCCC", $_[0]; $z; }
  592         1379  
  592         11404  
641             my $HASH_APP2_ICC =
642             {str2hex('A2B0') => 'AT0B0Tag',
643             str2hex('A2B1') => 'AToB1Tag',
644             str2hex('A2B2') => 'AToB2Tag',
645             str2hex('bXYZ') => 'BlueMatrixColumn',
646             str2hex('bTRC') => 'BlueTRC',
647             str2hex('B2A0') => 'BToA0Tag',
648             str2hex('B2A1') => 'BToA1Tag',
649             str2hex('B2A2') => 'BToA2Tag',
650             str2hex('calt') => 'CalibrationDateTime',
651             str2hex('targ') => 'CharTarget',
652             str2hex('chad') => 'ChromaticAdaptation',
653             str2hex('chrm') => 'Chromaticity',
654             str2hex('clro') => 'ColorantOrder',
655             str2hex('clrt') => 'ColorantTable',
656             str2hex('cprt') => 'Copyright',
657             str2hex('dmnd') => 'DeviceMfgDesc',
658             str2hex('dmdd') => 'DeviceModelDesc',
659             str2hex('gamt') => 'Gamut',
660             str2hex('kTRC') => 'GrayTRC',
661             str2hex('gXYZ') => 'GreenMatrixColumn',
662             str2hex('gTRC') => 'GreenTRC',
663             str2hex('lumi') => 'Luminance',
664             str2hex('meas') => 'Measurement',
665             str2hex('bkpt') => 'MediaBlackPoint',
666             str2hex('wtpt') => 'MediaWhitePoint',
667             str2hex('ncl2') => 'NamedColor2',
668             str2hex('resp') => 'OutputResponse',
669             str2hex('pre0') => 'Preview0',
670             str2hex('pre1') => 'Preview1',
671             str2hex('pre2') => 'Preview2',
672             str2hex('desc') => 'ProfileDescription',
673             str2hex('pseq') => 'ProfileSequenceDesc',
674             str2hex('rXYZ') => 'RedMatrixColumn',
675             str2hex('rTRC') => 'RedTRC',
676             str2hex('tech') => 'Technology',
677             str2hex('vued') => 'ViewingCondDesc',
678             str2hex('view') => 'ViewingConditions', };
679              
680             # Tags used by the 0-th IFD of an APP3 segment (reference ... ?)
681             my $HASH_APP3_IFD =
682             {0xc350 => 'FilmProductCode',
683             0xc351 => 'ImageSource',
684             0xc352 => 'PrintArea',
685             0xc353 => 'CameraOwner',
686             0xc354 => 'CameraSerialNumber',
687             0xc355 => 'GroupCaption',
688             0xc356 => 'DealerID',
689             0xc357 => 'OrderID',
690             0xc358 => 'BagNumber',
691             0xc359 => 'ScanFrameSeqNumber',
692             0xc35a => 'FilmCategory',
693             0xc35b => 'FilmGenCode',
694             0xc35c => 'ScanSoftware',
695             0xc35d => 'FilmSize',
696             0xc35e => 'SBARGBShifts',
697             0xc35f => 'SBAInputColor',
698             0xc360 => 'SBAInputBitDepth',
699             0xc361 => 'SBAExposureRec',
700             0xc362 => 'UserSBARGBShifts',
701             0xc363 => 'ImageRotationStatus',
702             0xc364 => 'RollGUID',
703             0xc365 => 'APP3Version',
704             0xc36e => 'SpecialEffectsIFD', # pointer to an IFD
705             0xc36f => 'BordersIFD', }; # pointer to an IFD
706              
707             my $HASH_APP3_SPECIAL =
708             {0x0000 => 'APP3_SpecialIFD_tag_0',
709             0x0001 => 'APP3_SpecialIFD_tag_1',
710             0x0002 => 'APP3_SpecialIFD_tag_2', };
711              
712             my $HASH_APP3_BORDERS =
713             {0x0000 => 'APP3_BordersIFD_tag_0',
714             0x0001 => 'APP3_BordersIFD_tag_1',
715             0x0002 => 'APP3_BordersIFD_tag_2',
716             0x0003 => 'APP3_BordersIFD_tag_3',
717             0x0004 => 'APP3_BordersIFD_tag_4',
718             0x0008 => 'APP3_BordersIFD_tag_8', };
719              
720             #============================================================================#
721             #============================================================================#
722             #============================================================================#
723             # See the "VALID TAGS FOR IPTC DATA" section in the Image::MetaData::JPEG #
724             # module perldoc page for further details on IPTC data. Also 1:xx datasets #
725             # are documented here, although only 2:xx datasets are likely to be found. #
726             # Note: I don't know why the standard says 4 for 'RecordVersion'; it turns #
727             # out that you always find 2 in JPEG files. #
728             #----------------------------------------------------------------------------#
729             # Hash keys are numeric tags in decimal (the IPTC standard uses base 10...). #
730             # Fields: 0 -> Tag name, 1 -> repeatability ('N' means non-repeatable), #
731             # 2,3 -> min and max length, 4 -> regular expression to match. #
732             # The regular expression for "words" is what they call graphic characters. #
733             #----------------------------------------------------------------------------#
734             my $IPTC_re_word = '^[^\000-\040\177]*$'; # words #
735             my $IPTC_re_line = '^[^\000-\037\177]*$'; # words + spaces #
736             my $IPTC_re_para = '^[^\000-\011\013\014\016-\037\177]*$'; # line + CR + LF #
737             my $IPTC_re_dt18 = $re_dt18; # CCYYMMDD #
738             my $IPTC_re_date = $re_date; # CCYYMMDD full #
739             my $IPTC_re_dura = $re_time; # HHMMSS #
740             my $IPTC_re_time = $IPTC_re_dura . '[\+-]' . $re_zone; # HHMMSS+/-HHMM #
741             my $vchr = '\040-\051\053-\071\073-\076\100-\176'; # (SubjectRef.) #
742             my $IPTC_re_sure='['.$vchr.']{1,32}?:[01]\d{7}(:['.$vchr.'\s]{0,64}?){3}'; #
743             #--- Mandatory records ------------------------------------------------------#
744             my $HASH_IPTC_MANDATORY_1 = {'ModelVersion' => "\000\004" }; #
745             my $HASH_IPTC_MANDATORY_2 = {'RecordVersion' => "\000\002" }; #
746             #--- Legal records' list ( datasets 1:xx ) ----------------------------------#
747             my $HASH_IPTC_GENERAL_1 = #
748             {0 => ['ModelVersion', 'N', 2, 2, 'binary' ], #
749             5 => ['Destination', ' ',1,1024, $IPTC_re_word ], #
750             20 => ['FileFormat', 'N', 2, 2, 'invalid,binary' ], #
751             22 => ['FileFormatVersion', 'N', 2, 2, 'invalid,binary' ], #
752             30 => ['ServiceIdentifier', 'N', 0, 10, $IPTC_re_word ], #
753             40 => ['EnvelopeNumber', 'N', 8, 8, 'invalid,\d{8}' ], #
754             50 => ['ProductID', ' ', 0, 32, $IPTC_re_word ], #
755             60 => ['EnvelopePriority', 'N', 1, 1, 'invalid,[1-9]' ], #
756             70 => ['DataSent', 'N', 8, 8, 'invalid,date' ], #
757             80 => ['TimeSent', 'N',11, 11, 'invalid,time' ], #
758             90 => ['CodedCharacterSet', 'N', 0, 32, '\033.{1,3}' ], #
759             100 => ['UNO', 'N',14, 80, 'invalid' ], #
760             120 => ['ARMIdentifier', 'N', 2, 2, 'invalid,binary' ], #
761             122 => ['ARMVersion', 'N', 2, 2, 'invalid,binary' ],}; #
762             #--- Legal records' list ( datasets 2:xx ) ----------------------------------#
763             my $HASH_IPTC_GENERAL_2 = #
764             {0 => ['RecordVersion', 'N', 2, 2, 'binary' ], #
765             3 => ['ObjectTypeReference', 'N', 3, 67, '\d{2}:[\w\s]{0,64}?' ], #
766             4 => ['ObjectAttributeReference', ' ', 4, 68, '\d{3}:[\w\s]{0,64}?' ], #
767             5 => ['ObjectName', 'N', 1, 64, $IPTC_re_line ], #
768             7 => ['EditStatus', 'N', 1, 64, $IPTC_re_line ], #
769             8 => ['EditorialUpdate', 'N', 2, 2, '01' ], #
770             10 => ['Urgency', 'N', 1, 1, '[1-8]' ], #
771             12 => ['SubjectReference', ' ',13,236, $IPTC_re_sure ], #
772             15 => ['Category', 'N', 1, 3, '[a-zA-Z]{1,3}?' ], #
773             20 => ['SupplementalCategory', ' ', 1, 32, $IPTC_re_line ], #
774             22 => ['FixtureIdentifier', 'N', 1, 32, $IPTC_re_word ], #
775             25 => ['Keywords', ' ', 1, 64, $IPTC_re_line ], #
776             26 => ['ContentLocationCode', ' ', 3, 3, '[A-Z]{3}' ], #
777             27 => ['ContentLocationName', ' ', 1, 64, $IPTC_re_line ], #
778             30 => ['ReleaseDate', 'N', 8, 8, $IPTC_re_dt18 ], #
779             35 => ['ReleaseTime', 'N',11, 11, $IPTC_re_time ], #
780             37 => ['ExpirationDate', 'N', 8, 8, $IPTC_re_dt18 ], #
781             38 => ['ExpirationTime', 'N',11, 11, $IPTC_re_time ], #
782             40 => ['SpecialInstructions', 'N', 1,256, $IPTC_re_line ], #
783             42 => ['ActionAdvised', 'N', 2, 2, '0[1-4]' ], #
784             45 => ['ReferenceService', ' ',10, 10, 'invalid' ], #
785             47 => ['ReferenceDate', ' ', 8, 8, 'invalid' ], #
786             50 => ['ReferenceNumber', ' ', 8, 8, 'invalid' ], #
787             55 => ['DateCreated', 'N', 8, 8, $IPTC_re_date ], #
788             60 => ['TimeCreated', 'N',11, 11, $IPTC_re_time ], #
789             62 => ['DigitalCreationDate', 'N', 8, 8, $IPTC_re_dt18 ], #
790             63 => ['DigitalCreationTime', 'N',11, 11, $IPTC_re_time ], #
791             65 => ['OriginatingProgram', 'N', 1, 32, $IPTC_re_line ], #
792             70 => ['ProgramVersion', 'N', 1, 10, $IPTC_re_line ], #
793             75 => ['ObjectCycle', 'N', 1, 1, '[apb]' ], #
794             80 => ['ByLine', ' ', 1, 32, $IPTC_re_line ], #
795             85 => ['ByLineTitle', ' ', 1, 32, $IPTC_re_line ], #
796             90 => ['City', 'N', 1, 32, $IPTC_re_line ], #
797             92 => ['SubLocation', 'N', 1, 32, $IPTC_re_line ], #
798             95 => ['Province/State', 'N', 1, 32, $IPTC_re_line ], #
799             100 => ['Country/PrimaryLocationCode', 'N', 3, 3, '[A-Z]{3}' ], #
800             101 => ['Country/PrimaryLocationName', 'N', 1, 64, $IPTC_re_line ], #
801             103 => ['OriginalTransmissionReference','N',1, 32, $IPTC_re_line ], #
802             105 => ['Headline', 'N', 1,256, $IPTC_re_line ], #
803             110 => ['Credit', 'N', 1, 32, $IPTC_re_line ], #
804             115 => ['Source', 'N', 1, 32, $IPTC_re_line ], #
805             116 => ['CopyrightNotice', 'N', 1,128, $IPTC_re_line ], #
806             118 => ['Contact', ' ', 1,128, $IPTC_re_line ], #
807             120 => ['Caption/Abstract', 'N', 1,2000,$IPTC_re_para ], #
808             122 => ['Writer/Editor', ' ', 1, 32, $IPTC_re_line ], #
809             125 => ['RasterizedCaption', 'N',7360,7360,'binary' ], #
810             130 => ['ImageType', 'N', 2, 2,'[0-49][WYMCKRGBTFLPS]'], #
811             131 => ['ImageOrientation', 'N', 1, 1, '[PLS]' ], #
812             135 => ['LanguageIdentifier', 'N', 2, 3, '[a-zA-Z]{2,3}?' ], #
813             150 => ['AudioType', 'N', 2, 2, '[012][ACMQRSTVW]' ], #
814             151 => ['AudioSamplingRate', 'N', 6, 6, '\d{6}' ], #
815             152 => ['AudioSamplingResolution', 'N', 2, 2, '\d{2}' ], #
816             153 => ['AudioDuration', 'N', 6, 6, $IPTC_re_dura ], #
817             154 => ['AudioOutcue', 'N', 1, 64, $IPTC_re_line ], #
818             200 => ['ObjDataPreviewFileFormat', 'N', 2, 2, 'invalid,binary' ], #
819             201 => ['ObjDataPreviewFileFormatVer', 'N', 2, 2, 'invalid,binary' ], #
820             202 => ['ObjDataPreviewData', 'N', 1,256000,'invalid,binary' ],}; #
821             #============================================================================#
822             #============================================================================#
823             #============================================================================#
824             # Esoteric tags for a Photoshop APP13 segment (not IPTC data); #
825             # see the "VALID TAGS FOR PHOTOSHOP-STYLE APP13 DATA" section in the #
826             # Image::MetaData::JPEG module perldoc page for further details. #
827             # [tags 0x07d0 --> 0x0bb6 are reserved for path information] #
828             #----------------------------------------------------------------------------#
829             # Hash keys are numeric tags, here written in hexadecimal base. #
830             # Fields: 0 -> Tag name, 1 -> repeatability ('N' means non-repeatable), #
831             # 2,3 -> min and max length, 4 -> regular expression to match. #
832             # The syntax specifications are currently just placeholder, but this could #
833             # change in future. The only effect is to inhibit a direct assignement of #
834             # the 'IPTC/NAA' dataset, which must be modified with specialised routines. #
835             #----------------------------------------------------------------------------#
836             my $HASH_PHOTOSHOP_PATHINFO = #
837             {( map { $_ => [ sprintf("PathInfo_%3x",$_), #
838             ' ', 0, 65535, 'binary'] } (0x07d0..0x0bb6) ), #
839             0x0bb7 => ['ClippingPathName', ' ', 0, 65535, 'binary' ], }; #
840             #----------------------------------------------------------------------------#
841             my $HASH_PHOTOSHOP_GENERAL = #
842             {0x03e8 => ['Photoshop2Info', ' ', 0, 65535, 'binary' ], #
843             0x03e9 => ['MacintoshPrintInfo', ' ', 0, 65535, 'binary' ], #
844             0x03eb => ['Photoshop2ColorTable', ' ', 0, 65535, 'binary' ], #
845             0x03ed => ['ResolutionInfo', ' ', 0, 65535, 'binary' ], #
846             0x03ee => ['AlphaChannelsNames', ' ', 0, 65535, 'binary' ], #
847             0x03ef => ['DisplayInfo', ' ', 0, 65535, 'binary' ], #
848             0x03f0 => ['PStringCaption', ' ', 0, 65535, 'binary' ], #
849             0x03f1 => ['BorderInformation', ' ', 0, 65535, 'binary' ], #
850             0x03f2 => ['BackgroundColor', ' ', 0, 65535, 'binary' ], #
851             0x03f3 => ['PrintFlags', ' ', 0, 65535, 'binary' ], #
852             0x03f4 => ['BWHalftoningInfo', ' ', 0, 65535, 'binary' ], #
853             0x03f5 => ['ColorHalftoningInfo', ' ', 0, 65535, 'binary' ], #
854             0x03f6 => ['DuotoneHalftoningInfo', ' ', 0, 65535, 'binary' ], #
855             0x03f7 => ['BWTransferFunc', ' ', 0, 65535, 'binary' ], #
856             0x03f8 => ['ColorTransferFuncs', ' ', 0, 65535, 'binary' ], #
857             0x03f9 => ['DuotoneTransferFuncs', ' ', 0, 65535, 'binary' ], #
858             0x03fa => ['DuotoneImageInfo', ' ', 0, 65535, 'binary' ], #
859             0x03fb => ['EffectiveBW', ' ', 0, 65535, 'binary' ], #
860             0x03fc => ['ObsoletePhotoshopTag1', ' ', 0, 65535, 'binary' ], #
861             0x03fd => ['EPSOptions', ' ', 0, 65535, 'binary' ], #
862             0x03fe => ['QuickMaskInfo', ' ', 0, 65535, 'binary' ], #
863             0x03ff => ['ObsoletePhotoshopTag2', ' ', 0, 65535, 'binary' ], #
864             0x0400 => ['LayerStateInfo', ' ', 0, 65535, 'binary' ], #
865             0x0401 => ['WorkingPathInfo', ' ', 0, 65535, 'binary' ], #
866             0x0402 => ['LayersGroupInfo', ' ', 0, 65535, 'binary' ], #
867             0x0403 => ['ObsoletePhotoshopTag3', ' ', 0, 65535, 'binary' ], #
868             0x0404 => ['IPTC/NAA', ' ', 0, 65535, 'invalid' ], #
869             0x0405 => ['RawImageMode', ' ', 0, 65535, 'binary' ], #
870             0x0406 => ['JPEGQuality', ' ', 0, 65535, 'binary' ], #
871             0x0408 => ['GridGuidesInfo', ' ', 0, 65535, 'binary' ], #
872             0x0409 => ['ThumbnailResource', ' ', 0, 65535, 'binary' ], #
873             0x040a => ['CopyrightFlag', ' ', 0, 65535, 'binary' ], #
874             0x040b => ['URL', ' ', 0, 65535, 'binary' ], #
875             0x040c => ['ThumbnailResource2', ' ', 0, 65535, 'binary' ], #
876             0x040d => ['GlobalAngle', ' ', 0, 65535, 'binary' ], #
877             0x040e => ['ColorSamplersResource', ' ', 0, 65535, 'binary' ], #
878             0x040f => ['ICCProfile', ' ', 0, 65535, 'binary' ], #
879             0x0410 => ['Watermark', ' ', 0, 65535, 'binary' ], #
880             0x0411 => ['ICCUntagged', ' ', 0, 65535, 'binary' ], #
881             0x0412 => ['EffectsVisible', ' ', 0, 65535, 'binary' ], #
882             0x0413 => ['SpotHalftone', ' ', 0, 65535, 'binary' ], #
883             0x0414 => ['IDsBaseValue', ' ', 0, 65535, 'binary' ], #
884             0x0415 => ['UnicodeAlphaNames', ' ', 0, 65535, 'binary' ], #
885             0x0416 => ['IndexedColourTableCount', ' ', 0, 65535, 'binary' ], #
886             0x0417 => ['TransparentIndex', ' ', 0, 65535, 'binary' ], #
887             0x0419 => ['GlobalAltitude', ' ', 0, 65535, 'binary' ], #
888             0x041a => ['Slices', ' ', 0, 65535, 'binary' ], #
889             0x041b => ['WorkflowURL', ' ', 0, 65535, 'binary' ], #
890             0x041c => ['JumpToXPEP', ' ', 0, 65535, 'binary' ], #
891             0x041d => ['AlphaIdentifiers', ' ', 0, 65535, 'binary' ], #
892             0x041e => ['URLList', ' ', 0, 65535, 'binary' ], #
893             0x0421 => ['VersionInfo', ' ', 0, 65535, 'binary' ], #
894             0x2710 => ['PrintFlagsInfo', ' ', 0, 65535, 'binary' ], }; #
895             #----------------------------------------------------------------------------#
896             @$HASH_PHOTOSHOP_GENERAL{keys %$HASH_PHOTOSHOP_PATHINFO} = #
897             values %$HASH_PHOTOSHOP_PATHINFO; #
898             #============================================================================#
899             #============================================================================#
900             #============================================================================#
901             # Some scalar-valued hashes, which were once original databases, are now #
902             # generated with "generate_lookup" from more general array-valued hashes #
903             # (in practice, a single column is singled out from a multi-column table). #
904             # %$HASH_APP1_IFD is built by merging the first column of 3 different hashes.#
905             #----------------------------------------------------------------------------#
906             my $HASH_PHOTOSHOP_TAGS = generate_lookup($HASH_PHOTOSHOP_GENERAL ,0); #
907             my $HASH_PHOTOSHOP_PHUT = generate_lookup($HASH_PHOTOSHOP_PATHINFO ,0); #
908             my $HASH_IPTC_TAGS_1 = generate_lookup($HASH_IPTC_GENERAL_1 ,0); #
909             my $HASH_IPTC_TAGS_2 = generate_lookup($HASH_IPTC_GENERAL_2 ,0); #
910             my $HASH_APP1_ROOT = generate_lookup($HASH_APP1_ROOT_GENERAL ,0); #
911             my $HASH_APP1_GPS = generate_lookup($HASH_GPS_GENERAL ,0); #
912             my $HASH_APP1_INTEROP = generate_lookup($HASH_INTEROP_GENERAL ,0); #
913             my $HASH_APP1_IFD = generate_lookup($HASH_APP1_IFD01_GENERAL ,0); #
914             my $HASH_APP1_SUBIFD = generate_lookup($HASH_APP1_SUBIFD_GENERAL ,0); #
915             #============================================================================#
916             #============================================================================#
917             #============================================================================#
918             # Some segments (APP1 and APP3 currently) have an IFD-like structure, i.e. #
919             # they can have "subdirectories" pointed to by offset tags. These subdirs #
920             # are bifurcation points for the lookup process, and are represented by #
921             # hash references instead of plain strings (scalars). #
922             #----------------------------------------------------------------------------#
923             $$HASH_APP1_IFD{SubIFD} = $HASH_APP1_SUBIFD; # Exif private tags #
924             $$HASH_APP1_IFD{GPS} = $HASH_APP1_GPS; # GPS tags #
925             $$HASH_APP3_IFD{Special} = $HASH_APP3_SPECIAL; # Special effect tags #
926             $$HASH_APP3_IFD{Borders} = $HASH_APP3_BORDERS; # Border tags #
927             $$HASH_APP1_SUBIFD{Interop} = $HASH_APP1_INTEROP; # Interoperability tags #
928             #============================================================================#
929             #============================================================================#
930             #============================================================================#
931             # MakerNote stuff is stored in a separated file; the return value of this #
932             # inclusion is the $HASH_MAKERNOTES hash reference, containing all relevant #
933             # parameters. We only have to link this new table into $HASH_APP1_SUBIFD. #
934             #----------------------------------------------------------------------------#
935             our $HASH_MAKERNOTES = require 'Image/MetaData/JPEG/data/Makernotes.pl'; #
936             $$HASH_APP1_SUBIFD{'MakerNoteData_' . $_} = #
937             generate_lookup($$HASH_MAKERNOTES{$_}{tags} ,0) #
938             for keys %$HASH_MAKERNOTES; #
939             #============================================================================#
940             #============================================================================#
941             #============================================================================#
942             # Syntax tables and mandatory records tables for IPTC data are hidden in the #
943             # corresponding tag hashes. Another %IFD_SUBDIRS is overkill here. #
944             #----------------------------------------------------------------------------#
945             $$HASH_IPTC_TAGS_1{__syntax} = $HASH_IPTC_GENERAL_1; #
946             $$HASH_IPTC_TAGS_1{__mandatory} = $HASH_IPTC_MANDATORY_1; #
947             $$HASH_IPTC_TAGS_2{__syntax} = $HASH_IPTC_GENERAL_2; #
948             $$HASH_IPTC_TAGS_2{__mandatory} = $HASH_IPTC_MANDATORY_2; #
949             $$HASH_PHOTOSHOP_TAGS{__syntax} = $HASH_PHOTOSHOP_GENERAL; #
950             #============================================================================#
951             #============================================================================#
952             #============================================================================#
953             # The following hash is the database for the tag-to-tagname translation; of #
954             # course, records with a textual tag are not listed here. The navigation #
955             # through this structure is best done with the help of the JPEG_lookup #
956             # function, so this hash is not exported (as it was some time ago). #
957             #----------------------------------------------------------------------------#
958             my $psdirname = sub { $APP13_PHOTOSHOP_DIRNAME . '_' . $_[0] }; #
959             #----------------------------------------------------------------------------#
960             my $JPEG_RECORD_NAME = #
961             {APP1 => {%$HASH_APP1_ROOT, # APP1 root #
962             IFD0 => $HASH_APP1_IFD, # main image #
963             IFD1 => $HASH_APP1_IFD, }, # thumbnail #
964             APP2 => {TagTable => $HASH_APP2_ICC, }, # ICC data #
965             APP3 => {IFD0 => $HASH_APP3_IFD, }, # main image #
966             APP13 => {&$psdirname('8BIM') => $HASH_PHOTOSHOP_TAGS, # PS:8BIM #
967             &$psdirname('8BPS') => $HASH_PHOTOSHOP_TAGS, # PS: < ver 4 #
968             &$psdirname('PHUT') => $HASH_PHOTOSHOP_PHUT, # PS:PHUT #
969             $APP13_IPTC_DIRNAME.'_1' => $HASH_IPTC_TAGS_1, # PS:IPTC R:1 #
970             $APP13_IPTC_DIRNAME.'_2' => $HASH_IPTC_TAGS_2, }, };# PS:IPTC R:2 #
971             #----------------------------------------------------------------------------#
972              
973             ###########################################################
974             # This helper function returns record data from the #
975             # %$JPEG_RECORD_NAME hash. The arguments are first joined #
976             # with the '@' character, and then splitted on the same #
977             # character to give a list of '@'-free strings (this al- #
978             # lows for greater flexibility at call time); this list #
979             # contains keys for exploring the %$JPEG_RECORD_NAME hash;#
980             # e.g., the arguments ('APP1', 'IFD0@GPS', 0x1e) select #
981             # $JPEG_RECORD_NAME{APP1}{IFD0}{GPS}{0x1e}, i.e. the #
982             # textual name of the GPS record with key = 0x1e in the #
983             # IFD0 in the APP1 segment. If, at some point during the #
984             # search, an argument fails (it is not a valid key) or it #
985             # is not defined, the search is interrupted, and undef is #
986             # returned. Note also that the return value could be a #
987             # string as well as a hash reference, depending on the #
988             # search depth. If the key lookup for the last argument #
989             # fails, a reverse lookup is run (i.e., the key corres- #
990             # ponding to the value equal to the last user argument is #
991             # searched). If even this lookup fails, undef is returned.#
992             ###########################################################
993             sub JPEG_lookup {
994             # all searches start from here
995 6417     6417 0 6720 my $lookup = $JPEG_RECORD_NAME;
996             # print a debugging message and return immediately unless
997             # all arguments are scalars (i.e., references are not allowed)
998 6417 50       7886 for (@_) { print "wrong argument(s) in JPEG_lookup call", return if ref; }
  16489         25337  
999             # delete all undefined or "false" arguments
1000 6417         7578 @_ = grep { defined $_ } @_;
  16489         23507  
1001             # join all remaining arguments
1002 6417         10358 my $keystring = join('@', @_);
1003             # split the resulting string on '@'
1004 6417         13995 my @keylist = split('@', $keystring);
1005             # extract and save the last argument for special treatment
1006 6417         7224 my $last = pop @keylist;
1007             # delete all false arguments
1008 6417         6259 @keylist = grep { $_ } @keylist;
  14831         19048  
1009             # refuse to work with $last undefined
1010 6417 50       10249 return unless defined $last;
1011             # consume the list of "normal" arguments: they must be successive
1012             # keys for navigation in a multi-level hash. Interrupt the search
1013             # as soon as an argument is undefined or $lookup is not a hash ref
1014 6417         6470 for (@keylist) {
1015             # return undef as soon as an argument is undefined
1016 14748 50       18927 return undef unless $_;
1017             # go one level deeper in the hash exploration
1018 14748         15193 $lookup = $$lookup{$_};
1019             # return undef if $lookup is no more a hash reference
1020 14748 50       24729 return undef unless ref $lookup eq 'HASH'; }
1021             # $lookup is a hash reference now. Return the value
1022             # corresponding to $last (used as a key) if it exists.
1023 6417 100       20136 return $$lookup{$last} if exists $$lookup{$last};
1024             # if we are still here, scan the hash looking for a value equal to
1025             # $last, and return its key. Avoid each %$lookup, since we could
1026             # exit the loop before the end and I don't want to reset the
1027             # iterator in that stupid manner.
1028 2386 100       34764 for (keys %$lookup) { return $_ if $$lookup{$_} eq $last; }
  199967         289867  
1029             # if we are still here, we have lost
1030 241         2488 return undef;
1031             };
1032              
1033             #============================================================================#
1034             #============================================================================#
1035             #============================================================================#
1036             # This hash is needed to overcome some complications due to the APP1/APP3 #
1037             # structure: some IFDs or sub-IFDs can contain offset tags (tags whose value #
1038             # is an offset in the JPEG file), linking to nested structures, which are #
1039             # represented internally as sub-lists pointed to by $REFERENCE records; the #
1040             # sub-lists deserve in general a more significant name than the offset tag #
1041             # name. Each key in the following hash is a path to an IFD or one of its #
1042             # subdirectories; the corresponding value is a hash reference, with the #
1043             # pointed hash mapping offset tag numerical values to subdirectory names. #
1044             # (the [tag names] -> [tag numerical values] translation is done afterwards) #
1045             #----------------------------------------------------------------------------#
1046             # A sub hash must also own the '__syntax' and '__mandatory' keys, returning #
1047             # a reference to a hash of syntactical properties to be respected by data in #
1048             # the corresponding IFD and a reference to a hash of mandatory records. #
1049             # These special entries are of course treated differently from the others ...#
1050             #----------------------------------------------------------------------------#
1051             # When the JPEG file is read, offset tag records are not stored; insted, we #
1052             # store a $REFERENCE record with the mapped name (and the name of the origi- #
1053             # nating offset tag saved in the "extra" field). The following hash can then #
1054             # be used in both directions to do data parsing/dumping. #
1055             #----------------------------------------------------------------------------#
1056             our %IFD_SUBDIRS = #
1057             ('APP1' => {'__syntax' => $HASH_APP1_ROOT_GENERAL, #
1058             '__mandatory' => $HASH_APP1_ROOT_MANDATORY }, #
1059             'APP1@IFD0' => {'__syntax' => $HASH_APP1_IFD01_GENERAL, #
1060             '__mandatory' => $HASH_APP1_IFD0_MANDATORY, #
1061             'GPSInfo' => 'GPS', #
1062             'ExifOffset' => 'SubIFD'}, #
1063             'APP1@IFD0@GPS' => {'__syntax' => $HASH_GPS_GENERAL, #
1064             '__mandatory' => $HASH_GPS_MANDATORY }, #
1065             'APP1@IFD0@SubIFD' => {'__syntax' => $HASH_APP1_SUBIFD_GENERAL, #
1066             '__mandatory' => $HASH_APP1_SUBIFD_MANDATORY, #
1067             'MakerNote' => 'MakerNoteData', #
1068             'InteroperabilityOffset' => 'Interop'}, #
1069             'APP1@IFD0@SubIFD@Interop' => {'__syntax' => $HASH_INTEROP_GENERAL, #
1070             '__mandatory'=> $HASH_INTEROP_MANDATORY }, #
1071             'APP1@IFD1' => {'__syntax' => $HASH_APP1_IFD01_GENERAL, #
1072             '__mandatory' => $HASH_APP1_IFD1_MANDATORY }, #
1073             'APP3@IFD0' => {'BordersIFD' => 'Borders', #
1074             'SpecialEffectsIFD' => 'Special'}, ); #
1075             #----------------------------------------------------------------------------#
1076             while (my ($ifd_path, $ifd_hash) = each %IFD_SUBDIRS) { #
1077             my %h = map { $_ =~ /__syntax|__mandatory/ ? ($_ => $$ifd_hash{$_}) : #
1078             (JPEG_lookup($ifd_path, $_) => $$ifd_hash{$_}) #
1079             } keys %$ifd_hash; #
1080             $IFD_SUBDIRS{$ifd_path} = \ %h; } #
1081             #============================================================================#
1082             #============================================================================#
1083             #============================================================================#
1084             # These parameters must be initialised with JPEG_lookup, because I don't #
1085             # want to have them written explicitely in more than one place. #
1086             #----------------------------------------------------------------------------#
1087             our $APP1_TH_TYPE = JPEG_lookup('APP1@IFD1@Compression'); #
1088             our $THJPEG_OFFSET = JPEG_lookup('APP1@IFD1@JPEGInterchangeFormat'); #
1089             our $THJPEG_LENGTH = JPEG_lookup('APP1@IFD1@JPEGInterchangeFormatLength'); #
1090             our $THTIFF_OFFSET = JPEG_lookup('APP1@IFD1@StripOffsets'); #
1091             our $THTIFF_LENGTH = JPEG_lookup('APP1@IFD1@StripByteCounts'); #
1092             our $MAKERNOTE_TAG = JPEG_lookup('APP1@IFD0@SubIFD@MakerNote'); #
1093             #----------------------------------------------------------------------------#
1094              
1095             # successful package load
1096             1;