| 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 |  | 352637 | use Exporter 'import'; | 
|  | 16 |  |  |  |  | 39 |  | 
|  | 16 |  |  |  |  | 763 |  | 
| 8 | 16 |  |  | 16 |  | 87 | use strict; | 
|  | 16 |  |  |  |  | 39 |  | 
|  | 16 |  |  |  |  | 544 |  | 
| 9 | 16 |  |  | 16 |  | 84 | use warnings; | 
|  | 16 |  |  |  |  | 40 |  | 
|  | 16 |  |  |  |  | 443 |  | 
| 10 | 16 |  |  | 16 |  | 23376 | no  integer; | 
|  | 16 |  |  |  |  | 169 |  | 
|  | 16 |  |  |  |  | 163 |  | 
| 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 | 886 | sub generate_lookup { my %a=map { $_ => $_[0]{$_}[$_[1]] } keys %{$_[0]}; \%a}; | 
|  | 43840 |  |  |  |  | 117902 |  | 
|  | 608 |  |  |  |  | 6705 |  | 
|  | 608 |  |  |  |  | 9998 |  | 
| 95 | 64 |  |  | 64 | 0 | 108 | sub generate_array  { map { $_[0]{$_}[$_[1]] } (0..(-1+scalar keys %{$_[0]}))}; | 
|  | 896 |  |  |  |  | 1849 |  | 
|  | 64 |  |  |  |  | 228 |  | 
| 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 | 716 | sub str2hex { my $z = 0; ($z *= 256) += $_ for unpack "CCCC", $_[0]; $z; } | 
|  | 592 |  |  |  |  | 2332 |  | 
|  | 592 |  |  |  |  | 27194 |  | 
| 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 | 6358 |  |  | 6358 | 0 | 19653 | 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 | 6358 | 50 |  |  |  | 14063 | for (@_) { print "wrong argument(s) in JPEG_lookup call", return if ref; } | 
|  | 16371 |  |  |  |  | 38572 |  | 
| 999 |  |  |  |  |  |  | # delete all undefined or "false" arguments | 
| 1000 | 6358 |  |  |  |  | 11642 | @_ = grep { defined $_ } @_; | 
|  | 16371 |  |  |  |  | 37983 |  | 
| 1001 |  |  |  |  |  |  | # join all remaining arguments | 
| 1002 | 6358 |  |  |  |  | 18017 | my $keystring = join('@', @_); | 
| 1003 |  |  |  |  |  |  | # split the resulting string on '@' | 
| 1004 | 6358 |  |  |  |  | 21771 | my @keylist = split('@', $keystring); | 
| 1005 |  |  |  |  |  |  | # extract and save the last argument for special treatment | 
| 1006 | 6358 |  |  |  |  | 11295 | my $last = pop @keylist; | 
| 1007 |  |  |  |  |  |  | # delete all false arguments | 
| 1008 | 6358 |  |  |  |  | 9332 | @keylist = grep { $_ } @keylist; | 
|  | 14682 |  |  |  |  | 37037 |  | 
| 1009 |  |  |  |  |  |  | # refuse to work with $last undefined | 
| 1010 | 6358 | 50 |  |  |  | 15075 | 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 | 6358 |  |  |  |  | 11089 | for (@keylist) { | 
| 1015 |  |  |  |  |  |  | # return undef as soon as an argument is undefined | 
| 1016 | 14599 | 50 |  |  |  | 27366 | return undef unless $_; | 
| 1017 |  |  |  |  |  |  | # go one level deeper in the hash exploration | 
| 1018 | 14599 |  |  |  |  | 24961 | $lookup = $$lookup{$_}; | 
| 1019 |  |  |  |  |  |  | # return undef if $lookup is no more a hash reference | 
| 1020 | 14599 | 50 |  |  |  | 41357 | 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 | 6358 | 100 |  |  |  | 33952 | 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 |  |  |  | 54677 | for (keys %$lookup) { return $_ if $$lookup{$_} eq $last; } | 
|  | 190369 |  |  |  |  | 421179 |  | 
| 1029 |  |  |  |  |  |  | # if we are still here, we have lost | 
| 1030 | 241 |  |  |  |  | 4330 | 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; |