Changeset 24107
- Timestamp:
- 2011-06-01T12:33:42+12:00 (13 years ago)
- Location:
- main/trunk/greenstone2/perllib/cpan
- Files:
-
- 89 added
- 87 edited
Legend:
- Unmodified
- Added
- Removed
-
main/trunk/greenstone2/perllib/cpan/File/RandomAccess.pm
r16844 r24107 4 4 # Description: Buffer to support random access reading of sequential file 5 5 # 6 # Revisions: 02/11/04 - P. Harvey Created 7 # 02/20/04 - P. Harvey Added flag to disable SeekTest in new() 8 # 11/18/04 - P. Harvey Fixed bug with seek relative to end of file 9 # 01/02/05 - P. Harvey Added DEBUG code 10 # 01/09/06 - P. Harvey Fixed bug in ReadLine() when using 11 # multi-character EOL sequences 12 # 02/20/06 - P. Harvey Fixed bug where seek past end of file could 13 # generate "substr outside string" warning 14 # 06/10/06 - P. Harvey Decreased $CHUNK_SIZE from 64k to 8k 15 # 11/23/06 - P. Harvey Limit reads to < 0x80000000 bytes 6 # Revisions: 02/11/2004 - P. Harvey Created 7 # 02/20/2004 - P. Harvey Added flag to disable SeekTest in new() 8 # 11/18/2004 - P. Harvey Fixed bug with seek relative to end of file 9 # 01/02/2005 - P. Harvey Added DEBUG code 10 # 01/09/2006 - P. Harvey Fixed bug in ReadLine() when using 11 # multi-character EOL sequences 12 # 02/20/2006 - P. Harvey Fixed bug where seek past end of file could 13 # generate "substr outside string" warning 14 # 06/10/2006 - P. Harvey Decreased $CHUNK_SIZE from 64k to 8k 15 # 11/23/2006 - P. Harvey Limit reads to < 0x80000000 bytes 16 # 11/26/2008 - P. Harvey Fixed bug in ReadLine when reading from a 17 # scalar with a multi-character newline 18 # 01/24/2009 - PH Protect against reading too much at once 16 19 # 17 20 # Notes: Calls the normal file i/o routines unless SeekTest() fails, in … … 22 25 # May also be used for string i/o (just pass a scalar reference) 23 26 # 24 # Legal: Copyright (c) 200 4-2006Phil Harvey (phil at owl.phy.queensu.ca)27 # Legal: Copyright (c) 2003-2010 Phil Harvey (phil at owl.phy.queensu.ca) 25 28 # This library is free software; you can redistribute it and/or 26 29 # modify it under the same terms as Perl itself. … … 34 37 35 38 use vars qw($VERSION @ISA @EXPORT_OK); 36 $VERSION = '1. 07';39 $VERSION = '1.10'; 37 40 @ISA = qw(Exporter); 41 42 sub Read($$$); 38 43 39 44 # constants … … 64 69 # file i/o 65 70 my $buff = ''; 66 $self = { 71 $self = { 67 72 FILE_PT => $filePt, # file pointer 68 73 BUFF_PT => \$buff, # reference to file data … … 156 161 #------------------------------------------------------------------------------ 157 162 # Read from the file 158 # Inputs: 0) reference to RandomAccess object 159 # 1) buffer, 2) bytes to read 163 # Inputs: 0) reference to RandomAccess object, 1) buffer, 2) bytes to read 160 164 # Returns: Number of bytes read 161 165 sub Read($$$) … … 165 169 my $rtnVal; 166 170 167 # avoid dying with "Negative length" error 168 return 0 if $len & 0x80000000; 169 171 # protect against reading too much at once 172 # (also from dying with a "Negative length" error) 173 if ($len & 0xf8000000) { 174 return 0 if $len < 0; 175 # read in smaller blocks because Windows attempts to pre-allocate 176 # memory for the full size, which can lead to an out-of-memory error 177 my $maxLen = 0x4000000; # (MUST be less than bitmask in "if" above) 178 my $num = Read($self, $_[0], $maxLen); 179 return $num if $num < $maxLen; 180 for (;;) { 181 $len -= $maxLen; 182 last if $len <= 0; 183 my $l = $len < $maxLen ? $len : $maxLen; 184 my $buff; 185 my $n = Read($self, $buff, $l); 186 last unless $n; 187 $_[0] .= $buff; 188 $num += $n; 189 last if $n < $l; 190 } 191 return $num; 192 } 193 # read through our buffer if necessary 170 194 if ($self->{TESTED} < 0) { 171 195 my $buff; … … 196 220 $self->{POS} += $rtnVal; 197 221 } else { 222 # read directly from file 198 223 $_[0] = '' unless defined $_[0]; 199 224 $rtnVal = read($self->{FILE_PT}, $_[0], $len) || 0; … … 204 229 $self->{DEBUG}->{$pos} = $rtnVal; 205 230 } 206 } 231 } 207 232 return $rtnVal; 208 233 } … … 217 242 my $rtnVal; 218 243 my $fp = $self->{FILE_PT}; 219 244 220 245 if ($self->{TESTED} < 0) { 221 246 my ($num, $buff); … … 231 256 # scan and read until we find the EOL (or hit EOF) 232 257 for (;;) { 233 $pos = index(${$self->{BUFF_PT}}, $/, $pos) + length($/); 234 last if $pos > 0; 258 $pos = index(${$self->{BUFF_PT}}, $/, $pos); 259 if ($pos >= 0) { 260 $pos += length($/); 261 last; 262 } 235 263 $pos = $self->{LEN}; # have scanned to end of buffer 236 264 $num = read($fp, $buff, $CHUNK_SIZE) or last; … … 240 268 } else { 241 269 # string i/o 242 $pos = index(${$self->{BUFF_PT}}, $/, $pos) + length($/); 243 $pos <= 0 and $pos = $self->{LEN}; 270 $pos = index(${$self->{BUFF_PT}}, $/, $pos); 271 if ($pos < 0) { 272 $pos = $self->{LEN}; 273 $self->{POS} = $pos if $self->{POS} > $pos; 274 } else { 275 $pos += length($/); 276 } 244 277 } 245 278 # read the line from our buffer … … 260 293 $self->{DEBUG}->{$pos} = $rtnVal; 261 294 } 262 } 263 return $rtnVal; 295 } 296 return $rtnVal; 264 297 } 265 298 … … 295 328 { 296 329 my $self = shift; 297 330 298 331 if ($self->{DEBUG}) { 299 332 local $_; -
main/trunk/greenstone2/perllib/cpan/File/RandomAccess.pod
r16844 r24107 4 4 # Description: Buffer to support random access reading of sequential file 5 5 # 6 # Legal: Copyright (c) 200 4-2006Phil Harvey (phil at owl.phy.queensu.ca)6 # Legal: Copyright (c) 2003-2010 Phil Harvey (phil at owl.phy.queensu.ca) 7 7 # This library is free software; you can redistribute it and/or 8 8 # modify it under the same terms as Perl itself. … … 218 218 =head1 AUTHOR 219 219 220 Copyright 200 4-2006Phil Harvey (phil at owl.phy.queensu.ca)220 Copyright 2003-2011 Phil Harvey (phil at owl.phy.queensu.ca) 221 221 222 222 This library is free software; you can redistribute it and/or modify it -
main/trunk/greenstone2/perllib/cpan/Image/ExifTool.pm
r23771 r24107 6 6 # URL: http://owl.phy.queensu.ca/~phil/exiftool/ 7 7 # 8 # Revisions: Nov. 12/ 03 - P. Harvey Created8 # Revisions: Nov. 12/2003 - P. Harvey Created 9 9 # (See html/history.html for revision history) 10 10 # 11 # Legal: Copyright (c) 2003-20 07, Phil Harvey (phil at owl.phy.queensu.ca)11 # Legal: Copyright (c) 2003-2010, Phil Harvey (phil at owl.phy.queensu.ca) 12 12 # This library is free software; you can redistribute it and/or 13 13 # modify it under the same terms as Perl itself. … … 20 20 require Exporter; 21 21 use File::RandomAccess; 22 22 23 23 use vars qw($VERSION $RELEASE @ISA %EXPORT_TAGS $AUTOLOAD @fileTypes %allTables 24 @tableOrder $exifAPP1hdr $xmpAPP1hdr $psAPP13hdr $psAPP13old 25 @loadAllTables %UserDefined $evalWarning); 26 27 $VERSION = '7.00'; 24 @tableOrder $exifAPP1hdr $xmpAPP1hdr $xmpExtAPP1hdr $psAPP13hdr 25 $psAPP13old @loadAllTables %UserDefined $evalWarning %noWriteFile 26 %magicNumber @langs $defaultLang %langName %charsetName %mimeType 27 $swapBytes $swapWords $currentByteOrder %unpackStd); 28 29 $VERSION = '8.57'; 28 30 $RELEASE = ''; 29 31 @ISA = qw(Exporter); 30 32 %EXPORT_TAGS = ( 31 # all public non-object-oriented functions 33 # all public non-object-oriented functions: 32 34 Public => [qw( 33 35 ImageInfo GetTagName GetShortcuts GetAllTags GetWritableTags 34 36 GetAllGroups GetDeleteGroups GetFileType CanWrite CanCreate 35 37 )], 38 # exports not part of the public API, but used by ExifTool modules: 36 39 DataAccess => [qw( 37 40 ReadValue GetByteOrder SetByteOrder ToggleByteOrder Get8u Get8s Get16u 38 Get16s Get32u Get32s Get Float GetDouble GetFixed32s Write WriteValue39 Tell Set8u Set8s Set16u Set32u41 Get16s Get32u Get32s Get64u GetFloat GetDouble GetFixed32s Write 42 WriteValue Tell Set8u Set8s Set16u Set32u 40 43 )], 41 Utils => [qw( 42 GetTagTable TagTableKeys GetTagInfoList GenerateTagIDs SetFileType 43 HtmlDump 44 )], 45 Vars => [qw( 46 %allTables @tableOrder @fileTypes 47 )], 44 Utils => [qw(GetTagTable TagTableKeys GetTagInfoList)], 45 Vars => [qw(%allTables @tableOrder @fileTypes)], 48 46 ); 49 47 # set all of our EXPORT_TAGS in EXPORT_OK … … 54 52 55 53 # The following functions defined in Image::ExifTool::Writer are declared 56 # here so their prototypes will be available. The Writer routines will be57 # autoloaded when any of the se arecalled.54 # here so their prototypes will be available. These Writer routines will be 55 # autoloaded when any of them is called. 58 56 sub SetNewValue($;$$%); 59 57 sub SetNewValuesFromFile($$;@); … … 74 72 sub IsWritable($); 75 73 sub GetNewFileName($$); 74 sub NextTagKey($$); 76 75 sub LoadAllTables(); 77 76 sub GetNewTagInfoList($;$); … … 88 87 sub VerboseInfo($$$%); 89 88 sub VerboseDir($$;$$); 89 sub VerboseValue($$$;$); 90 90 sub VPrint($$@); 91 91 sub Rationalize($;$); 92 92 sub Write($@); 93 sub ProcessTrailers($$);94 93 sub WriteTrailerBuffer($$$); 95 94 sub AddNewTrailers($;@); … … 100 99 sub CheckBinaryData($$$); 101 100 sub WriteTIFF($$$); 102 sub Charset2Unicode($$;$);103 sub Latin2Unicode($$);104 sub UTF82Unicode($$;$);105 sub Unicode2Charset($$;$);106 sub Unicode2Latin($$;$);107 sub Unicode2UTF8($$);108 101 sub PackUTF8(@); 109 102 sub UnpackUTF8($); 103 sub SetPreferredByteOrder($); 104 sub CopyBlock($$$); 105 sub CopyFileAttrs($$); 106 107 # other subroutine definitions 108 sub DoEscape($$); 109 sub ConvertFileSize($); 110 sub ParseArguments($;@); #(defined in attempt to avoid mod_perl problem) 110 111 111 112 # list of main tag tables to load in LoadAllTables() (sub-tables are recursed 112 113 # automatically). Note: They will appear in this order in the documentation 113 # (unless tweaked in BuildTagLookup::GetTableOrder()), so put Exif first.114 # unless tweaked in BuildTagLookup::GetTableOrder(). 114 115 @loadAllTables = qw( 115 PhotoMechanic Exif GeoTiff CanonRaw KyoceraRaw MinoltaRaw SigmaRaw JPEG 116 Jpeg2000 BMP BMP PICT PNG MNG MIFF PDF PostScript Photoshop::Header 117 FujiFilm::RAF Panasonic::Raw Sony::SR2SubIFD ID3 Vorbis FLAC APE 118 APE::NewHeader APE::OldHeader MPC MPEG::Audio MPEG::Video QuickTime 119 QuickTime::ImageFile Flash Flash::FLV Real::Media Real::Audio 120 Real::Metafile RIFF AIFF ASF DICOM MIE HTML 116 PhotoMechanic Exif GeoTiff CanonRaw KyoceraRaw MinoltaRaw PanasonicRaw 117 SigmaRaw JPEG GIMP Jpeg2000 GIF BMP BMP::OS2 PICT PNG MNG DjVu PGF MIFF PSP 118 PDF PostScript Photoshop::Header FujiFilm::RAF Sony::SRF2 Sony::SR2SubIFD 119 Sony::PMP ITC ID3 Vorbis FLAC APE APE::NewHeader APE::OldHeader MPC 120 MPEG::Audio MPEG::Video MPEG::Xing M2TS QuickTime QuickTime::ImageFile 121 Matroska MXF DV Flash Flash::FLV Real::Media Real::Audio Real::Metafile RIFF 122 AIFF ASF DICOM MIE HTML XMP::SVG EXE EXE::PEVersion EXE::PEString EXE::MachO 123 EXE::PEF EXE::ELF LNK Font RSRC Rawzor ZIP ZIP::GZIP ZIP::RAR RTF OOXML 124 iWork 125 ); 126 127 # alphabetical list of current Lang modules 128 @langs = qw(cs de en en_ca en_gb es fr it ja ko nl pl ru sv tr zh_cn zh_tw); 129 130 $defaultLang = 'en'; # default language 131 132 # language names 133 %langName = ( 134 cs => 'Czech (ÄeÅ¡tina)', 135 de => 'German (Deutsch)', 136 en => 'English', 137 en_ca => 'Canadian English', 138 en_gb => 'British English', 139 es => 'Spanish (Español)', 140 fr => 'French (Français)', 141 it => 'Italian (Italiano)', 142 ja => 'Japanese (æ¥æ¬èª)', 143 ko => 'Korean (íêµìŽ)', 144 nl => 'Dutch (Nederlands)', 145 pl => 'Polish (Polski)', 146 ru => 'Russian (Ð ÑÑÑкОй)', 147 sv => 'Swedish (Svenska)', 148 'tr'=> 'Turkish (TÃŒrkçe)', 149 zh_cn => 'Simplified Chinese (ç®äœäžæ)', 150 zh_tw => 'Traditional Chinese (ç¹é«äžæ)', 121 151 ); 122 152 123 153 # recognized file types, in the order we test unknown files 124 154 # Notes: 1) There is no need to test for like types separately here 125 # 2) Put types with no file signatureat end of list to avoid false matches155 # 2) Put types with weak file signatures at end of list to avoid false matches 126 156 @fileTypes = qw(JPEG CRW TIFF GIF MRW RAF X3F JP2 PNG MIE MIFF PS PDF PSD XMP 127 BMP PPM RIFF AIFF ASF MOV MPEG Real SWF FLV OGG FLAC APE MPC 128 ICC HTML VRD QTIF FPX PICT MP3 DICM RAW); 157 BMP PPM RIFF AIFF ASF MOV MPEG Real SWF PSP FLV OGG FLAC APE MPC 158 MKV MXF DV PMP IND PGF ICC ITC HTML VRD RTF XCF QTIF FPX PICT 159 ZIP GZIP RAR BZ2 TAR RWZ EXE LNK RAW Font RSRC M2TS MP3 DICM); 129 160 130 161 # file types that we can write (edit) 131 my @writeTypes = qw(JPEG TIFF GIF CRW MRW ORF PNG MIE PSD XMP PPM EPS PS ICC 132 VRD JP2); 162 my @writeTypes = qw(JPEG TIFF GIF CRW MRW ORF RAF RAW PNG MIE PSD XMP PPM 163 EPS X3F PS PDF ICC VRD JP2 EXIF AI AIT IND); 164 165 # file extensions that we can't write for various base types 166 %noWriteFile = ( 167 TIFF => [ qw(3FR DCR K25 KDC SRF) ], 168 XMP => [ 'SVG' ], 169 ); 133 170 134 171 # file types that we can create from scratch 135 my @createTypes = qw(XMP ICC MIE VRD); 172 # - must update CanCreate() documentation if this list is changed! 173 my %createTypes = (XMP=>1, ICC=>1, MIE=>1, VRD=>1, EXIF=>1); 136 174 137 175 # file type lookup for all recognized file extensions 138 176 my %fileTypeLookup = ( 177 '3FR' => ['TIFF', 'Hasselblad RAW format'], 178 '3G2' => ['MOV', '3rd Gen. Partnership Project 2 audio/video'], 179 '3GP' => ['MOV', '3rd Gen. Partnership Project audio/video'], 180 '3GP2'=> '3G2', 181 '3GPP'=> '3GP', 139 182 ACR => ['DICM', 'American College of Radiology ACR-NEMA'], 140 AI => [['PDF','PS'], 'Adobe Illustrator (PDF-like or PS-like)'], 141 AIF => ['AIFF', 'Audio Interchange File Format'], 183 ACFM => ['Font', 'Adobe Composite Font Metrics'], 184 AFM => ['Font', 'Adobe Font Metrics'], 185 AMFM => ['Font', 'Adobe Multiple Master Font Metrics'], 186 AI => [['PDF','PS'], 'Adobe Illustrator'], 187 AIF => 'AIFF', 142 188 AIFC => ['AIFF', 'Audio Interchange File Format Compressed'], 143 189 AIFF => ['AIFF', 'Audio Interchange File Format'], 190 AIT => 'AI', 144 191 APE => ['APE', "Monkey's Audio format"], 145 ARW => ['TIFF', 'Sony Alpha RAW format (TIFF-like)'],192 ARW => ['TIFF', 'Sony Alpha RAW format'], 146 193 ASF => ['ASF', 'Microsoft Advanced Systems Format'], 147 AVI => ['RIFF', 'Audio Video Interleaved (RIFF-based)'], 148 BMP => ['BMP', 'Windows BitMaP'], 149 BTF => ['BTF', 'Big Tagged Image File Format'], 150 CIFF => ['CRW', 'Camera Image File Format (same as CRW)'], 151 CR2 => ['TIFF', 'Canon RAW 2 format (TIFF-like)'], 194 AVI => ['RIFF', 'Audio Video Interleaved'], 195 BMP => ['BMP', 'Windows Bitmap'], 196 BTF => ['BTF', 'Big Tagged Image File Format'], #(unofficial) 197 BZ2 => ['BZ2', 'BZIP2 archive'], 198 CIFF => ['CRW', 'Camera Image File Format'], 199 COS => ['COS', 'Capture One Settings'], 200 CR2 => ['TIFF', 'Canon RAW 2 format'], 152 201 CRW => ['CRW', 'Canon RAW format'], 153 CS1 => ['PSD', 'Sinar CaptureShop 1-Shot RAW (PSD-like)'], 154 DC3 => ['DICM', 'DICOM image file'], 155 DCM => ['DICM', 'DICOM image file'], 156 DIB => ['BMP', 'Device Independent Bitmap (aka. BMP)'], 157 DIC => ['DICM', 'DICOM image file'], 158 DICM => ['DICM', 'DICOM image file'], 159 DNG => ['TIFF', 'Digital Negative (TIFF-like)'], 160 DCR => ['TIFF', 'Kodak Digital Camera RAW (TIFF-like)'], 161 DOC => ['FPX', 'Microsoft Word Document (FPX-like)'], 202 CS1 => ['PSD', 'Sinar CaptureShop 1-Shot RAW'], 203 DC3 => 'DICM', 204 DCM => 'DICM', 205 DCP => ['TIFF', 'DNG Camera Profile'], 206 DCR => ['TIFF', 'Kodak Digital Camera RAW'], 207 DFONT=> ['Font', 'Macintosh Data fork Font'], 208 DIB => ['BMP', 'Device Independent Bitmap'], 209 DIC => 'DICM', 210 DICM => ['DICM', 'Digital Imaging and Communications in Medicine'], 211 DIVX => ['ASF', 'DivX media format'], 212 DJV => 'DJVU', 213 DJVU => ['AIFF', 'DjVu image'], 214 DLL => ['EXE', 'Windows Dynamic Link Library'], 215 DNG => ['TIFF', 'Digital Negative'], 216 DOC => ['FPX', 'Microsoft Word Document'], 217 DOCM => [['ZIP','FPX'], 'Office Open XML Document Macro-enabled'], 218 # Note: I have seen a password-protected DOCX file which was FPX-like, so I assume 219 # that any other MS Office file could be like this too. The only difference is 220 # that the ZIP and FPX formats are checked first, so if this is wrong, no biggie. 221 DOCX => [['ZIP','FPX'], 'Office Open XML Document'], 222 DOT => ['FPX', 'Microsoft Word Template'], 223 DOTM => [['ZIP','FPX'], 'Office Open XML Document Template Macro-enabled'], 224 DOTX => [['ZIP','FPX'], 'Office Open XML Document Template'], 225 DV => ['DV', 'Digital Video'], 226 DVB => ['MOV', 'Digital Video Broadcasting'], 227 DYLIB=> ['EXE', 'Mach-O Dynamic Link Library'], 228 EIP => ['ZIP', 'Capture One Enhanced Image Package'], 162 229 EPS => ['EPS', 'Encapsulated PostScript Format'], 163 EPSF => ['EPS', 'Encapsulated PostScript Format'], 164 ERF => ['TIFF', 'Epson Raw Format (TIFF-like)'], 230 EPS2 => 'EPS', 231 EPS3 => 'EPS', 232 EPSF => 'EPS', 233 ERF => ['TIFF', 'Epson Raw Format'], 234 EXE => ['EXE', 'Windows executable file'], 235 EXIF => ['EXIF', 'Exchangable Image File Metadata'], 236 F4A => ['MOV', 'Adobe Flash Player 9+ Audio'], 237 F4B => ['MOV', 'Adobe Flash Player 9+ audio Book'], 238 F4P => ['MOV', 'Adobe Flash Player 9+ Protected'], 239 F4V => ['MOV', 'Adobe Flash Player 9+ Video'], 165 240 FLAC => ['FLAC', 'Free Lossless Audio Codec'], 241 FLA => ['FPX', 'Macromedia/Adobe Flash project'], 166 242 FLV => ['FLV', 'Flash Video'], 167 243 FPX => ['FPX', 'FlashPix'], 168 244 GIF => ['GIF', 'Compuserve Graphics Interchange Format'], 169 HTM => ['HTML', 'HyperText Markup Language'], 245 GZ => 'GZIP', 246 GZIP => ['GZIP', 'GNU ZIP compressed archive'], 247 HDP => ['TIFF', 'Windows HD Photo'], 248 HTM => 'HTML', 170 249 HTML => ['HTML', 'HyperText Markup Language'], 171 250 ICC => ['ICC', 'International Color Consortium'], 172 ICM => ['ICC', 'International Color Consortium'], 173 JNG => ['PNG', 'JPG Network Graphics (PNG-like)'], 251 ICM => 'ICC', 252 IIQ => ['TIFF', 'Phase One Intelligent Image Quality RAW'], 253 IND => ['IND', 'Adobe InDesign'], 254 INDD => ['IND', 'Adobe InDesign Document'], 255 INDT => ['IND', 'Adobe InDesign Template'], 256 ITC => ['ITC', 'iTunes Cover Flow'], 257 JNG => ['PNG', 'JPG Network Graphics'], 174 258 JP2 => ['JP2', 'JPEG 2000 file'], 175 JPEG => ['JPEG', 'Joint Photographic Experts Group'], 259 # JP4? - looks like a JPEG but the image data is different 260 JPEG => 'JPG', 176 261 JPG => ['JPEG', 'Joint Photographic Experts Group'], 177 JPX => ['JP2', 'JPEG 2000 file'], 178 K25 => ['TIFF', 'Kodak DC25 RAW (TIFF-like)'], 179 M4A => ['MOV', 'MPG4 Audio (QuickTime-based)'], 180 MEF => ['TIFF', 'Mamiya (RAW) Electronic Format (TIFF-like)'], 262 JPM => ['JP2', 'JPEG 2000 compound image'], 263 JPX => ['JP2', 'JPEG 2000 with extensions'], 264 K25 => ['TIFF', 'Kodak DC25 RAW'], 265 KDC => ['TIFF', 'Kodak Digital Camera RAW'], 266 KEY => ['ZIP', 'Apple Keynote presentation'], 267 KTH => ['ZIP', 'Apple Keynote Theme'], 268 LNK => ['LNK', 'Windows shortcut'], 269 M2T => 'M2TS', 270 M2TS => ['M2TS', 'MPEG-2 Transport Stream'], 271 M2V => ['MPEG', 'MPEG-2 Video'], 272 M4A => ['MOV', 'MPEG-4 Audio'], 273 M4B => ['MOV', 'MPEG-4 audio Book'], 274 M4P => ['MOV', 'MPEG-4 Protected'], 275 M4V => ['MOV', 'MPEG-4 Video'], 276 MEF => ['TIFF', 'Mamiya (RAW) Electronic Format'], 181 277 MIE => ['MIE', 'Meta Information Encapsulation format'], 182 MIF => ['MIFF', 'Magick Image File Format'],278 MIF => 'MIFF', 183 279 MIFF => ['MIFF', 'Magick Image File Format'], 184 MNG => ['PNG', 'Multiple-image Network Graphics (PNG-like)'], 185 MOS => ['TIFF', 'Creo Leaf Mosaic (TIFF-like)'], 280 MKA => ['MKV', 'Matroska Audio'], 281 MKS => ['MKV', 'Matroska Subtitle'], 282 MKV => ['MKV', 'Matroska Video'], 283 MNG => ['PNG', 'Multiple-image Network Graphics'], 284 # MODD => ['PLIST','Sony Picture Motion Metadata'], 285 MOS => ['TIFF', 'Creo Leaf Mosaic'], 186 286 MOV => ['MOV', 'Apple QuickTime movie'], 187 MP3 => ['MP3', 'MPEG Layer 3 audio (uses ID3 information)'],188 MP4 => ['MOV', 'MPEG Layer 4 video (QuickTime-based)'],287 MP3 => ['MP3', 'MPEG-1 Layer 3 audio'], 288 MP4 => ['MOV', 'MPEG-4 video'], 189 289 MPC => ['MPC', 'Musepack Audio'], 190 MPEG => ['MPEG', 'MPEG audio/video format 1'], 191 MPG => ['MPEG', 'MPEG audio/video format 1'], 290 MPEG => ['MPEG', 'MPEG-1 or MPEG-2 audio/video'], 291 MPG => 'MPEG', 292 MPO => ['JPEG', 'Extended Multi-Picture format'], 293 MQV => ['MOV', 'Sony Mobile Quicktime Video'], 192 294 MRW => ['MRW', 'Minolta RAW format'], 193 NEF => ['TIFF', 'Nikon (RAW) Electronic Format (TIFF-like)'], 295 MTS => ['M2TS', 'MPEG-2 Transport Stream'], 296 MXF => ['MXF', 'Material Exchange Format'], 297 # NDPI => ['TIFF', 'Hamamatsu NanoZoomer Digital Pathology Image'], 298 NEF => ['TIFF', 'Nikon (RAW) Electronic Format'], 299 NEWER => 'COS', 300 NMBTEMPLATE => ['ZIP','Apple Numbers Template'], 301 NRW => ['TIFF', 'Nikon RAW (2)'], 302 NUMBERS => ['ZIP','Apple Numbers spreadsheet'], 303 ODP => ['ZIP', 'Open Document Presentation'], 304 ODS => ['ZIP', 'Open Document Spreadsheet'], 305 ODT => ['ZIP', 'Open Document Text file'], 194 306 OGG => ['OGG', 'Ogg Vorbis audio file'], 195 307 ORF => ['ORF', 'Olympus RAW format'], 196 PBM => ['PPM', 'Portable BitMap (PPM-like)'], 197 PCT => ['PICT', 'Apple PICTure'], 308 OTF => ['Font', 'Open Type Font'], 309 PAGES => ['ZIP', 'Apple Pages document'], 310 PBM => ['PPM', 'Portable BitMap'], 311 PCT => 'PICT', 198 312 PDF => ['PDF', 'Adobe Portable Document Format'], 199 PEF => ['TIFF', 'Pentax (RAW) Electronic Format (TIFF-like)'], 200 PGM => ['PPM', 'Portable Gray Map (PPM-like)'], 313 PEF => ['TIFF', 'Pentax (RAW) Electronic Format'], 314 PFA => ['Font', 'PostScript Font ASCII'], 315 PFB => ['Font', 'PostScript Font Binary'], 316 PFM => ['Font', 'Printer Font Metrics'], 317 PGF => ['PGF', 'Progressive Graphics File'], 318 PGM => ['PPM', 'Portable Gray Map'], 201 319 PICT => ['PICT', 'Apple PICTure'], 320 # PLIST=> ['PLIST','Apple Property List'], 321 PMP => ['PMP', 'Sony DSC-F1 Cyber-Shot PMP'], # should stand for Proprietery Metadata Package ;) 202 322 PNG => ['PNG', 'Portable Network Graphics'], 323 POT => ['FPX', 'Microsoft PowerPoint Template'], 324 POTM => [['ZIP','FPX'], 'Office Open XML Presentation Template Macro-enabled'], 325 POTX => [['ZIP','FPX'], 'Office Open XML Presentation Template'], 203 326 PPM => ['PPM', 'Portable Pixel Map'], 204 PPT => ['FPX', 'Microsoft PowerPoint presentation (FPX-like)'], 327 PPS => ['FPX', 'Microsoft PowerPoint Slideshow'], 328 PPSM => [['ZIP','FPX'], 'Office Open XML Presentation Slideshow Macro-enabled'], 329 PPSX => [['ZIP','FPX'], 'Office Open XML Presentation Slideshow'], 330 PPT => ['FPX', 'Microsoft PowerPoint Presentation'], 331 PPTM => [['ZIP','FPX'], 'Office Open XML Presentation Macro-enabled'], 332 PPTX => [['ZIP','FPX'], 'Office Open XML Presentation'], 205 333 PS => ['PS', 'PostScript'], 206 PSD => ['PSD', 'PhotoShop Drawing'], 207 QIF => ['QTIF', 'QuickTime Image File'], 334 PS2 => 'PS', 335 PS3 => 'PS', 336 PSB => ['PSD', 'Photoshop Large Document'], 337 PSD => ['PSD', 'Photoshop Drawing'], 338 PSP => ['PSP', 'Paint Shop Pro'], 339 PSPFRAME => 'PSP', 340 PSPIMAGE => 'PSP', 341 PSPSHAPE => 'PSP', 342 PSPTUBE => 'PSP', 343 QIF => 'QTIF', 208 344 QT => ['MOV', 'QuickTime movie'], 209 QTI => ['QTIF', 'QuickTime Image File'],345 QTI => 'QTIF', 210 346 QTIF => ['QTIF', 'QuickTime Image File'], 211 347 RA => ['Real', 'Real Audio'], 212 348 RAF => ['RAF', 'FujiFilm RAW Format'], 213 349 RAM => ['Real', 'Real Audio Metafile'], 214 RAW => ['RAW', 'Kyocera Contax N Digital RAW or Panasonic RAW'], 215 RIF => ['RIFF', 'Resource Interchange File Format'], 350 RAR => ['RAR', 'RAR Archive'], 351 RAW => [['RAW','TIFF'], 'Kyocera Contax N Digital RAW or Panasonic RAW'], 352 RIF => 'RIFF', 216 353 RIFF => ['RIFF', 'Resource Interchange File Format'], 217 354 RM => ['Real', 'Real Media'], 218 355 RMVB => ['Real', 'Real Media Variable Bitrate'], 219 356 RPM => ['Real', 'Real Media Plug-in Metafile'], 357 RSRC => ['RSRC', 'Mac OS Resource'], 358 RTF => ['RTF', 'Rich Text Format'], 220 359 RV => ['Real', 'Real Video'], 221 SR2 => ['TIFF', 'Sony RAW Format 2 (TIFF-like)'], 222 SRF => ['TIFF', 'Sony RAW Format (TIFF-like)'], 360 RW2 => ['TIFF', 'Panasonic RAW 2'], 361 RWL => ['TIFF', 'Leica RAW'], 362 RWZ => ['RWZ', 'Rawzor compressed image'], 363 SO => ['EXE', 'Shared Object file'], 364 SR2 => ['TIFF', 'Sony RAW Format 2'], 365 SRF => ['TIFF', 'Sony RAW Format'], 366 SRW => ['TIFF', 'Samsung RAW format'], 367 SVG => ['XMP', 'Scalable Vector Graphics'], 223 368 SWF => ['SWF', 'Shockwave Flash'], 224 THM => ['JPEG', 'Canon Thumbnail (aka. JPG)'], 225 TIF => ['TIFF', 'Tagged Image File Format'], 369 TAR => ['TAR', 'TAR archive'], 370 THM => ['JPEG', 'Canon Thumbnail'], 371 THMX => [['ZIP','FPX'], 'Office Open XML Theme'], 372 TIF => 'TIFF', 226 373 TIFF => ['TIFF', 'Tagged Image File Format'], 227 VRD => ['VRD', 'Canon VRD Recipe Data (written by DPP)'], 228 WAV => ['RIFF', 'WAVeform (Windows digital audio format)'], 229 WDP => ['TIFF', 'Windows Media Photo (TIFF-based)'], 230 WMA => ['ASF', 'Windows Media Audio (ASF-based)'], 231 WMV => ['ASF', 'Windows Media Video (ASF-based)'], 374 TS => 'M2TS', 375 TTC => ['Font', 'True Type Font Collection'], 376 TTF => ['Font', 'True Type Font'], 377 TUB => 'PSP', 378 VOB => ['MPEG', 'Video Object'], 379 VRD => ['VRD', 'Canon VRD Recipe Data'], 380 VSD => ['FPX', 'Microsoft Visio Drawing'], 381 WAV => ['RIFF', 'WAVeform (Windows digital audio)'], 382 WDP => ['TIFF', 'Windows Media Photo'], 383 WEBM => ['MKV', 'Google Web Movie'], 384 WEBP => ['RIFF', 'Google Web Picture'], 385 WMA => ['ASF', 'Windows Media Audio'], 386 WMV => ['ASF', 'Windows Media Video'], 232 387 X3F => ['X3F', 'Sigma RAW format'], 388 XCF => ['XCF', 'GIMP native image format'], 233 389 XHTML=> ['HTML', 'Extensible HyperText Markup Language'], 234 XLS => ['FPX', 'Microsoft Excel worksheet (FPX-like)'], 235 XMP => ['XMP', 'Extensible Metadata Platform data file'], 390 XLA => ['FPX', 'Microsoft Excel Add-in'], 391 XLAM => [['ZIP','FPX'], 'Office Open XML Spreadsheet Add-in Macro-enabled'], 392 XLS => ['FPX', 'Microsoft Excel Spreadsheet'], 393 XLSB => [['ZIP','FPX'], 'Office Open XML Spreadsheet Binary'], 394 XLSM => [['ZIP','FPX'], 'Office Open XML Spreadsheet Macro-enabled'], 395 XLSX => [['ZIP','FPX'], 'Office Open XML Spreadsheet'], 396 XLT => ['FPX', 'Microsoft Excel Template'], 397 XLTM => [['ZIP','FPX'], 'Office Open XML Spreadsheet Template Macro-enabled'], 398 XLTX => [['ZIP','FPX'], 'Office Open XML Spreadsheet Template'], 399 XMP => ['XMP', 'Extensible Metadata Platform'], 400 ZIP => ['ZIP', 'ZIP archive'], 236 401 ); 237 402 403 # descriptions for file types not found in above file extension lookup 404 my %fileDescription = ( 405 DICOM => 'Digital Imaging and Communications in Medicine', 406 PLIST => 'Property List', 407 XML => 'Extensible Markup Language', 408 'DJVU (multi-page)' => 'DjVu multi-page image', 409 'Win32 EXE' => 'Windows 32-bit Executable', 410 'Win32 DLL' => 'Windows 32-bit Dynamic Link Library', 411 ); 412 238 413 # MIME types for applicable file types above 239 # (missing entries default to 'application/unknown') 240 my %mimeType = ( 241 AIFF => 'audio/aiff', 414 # (missing entries default to 'application/unknown', but note that 415 # other mime types may be specified by some modules, ie. QuickTime.pm) 416 %mimeType = ( 417 '3FR' => 'image/x-hasselblad-3fr', 418 AI => 'application/vnd.adobe.illustrator', 419 AIFF => 'audio/x-aiff', 242 420 APE => 'audio/x-monkeys-audio', 243 421 ASF => 'video/x-ms-asf', 244 ARW => 'image/x- raw',245 AVI => 'video/ avi',422 ARW => 'image/x-sony-arw', 423 AVI => 'video/x-msvideo', 246 424 BMP => 'image/bmp', 247 BTF => 'application/unknown', #TEMPORARY! 248 CR2 => 'image/x-raw', 249 CRW => 'image/x-raw', 425 BTF => 'image/x-tiff-big', #(NC) (ref http://www.asmail.be/msg0055371937.html) 426 BZ2 => 'application/bzip2', 427 'Canon 1D RAW' => 'image/x-raw', # (uses .TIF file extension) 428 CR2 => 'image/x-canon-cr2', 429 CRW => 'image/x-canon-crw', 430 DCR => 'image/x-kodak-dcr', 431 DFONT=> 'application/x-dfont', 432 DICM => 'application/dicom', 433 DIVX => 'video/divx', 434 DJVU => 'image/vnd.djvu', 435 DNG => 'image/x-adobe-dng', 436 DOC => 'application/msword', 437 DOCM => 'application/vnd.ms-word.document.macroEnabled', 438 DOCX => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 439 DOT => 'application/msword', 440 DOTM => 'application/vnd.ms-word.template.macroEnabledTemplate', 441 DOTX => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template', 442 DV => 'video/x-dv', 443 EIP => 'application/x-captureone', #(NC) 250 444 EPS => 'application/postscript', 251 ERF => 'image/x-raw', 252 DCR => 'image/x-raw', 253 DICM => 'application/dicom', 254 DNG => 'image/x-raw', 255 DOC => 'application/msword', 445 ERF => 'image/x-epson-erf', 446 EXE => 'application/octet-stream', 447 FLA => 'application/vnd.adobe.fla', 256 448 FLAC => 'audio/flac', 257 449 FLV => 'video/x-flv', 450 Font => 'application/x-font-type1', # covers PFA, PFB and PFM (not sure about PFM) 258 451 FPX => 'image/vnd.fpx', 259 452 GIF => 'image/gif', 453 GZIP => 'application/x-gzip', 454 HDP => 'image/vnd.ms-photo', 260 455 HTML => 'text/html', 456 ICC => 'application/vnd.iccprofile', 457 IIQ => 'image/x-raw', 458 IND => 'application/x-indesign', 459 ITC => 'application/itunes', 261 460 JNG => 'image/jng', 262 JP2 => 'image/jp eg2000',461 JP2 => 'image/jp2', 263 462 JPEG => 'image/jpeg', 264 K25 => 'image/x-raw', 265 M4A => 'audio/mp4', 266 MEF => 'image/x-raw', 463 JPM => 'image/jpm', 464 JPX => 'image/jpx', 465 K25 => 'image/x-kodak-k25', 466 KDC => 'image/x-kodak-kdc', 467 LNK => 'application/octet-stream', 468 M2T => 'video/mpeg', 469 M2TS => 'video/m2ts', 470 MEF => 'image/x-mamiya-mef', 267 471 MIE => 'application/x-mie', 268 472 MIFF => 'application/x-magick-image', 473 MKA => 'audio/x-matroska', 474 MKS => 'application/x-matroska', 475 MKV => 'video/x-matroska', 269 476 MNG => 'video/mng', 270 477 MOS => 'image/x-raw', … … 274 481 MPC => 'audio/x-musepack', 275 482 MPEG => 'video/mpeg', 276 MRW => 'image/x-raw', 277 NEF => 'image/x-raw', 483 MRW => 'image/x-minolta-mrw', 484 MXF => 'application/mxf', 485 NEF => 'image/x-nikon-nef', 486 NRW => 'image/x-nikon-nrw', 487 ODP => 'application/vnd.oasis.opendocument.presentation', 488 ODS => 'application/vnd.oasis.opendocument.spreadsheet', 489 ODT => 'application/vnd.oasis.opendocument.text', 278 490 OGG => 'audio/x-ogg', 279 ORF => 'image/x-raw', 491 ORF => 'image/x-olympus-orf', 492 OTF => 'application/x-font-otf', 280 493 PBM => 'image/x-portable-bitmap', 281 494 PDF => 'application/pdf', 282 PEF => 'image/x-raw', 495 PEF => 'image/x-pentax-pef', 496 PGF => 'image/pgf', 283 497 PGM => 'image/x-portable-graymap', 284 498 PICT => 'image/pict', 499 PLIST=> 'application/xml', 285 500 PNG => 'image/png', 501 POT => 'application/vnd.ms-powerpoint', 502 POTM => 'application/vnd.ms-powerpoint.template.macroEnabled', 503 POTX => 'application/vnd.openxmlformats-officedocument.presentationml.template', 286 504 PPM => 'image/x-portable-pixmap', 505 PPS => 'application/vnd.ms-powerpoint', 506 PPSM => 'application/vnd.ms-powerpoint.slideshow.macroEnabled', 507 PPSX => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow', 287 508 PPT => 'application/vnd.ms-powerpoint', 509 PPTM => 'application/vnd.ms-powerpoint.presentation.macroEnabled', 510 PPTX => 'application/vnd.openxmlformats-officedocument.presentationml.presentation', 288 511 PS => 'application/postscript', 289 PSD => 'application/photoshop', 512 PSD => 'application/vnd.adobe.photoshop', 513 PSP => 'image/x-paintshoppro', #(NC) 290 514 QTIF => 'image/x-quicktime', 291 515 RA => 'audio/x-pn-realaudio', 292 RAF => 'image/x- raw',516 RAF => 'image/x-fujifilm-raf', 293 517 RAM => 'audio/x-pn-realaudio', 518 RAR => 'application/x-rar-compressed', 294 519 RAW => 'image/x-raw', 295 520 RM => 'application/vnd.rn-realmedia', 296 521 RMVB => 'application/vnd.rn-realmedia-vbr', 297 522 RPM => 'audio/x-pn-realaudio-plugin', 523 RSRC => 'application/ResEdit', 524 RTF => 'text/rtf', 298 525 RV => 'video/vnd.rn-realvideo', 299 SR2 => 'image/x-raw', 300 SRF => 'image/x-raw', 526 RW2 => 'image/x-panasonic-rw2', 527 RWL => 'image/x-leica-rwl', 528 RWZ => 'image/x-rawzor', #(duplicated in Rawzor.pm) 529 SR2 => 'image/x-sony-sr2', 530 SRF => 'image/x-sony-srf', 531 SRW => 'image/x-samsung-srw', 532 SVG => 'image/svg+xml', 301 533 SWF => 'application/x-shockwave-flash', 534 TAR => 'application/x-tar', 535 THMX => 'application/vnd.ms-officetheme', 302 536 TIFF => 'image/tiff', 537 TTC => 'application/x-font-ttf', 538 TTF => 'application/x-font-ttf', 539 VSD => 'application/x-visio', 303 540 WAV => 'audio/x-wav', 304 541 WDP => 'image/vnd.ms-photo', 542 WEBM => 'video/webm', 543 WEBP => 'image/webp', 305 544 WMA => 'audio/x-ms-wma', 306 545 WMV => 'video/x-ms-wmv', 307 X3F => 'image/x-raw', 546 X3F => 'image/x-sigma-x3f', 547 XCF => 'image/x-xcf', 548 XLA => 'application/vnd.ms-excel', 549 XLAM => 'application/vnd.ms-excel.addin.macroEnabled', 308 550 XLS => 'application/vnd.ms-excel', 551 XLSB => 'application/vnd.ms-excel.sheet.binary.macroEnabled', 552 XLSM => 'application/vnd.ms-excel.sheet.macroEnabled', 553 XLSX => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 554 XLT => 'application/vnd.ms-excel', 555 XLTM => 'application/vnd.ms-excel.template.macroEnabled', 556 XLTX => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template', 557 XML => 'application/xml', 309 558 XMP => 'application/rdf+xml', 559 ZIP => 'application/zip', 310 560 ); 311 561 312 # module names for each file type 313 # (missing entries have same module name as file type) 562 # module names for processing routines of each file type 563 # - undefined entries default to same module name as file type 564 # - module name '' defaults to Image::ExifTool 565 # - module name '0' indicates a recognized but unsupported file 314 566 my %moduleName = ( 315 567 BTF => 'BigTIFF', 568 BZ2 => 0, 316 569 CRW => 'CanonRaw', 317 570 DICM => 'DICOM', 571 COS => 'CaptureOne', 572 DOCX => 'OOXML', 318 573 EPS => 'PostScript', 574 EXIF => '', 319 575 ICC => 'ICC_Profile', 576 IND => 'InDesign', 320 577 FLV => 'Flash', 321 578 FPX => 'FlashPix', 579 GZIP => 'ZIP', 322 580 JP2 => 'Jpeg2000', 323 JPEG => '', # (in the current module) 581 JPEG => '', 582 # MODD => 'XML', 324 583 MOV => 'QuickTime', 584 MKV => 'Matroska', 325 585 MP3 => 'ID3', 326 586 MRW => 'MinoltaRaw', 327 587 OGG => 'Vorbis', 328 588 ORF => 'Olympus', 589 # PLIST=> 'XML', 590 PMP => 'Sony', 329 591 PS => 'PostScript', 330 592 PSD => 'Photoshop', 331 593 QTIF => 'QuickTime', 332 594 RAF => 'FujiFilm', 595 RAR => 'ZIP', 333 596 RAW => 'KyoceraRaw', 597 RWZ => 'Rawzor', 334 598 SWF => 'Flash', 599 TAR => 0, 335 600 TIFF => '', 336 601 VRD => 'CanonVRD', 337 602 X3F => 'SigmaRaw', 603 XCF => 'GIMP', 604 ); 605 606 # quick "magic number" file test used to avoid loading module unnecessarily: 607 # - regular expression evaluated on first 1024 bytes of file 608 # - must match beginning at first byte in file 609 # - this test must not be more stringent than module logic 610 %magicNumber = ( 611 AIFF => '(FORM....AIF[FC]|AT&TFORM)', 612 APE => '(MAC |APETAGEX|ID3)', 613 ASF => '\x30\x26\xb2\x75\x8e\x66\xcf\x11\xa6\xd9\x00\xaa\x00\x62\xce\x6c', 614 BMP => 'BM', 615 BTF => '(II\x2b\0|MM\0\x2b)', 616 BZ2 => 'BZh[1-9]\x31\x41\x59\x26\x53\x59', 617 CRW => '(II|MM).{4}HEAP(CCDR|JPGM)', 618 DICM => '(.{128}DICM|\0[\x02\x04\x06\x08]\0[\0-\x20]|[\x02\x04\x06\x08]\0[\0-\x20]\0)', 619 DOCX => 'PK\x03\x04', 620 DV => '\x1f\x07\0[\x3f\xbf]', # (not tested if extension recognized) 621 EPS => '(%!PS|%!Ad|\xc5\xd0\xd3\xc6)', 622 EXE => '(MZ|\xca\xfe\xba\xbe|\xfe\xed\xfa[\xce\xcf]|[\xce\xcf]\xfa\xed\xfe|Joy!peff|\x7fELF|#!\s*/\S*bin/|!<arch>\x0a)', 623 EXIF => '(II\x2a\0|MM\0\x2a)', 624 FLAC => '(fLaC|ID3)', 625 FLV => 'FLV\x01', 626 Font => '((\0\x01\0\0|OTTO|true|typ1)[\0\x01]|ttcf\0[\x01\x02]\0\0|\0[\x01\x02]|' . 627 '(.{6})?%!(PS-(AdobeFont-|Bitstream )|FontType1-)|Start(Comp|Master)?FontMetrics)', 628 FPX => '\xd0\xcf\x11\xe0\xa1\xb1\x1a\xe1', 629 GIF => 'GIF8[79]a', 630 GZIP => '\x1f\x8b\x08', 631 HTML => '(?i)<(!DOCTYPE\s+HTML|HTML|\?xml)', # (case insensitive) 632 ICC => '.{12}(scnr|mntr|prtr|link|spac|abst|nmcl|nkpf)(XYZ |Lab |Luv |YCbr|Yxy |RGB |GRAY|HSV |HLS |CMYK|CMY |[2-9A-F]CLR){2}', 633 IND => '\x06\x06\xed\xf5\xd8\x1d\x46\xe5\xbd\x31\xef\xe7\xfe\x74\xb7\x1d', 634 ITC => '.{4}itch', 635 JP2 => '\0\0\0\x0cjP( |\x1a\x1a)\x0d\x0a\x87\x0a', 636 JPEG => '\xff\xd8\xff', 637 LNK => '.{4}\x01\x14\x02\0{5}\xc0\0{6}\x46', 638 M2TS => '(....)?\x47', 639 MIE => '~[\x10\x18]\x04.0MIE', 640 MIFF => 'id=ImageMagick', 641 MKV => '\x1a\x45\xdf\xa3', 642 MOV => '.{4}(free|skip|wide|ftyp|pnot|PICT|pict|moov|mdat|junk|uuid)', 643 # MP3 => difficult to rule out 644 MPC => '(MP\+|ID3)', 645 MPEG => '\0\0\x01[\xb0-\xbf]', 646 MRW => '\0MR[MI]', 647 MXF => '\x06\x0e\x2b\x34\x02\x05\x01\x01\x0d\x01\x02', # (not tested if extension recognized) 648 OGG => '(OggS|ID3)', 649 ORF => '(II|MM)', 650 PDF => '%PDF-\d+\.\d+', 651 PGF => 'PGF', 652 PICT => '(.{10}|.{522})(\x11\x01|\x00\x11)', 653 PMP => '.{8}\0{3}\x7c.{112}\xff\xd8\xff\xdb', 654 PNG => '(\x89P|\x8aM|\x8bJ)NG\r\n\x1a\n', 655 PPM => 'P[1-6]\s+', 656 PS => '(%!PS|%!Ad|\xc5\xd0\xd3\xc6)', 657 PSD => '8BPS\0[\x01\x02]', 658 PSP => 'Paint Shop Pro Image File\x0a\x1a\0{5}', 659 QTIF => '.{4}(idsc|idat|iicc)', 660 RAF => 'FUJIFILM', 661 RAR => 'Rar!\x1a\x07\0', 662 RAW => '(.{25}ARECOYK|II|MM)', 663 Real => '(\.RMF|\.ra\xfd|pnm://|rtsp://|http://)', 664 RIFF => 'RIFF', 665 RSRC => '(....)?\0\0\x01\0', 666 RTF => '[\n\r]*\\{[\n\r]*\\\\rtf', 667 # (don't be too restrictive for RW2/RWL -- how does magic number change for big-endian?) 668 RW2 => '(II|MM)', #(\x55\0\x18\0\0\0\x88\xe7\x74\xd8\xf8\x25\x1d\x4d\x94\x7a\x6e\x77\x82\x2b\x5d\x6a) 669 RWL => '(II|MM)', #(ditto) 670 RWZ => 'rawzor', 671 SWF => '[FC]WS[^\0]', 672 TAR => '.{257}ustar( )?\0', # (this doesn't catch old-style tar files) 673 TIFF => '(II|MM)', # don't test magic number (some raw formats are different) 674 VRD => 'CANON OPTIONAL DATA\0', 675 X3F => 'FOVb', 676 XCF => 'gimp xcf ', 677 XMP => '\0{0,3}(\xfe\xff|\xff\xfe|\xef\xbb\xbf)?\0{0,3}\s*<', 678 ZIP => 'PK\x03\x04', 679 ); 680 681 # lookup for valid character set names (keys are all lower case) 682 %charsetName = ( 683 # Charset setting alias(es) 684 # ------------------------- -------------------------------------------- 685 utf8 => 'UTF8', cp65001 => 'UTF8', 'utf-8' => 'UTF8', 686 latin => 'Latin', cp1252 => 'Latin', latin1 => 'Latin', 687 latin2 => 'Latin2', cp1250 => 'Latin2', 688 cyrillic => 'Cyrillic', cp1251 => 'Cyrillic', russian => 'Cyrillic', 689 greek => 'Greek', cp1253 => 'Greek', 690 turkish => 'Turkish', cp1254 => 'Turkish', 691 hebrew => 'Hebrew', cp1255 => 'Hebrew', 692 arabic => 'Arabic', cp1256 => 'Arabic', 693 baltic => 'Baltic', cp1257 => 'Baltic', 694 vietnam => 'Vietnam', cp1258 => 'Vietnam', 695 thai => 'Thai', cp874 => 'Thai', 696 macroman => 'MacRoman', cp10000 => 'MacRoman', mac => 'MacRoman', roman => 'MacRoman', 697 maclatin2 => 'MacLatin2', cp10029 => 'MacLatin2', 698 maccyrillic => 'MacCyrillic', cp10007 => 'MacCyrillic', 699 macgreek => 'MacGreek', cp10006 => 'MacGreek', 700 macturkish => 'MacTurkish', cp10081 => 'MacTurkish', 701 macromanian => 'MacRomanian', cp10010 => 'MacRomanian', 702 maciceland => 'MacIceland', cp10079 => 'MacIceland', 703 maccroatian => 'MacCroatian', cp10082 => 'MacCroatian', 338 704 ); 339 705 … … 343 709 # group hash for ExifTool-generated tags 344 710 my %allGroupsExifTool = ( 0 => 'ExifTool', 1 => 'ExifTool', 2 => 'ExifTool' ); 711 712 # special tag names (not used for tag info) 713 my %specialTags = ( 714 TABLE_NAME=>1, SHORT_NAME=>1, PROCESS_PROC=>1, WRITE_PROC=>1, CHECK_PROC=>1, 715 GROUPS=>1, FORMAT=>1, FIRST_ENTRY=>1, TAG_PREFIX=>1, PRINT_CONV=>1, 716 WRITABLE=>1, TABLE_DESC=>1, NOTES=>1, IS_OFFSET=>1, EXTRACT_UNKNOWN=>1, 717 NAMESPACE=>1, PREFERRED=>1, SRC_TABLE=>1, PRIORITY=>1, WRITE_GROUP=>1, 718 LANG_INFO=>1, VARS=>1, DATAMEMBER=>1, IS_SUBDIR=>1, SET_GROUP1=>1, 719 ); 345 720 346 721 # headers for various segment types 347 722 $exifAPP1hdr = "Exif\0\0"; 348 723 $xmpAPP1hdr = "http://ns.adobe.com/xap/1.0/\0"; 724 $xmpExtAPP1hdr = "http://ns.adobe.com/xmp/extension/\0"; 349 725 $psAPP13hdr = "Photoshop 3.0\0"; 350 726 $psAPP13old = 'Adobe_Photoshop2.5:'; 351 727 352 728 sub DummyWriteProc { return 1; } 729 730 # lookup for user lenses defined in @Image::ExifTool::UserDefined::Lenses 731 %Image::ExifTool::userLens = ( ); 732 733 # queued plug-in tags to add to lookup 734 @Image::ExifTool::pluginTags = ( ); 735 %Image::ExifTool::pluginTags = ( ); 353 736 354 737 # tag information for preview image -- this should be used for all … … 360 743 WriteCheck => '$val eq "none" ? undef : $self->CheckImage(\$val)', 361 744 DataTag => 'PreviewImage', 745 # accept either scalar or scalar reference 746 RawConv => '$self->ValidateImage(ref $val ? $val : \$val, $tag)', 362 747 # we allow preview image to be set to '', but we don't want a zero-length value 363 748 # in the IFD, so set it temorarily to 'none'. Note that the length is <= 4, 364 749 # so this value will fit in the IFD so the preview fixup won't be generated. 365 ValueConv => '$self->ValidateImage(\$val,$tag)',366 750 ValueConvInv => '$val eq "" and $val="none"; $val', 367 751 ); … … 372 756 %Image::ExifTool::Extra = ( 373 757 GROUPS => { 0 => 'File', 1 => 'File', 2 => 'Image' }, 374 DID_TAG_ID => 1,# tag ID's aren't meaningful for these tags758 VARS => { NO_ID => 1 }, # tag ID's aren't meaningful for these tags 375 759 WRITE_PROC => \&DummyWriteProc, 760 Error => { Priority => 0, Groups => \%allGroupsExifTool }, 761 Warning => { Priority => 0, Groups => \%allGroupsExifTool }, 376 762 Comment => { 377 763 Notes => 'comment embedded in JPEG, GIF89a or PPM/PGM/PBM image', … … 381 767 }, 382 768 Directory => { 769 Groups => { 1 => 'System' }, 383 770 Writable => 1, 384 771 Protected => 1, … … 387 774 }, 388 775 FileName => { 776 Groups => { 1 => 'System' }, 389 777 Writable => 1, 390 778 Protected => 1, 779 Notes => q{ 780 may be written with a full path name to set FileName and Directory in one 781 operation. See L<filename.html|../filename.html> for more information on 782 writing the FileName and Directory tags 783 }, 391 784 ValueConvInv => '$val=~tr/\\\\/\//; $val', 392 785 }, 393 786 FileSize => { 394 PrintConv => sub { 395 my $val = shift; 396 $val < 2048 and return "$val bytes"; 397 $val < 2097152 and return sprintf('%.0f kB', $val / 1024); 398 return sprintf('%.0f MB', $val / 1048576); 787 Groups => { 1 => 'System' }, 788 PrintConv => \&ConvertFileSize, 789 }, 790 ResourceForkSize => { 791 Groups => { 1 => 'System' }, 792 Notes => q{ 793 [Mac OS only] size of the file's resource fork if it contains data. If this 794 tag is generated the ExtractEmbedded option may be used to extract 795 resource-fork information as a sub-document 399 796 }, 797 PrintConv => \&ConvertFileSize, 400 798 }, 401 799 FileType => { }, … … 403 801 Description => 'File Modification Date/Time', 404 802 Notes => 'the filesystem modification time', 405 Groups => { 2 => 'Time' },803 Groups => { 1 => 'System', 2 => 'Time' }, 406 804 Writable => 1, 805 # all pseudo-tags must be protected so -tagsfromfile fails with 806 # unrecognized files unless a pseudo tag is specified explicitly 807 Protected => 1, 407 808 Shift => 'Time', 408 ValueConv => 'ConvertUnixTime($val, "local")',409 ValueConvInv => 'GetUnixTime($val, "local")',809 ValueConv => 'ConvertUnixTime($val,1)', 810 ValueConvInv => 'GetUnixTime($val,1)', 410 811 PrintConv => '$self->ConvertDateTime($val)', 411 PrintConvInv => '$val', 812 PrintConvInv => '$self->InverseDateTime($val)', 813 }, 814 FilePermissions => { 815 Groups => { 1 => 'System' }, 816 Notes => q{ 817 r=read, w=write and x=execute permissions for the file owner, group and 818 others. The ValueConv value is an octal number so bit test operations on 819 this value should be done in octal, ie. "oct($filePermissions) & 0200" 820 }, 821 ValueConv => 'sprintf("%.3o", $val & 0777)', 822 PrintConv => sub { 823 my ($mask, $str, $val) = (0400, '', oct(shift)); 824 while ($mask) { 825 foreach (qw(r w x)) { 826 $str .= $val & $mask ? $_ : '-'; 827 $mask >>= 1; 828 } 829 } 830 return $str; 831 }, 412 832 }, 413 833 MIMEType => { }, … … 418 838 MaxVal => { }, # max pixel value in PPM or PGM image 419 839 EXIF => { 420 Notes => 'the full EXIF data block', 421 Groups => { 0 => 'EXIF' }, 422 Binary => 1, 840 Notes => 'the full EXIF data block from JPEG, PNG, JP2, MIE and MIFF images', 841 Groups => { 0 => 'EXIF', 1 => 'EXIF' }, 842 Flags => ['Writable' ,'Protected', 'Binary'], 843 WriteCheck => q{ 844 return undef if $val =~ /^(II\x2a\0|MM\0\x2a)/; 845 return 'Invalid EXIF data'; 846 }, 423 847 }, 424 848 ICC_Profile => { 425 849 Notes => 'the full ICC_Profile data block', 426 Groups => { 0 => 'ICC_Profile' },850 Groups => { 0 => 'ICC_Profile', 1 => 'ICC_Profile' }, 427 851 Flags => ['Writable' ,'Protected', 'Binary'], 428 852 WriteCheck => q{ … … 433 857 XMP => { 434 858 Notes => 'the full XMP data block', 435 Groups => { 0 => 'XMP' }, 436 Flags => [ 'Writable', 'Binary' ], 859 Groups => { 0 => 'XMP', 1 => 'XMP' }, 860 Flags => ['Writable', 'Protected', 'Binary'], 861 Priority => 0, # so main xmp (which usually comes first) takes priority 437 862 WriteCheck => q{ 438 863 require Image::ExifTool::XMP; … … 442 867 CanonVRD => { 443 868 Notes => 'the full Canon DPP VRD trailer block', 444 Groups => { 0 => 'CanonVRD' },869 Groups => { 0 => 'CanonVRD', 1 => 'CanonVRD' }, 445 870 Flags => ['Writable' ,'Protected', 'Binary'], 871 Permanent => 0, # (this is 1 by default for MakerNotes tags) 446 872 WriteCheck => q{ 447 873 return undef if $val =~ /^CANON OPTIONAL DATA\0/; … … 449 875 }, 450 876 }, 451 Encryption => { }, # PDF encryption filter 877 CurrentIPTCDigest => { 878 Notes => q{ 879 MD5 digest of existing IPTC data. All zeros if IPTC exists but Digest::MD5 880 is not installed. Only calculated for IPTC in the standard location as 881 specified by the L<MWG|http://www.metadataworkinggroup.org/>. ExifTool 882 automates the handling of this tag in the MWG module -- see the 883 L<MWG Tag Name documentation|MWG.html> for details 884 }, 885 ValueConv => 'unpack("H*", $val)', 886 }, 887 PreviewImage => { 888 Writable => 1, 889 WriteCheck => '$self->CheckImage(\$val)', 890 # can't delete, so set to empty string and return no error 891 DelCheck => '$val = ""; return undef', 892 # accept either scalar or scalar reference 893 RawConv => '$self->ValidateImage(ref $val ? $val : \$val, $tag)', 894 }, 895 PreviewPNG => { Binary => 1 }, 452 896 ExifByteOrder => { 453 897 Writable => 1, 454 898 Notes => 'only writable for newly created EXIF segments', 455 899 PrintConv => { 456 II => 'Little-endian (Intel)', 457 MM => 'Big-endian (Motorola)', 900 II => 'Little-endian (Intel, II)', 901 MM => 'Big-endian (Motorola, MM)', 902 }, 903 }, 904 ExifUnicodeByteOrder => { 905 Writable => 1, 906 Notes => q{ 907 the EXIF specification is particularly vague about the byte ordering for 908 Unicode text, and different applications use different conventions. By 909 default ExifTool writes Unicode text in EXIF byte order, but this write-only 910 tag may be used to force a specific byte order 911 }, 912 PrintConv => { 913 II => 'Little-endian (Intel, II)', 914 MM => 'Big-endian (Motorola, MM)', 458 915 }, 459 916 }, 460 917 ExifToolVersion => { 461 918 Description => 'ExifTool Version Number', 462 Groups => \%allGroupsExifTool919 Groups => \%allGroupsExifTool, 463 920 }, 464 Error => { Priority => 0, Groups => \%allGroupsExifTool }, 465 Warning => { Priority => 0, Groups => \%allGroupsExifTool }, 921 RAFVersion => { }, 922 JPEGDigest => { 923 Notes => q{ 924 an MD5 digest of the JPEG quantization tables is combined with the component 925 sub-sampling values to generate the value of this tag. The result is 926 compared to known values in an attempt to deduce the originating software 927 based only on the JPEG image data. For performance reasons, this tag is 928 generated only if specifically requested 929 }, 930 }, 931 Now => { 932 Groups => { 0 => 'ExifTool', 1 => 'ExifTool', 2 => 'Time' }, 933 Notes => q{ 934 the current date/time. Useful when setting the tag values, ie. 935 C<"-modifydate<now">. Not generated unless specifically requested 936 }, 937 ValueConv => sub { 938 my $time = shift; 939 my @tm = localtime $time; 940 my $tz = Image::ExifTool::TimeZoneString(\@tm, $time); 941 sprintf("%4d:%.2d:%.2d %.2d:%.2d:%.2d%s", $tm[5]+1900, $tm[4]+1, $tm[3], 942 $tm[2], $tm[1], $tm[0], $tz); 943 }, 944 PrintConv => '$self->ConvertDateTime($val)', 945 }, 946 ID3Size => { }, 947 Geotag => { 948 Writable => 1, 949 AllowGroup => '(exif|gps|xmp|xmp-exif)', 950 Notes => q{ 951 this write-only tag is used to define the GPS track log data or track log 952 file name. Currently supported track log formats are GPX, NMEA RMC/GGA/GLL, 953 KML, IGC, Garmin XML and TCX, and Magellan PMGNTRK. See 954 L<geotag.html|../geotag.html> for details 955 }, 956 DelCheck => q{ 957 require Image::ExifTool::Geotag; 958 # delete associated tags 959 Image::ExifTool::Geotag::SetGeoValues($self, undef, $wantGroup); 960 }, 961 ValueConvInv => q{ 962 require Image::ExifTool::Geotag; 963 # always warn because this tag is never set (warning is "\n" on success) 964 my $result = Image::ExifTool::Geotag::LoadTrackLog($self, $val); 965 return '' if not defined $result; # deleting geo tags 966 return $result if ref $result; # geotag data hash reference 967 warn "$result\n"; # error string 968 }, 969 }, 970 Geotime => { 971 Writable => 1, 972 AllowGroup => '(exif|gps|xmp|xmp-exif)', 973 Notes => q{ 974 this write-only tag is used to define a date/time for interpolating a 975 position in the GPS track specified by the Geotag tag. Writing this tag 976 causes the following 8 tags to be written: GPSLatitude, GPSLatitudeRef, 977 GPSLongitude, GPSLongitudeRef, GPSAltitude, GPSAltitudeRef, GPSDateStamp and 978 GPSTimeStamp. The local system timezone is assumed if the date/time value 979 does not contain a timezone. May be deleted to delete associated GPS tags. 980 A group name of 'EXIF' or 'XMP' may be specified to write or delete only 981 EXIF or XMP GPS tags. The value of Geotag must be assigned before this tag 982 }, 983 DelCheck => q{ 984 require Image::ExifTool::Geotag; 985 # delete associated tags 986 Image::ExifTool::Geotag::SetGeoValues($self, undef, $wantGroup); 987 }, 988 ValueConvInv => q{ 989 require Image::ExifTool::Geotag; 990 warn Image::ExifTool::Geotag::SetGeoValues($self, $val, $wantGroup) . "\n"; 991 return undef; 992 }, 993 }, 994 Geosync => { 995 Writable => 1, 996 AllowGroup => '(exif|gps|xmp|xmp-exif)', 997 Shift => 'Time', # enables "+=" syntax as well as "=+" 998 Notes => q{ 999 this write-only tag specifies a time difference to add to Geotime for 1000 synchronization with the GPS clock. For example, set this to "-12" if the 1001 camera clock is 12 seconds faster than GPS time. Input format is 1002 "[+-][[[DD ]HH:]MM:]SS[.ss]". Must be set before Geotime to be effective. 1003 Additional features allow calculation of time differences and time drifts, 1004 and extraction of synchronization times from image files. See the 1005 L<geotagging documentation|../geotag.html> for details 1006 }, 1007 ValueConvInv => q{ 1008 require Image::ExifTool::Geotag; 1009 return Image::ExifTool::Geotag::ConvertGeosync($self, $val); 1010 }, 1011 }, 466 1012 ); 467 1013 468 # information decoded from JPEG SOF frame 469 # (define this here to avoid loading JPEG.pm) 1014 # YCbCrSubSampling values (used by JPEG SOF, EXIF and XMP) 1015 %Image::ExifTool::JPEG::yCbCrSubSampling = ( 1016 '1 1' => 'YCbCr4:4:4 (1 1)', #PH 1017 '2 1' => 'YCbCr4:2:2 (2 1)', #14 in Exif.pm 1018 '2 2' => 'YCbCr4:2:0 (2 2)', #14 in Exif.pm 1019 '4 1' => 'YCbCr4:1:1 (4 1)', #14 in Exif.pm 1020 '4 2' => 'YCbCr4:1:0 (4 2)', #PH 1021 '1 2' => 'YCbCr4:4:0 (1 2)', #PH 1022 '1 4' => 'YCbCr4:4:1 (1 4)', #JD 1023 '2 4' => 'YCbCr4:2:1 (2 4)', #JD 1024 ); 1025 1026 # define common JPEG segments here to avoid overhead of loading JPEG module 1027 1028 # JPEG SOF (start of frame) tags 470 1029 # (ref http://www.w3.org/Graphics/JPEG/itu-t81.pdf) 471 1030 %Image::ExifTool::JPEG::SOF = ( 472 1031 GROUPS => { 0 => 'File', 1 => 'File', 2 => 'Image' }, 473 1032 NOTES => 'This information is extracted from the JPEG Start Of Frame segment.', 474 VARS => { NO_ID => 1 }, 475 476 477 478 1033 VARS => { NO_ID => 1 }, # tag ID's aren't meaningful for these tags 1034 EncodingProcess => { 1035 PrintHex => 1, 1036 PrintConv => { 1037 0x0 => 'Baseline DCT, Huffman coding', 479 1038 0x1 => 'Extended sequential DCT, Huffman coding', 480 1039 0x2 => 'Progressive DCT, Huffman coding', … … 497 1056 YCbCrSubSampling => { 498 1057 Notes => 'calculated from components table', 499 PrintConv => { 500 '1 1' => 'YCbCr4:4:4 (1 1)', 501 '2 1' => 'YCbCr4:2:2 (2 1)', 502 '2 2' => 'YCbCr4:2:0 (2 2)', 503 '4 1' => 'YCbCr4:1:1 (4 1)', 504 '4 2' => 'YCbCr4:1:0 (4 2)', 505 '1 2' => 'YCbCr4:4:0 (1 2)', 506 }, 1058 PrintConv => \%Image::ExifTool::JPEG::yCbCrSubSampling, 507 1059 }, 508 1060 ); 509 1061 510 # static private ExifTool variables 511 512 %allTables = ( ); # list of all tables loaded (except composite tags) 513 @tableOrder = ( ); # order the tables were loaded 514 515 my $didTagID; # flag indicating we are accessing tag ID's 516 517 # composite tags (accumulation of all Composite tag tables) 518 %Image::ExifTool::Composite = ( 519 GROUPS => { 0 => 'Composite', 1 => 'Composite' }, 520 DID_TAG_ID => 1, # want empty tagID's for composite tags 521 WRITE_PROC => \&DummyWriteProc, 522 ); 523 524 # JFIF APP0 definitions 1062 # JPEG JFIF APP0 definitions 525 1063 %Image::ExifTool::JFIF::Main = ( 526 1064 PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData, … … 532 1070 Name => 'JFIFVersion', 533 1071 Format => 'int8u[2]', 534 PrintConv => ' $val=~tr/ /./;$val',1072 PrintConv => 'sprintf("%d.%.2d", split(" ",$val))', 535 1073 }, 536 1074 2 => { … … 564 1102 0x10 => { 565 1103 Name => 'ThumbnailImage', 566 ValueConv => '$self->ValidateImage(\$val,$tag)',1104 RawConv => '$self->ValidateImage(\$val,$tag)', 567 1105 }, 568 1106 ); 569 1107 570 # special tag names (not used for tag info)571 my %specialTags= (572 PROCESS_PROC=>1, WRITE_PROC=>1, CHECK_PROC=>1, GROUPS=>1, FORMAT=>1,573 FIRST_ENTRY=>1, TAG_PREFIX=>1, PRINT_CONV=>1, DID_TAG_ID=>1, WRITABLE=>1,574 NOTES=>1, IS_OFFSET=>1, EXTRACT_UNKNOWN=>1, NAMESPACE=>1, PREFERRED=>1,575 PARENT=>1, PRIORITY=>1, WRITE_GROUP=>1, LANG_INFO=>1, VARS=>1,576 DATAMEMBER=>1,1108 # Composite tags (accumulation of all Composite tag tables) 1109 %Image::ExifTool::Composite = ( 1110 GROUPS => { 0 => 'Composite', 1 => 'Composite' }, 1111 TABLE_NAME => 'Image::ExifTool::Composite', 1112 SHORT_NAME => 'Composite', 1113 VARS => { NO_ID => 1 }, # want empty tagID's for Composite tags 1114 WRITE_PROC => \&DummyWriteProc, 577 1115 ); 1116 1117 # static private ExifTool variables 1118 1119 %allTables = ( ); # list of all tables loaded (except Composite tags) 1120 @tableOrder = ( ); # order the tables were loaded 578 1121 579 1122 #------------------------------------------------------------------------------ … … 605 1148 # New - create new ExifTool object 606 1149 # Inputs: 0) reference to exiftool object or ExifTool class name 1150 # Returns: blessed ExifTool object ref 607 1151 sub new 608 1152 { … … 617 1161 $self->ClearOptions(); # create default options hash 618 1162 $self->{VALUE} = { }; # must initialize this for warning messages 619 $self->{DEL_GROUP} = { }; # l ist ofgroups to delete when writing1163 $self->{DEL_GROUP} = { }; # lookup for groups to delete when writing 620 1164 621 1165 # initialize our new groups for writing … … 633 1177 # Notes: 634 1178 # - if no tags names are specified, the values of all tags are returned 635 # - tags may be specified with leading '-' to exclude 1179 # - tags may be specified with leading '-' to exclude, or trailing '#' for ValueConv 636 1180 # - can pass a reference to list of tags to find, in which case the list will 637 1181 # be updated with the tags found in the proper case and in the specified order. … … 684 1228 while (@_) { 685 1229 my $param = shift; 686 $oldVal = $ options->{$param};1230 $oldVal = $$options{$param}; 687 1231 last unless @_; 688 $options->{$param} = shift; 689 # clone Exclude list and expand shortcuts 690 if ($param eq 'Exclude' and defined $options->{$param}) { 1232 my $newVal = shift; 1233 if ($param eq 'Lang') { 1234 # allow this to be set to undef to select the default language 1235 $newVal = $defaultLang unless defined $newVal; 1236 if ($newVal eq $defaultLang) { 1237 $$options{$param} = $newVal; 1238 delete $$self{CUR_LANG}; 1239 # make sure the language is available 1240 } elsif (eval "require Image::ExifTool::Lang::$newVal") { 1241 my $xlat = "Image::ExifTool::Lang::${newVal}::Translate"; 1242 no strict 'refs'; 1243 if (%$xlat) { 1244 $$self{CUR_LANG} = \%$xlat; 1245 $$options{$param} = $newVal; 1246 } 1247 } # else don't change Lang 1248 } elsif ($param eq 'Exclude' and defined $newVal) { 1249 # clone Exclude list and expand shortcuts 691 1250 my @exclude; 692 my $val = $options->{$param}; 693 if (ref $val eq 'ARRAY') { 694 @exclude = @$val; 1251 if (ref $newVal eq 'ARRAY') { 1252 @exclude = @$newVal; 695 1253 } else { 696 @exclude = ($val); 697 } 698 ExpandShortcuts(\@exclude); 699 $options->{$param} = \@exclude; 1254 @exclude = ($newVal); 1255 } 1256 ExpandShortcuts(\@exclude, 1); # (also remove '#' suffix) 1257 $$options{$param} = \@exclude; 1258 } elsif ($param =~ /^Charset/ or $param eq 'IPTCCharset') { 1259 # only allow valid character sets to be set 1260 if ($newVal) { 1261 my $charset = $charsetName{lc $newVal}; 1262 if ($charset) { 1263 $$options{$param} = $charset; 1264 # maintain backward-compatibility with old IPTCCharset option 1265 $$options{CharsetIPTC} = $charset if $param eq 'IPTCCharset'; 1266 } else { 1267 warn "Invalid Charset $newVal\n"; 1268 } 1269 } 1270 } else { 1271 if ($param eq 'Escape') { 1272 # set ESCAPE_PROC 1273 if (defined $newVal and $newVal eq 'XML') { 1274 require Image::ExifTool::XMP; 1275 $$self{ESCAPE_PROC} = \&Image::ExifTool::XMP::EscapeXML; 1276 } elsif (defined $newVal and $newVal eq 'HTML') { 1277 require Image::ExifTool::HTML; 1278 $$self{ESCAPE_PROC} = \&Image::ExifTool::HTML::EscapeHTML; 1279 } else { 1280 delete $$self{ESCAPE_PROC}; 1281 } 1282 # must forget saved values since they depend on Escape method 1283 $self->{BOTH} = { }; 1284 } 1285 $$options{$param} = $newVal; 700 1286 } 701 1287 } … … 713 1299 # create options hash with default values 714 1300 # (commented out options don't need initializing) 1301 # +-----------------------------------------------------+ 1302 # ! DON'T FORGET!! When adding any new option, must ! 1303 # ! decide how it is handled in SetNewValuesFromFile() ! 1304 # +-----------------------------------------------------+ 715 1305 $self->{OPTIONS} = { 716 1306 # Binary => undef, # flag to extract binary values even if tag not specified 717 1307 # ByteOrder => undef, # default byte order when creating EXIF information 718 1308 Charset => 'UTF8', # character set for converting Unicode characters 1309 CharsetID3 => 'Latin', # internal ID3v1 character set 1310 CharsetIPTC => 'Latin', # fallback IPTC character set if no CodedCharacterSet 719 1311 # Compact => undef, # compact XMP and IPTC data 720 1312 Composite => 1, # flag to calculate Composite tags … … 723 1315 # DateFormat => undef, # format for date/time 724 1316 Duplicates => 1, # flag to save duplicate tag values 1317 # Escape => undef, # escape special characters 725 1318 # Exclude => undef, # tags to exclude 1319 # ExtractEmbedded =>undef,# flag to extract information from embedded documents 726 1320 # FastScan => undef, # flag to avoid scanning for trailer 727 1321 # FixBase => undef, # fix maker notes base offsets 1322 # GeoMaxIntSecs => undef, # geotag maximum interpolation time (secs) 1323 # GeoMaxExtSecs => undef, # geotag maximum extrapolation time (secs) 1324 # GeoMaxHDOP => undef, # geotag maximum HDOP 1325 # GeoMaxPDOP => undef, # geotag maximum PDOP 1326 # GeoMinSats => undef, # geotag minimum satellites 728 1327 # Group# => undef, # return tags for specified groups in family # 729 1328 HtmlDump => 0, # HTML dump (0-3, higher # = bigger limit) 730 1329 # HtmlDumpBase => undef, # base address for HTML dump 731 1330 # IgnoreMinorErrors => undef, # ignore minor errors when reading/writing 1331 Lang => $defaultLang,# localized language for descriptions etc 1332 # LargeFileSupport => undef, # flag indicating support of 64-bit file offsets 732 1333 # List => undef, # extract lists of PrintConv values into arrays 1334 ListSep => ', ', # list item separator 1335 # ListSplit => undef, # regex for splitting list-type tag values when writing 733 1336 # MakerNotes => undef, # extract maker notes as a block 734 1337 # MissingTagValue =>undef,# value for missing tags when expanded in expressions 1338 # Password => undef, # password for password-protected PDF documents 735 1339 PrintConv => 1, # flag to enable print conversion 1340 # SavePath => undef, # (undocumented) save family 5 location path 736 1341 # ScanForXMP => undef, # flag to scan for XMP information in all files 737 1342 Sort => 'Input', # order to sort found tags (Input, File, Alpha, Group#) 738 1343 # StrictDate => undef, # flag to return undef for invalid date conversions 1344 # Struct => undef, # return structures as hash references 739 1345 TextOut => \*STDOUT,# file for Verbose/HtmlDump output 740 1346 Unknown => 0, # flag to get values of unknown tags (0-2) 741 Verbose => 0, # print verbose messages (0- 4, higher # = more verbose)1347 Verbose => 0, # print verbose messages (0-5, higher # = more verbose) 742 1348 }; 1349 # keep necessary member variables in sync with options 1350 delete $$self{CUR_LANG}; 1351 delete $$self{ESCAPE_PROC}; 1352 1353 # load user-defined default options 1354 if (%Image::ExifTool::UserDefined::Options) { 1355 foreach (keys %Image::ExifTool::UserDefined::Options) { 1356 $self->Options($_, $Image::ExifTool::UserDefined::Options{$_}); 1357 } 1358 } 743 1359 } 744 1360 … … 749 1365 # Returns: 1 if this was a valid image, 0 otherwise 750 1366 # Notes: pass an undefined value to avoid parsing arguments 1367 # Internal 'ReEntry' option allows this routine to be called recursively 751 1368 sub ExtractInfo($;@) 752 1369 { … … 754 1371 my $self = shift; 755 1372 my $options = $self->{OPTIONS}; # pointer to current options 756 my %saveOptions; 757 758 if (defined $_[0] or $options->{HtmlDump}) { 759 %saveOptions = %$options; # save original options 760 761 # require duplicates for html dump 762 $self->Options(Duplicates => 1) if $options->{HtmlDump}; 763 764 if (defined $_[0]) { 765 # only initialize filename if called with arguments 766 $self->{FILENAME} = undef; # name of file (or '' if we didn't open it) 767 $self->{RAF} = undef; # RandomAccess object reference 1373 my (%saveOptions, $reEntry, $rsize); 1374 1375 # check for internal ReEntry option to allow recursive calls to ExtractInfo 1376 if (ref $_[1] eq 'HASH' and $_[1]{ReEntry} and 1377 (ref $_[0] eq 'SCALAR' or ref $_[0] eq 'GLOB')) 1378 { 1379 # save necessary members for restoring later 1380 $reEntry = { 1381 RAF => $$self{RAF}, 1382 PROCESSED => $$self{PROCESSED}, 1383 EXIF_DATA => $$self{EXIF_DATA}, 1384 EXIF_POS => $$self{EXIF_POS}, 1385 FILE_TYPE => $$self{FILE_TYPE}, 1386 }; 1387 $self->{RAF} = new File::RandomAccess($_[0]); 1388 $$self{PROCESSED} = { }; 1389 delete $$self{EXIF_DATA}; 1390 delete $$self{EXIF_POS}; 1391 } else { 1392 if (defined $_[0] or $options->{HtmlDump}) { 1393 %saveOptions = %$options; # save original options 768 1394 769 $self->ParseArguments(@_); # initialize from our arguments 770 } 771 } 772 # initialize ExifTool object members 773 $self->Init(); 774 775 delete $self->{MAKER_NOTE_FIXUP}; # fixup information for extracted maker notes 776 delete $self->{MAKER_NOTE_BYTE_ORDER}; 777 delete $self->{DONE_ID3}; 778 1395 # require duplicates for html dump 1396 $self->Options(Duplicates => 1) if $options->{HtmlDump}; 1397 1398 if (defined $_[0]) { 1399 # only initialize filename if called with arguments 1400 $self->{FILENAME} = undef; # name of file (or '' if we didn't open it) 1401 $self->{RAF} = undef; # RandomAccess object reference 1402 1403 $self->ParseArguments(@_); # initialize from our arguments 1404 } 1405 } 1406 # initialize ExifTool object members 1407 $self->Init(); 1408 1409 delete $self->{MAKER_NOTE_FIXUP}; # fixup information for extracted maker notes 1410 delete $self->{MAKER_NOTE_BYTE_ORDER}; 1411 1412 # return our version number 1413 $self->FoundTag('ExifToolVersion', "$VERSION$RELEASE"); 1414 $self->FoundTag('Now', time()) if $self->{REQ_TAG_LOOKUP}{now} or $self->{TAGS_FROM_FILE}; 1415 } 779 1416 my $filename = $self->{FILENAME}; # image file name ('' if already open) 780 1417 my $raf = $self->{RAF}; # RandomAccess object 781 1418 782 # return our version number783 $self->FoundTag('ExifToolVersion', "$VERSION$RELEASE");784 785 1419 local *EXIFTOOL_FILE; # avoid clashes with global namespace 786 1420 1421 my $realname = $filename; 787 1422 unless ($raf) { 788 1423 # save file name 789 1424 if (defined $filename and $filename ne '') { 790 1425 unless ($filename eq '-') { 791 my $name = $filename;792 1426 # extract file name from pipe if necessary 793 $ name =~ /\|$/ and $name =~ s/.*?"(.*)".*/$1/;794 my $dir;1427 $realname =~ /\|$/ and $realname =~ s/.*?"(.*?)".*/$1/; 1428 my ($dir, $name); 795 1429 if (eval 'require File::Basename') { 796 $dir = File::Basename::dirname($ name);797 $name = File::Basename::basename($ name);1430 $dir = File::Basename::dirname($realname); 1431 $name = File::Basename::basename($realname); 798 1432 } else { 799 $name =~ tr/\\/\//; 800 if ($name =~ s/(.*)\///) { # remove path 801 $dir = length($1) ? $1 : '/'; 802 } 1433 ($name = $realname) =~ tr/\\/\//; 1434 # remove path 1435 $dir = length($1) ? $1 : '/' if $name =~ s/(.*)\///; 803 1436 } 804 1437 $self->FoundTag('FileName', $name); 805 1438 $self->FoundTag('Directory', $dir) if defined $dir and length $dir; 1439 # get size of resource fork on Mac OS 1440 $rsize = -s "$filename/rsrc" if $^O eq 'darwin' and not $$self{IN_RESOURCE}; 806 1441 } 807 1442 # open the file 808 if (open(EXIFTOOL_FILE,$filename)) { 809 my $filePt = \*EXIFTOOL_FILE; 1443 if (open(EXIFTOOL_FILE, $filename)) { 810 1444 # create random access file object 811 $raf = new File::RandomAccess( $filePt);1445 $raf = new File::RandomAccess(\*EXIFTOOL_FILE); 812 1446 # patch to force pipe to be buffered because seek returns success 813 1447 # in Windows cmd shell pipe even though it really failed … … 823 1457 824 1458 if ($raf) { 825 # get file size and last modified time if this is a plain file 826 if ($raf->{FILE_PT} and -f $raf->{FILE_PT}) { 1459 if ($reEntry) { 1460 # we already set these tags 1461 } elsif (not $raf->{FILE_PT}) { 1462 # get file size from image in memory 1463 $self->FoundTag('FileSize', length ${$raf->{BUFF_PT}}); 1464 } elsif (-f $raf->{FILE_PT}) { 1465 # get file size and last modified time if this is a plain file 827 1466 my $fileSize = -s _; 828 1467 my $fileTime = -M _; 1468 my @stat = stat _; 829 1469 $self->FoundTag('FileSize', $fileSize) if defined $fileSize; 1470 $self->FoundTag('ResourceForkSize', $rsize) if $rsize; 830 1471 $self->FoundTag('FileModifyDate', $^T - $fileTime*(24*3600)) if defined $fileTime; 1472 $self->FoundTag('FilePermissions', $stat[2]) if defined $stat[2]; 831 1473 } 832 1474 833 1475 # get list of file types to check 834 my $tiffType;835 $self->{FILE_EXT} = GetFileExtension($ filename);836 my @fileTypeList = GetFileType($ filename);1476 my ($tiffType, %noMagic); 1477 $self->{FILE_EXT} = GetFileExtension($realname); 1478 my @fileTypeList = GetFileType($realname); 837 1479 if (@fileTypeList) { 838 1480 # add remaining types to end of list so we test them all … … 840 1482 push @fileTypeList, grep(!/^($pat)$/, @fileTypes); 841 1483 $tiffType = $self->{FILE_EXT}; 1484 $noMagic{MXF} = 1; # don't do magic number test on MXF or DV files 1485 $noMagic{DV} = 1; 842 1486 } else { 843 1487 # scan through all recognized file types … … 851 1495 my %dirInfo = ( RAF => $raf, Base => $pos ); 852 1496 # loop through list of file types to test 853 my $type; 854 for (;;) { 1497 my ($type, $buff, $seekErr); 1498 # read first 1024 bytes of file for testing 1499 $raf->Read($buff, 1024) or $buff = ''; 1500 $raf->Seek($pos, 0) or $seekErr = 1; 1501 until ($seekErr) { 855 1502 $type = shift @fileTypeList; 856 unless ($type) { 1503 if ($type) { 1504 # do quick test for this file type to avoid loading module unnecessarily 1505 next if $magicNumber{$type} and $buff !~ /^$magicNumber{$type}/s and 1506 not $noMagic{$type}; 1507 } else { 857 1508 last unless defined $type; 858 1509 # last ditch effort to scan past unknown header for JPEG/TIFF 859 my $buff;860 $raf->Read($buff, 1024);861 1510 next unless $buff =~ /(\xff\xd8\xff|MM\0\x2a|II\x2a\0)/g; 862 1511 $type = ($1 eq "\xff\xd8\xff") ? 'JPEG' : 'TIFF'; 863 1512 my $skip = pos($buff) - length($1); 864 1513 $dirInfo{Base} = $pos + $skip; 865 $raf->Seek($pos + $skip, 0) ;1514 $raf->Seek($pos + $skip, 0) or $seekErr = 1, last; 866 1515 $self->Warn("Skipped unknown $skip byte header"); 867 1516 } 868 1517 # save file type in member variable 869 $self->{FILE_TYPE} = $ type;1518 $self->{FILE_TYPE} = $self->{PATH}[0] = $type; 870 1519 $dirInfo{Parent} = ($type eq 'TIFF') ? $tiffType : $type; 871 1520 my $module = $moduleName{$type}; … … 877 1526 require "Image/ExifTool/$module.pm"; 878 1527 $func = "Image::ExifTool::${module}::$func"; 1528 } elsif ($module eq '0') { 1529 $self->SetFileType(); 1530 $self->Warn('Unsupported file type'); 1531 last; 879 1532 } 880 1533 # process the file … … 884 1537 885 1538 # seek back to try again from the same position in the file 886 unless ($raf->Seek($pos, 0)) { 887 $self->Error('Error seeking in file'); 888 last; 889 } 890 } 891 # scan for XMP if specified 892 if ($self->Options('ScanForXMP') and (not defined $type or 1539 $raf->Seek($pos, 0) or $seekErr = 1, last; 1540 } 1541 if ($seekErr) { 1542 $self->Error('Error seeking in file'); 1543 } elsif ($self->Options('ScanForXMP') and (not defined $type or 893 1544 (not $self->Options('FastScan') and not $$self{FoundXMP}))) 894 1545 { 1546 # scan for XMP 895 1547 $raf->Seek($pos, 0); 896 1548 require Image::ExifTool::XMP; … … 901 1553 # must be a format error since we couldn't read it, otherwise 902 1554 # it is likely we don't support images of this type 903 $self->Error(GetFileType($filename) ? 904 'File format error' : 'Unknown file type'); 1555 my $fileType = GetFileType($realname); 1556 my $err; 1557 if (not $fileType) { 1558 $err = 'Unknown file type'; 1559 } elsif ($fileType eq 'RAW') { 1560 $err = 'Unsupported RAW file type'; 1561 } else { 1562 $err = 'File format error'; 1563 } 1564 $self->Error($err); 905 1565 } 906 1566 # extract binary EXIF data block only if requested 907 if (defined $self->{EXIF_DATA} and $self->{REQ_TAG_LOOKUP}->{exif}) { 1567 if (defined $self->{EXIF_DATA} and length $$self{EXIF_DATA} > 16 and 1568 ($self->{REQ_TAG_LOOKUP}{exif} or $self->{OPTIONS}{Binary})) 1569 { 908 1570 $self->FoundTag('EXIF', $self->{EXIF_DATA}); 909 1571 } 910 # calculate composite tags 911 $self->BuildCompositeTags() if $options->{Composite}; 912 913 # do our HTML dump if requested 914 if ($self->{HTML_DUMP}) { 915 $raf->Seek(0, 2); # seek to end of file 916 $self->{HTML_DUMP}->FinishTiffDump($self, $raf->Tell()); 917 my $pos = $options->{HtmlDumpBase}; 918 $pos = ($self->{FIRST_EXIF_POS} || 0) unless defined $pos; 919 my $dataPt = defined $self->{EXIF_DATA} ? \$self->{EXIF_DATA} : undef; 920 undef $dataPt if defined $self->{EXIF_POS} and $pos != $self->{EXIF_POS}; 921 $self->{HTML_DUMP}->Print($raf, $dataPt, $pos, 922 $options->{TextOut}, $options->{HtmlDump}, 923 $self->{FILENAME} ? "HTML Dump ($self->{FILENAME})" : 'HTML Dump'); 924 } 925 926 $raf->Close() if $filename; # close the file if we opened it 1572 unless ($reEntry) { 1573 $self->{PATH} = [ ]; # reset PATH 1574 # calculate Composite tags 1575 $self->BuildCompositeTags() if $options->{Composite}; 1576 # do our HTML dump if requested 1577 if ($self->{HTML_DUMP}) { 1578 $raf->Seek(0, 2); # seek to end of file 1579 $self->{HTML_DUMP}->FinishTiffDump($self, $raf->Tell()); 1580 my $pos = $options->{HtmlDumpBase}; 1581 $pos = ($self->{FIRST_EXIF_POS} || 0) unless defined $pos; 1582 my $dataPt = defined $self->{EXIF_DATA} ? \$self->{EXIF_DATA} : undef; 1583 undef $dataPt if defined $self->{EXIF_POS} and $pos != $self->{EXIF_POS}; 1584 my $success = $self->{HTML_DUMP}->Print($raf, $dataPt, $pos, 1585 $options->{TextOut}, $options->{HtmlDump}, 1586 $self->{FILENAME} ? "HTML Dump ($self->{FILENAME})" : 'HTML Dump'); 1587 $self->Warn("Error reading $self->{HTML_DUMP}{ERROR}") if $success < 0; 1588 } 1589 } 1590 if ($filename) { 1591 $raf->Close(); # close the file if we opened it 1592 # process the resource fork as an embedded file on Mac filesystems 1593 if ($rsize and $options->{ExtractEmbedded}) { 1594 local *RESOURCE_FILE; 1595 if (open(RESOURCE_FILE, "$filename/rsrc")) { 1596 $$self{DOC_NUM} = $$self{DOC_COUNT} + 1; 1597 $$self{IN_RESOURCE} = 1; 1598 $self->ExtractInfo(\*RESOURCE_FILE, { ReEntry => 1 }); 1599 close RESOURCE_FILE; 1600 delete $$self{IN_RESOURCE}; 1601 } else { 1602 $self->Warn('Error opening resource fork'); 1603 } 1604 } 1605 } 927 1606 } 928 1607 … … 930 1609 %saveOptions and $self->{OPTIONS} = \%saveOptions; 931 1610 932 return exists $self->{VALUE}->{Error} ? 0 : 1; 1611 if ($reEntry) { 1612 # restore necessary members when exiting re-entrant code 1613 $$self{$_} = $$reEntry{$_} foreach keys %$reEntry; 1614 } 1615 1616 return exists $self->{VALUE}{Error} ? 0 : 1; 933 1617 } 934 1618 … … 941 1625 # - If groups are specified, first groups take precedence if duplicate 942 1626 # tags found but Duplicates option not set. 1627 # - tag names may end in '#' to extract ValueConv value 943 1628 sub GetInfo($;@) 944 1629 { … … 955 1640 956 1641 # get reference to list of tags for which we will return info 957 my $rtnTags= $self->SetFoundTags();1642 my ($rtnTags, $byValue) = $self->SetFoundTags(); 958 1643 959 1644 # build hash of tag information 960 1645 my (%info, %ignored); 961 my $conv = $self->{OPTIONS} ->{PrintConv} ? 'PrintConv' : 'ValueConv';1646 my $conv = $self->{OPTIONS}{PrintConv} ? 'PrintConv' : 'ValueConv'; 962 1647 foreach (@$rtnTags) { 963 1648 my $val = $self->GetValue($_, $conv); 964 1649 defined $val or $ignored{$_} = 1, next; 965 1650 $info{$_} = $val; 1651 } 1652 1653 # override specified tags with ValueConv value if necessary 1654 if (@$byValue and $conv ne 'ValueConv') { 1655 # first determine the number of times each non-ValueConv value is used 1656 my %nonVal; 1657 $nonVal{$_} = ($nonVal{$_} || 0) + 1 foreach @$rtnTags; 1658 --$nonVal{$$rtnTags[$_]} foreach @$byValue; 1659 # loop through ValueConv tags, updating tag keys and returned values 1660 foreach (@$byValue) { 1661 my $tag = $$rtnTags[$_]; 1662 my $val = $self->GetValue($tag, 'ValueConv'); 1663 next unless defined $val; 1664 my $vtag = $tag; 1665 # generate a new tag key like "Tag #" or "Tag #(1)" 1666 $vtag =~ s/( |$)/ #/; 1667 unless (defined $self->{VALUE}->{$vtag}) { 1668 $self->{VALUE}{$vtag} = $self->{VALUE}{$tag}; 1669 $self->{TAG_INFO}{$vtag} = $self->{TAG_INFO}{$tag}; 1670 $self->{TAG_EXTRA}{$vtag} = $self->{TAG_EXTRA}{$tag}; 1671 $self->{FILE_ORDER}{$vtag} = $self->{FILE_ORDER}{$tag}; 1672 # remove existing PrintConv entry unless we are using it too 1673 delete $info{$tag} unless $nonVal{$tag}; 1674 } 1675 $$rtnTags[$_] = $vtag; # store ValueConv value with new tag key 1676 $info{$vtag} = $val; # return ValueConv value 1677 } 966 1678 } 967 1679 … … 980 1692 # use file order by default if no tags specified 981 1693 # (no such thing as 'Input' order in this case) 982 my $sortOrder = $self->{OPTIONS} ->{Sort};1694 my $sortOrder = $self->{OPTIONS}{Sort}; 983 1695 unless (@$reqTags or ($sortOrder and $sortOrder ne 'Input')) { 984 1696 $sortOrder = 'File'; … … 1003 1715 local $_; 1004 1716 my $self = shift; 1005 my (%combinedInfo, $info );1006 1007 if ($self->{OPTIONS} ->{Duplicates}) {1717 my (%combinedInfo, $info, $tag, %haveInfo); 1718 1719 if ($self->{OPTIONS}{Duplicates}) { 1008 1720 while ($info = shift) { 1009 my $key; 1010 foreach $key (keys %$info) { 1011 $combinedInfo{$key} = $$info{$key}; 1721 foreach $tag (keys %$info) { 1722 $combinedInfo{$tag} = $$info{$tag}; 1012 1723 } 1013 1724 } 1014 1725 } else { 1015 my (%haveInfo, $tag);1016 1726 while ($info = shift) { 1017 1727 foreach $tag (keys %$info) { … … 1056 1766 $foundTags = $self->{FOUND_TAGS} || $self->SetFoundTags() or return undef; 1057 1767 } 1058 $sortOrder or $sortOrder = $self->{OPTIONS} ->{Sort};1768 $sortOrder or $sortOrder = $self->{OPTIONS}{Sort}; 1059 1769 1060 1770 # return original list if no sort order specified … … 1063 1773 if ($sortOrder eq 'Alpha') { 1064 1774 return sort @$foundTags; 1065 } elsif ($sortOrder =~ /^Group(\d* )/) {1775 } elsif ($sortOrder =~ /^Group(\d*(:\d+)*)/) { 1066 1776 my $family = $1 || 0; 1067 1777 # want to maintain a basic file order with the groups … … 1086 1796 # Get list of found tags in specified sort order 1087 1797 # Inputs: 0) ExifTool object reference, 1) sort order ('File', 'Input', ...) 1088 # Returns: List of tag s in specified order1798 # Returns: List of tag keys in specified order 1089 1799 # Notes: If not specified, sort order is taken from OPTIONS 1090 1800 sub GetFoundTags($;$) … … 1099 1809 # Get list of requested tags 1100 1810 # Inputs: 0) ExifTool object reference 1101 # Returns: List of requested tag s1811 # Returns: List of requested tag keys 1102 1812 sub GetRequestedTags($) 1103 1813 { 1104 1814 local $_; 1105 return @{$_[0] ->{REQUESTED_TAGS}};1815 return @{$_[0]{REQUESTED_TAGS}}; 1106 1816 } 1107 1817 1108 1818 #------------------------------------------------------------------------------ 1109 1819 # Get tag value 1110 # Inputs: 0) ExifTool object reference, 1) tag key 1820 # Inputs: 0) ExifTool object reference 1821 # 1) tag key (or flattened tagInfo for getting field values, not part of public API) 1111 1822 # 2) [optional] Value type: PrintConv, ValueConv, Both or Raw, the default 1112 1823 # is PrintConv or ValueConv, depending on the PrintConv option setting 1824 # 3) raw field value (not part of public API) 1113 1825 # Returns: Scalar context: tag value or undefined 1114 1826 # List context: list of values or empty list … … 1116 1828 { 1117 1829 local $_; 1118 my ($self, $tag, $type) = @_; 1830 my ($self, $tag, $type) = @_; # plus: ($fieldValue) 1831 my (@convTypes, $tagInfo, $valueConv, $both); 1832 1833 # figure out what conversions to do 1834 $type or $type = $self->{OPTIONS}{PrintConv} ? 'PrintConv' : 'ValueConv'; 1119 1835 1120 1836 # start with the raw value 1121 my $value = $self->{VALUE}->{$tag}; 1122 return wantarray ? () : undef unless defined $value; 1123 1124 # figure out what conversions to do 1125 my (@convTypes, $tagInfo); 1126 $type or $type = $self->{OPTIONS}->{PrintConv} ? 'PrintConv' : 'ValueConv'; 1127 unless ($type eq 'Raw') { 1128 $tagInfo = $self->{TAG_INFO}->{$tag}; 1129 push @convTypes, 'ValueConv'; 1130 push @convTypes, 'PrintConv' unless $type eq 'ValueConv'; 1837 my $value = $self->{VALUE}{$tag}; 1838 if (not defined $value) { 1839 return wantarray ? () : undef unless ref $tag; 1840 # get the value of a structure field 1841 $tagInfo = $tag; 1842 $tag = $$tagInfo{Name}; 1843 $value = $_[3]; 1844 # (note: type "Both" is not allowed for structure fields) 1845 if ($type ne 'Raw') { 1846 push @convTypes, 'ValueConv'; 1847 push @convTypes, 'PrintConv' unless $type eq 'ValueConv'; 1848 } 1849 } else { 1850 $tagInfo = $self->{TAG_INFO}{$tag}; 1851 if ($$tagInfo{Struct} and ref $value) { 1852 # must load XMPStruct.pl just in case (should already be loaded if 1853 # a structure was extracted, but we could also arrive here if a simple 1854 # list of values was stored incorrectly in a Struct tag) 1855 require 'Image/ExifTool/XMPStruct.pl'; 1856 # convert strucure field values 1857 unless ($type eq 'Both') { 1858 # (note: ConvertStruct handles the escape too if necessary) 1859 return Image::ExifTool::XMP::ConvertStruct($self,$tagInfo,$value,$type); 1860 } 1861 $valueConv = Image::ExifTool::XMP::ConvertStruct($self,$tagInfo,$value,'ValueConv'); 1862 $value = Image::ExifTool::XMP::ConvertStruct($self,$tagInfo,$value,'PrintConv'); 1863 # (must not save these in $$self{BOTH} because the values may have been escaped) 1864 return ($valueConv, $value); 1865 } 1866 if ($type ne 'Raw') { 1867 # use values we calculated already if we stored them 1868 $both = $self->{BOTH}{$tag}; 1869 if ($both) { 1870 if ($type eq 'PrintConv') { 1871 $value = $$both[1]; 1872 } elsif ($type eq 'ValueConv') { 1873 $value = $$both[0]; 1874 $value = $$both[1] unless defined $value; 1875 } else { 1876 ($valueConv, $value) = @$both; 1877 } 1878 } else { 1879 push @convTypes, 'ValueConv'; 1880 push @convTypes, 'PrintConv' unless $type eq 'ValueConv'; 1881 } 1882 } 1131 1883 } 1132 1884 1133 1885 # do the conversions 1134 my (@val, @prt, @raw, $convType , $valueConv);1886 my (@val, @prt, @raw, $convType); 1135 1887 foreach $convType (@convTypes) { 1136 last if ref $value eq 'SCALAR'; # don't convert a scalar reference 1888 # don't convert a scalar reference or structure 1889 last if ref $value eq 'SCALAR'; 1137 1890 my $conv = $$tagInfo{$convType}; 1138 1891 unless (defined $conv) { … … 1141 1894 $conv = '\$val'; # return scalar reference for binary values 1142 1895 } else { 1143 # use PRINT_CONV from tag table if PrintConv not defined 1144 next unless defined($conv = $tagInfo->{Table}->{PRINT_CONV}); 1896 # use PRINT_CONV from tag table if PrintConv doesn't exist 1897 next unless defined($conv = $tagInfo->{Table}{PRINT_CONV}); 1898 next if exists $$tagInfo{$convType}; 1145 1899 } 1146 1900 } … … 1153 1907 $conv = $$convList[0]; 1154 1908 my @valList = split ' ', $value; 1155 $value = \@valList; 1909 # reorganize list if specified (Note: The writer currently doesn't 1910 # relist values, so they may be grouped but the order must not change) 1911 my $relist = $$tagInfo{Relist}; 1912 if ($relist) { 1913 my (@newList, $oldIndex); 1914 foreach $oldIndex (@$relist) { 1915 my ($newVal, @join); 1916 if (ref $oldIndex) { 1917 foreach (@$oldIndex) { 1918 push @join, $valList[$_] if defined $valList[$_]; 1919 } 1920 $newVal = join(' ', @join) if @join; 1921 } else { 1922 $newVal = $valList[$oldIndex]; 1923 } 1924 push @newList, $newVal if defined $newVal; 1925 } 1926 $value = \@newList; 1927 } else { 1928 $value = \@valList; 1929 } 1156 1930 } 1157 1931 # initialize array so we can iterate over values in list … … 1166 1940 for (;;) { 1167 1941 if (defined $conv) { 1168 # get values of required tags if this is a composite tag1942 # get values of required tags if this is a Composite tag 1169 1943 if (ref $val eq 'HASH' and not @val) { 1944 # disable escape of source values so we don't double escape them 1945 my $oldEscape = $$self{ESCAPE_PROC}; 1946 delete $$self{ESCAPE_PROC}; 1170 1947 foreach (keys %$val) { 1171 $raw[$_] = $self->{VALUE} ->{$$val{$_}};1948 $raw[$_] = $self->{VALUE}{$$val{$_}}; 1172 1949 ($val[$_], $prt[$_]) = $self->GetValue($$val{$_}, 'Both'); 1173 next if defined $val[$_] or not $tagInfo->{Require}->{$_}; 1950 next if defined $val[$_] or not $tagInfo->{Require}{$_}; 1951 $$self{ESCAPE_PROC} = $oldEscape; 1174 1952 return wantarray ? () : undef; 1175 1953 } 1954 $$self{ESCAPE_PROC} = $oldEscape; 1176 1955 # set $val to $val[0], or \@val for a CODE ref conversion 1177 1956 $val = ref $conv eq 'CODE' ? \@val : $val[0]; … … 1179 1958 if (ref $conv eq 'HASH') { 1180 1959 # look up converted value in hash 1181 unless (defined($value = $$conv{$val})) { 1960 my $lc; 1961 if (defined($value = $$conv{$val})) { 1962 # override with our localized language PrintConv if available 1963 if ($$self{CUR_LANG} and $convType eq 'PrintConv' and 1964 # (no need to check for lang-alt tag names -- they won't have a PrintConv) 1965 ref($lc = $self->{CUR_LANG}{$$tagInfo{Name}}) eq 'HASH' and 1966 ($lc = $$lc{PrintConv}) and ($lc = $$lc{$value})) 1967 { 1968 $value = $self->Decode($lc, 'UTF8'); 1969 } 1970 } else { 1182 1971 if ($$conv{BITMASK}) { 1183 1972 $value = DecodeBits($val, $$conv{BITMASK}); 1184 } else { 1185 if ($$tagInfo{PrintHex} and $val and IsInt($val) and 1186 $convType eq 'PrintConv') 1973 # override with localized language strings 1974 if (defined $value and $$self{CUR_LANG} and $convType eq 'PrintConv' and 1975 ref($lc = $self->{CUR_LANG}{$$tagInfo{Name}}) eq 'HASH' and 1976 ($lc = $$lc{PrintConv})) 1977 { 1978 my @vals = split ', ', $value; 1979 foreach (@vals) { 1980 $_ = $$lc{$_} if defined $$lc{$_}; 1981 } 1982 $value = join ', ', @vals; 1983 } 1984 } elsif (not $$conv{OTHER} or 1985 # use alternate conversion routine if available 1986 not defined($value = &{$$conv{OTHER}}($val, undef, $conv))) 1987 { 1988 if (($$tagInfo{PrintHex} or 1989 ($$tagInfo{Mask} and not defined $$tagInfo{PrintHex})) 1990 and $val and IsInt($val) and $convType eq 'PrintConv') 1187 1991 { 1188 1992 $val = sprintf('0x%x',$val); … … 1202 2006 $@ and $evalWarning = $@; 1203 2007 } 1204 if ($evalWarning) { 1205 delete $SIG{'__WARN__'}; 1206 warn "$convType $tag: " . CleanWarning() . "\n"; 1207 } 2008 $self->Warn("$convType $tag: " . CleanWarning()) if $evalWarning; 1208 2009 } 1209 2010 } else { … … 1228 2029 } 1229 2030 if ($type eq 'Both') { 1230 # $valueConv is undefined if there was no print conversion done 1231 $valueConv = $value unless defined $valueConv; 2031 # save both (unescaped) values because we often need them again 2032 # (Composite tags need "Both" and often Require one tag for various Composite tags) 2033 $self->{BOTH}{$tag} = [ $valueConv, $value ] unless $both; 2034 # escape values if necessary 2035 if ($$self{ESCAPE_PROC}) { 2036 DoEscape($value, $$self{ESCAPE_PROC}); 2037 if (defined $valueConv) { 2038 DoEscape($valueConv, $$self{ESCAPE_PROC}); 2039 } else { 2040 $valueConv = $value; 2041 } 2042 } elsif (not defined $valueConv) { 2043 # $valueConv is undefined if there was no print conversion done 2044 $valueConv = $value; 2045 } 1232 2046 # return Both values as a list (ValueConv, PrintConv) 1233 2047 return ($valueConv, $value); 1234 2048 } 2049 # escape value if necessary 2050 DoEscape($value, $$self{ESCAPE_PROC}) if $$self{ESCAPE_PROC}; 2051 1235 2052 if (ref $value eq 'ARRAY') { 1236 2053 # return array if requested 1237 2054 return @$value if wantarray; 1238 # return list reference for Raw, ValueConv or if List o ption set1239 return $value if @convTypes < 2 or $self->{OPTIONS}->{List};2055 # return list reference for Raw, ValueConv or if List or not a list of scalars 2056 return $value if $type ne 'PrintConv' or $self->{OPTIONS}{List} or ref $$value[0]; 1240 2057 # otherwise join in comma-separated string 1241 $value = join ', ', @$value;2058 $value = join $self->{OPTIONS}{ListSep}, @$value; 1242 2059 } 1243 2060 return $value; … … 1247 2064 # Get tag identification number 1248 2065 # Inputs: 0) ExifTool object reference, 1) tag key 1249 # Returns: Tag ID if available, otherwise '' 2066 # Returns: Scalar context: Tag ID if available, otherwise '' 2067 # List context: 0) Tag ID (or ''), 1) language code (or undef) 1250 2068 sub GetTagID($$) 1251 2069 { 1252 local $_;1253 2070 my ($self, $tag) = @_; 1254 my $tagInfo = $self->{TAG_INFO}->{$tag}; 1255 1256 if ($tagInfo) { 1257 GenerateAllTagIDs(); # make sure tag ID's are generated 1258 defined $$tagInfo{TagID} and return $$tagInfo{TagID}; 1259 } 1260 # no ID for this tag (shouldn't happen) 1261 return ''; 2071 my $tagInfo = $self->{TAG_INFO}{$tag}; 2072 return '' unless $tagInfo and defined $$tagInfo{TagID}; 2073 return ($$tagInfo{TagID}, $$tagInfo{LangCode}) if wantarray; 2074 return $$tagInfo{TagID}; 2075 } 2076 2077 #------------------------------------------------------------------------------ 2078 # Get tag table name 2079 # Inputs: 0) ExifTool object reference, 1) tag key 2080 # Returns: Table name if available, otherwise '' 2081 sub GetTableName($$) 2082 { 2083 my ($self, $tag) = @_; 2084 my $tagInfo = $self->{TAG_INFO}{$tag} or return ''; 2085 return $tagInfo->{Table}{SHORT_NAME}; 2086 } 2087 2088 #------------------------------------------------------------------------------ 2089 # Get tag index number 2090 # Inputs: 0) ExifTool object reference, 1) tag key 2091 # Returns: Table index number, or undefined if this tag isn't indexed 2092 sub GetTagIndex($$) 2093 { 2094 my ($self, $tag) = @_; 2095 my $tagInfo = $self->{TAG_INFO}{$tag} or return undef; 2096 return $$tagInfo{Index}; 1262 2097 } 1263 2098 … … 1271 2106 local $_; 1272 2107 my ($self, $tag) = @_; 1273 my $tagInfo = $self->{TAG_INFO}->{$tag}; 1274 # ($tagInfo should be defined for any extracted tag, 1275 # but we might as well handle the case where it isn't) 1276 my $desc; 1277 $desc = $$tagInfo{Description} if $tagInfo; 2108 my ($desc, $name); 2109 my $tagInfo = $self->{TAG_INFO}{$tag}; 2110 # ($tagInfo won't be defined for missing tags extracted with -f) 2111 if ($tagInfo) { 2112 # use alternate language description if available 2113 while ($$self{CUR_LANG}) { 2114 $desc = $self->{CUR_LANG}{$$tagInfo{Name}}; 2115 if ($desc) { 2116 # must look up Description if this tag also has a PrintConv 2117 $desc = $$desc{Description} or last if ref $desc; 2118 } else { 2119 # look up default language of lang-alt tag 2120 last unless $$tagInfo{LangCode} and 2121 ($name = $$tagInfo{Name}) =~ s/-$$tagInfo{LangCode}$// and 2122 $desc = $self->{CUR_LANG}{$name}; 2123 $desc = $$desc{Description} or last if ref $desc; 2124 $desc .= " ($$tagInfo{LangCode})"; 2125 } 2126 # escape description if necessary 2127 DoEscape($desc, $$self{ESCAPE_PROC}) if $$self{ESCAPE_PROC}; 2128 # return description in proper Charset 2129 return $self->Decode($desc, 'UTF8'); 2130 } 2131 $desc = $$tagInfo{Description}; 2132 } 1278 2133 # just make the tag more readable if description doesn't exist 1279 2134 unless ($desc) { … … 1289 2144 # Inputs: 0) ExifTool object reference 1290 2145 # 1) tag key (or reference to tagInfo hash, not part of the public API) 1291 # 2) [optional] group family number(-1 to get extended group list)2146 # 2) [optional] group family (-1 to get extended group list) 1292 2147 # Returns: Scalar context: Group name (for family 0 if not otherwise specified) 1293 2148 # Array context: Group name if family specified, otherwise list of 1294 # group names for each family. 2149 # group names for each family. Returns '' for undefined tag. 2150 # Notes: Mutiple families may be specified with ':' in family argument (ie. '1:2') 1295 2151 sub GetGroup($$;$) 1296 2152 { 1297 2153 local $_; 1298 2154 my ($self, $tag, $family) = @_; 1299 my ($tagInfo, @groups, $extra);2155 my ($tagInfo, @groups, @families, $simplify, $byTagInfo); 1300 2156 if (ref $tag eq 'HASH') { 1301 2157 $tagInfo = $tag; 1302 $tag = $tagInfo->{Name}; 2158 $tag = $$tagInfo{Name}; 2159 # set flag so we don't get extra information for an extracted tag 2160 $byTagInfo = 1; 1303 2161 } else { 1304 $tagInfo = $self->{TAG_INFO} ->{$tag} or return '';2162 $tagInfo = $self->{TAG_INFO}{$tag} or return ''; 1305 2163 } 1306 2164 my $groups = $$tagInfo{Groups}; 1307 2165 # fill in default groups unless already done 2166 # (after this, Groups 0-2 in tagInfo are guaranteed to be defined) 1308 2167 unless ($$tagInfo{GotGroups}) { 1309 2168 my $tagTablePtr = $$tagInfo{Table}; … … 1313 2172 # fill in default groups 1314 2173 foreach (keys %{$$tagTablePtr{GROUPS}}) { 1315 $$groups{$_} or $$groups{$_} = $tagTablePtr->{GROUPS} ->{$_};2174 $$groups{$_} or $$groups{$_} = $tagTablePtr->{GROUPS}{$_}; 1316 2175 } 1317 2176 } … … 1319 2178 $$tagInfo{GotGroups} = 1; 1320 2179 } 1321 if (defined $family and $family >= 0) { 1322 return $$groups{$family} || 'Other' unless $family == 1; 1323 $groups[$family] = $$groups{$family}; 2180 if (defined $family and $family ne '-1') { 2181 if ($family =~ /[^\d]/) { 2182 @families = ($family =~ /\d+/g); 2183 return $$groups{0} unless @families; 2184 $simplify = 1 unless $family =~ /^:/; 2185 undef $family; 2186 foreach (0..2) { $groups[$_] = $$groups{$_}; } 2187 } else { 2188 return $$groups{$family} if $family == 0 or $family == 2; 2189 $groups[1] = $$groups{1}; 2190 } 1324 2191 } else { 1325 2192 return $$groups{0} unless wantarray; 1326 2193 foreach (0..2) { $groups[$_] = $$groups{$_}; } 1327 2194 } 1328 # modify family 1 group name if necessary 1329 if ($extra = $self->{GROUP1}->{$tag}) { 1330 if ($extra =~ /^\+(.*)/) { 1331 $groups[1] .= $1; 1332 } else { 1333 $groups[1] = $extra; 1334 } 2195 $groups[3] = 'Main'; 2196 $groups[4] = ($tag =~ /\((\d+)\)$/) ? "Copy$1" : ''; 2197 # handle dynamic group names if necessary 2198 my $ex = $self->{TAG_EXTRA}{$tag}; 2199 if ($ex and not $byTagInfo) { 2200 $groups[0] = $$ex{G0} if $$ex{G0}; 2201 $groups[1] = $$ex{G1} =~ /^\+(.*)/ ? "$groups[1]$1" : $$ex{G1} if $$ex{G1}; 2202 $groups[3] = 'Doc' . $$ex{G3} if $$ex{G3}; 2203 $groups[5] = $$ex{G5} || $groups[1] if defined $$ex{G5}; 1335 2204 } 1336 2205 if ($family) { 1337 return $groups[ 1] if $family == 1;2206 return $groups[$family] || '' if $family > 0; 1338 2207 # add additional matching group names to list 1339 2208 # ie) for MIE-Doc, also add MIE1, MIE1-Doc, MIE-Doc1 and MIE1-Doc1 … … 1346 2215 } 1347 2216 } 2217 if (@families) { 2218 my @grps; 2219 # create list of group names (without identical adjacent groups if simplifying) 2220 foreach (@families) { 2221 my $grp = $groups[$_] or next; 2222 push @grps, $grp unless $simplify and @grps and $grp eq $grps[-1]; 2223 } 2224 # remove leading "Main:" if simplifying 2225 shift @grps if $simplify and @grps > 1 and $grps[0] eq 'Main'; 2226 # return colon-separated string of group names 2227 return join ':', @grps; 2228 } 1348 2229 return @groups; 1349 2230 } … … 1353 2234 # Inputs: 0) ExifTool object reference 1354 2235 # 1) [optional] information hash reference (default all extracted info) 1355 # 2) [optional] group family number(default 0)2236 # 2) [optional] group family (default 0) 1356 2237 # Returns: List of group names in alphabetical order 1357 2238 sub GetGroups($;$$) … … 1401 2282 1402 2283 #------------------------------------------------------------------------------ 1403 # Build composite tags from required tags2284 # Build Composite tags from Require'd/Desire'd tags 1404 2285 # Inputs: 0) ExifTool object reference 1405 2286 # Note: Tag values are calculated in alphabetical order unless a tag Require's 1406 # or Desire's another composite tag, in which case the calculation is2287 # or Desire's another Composite tag, in which case the calculation is 1407 2288 # deferred until after the other tag is calculated. 1408 2289 sub BuildCompositeTags($) … … 1411 2292 my $self = shift; 1412 2293 1413 # first, add user-defined composite tags if necessary 2294 $$self{BuildingComposite} = 1; 2295 # first, add user-defined Composite tags if necessary 1414 2296 if (%UserDefined and $UserDefined{'Image::ExifTool::Composite'}) { 1415 AddCompositeTags($UserDefined{'Image::ExifTool::Composite'}, 1);2297 AddCompositeTags($UserDefined{'Image::ExifTool::Composite'}, 1); 1416 2298 delete $UserDefined{'Image::ExifTool::Composite'}; 1417 2299 } … … 1422 2304 for (;;) { 1423 2305 my %notBuilt; 1424 foreach (@tagList) { 1425 $notBuilt{$_} = 1; 1426 } 2306 $notBuilt{$_} = 1 foreach @tagList; 1427 2307 my @deferredTags; 1428 2308 my $tag; … … 1433 2313 next unless $tagInfo; 1434 2314 # put required tags into array and make sure they all exist 1435 my (%tagKey, $type, $found); 1436 foreach $type ('Require','Desire') { 1437 my $req = $$tagInfo{$type} or next; 2315 my $subDoc = ($$tagInfo{SubDoc} and $$self{DOC_COUNT}); 2316 my $require = $$tagInfo{Require} || { }; 2317 my $desire = $$tagInfo{Desire} || { }; 2318 # loop through sub-documents if necessary 2319 my $doc; 2320 for (;;) { 2321 my (%tagKey, $found, $index); 1438 2322 # save Require'd and Desire'd tag values in list 1439 my $index; 1440 foreach $index (keys %$req) { 1441 my $reqTag = $$req{$index}; 2323 for ($index=0; ; ++$index) { 2324 my $reqTag = $$require{$index} || $$desire{$index} or last; 2325 # add family 3 group if generating Composite tags for sub-documents 2326 # (unless tag already begins with family 3 group name) 2327 if ($subDoc and $reqTag !~ /^(Main|Doc\d+):/) { 2328 $reqTag = ($doc ? "Doc$doc:" : 'Main:') . $reqTag; 2329 } 1442 2330 # allow tag group to be specified 1443 if ($reqTag =~ / (.+?):(.+)/) {2331 if ($reqTag =~ /^(.*):(.+)/) { 1444 2332 my ($reqGroup, $name) = ($1, $2); 1445 my $family; 1446 $family = $1 if $reqGroup =~ s/^(\d+)//; 1447 my $i = 0; 1448 for (;;++$i) { 1449 $reqTag = $name; 1450 $reqTag .= " ($i)" if $i; 1451 last unless defined $$rawValue{$reqTag}; 1452 my @groups = $self->GetGroup($reqTag, $family); 1453 last if grep { $reqGroup eq $_ } @groups; 2333 if ($reqGroup eq 'Composite' and $notBuilt{$name}) { 2334 push @deferredTags, $tag; 2335 next COMPOSITE_TAG; 1454 2336 } 2337 my ($i, $key, @keys); 2338 for ($i=0; ; ++$i) { 2339 $key = $name; 2340 $key .= " ($i)" if $i; 2341 last unless defined $$rawValue{$key}; 2342 push @keys, $key; 2343 } 2344 # find first matching tag 2345 $key = $self->GroupMatches($reqGroup, \@keys); 2346 $reqTag = $key if $key; 1455 2347 } elsif ($notBuilt{$reqTag}) { 1456 2348 # calculate this tag later if it relies on another … … 1461 2353 if (defined $$rawValue{$reqTag}) { 1462 2354 $found = 1; 1463 } els e{1464 # don't continue if we require this tag1465 $type eq 'Require' and next COMPOSITE_TAG;2355 } elsif ($$require{$index}) { 2356 $found = 0; 2357 last; # don't continue since we require this tag 1466 2358 } 1467 2359 $tagKey{$index} = $reqTag; 1468 2360 } 1469 } 1470 delete $notBuilt{$tag}; # this tag is OK to build now 1471 next unless $found; # can't build tag if no values found 1472 # keep track of all require'd tag keys 1473 foreach (keys %tagKey) { 1474 # only tag keys with same name as a composite tag can be replaced 1475 # (also eliminates keys with instance numbers which can't be replaced either) 1476 next unless $Image::ExifTool::Composite{$tagKey{$_}}; 1477 my $keyRef = \$tagKey{$_}; 1478 $tagsUsed{$$keyRef} or $tagsUsed{$$keyRef} = [ ]; 1479 push @{$tagsUsed{$$keyRef}}, $keyRef; 1480 } 1481 # save reference to tag key lookup as value for composite tag 1482 my $key = $self->FoundTag($tagInfo, \%tagKey); 1483 # check to see if we just replaced one of the tag keys we require'd 1484 next unless defined $key and $tagsUsed{$key}; 1485 foreach (@{$tagsUsed{$key}}) { 1486 $$_ = $self->{MOVED_KEY}; # replace with new tag key 1487 } 1488 delete $tagsUsed{$key}; # can't be replaced again 2361 if ($doc) { 2362 if ($found) { 2363 $self->{DOC_NUM} = $doc; 2364 $self->FoundTag($tagInfo, \%tagKey); 2365 delete $self->{DOC_NUM}; 2366 } 2367 next if ++$doc <= $self->{DOC_COUNT}; 2368 last; 2369 } elsif ($found) { 2370 delete $notBuilt{$tag}; # this tag is OK to build now 2371 # keep track of all Require'd tag keys 2372 foreach (keys %tagKey) { 2373 # only tag keys with same name as a Composite tag 2374 # can be replaced (also eliminates keys with 2375 # instance numbers which can't be replaced either) 2376 next unless $Image::ExifTool::Composite{$tagKey{$_}}; 2377 my $keyRef = \$tagKey{$_}; 2378 $tagsUsed{$$keyRef} or $tagsUsed{$$keyRef} = [ ]; 2379 push @{$tagsUsed{$$keyRef}}, $keyRef; 2380 } 2381 # save reference to tag key lookup as value for Composite tag 2382 my $key = $self->FoundTag($tagInfo, \%tagKey); 2383 # check to see if we just replaced one of the tag keys we Require'd 2384 if (defined $key and $tagsUsed{$key}) { 2385 foreach (@{$tagsUsed{$key}}) { 2386 $$_ = $self->{MOVED_KEY}; # replace with new tag key 2387 } 2388 delete $tagsUsed{$key}; # can't be replaced again 2389 } 2390 } elsif (not defined $found) { 2391 delete $notBuilt{$tag}; # tag can't be built anyway 2392 } 2393 last unless $subDoc; 2394 $doc = 1; # continue to process the 1st sub-document 2395 } 1489 2396 } 1490 2397 last unless @deferredTags; … … 1497 2404 @tagList = @deferredTags; # calculate deferred tags now 1498 2405 } 2406 delete $$self{BuildingComposite}; 1499 2407 } 1500 2408 … … 1522 2430 #------------------------------------------------------------------------------ 1523 2431 # Get file type for specified extension 1524 # Inputs: 0) file name or extension (case is not significant) 1525 # 1) flag to return long description instead of type 1526 # Returns: File type (or desc) or undef if extension not supported. In array 2432 # Inputs: 0) file name or extension (case is not significant), 2433 # or FileType value if a description is requested 2434 # 1) flag to return long description instead of type ('0' to return any recognized type) 2435 # Returns: File type (or desc) or undef if extension not supported or if 2436 # description is the same as the input FileType. In array 1527 2437 # context, may return more than one file type if the file may be 1528 # different formats. Returns list of all recognized extensions if no2438 # different formats. Returns list of all supported extensions if no 1529 2439 # file specified 1530 2440 sub GetFileType(;$$) … … 1532 2442 local $_; 1533 2443 my ($file, $desc) = @_; 1534 return sort keys %fileTypeLookup unless defined $file; 2444 unless (defined $file) { 2445 my @types; 2446 if (defined $desc and $desc eq '0') { 2447 # return all recognized types 2448 @types = sort keys %fileTypeLookup; 2449 } else { 2450 # return all supported types 2451 foreach (sort keys %fileTypeLookup) { 2452 push @types, $_ unless defined $moduleName{$_} and $moduleName{$_} eq '0'; 2453 } 2454 } 2455 return @types; 2456 } 1535 2457 my $fileType; 1536 2458 my $fileExt = GetFileExtension($file); 1537 2459 $fileExt = uc($file) unless $fileExt; 1538 2460 $fileExt and $fileType = $fileTypeLookup{$fileExt}; # look up the file type 1539 return $$fileType[1] if $desc; # return description if specified 2461 $fileType = $fileTypeLookup{$fileType} unless ref $fileType or not $fileType; 2462 # return description if specified 2463 # (allow input $file to be a FileType for this purpose) 2464 if ($desc) { 2465 return $fileType ? $$fileType[1] : $fileDescription{$file}; 2466 } elsif ($fileType and (not defined $desc or $desc ne '0')) { 2467 # return only supported file types 2468 my $mod = $moduleName{$$fileType[0]}; 2469 undef $fileType if defined $mod and $mod eq '0'; 2470 } 2471 $fileType or return wantarray ? () : undef; 1540 2472 $fileType = $$fileType[0]; # get file type (or list of types) 1541 2473 if (wantarray) { 1542 return () unless $fileType;1543 2474 return @$fileType if ref $fileType eq 'ARRAY'; 1544 2475 } elsif ($fileType) { … … 1550 2481 #------------------------------------------------------------------------------ 1551 2482 # Return true if we can write the specified file type 1552 # Inputs: 0) file name or ext ,2483 # Inputs: 0) file name or ext 1553 2484 # Returns: true if writable, 0 if not writable, undef if unrecognized 1554 # Note: This will return true for some TIFF-based RAW images which we shouldn't really write1555 2485 sub CanWrite($) 1556 2486 { … … 1558 2488 my $file = shift or return undef; 1559 2489 my $type = GetFileType($file) or return undef; 2490 if ($noWriteFile{$type}) { 2491 # can't write TIFF files with certain extensions (various RAW formats) 2492 my $ext = GetFileExtension($file) || uc($file); 2493 return grep(/^$ext$/, @{$noWriteFile{$type}}) ? 0 : 1 if $ext; 2494 } 1560 2495 return scalar(grep /^$type$/, @writeTypes); 1561 2496 } … … 1563 2498 #------------------------------------------------------------------------------ 1564 2499 # Return true if we can create the specified file type 1565 # Inputs: 0) file name or ext ,2500 # Inputs: 0) file name or ext 1566 2501 # Returns: true if creatable, 0 if not writable, undef if unrecognized 1567 2502 sub CanCreate($) … … 1569 2504 local $_; 1570 2505 my $file = shift or return undef; 2506 my $ext = GetFileExtension($file) || uc($file); 1571 2507 my $type = GetFileType($file) or return undef; 1572 return scalar(grep /^$type$/, @createTypes); 2508 return 1 if $createTypes{$ext} or $createTypes{$type}; 2509 return 0; 1573 2510 } 1574 2511 … … 1590 2527 delete $self->{EXIF_POS}; # EXIF position in file 1591 2528 delete $self->{FIRST_EXIF_POS}; # position of first EXIF in file 1592 delete $self->{EXIF_BYTE_ORDER};# the EXIF byte ordering1593 2529 delete $self->{HTML_DUMP}; # html dump information 2530 delete $self->{SET_GROUP1}; # group1 name override 2531 delete $self->{DOC_NUM}; # current embedded document number 2532 $self->{DOC_COUNT} = 0; # count of embedded documents processed 1594 2533 $self->{BASE} = 0; # base for offsets from start of file 1595 $self->{FILE_ORDER} = { }; # hash of tag order in file 1596 $self->{VALUE} = { }; # hash of raw tag values 1597 $self->{TAG_INFO} = { }; # hash of tag information 1598 $self->{GROUP1} = { }; # hash of family 1 group names 1599 $self->{PRIORITY} = { }; # priority of current tags 2534 $self->{FILE_ORDER} = { }; # * hash of tag order in file 2535 $self->{VALUE} = { }; # * hash of raw tag values 2536 $self->{BOTH} = { }; # * hash for Value/PrintConv values of Require'd tags 2537 $self->{TAG_INFO} = { }; # * hash of tag information 2538 $self->{TAG_EXTRA} = { }; # * hash of extra tag information (dynamic group names) 2539 $self->{PRIORITY} = { }; # * priority of current tags 2540 $self->{LIST_TAGS} = { }; # hash of tagInfo refs for active List-type tags 1600 2541 $self->{PROCESSED} = { }; # hash of processed directory start positions 1601 2542 $self->{DIR_COUNT} = { }; # count various types of directories 2543 $self->{DUPL_TAG} = { }; # last-used index for duplicate-tag keys 2544 $self->{WARNED_ONCE}= { }; # WarnOnce() warnings already issued 2545 $self->{PATH} = [ ]; # current subdirectory path in file when reading 1602 2546 $self->{NUM_FOUND} = 0; # total number of tags found (incl. duplicates) 1603 2547 $self->{CHANGED} = 0; # number of tags changed (writer only) 1604 2548 $self->{INDENT} = ' '; # initial indent for verbose messages 1605 2549 $self->{PRIORITY_DIR} = ''; # the priority directory name 2550 $self->{LOW_PRIORITY_DIR} = { PreviewIFD => 1 }; # names of priority 0 directories 1606 2551 $self->{TIFF_TYPE} = ''; # type of TIFF data (APP1, TIFF, NEF, etc...) 1607 $self->{ CameraMake}= ''; # camera make1608 $self->{ CameraModel}= ''; # camera model2552 $self->{Make} = ''; # camera make 2553 $self->{Model} = ''; # camera model 1609 2554 $self->{CameraType} = ''; # Olympus camera type 1610 2555 if ($self->Options('HtmlDump')) { … … 1613 2558 } 1614 2559 # make sure our TextOut is a file reference 1615 $self->{OPTIONS} ->{TextOut} = \*STDOUT unless ref $self->{OPTIONS}->{TextOut};2560 $self->{OPTIONS}{TextOut} = \*STDOUT unless ref $self->{OPTIONS}{TextOut}; 1616 2561 } 1617 2562 … … 1655 2600 undef @oldGroupOpts; 1656 2601 } 1657 $ options->{$opt} = $$arg{$opt};2602 $self->Options($opt, $$arg{$opt}); 1658 2603 $opt eq 'Exclude' and $wasExcludeOpt = 1; 1659 2604 } … … 1662 2607 # convert image data from UTF-8 to character stream if necessary 1663 2608 # (patches RHEL 3 UTF8 LANG problem) 1664 if (ref $arg eq 'SCALAR' and eval 'require Encode; Encode::is_utf8($$arg)') { 1665 my $buff = pack('C*', unpack('U0U*', $$arg)); 2609 if (ref $arg eq 'SCALAR' and $] >= 5.006 and 2610 (eval 'require Encode; Encode::is_utf8($$arg)' or $@)) 2611 { 2612 # repack by hand if Encode isn't available 2613 my $buff = $@ ? pack('C*',unpack('U0C*',$$arg)) : Encode::encode('utf8',$$arg); 1666 2614 $arg = \$buff; 1667 2615 } … … 1670 2618 # we have a file but we didn't open it 1671 2619 $self->{FILENAME} = ''; 2620 } elsif (UNIVERSAL::isa($arg, 'File::RandomAccess')) { 2621 $self->{RAF} = $arg; 2622 $self->{FILENAME} = ''; 1672 2623 } else { 1673 2624 warn "Don't understand ImageInfo argument $arg\n"; … … 1688 2639 # initialize lookup for requested tags 1689 2640 foreach (@{$self->{REQUESTED_TAGS}}) { 1690 $self->{REQ_TAG_LOOKUP} ->{lc(/.+?:(.+)/ ? $1 : $_)} = 1;2641 $self->{REQ_TAG_LOOKUP}{lc(/.+:(.+)/ ? $1 : $_)} = 1; 1691 2642 } 1692 2643 } … … 1703 2654 $options->{Exclude} = \@exclude; 1704 2655 # expand shortcuts in new exclude list 1705 ExpandShortcuts($options->{Exclude}); 1706 } 1707 } 1708 1709 #------------------------------------------------------------------------------ 1710 # Set list of found tags 2656 ExpandShortcuts($options->{Exclude}, 1); # (also remove '#' suffix) 2657 } 2658 } 2659 2660 #------------------------------------------------------------------------------ 2661 # Get list of tags in specified group 2662 # Inputs: 0) ExifTool ref, 1) group spec, 2) tag key or reference to list of tag keys 2663 # Returns: list of matching tags in list context, or first match in scalar context 2664 # Notes: Group spec may contain multiple groups separated by colons, each 2665 # possibly with a leading family number 2666 sub GroupMatches($$$) 2667 { 2668 my ($self, $group, $tagList) = @_; 2669 $tagList = [ $tagList ] unless ref $tagList; 2670 my ($tag, @matches); 2671 if ($group =~ /:/) { 2672 # check each group name individually (ie. "Author:1IPTC") 2673 my @grps = split ':', lc $group; 2674 my (@fmys, $g); 2675 for ($g=0; $g<@grps; ++$g) { 2676 $fmys[$g] = $1 if $grps[$g] =~ s/^(\d+)//; 2677 } 2678 foreach $tag (@$tagList) { 2679 my @groups = $self->GetGroup($tag, -1); 2680 for ($g=0; $g<@grps; ++$g) { 2681 my $grp = $grps[$g]; 2682 next if $grp eq '*' or $grp eq 'all'; 2683 if (defined $fmys[$g]) { 2684 my $f = $fmys[$g]; 2685 last unless $groups[$f] and $grps[$g] eq lc $groups[$f]; 2686 } else { 2687 last unless grep /^$grps[$g]$/i, @groups; 2688 } 2689 } 2690 push @matches, $tag if $g == @grps; 2691 } 2692 } else { 2693 my $family = ($group =~ s/^(\d+)//) ? $1 : -1; 2694 foreach $tag (@$tagList) { 2695 my @groups = $self->GetGroup($tag, $family); 2696 push @matches, $tag if grep(/^$group$/i, @groups); 2697 } 2698 } 2699 return wantarray ? @matches : $matches[0]; 2700 } 2701 2702 #------------------------------------------------------------------------------ 2703 # Set list of found tags from previously requested tags 1711 2704 # Inputs: 0) ExifTool object reference 1712 # Returns: Reference to found tags list (in order of requested tags) 2705 # Returns: 0) Reference to list of found tag keys (in order of requested tags) 2706 # 1) Reference to list of indices for tags requested by value 1713 2707 sub SetFoundTags($) 1714 2708 { … … 1721 2715 my @groupOptions = sort grep /^Group/, keys %$options; 1722 2716 my $doDups = $duplicates || $exclude || @groupOptions; 1723 my ($tag, $rtnTags );2717 my ($tag, $rtnTags, @byValue); 1724 2718 1725 2719 # only return requested tags if specified … … 1730 2724 my $reqTag; 1731 2725 foreach $reqTag (@$reqTags) { 1732 my (@matches, $group, $family, $allGrp, $allTag); 1733 if ($reqTag =~ /^(\d+)?(.+?):(.+)/) { 1734 ($family, $group, $tag) = ($1, $2, $3); 1735 $allGrp = 1 if $group =~ /^(\*|all)$/i; 1736 $family = -1 unless defined $family; 2726 my (@matches, $group, $allGrp, $allTag, $byValue); 2727 if ($reqTag =~ /^(.*):(.+)/) { 2728 ($group, $tag) = ($1, $2); 2729 if ($group =~ /^(\*|all)$/i) { 2730 $allGrp = 1; 2731 } elsif ($group !~ /^[-\w:]*$/) { 2732 $self->Warn("Invalid group name '$group'"); 2733 $group = 'invalid'; 2734 } 1737 2735 } else { 1738 2736 $tag = $reqTag; 1739 $family = -1;1740 }2737 } 2738 $byValue = 1 if $tag =~ s/#$//; 1741 2739 if (defined $tagHash->{$reqTag} and not $doDups) { 1742 2740 $matches[0] = $tag; … … 1750 2748 next unless @matches; # don't want entry in list for '*' tag 1751 2749 $allTag = 1; 2750 } elsif ($tag =~ /[*?]/) { 2751 # allow wildcards in tag names 2752 $tag =~ s/\*/[-\\w]*/g; 2753 $tag =~ s/\?/[-\\w]/g; 2754 $tag .= '( .*)?' if $doDups or $allGrp; 2755 @matches = grep(/^$tag$/i, keys %$tagHash); 2756 next unless @matches; # don't want entry in list for wildcard tags 2757 $allTag = 1; 1752 2758 } elsif ($doDups or defined $group) { 1753 2759 # must also look for tags like "Tag (1)" 1754 @matches = grep(/^$tag( \s|$)/i, keys %$tagHash);1755 } els e{2760 @matches = grep(/^$tag( |$)/i, keys %$tagHash); 2761 } elsif ($tag =~ /^[-\w]+$/) { 1756 2762 # find first matching value 1757 2763 # (use in list context to return value instead of count) 1758 2764 ($matches[0]) = grep /^$tag$/i, keys %$tagHash; 1759 2765 defined $matches[0] or undef @matches; 2766 } else { 2767 $self->Warn("Invalid tag name '$tag'"); 1760 2768 } 1761 2769 if (defined $group and not $allGrp) { 1762 2770 # keep only specified group 1763 my @grpMatches; 1764 foreach (@matches) { 1765 my @groups = $self->GetGroup($_, $family); 1766 next unless grep /^$group$/i, @groups; 1767 push @grpMatches, $_; 1768 } 1769 @matches = @grpMatches; 2771 @matches = $self->GroupMatches($group, \@matches); 1770 2772 next unless @matches or not $allTag; 1771 2773 } … … 1776 2778 unless ($doDups or $allTag or $allGrp) { 1777 2779 $tag = shift @matches; 1778 my $oldPriority = $self->{PRIORITY} ->{$tag} || 1;2780 my $oldPriority = $self->{PRIORITY}{$tag} || 1; 1779 2781 foreach (@matches) { 1780 my $priority = $self->{PRIORITY} ->{$_};2782 my $priority = $self->{PRIORITY}{$_}; 1781 2783 $priority = 1 unless defined $priority; 1782 2784 next unless $priority >= $oldPriority; … … 1790 2792 $matches[0] = "$tag (0)"; 1791 2793 # bogus file order entry to avoid warning if sorting in file order 1792 $self->{FILE_ORDER}->{$matches[0]} = 999; 1793 } 2794 $self->{FILE_ORDER}{$matches[0]} = 999; 2795 } 2796 # save indices of tags extracted by value 2797 push @byValue, scalar(@$rtnTags) .. (scalar(@$rtnTags)+scalar(@matches)-1) if $byValue; 1794 2798 push @$rtnTags, @matches; 1795 2799 } … … 1811 2815 while (($exclude or @groupOptions) and @$rtnTags) { 1812 2816 if ($exclude) { 1813 my @filteredTags; 1814 EX_TAG: foreach $tag (@$rtnTags) { 1815 my $tagName = GetTagName($tag); 1816 my @matches = grep /(^|:)($tagName|\*|all)$/i, @$exclude; 1817 foreach (@matches) { 1818 next EX_TAG unless /^(\d+)?(.+?):/; 1819 my ($family, $group) = ($1, $2); 1820 next EX_TAG if $group =~ /^(\*|all)$/i; 1821 $family = -1 unless defined $family; 1822 my @groups = $self->GetGroup($tag, $family); 1823 next EX_TAG if grep /^$group$/i, @groups; 2817 my ($pat, %exclude); 2818 foreach $pat (@$exclude) { 2819 my $group; 2820 if ($pat =~ /^(.*):(.+)/) { 2821 ($group, $tag) = ($1, $2); 2822 if ($group =~ /^(\*|all)$/i) { 2823 undef $group; 2824 } elsif ($group !~ /^[-\w:]*$/) { 2825 $self->Warn("Invalid group name '$group'"); 2826 $group = 'invalid'; 2827 } 2828 } else { 2829 $tag = $pat; 1824 2830 } 1825 push @filteredTags, $tag; 1826 } 1827 $rtnTags = \@filteredTags; # use new filtered tag list 2831 my @matches; 2832 if ($tag =~ /^(\*|all)$/i) { 2833 @matches = @$rtnTags; 2834 } else { 2835 # allow wildcards in tag names 2836 $tag =~ s/\*/[-\\w]*/g; 2837 $tag =~ s/\?/[-\\w]/g; 2838 @matches = grep(/^$tag( |$)/i, @$rtnTags); 2839 } 2840 @matches = $self->GroupMatches($group, \@matches) if $group and @matches; 2841 $exclude{$_} = 1 foreach @matches; 2842 } 2843 if (%exclude) { 2844 my @filteredTags; 2845 $exclude{$_} or push @filteredTags, $_ foreach @$rtnTags; 2846 $rtnTags = \@filteredTags; # use new filtered tag list 2847 last unless @filteredTags; # all done if nothing left 2848 } 1828 2849 last if $duplicates and not @groupOptions; 1829 2850 } … … 1834 2855 my $wantOrder = 0; 1835 2856 foreach $groupOpt (@groupOptions) { 1836 $groupOpt =~ /^Group(\d* )/ or next;2857 $groupOpt =~ /^Group(\d*(:\d+)*)/ or next; 1837 2858 $family = $1 || 0; 1838 2859 $wantGroup{$family} or $wantGroup{$family} = { }; … … 1856 2877 $allGroups = 0; # don't want all groups if we requested one 1857 2878 } 1858 $wantGroup{$family} ->{$groupName} = $want;2879 $wantGroup{$family}{$groupName} = $want; 1859 2880 } 1860 2881 } … … 1865 2886 foreach $family (keys %wantGroup) { 1866 2887 my $group = $self->GetGroup($tag, $family); 1867 my $wanted = $wantGroup{$family} ->{$group};2888 my $wanted = $wantGroup{$family}{$group}; 1868 2889 next unless defined $wanted; 1869 2890 next GR_TAG unless $wanted; # skip tag if group excluded … … 1903 2924 last; 1904 2925 } 1905 1906 # save found tags and return reference 1907 return $self->{FOUND_TAGS} = $rtnTags; 2926 $self->{FOUND_TAGS} = $rtnTags; # save found tags 2927 2928 # return reference to found tag keys (and list of indices of tags to extract by value) 2929 return wantarray ? ($rtnTags, \@byValue) : $rtnTags; 1908 2930 } 1909 2931 … … 1955 2977 my ($self, $str, $ignorable) = @_; 1956 2978 if ($ignorable) { 1957 return 0 if $self->{OPTIONS} ->{IgnoreMinorErrors};2979 return 0 if $self->{OPTIONS}{IgnoreMinorErrors}; 1958 2980 $str = "[minor] $str"; 1959 2981 } 1960 2982 $self->FoundTag('Warning', $str); 2983 return 1; 2984 } 2985 2986 #------------------------------------------------------------------------------ 2987 # Add warning tag only once per processed file 2988 # Inputs: 0) ExifTool object reference, 1) warning message, 2) true if minor 2989 # Returns: true if warning tag was added 2990 sub WarnOnce($$;$) 2991 { 2992 my ($self, $str, $ignorable) = @_; 2993 return 0 if $ignorable and $self->{OPTIONS}{IgnoreMinorErrors}; 2994 unless ($$self{WARNED_ONCE}{$str}) { 2995 $self->Warn($str, $ignorable); 2996 $$self{WARNED_ONCE}{$str} = 1; 2997 } 1961 2998 return 1; 1962 2999 } … … 1970 3007 my ($self, $str, $ignorable) = @_; 1971 3008 if ($ignorable) { 1972 if ($self->{OPTIONS} ->{IgnoreMinorErrors}) {3009 if ($self->{OPTIONS}{IgnoreMinorErrors}) { 1973 3010 $self->Warn($str); 1974 3011 return 0; … … 1982 3019 #------------------------------------------------------------------------------ 1983 3020 # Expand shortcuts 1984 # Inputs: 0) reference to list of tags 1985 # Notes: Handles leading '-' for excluded tags, group names, and redirected tags 1986 sub ExpandShortcuts($) 1987 { 1988 my $tagList = shift || return; 3021 # Inputs: 0) reference to list of tags, 1) set to remove trailing '#' 3022 # Notes: Handles leading '-' for excluded tags, trailing '#' for ValueConv, 3023 # multiple group names, and redirected tags 3024 sub ExpandShortcuts($;$) 3025 { 3026 my ($tagList, $removeSuffix) = @_; 3027 return unless $tagList and @$tagList; 1989 3028 1990 3029 require Image::ExifTool::Shortcuts; 1991 3030 1992 3031 # expand shortcuts 3032 my $suffix = $removeSuffix ? '' : '#'; 1993 3033 my @expandedTags; 1994 3034 my ($entry, $tag, $excl); 1995 3035 foreach $entry (@$tagList) { 3036 # skip things like options hash references in list 3037 if (ref $entry) { 3038 push @expandedTags, $entry; 3039 next; 3040 } 1996 3041 # remove leading '-' 1997 3042 ($excl, $tag) = $entry =~ /^(-?)(.*)/s; 1998 my ($post, @post );3043 my ($post, @post, $pre, $v); 1999 3044 # handle redirection 2000 if ( $tag =~ /(.+?)([-+]?[<>].+)/s and not $excl) {3045 if (not $excl and $tag =~ /(.+?)([-+]?[<>].+)/s) { 2001 3046 ($tag, $post) = ($1, $2); 2002 3047 if ($post =~ /^[-+]?>/ or $post !~ /\$/) { 2003 3048 # expand shortcuts in postfix (rhs of redirection) 2004 my ($op, $p2, $t2) = ($post =~ /([-+]?[<>])(.+ ?:)?(.+)/);3049 my ($op, $p2, $t2) = ($post =~ /([-+]?[<>])(.+:)?(.+)/); 2005 3050 $p2 = '' unless defined $p2; 3051 $v = ($t2 =~ s/#$//) ? $suffix : ''; # ValueConv suffix 2006 3052 my ($match) = grep /^\Q$t2\E$/i, keys %Image::ExifTool::Shortcuts::Main; 2007 3053 if ($match) { 2008 3054 foreach (@{$Image::ExifTool::Shortcuts::Main{$match}}) { 2009 3055 /^-/ and next; # ignore excluded tags 2010 if ($p2 and /(.+ ?:)(.+)/) {2011 push @post, "$op$_ ";3056 if ($p2 and /(.+:)(.+)/) { 3057 push @post, "$op$_$v"; 2012 3058 } else { 2013 push @post, "$op$p2$_ ";3059 push @post, "$op$p2$_$v"; 2014 3060 } 2015 3061 } … … 2022 3068 } 2023 3069 # handle group names 2024 my $pre; 2025 if ($tag =~ /(.+?:)(.+)/) { 3070 if ($tag =~ /(.+:)(.+)/) { 2026 3071 ($pre, $tag) = ($1, $2); 2027 3072 } else { 2028 3073 $pre = ''; 2029 3074 } 3075 $v = ($tag =~ s/#$//) ? $suffix : ''; # ValueConv suffix 2030 3076 # loop over all postfixes 2031 3077 for (;;) { … … 2038 3084 /^-/ and next; # ignore excluded exclude tags 2039 3085 # group of expanded tag takes precedence 2040 if ($pre and /(.+ ?:)(.+)/) {3086 if ($pre and /(.+:)(.+)/) { 2041 3087 push @expandedTags, "$excl$_"; 2042 3088 } else { … … 2044 3090 } 2045 3091 } 2046 } elsif (length $pre or length $post ) {3092 } elsif (length $pre or length $post or $v) { 2047 3093 foreach (@{$Image::ExifTool::Shortcuts::Main{$match}}) { 2048 /(-?)(.+ ?:)?(.+)/;3094 /(-?)(.+:)?(.+)/; 2049 3095 if ($2) { 2050 3096 # group from expanded tag takes precedence 2051 push @expandedTags, "$_$ post";3097 push @expandedTags, "$_$v$post"; 2052 3098 } else { 2053 push @expandedTags, "$1$pre$3$ post";3099 push @expandedTags, "$1$pre$3$v$post"; 2054 3100 } 2055 3101 } … … 2058 3104 } 2059 3105 } else { 2060 push @expandedTags, "$excl$pre$tag$ post";3106 push @expandedTags, "$excl$pre$tag$v$post"; 2061 3107 } 2062 3108 last unless @post; … … 2068 3114 2069 3115 #------------------------------------------------------------------------------ 2070 # Add hash of composite tags to our composites2071 # Inputs: 0) hash reference to table of composite tags to add or module name,3116 # Add hash of Composite tags to our composites 3117 # Inputs: 0) hash reference to table of Composite tags to add or module name, 2072 3118 # 1) overwrite existing tag 2073 3119 sub AddCompositeTags($;$) … … 2092 3138 $defaultGroups = $$add{GROUPS} = { 0 => 'Composite', 1 => 'Composite', 2 => 'Other' }; 2093 3139 } 2094 SetupTagTable($add); 3140 SetupTagTable($add); # generate tag Name, etc 2095 3141 my $tagID; 2096 foreach $tagID ( keys %$add) {3142 foreach $tagID (sort keys %$add) { 2097 3143 next if $specialTags{$tagID}; # must skip special tags 2098 3144 my $tagInfo = $$add{$tagID}; … … 2100 3146 my $tag = $$tagInfo{Name}; 2101 3147 $$tagInfo{Module} = $module if $$tagInfo{Writable}; 2102 # allow composite tags with the same name3148 # allow Composite tags with the same name 2103 3149 my ($t, $n, $type); 2104 3150 while ($Image::ExifTool::Composite{$tag} and not $overwrite) { 2105 $n ? $n += 1 : $n = 2, $t = $tag;3151 $n ? $n += 1 : ($n = 2, $t = $tag); 2106 3152 $tag = "${t}_$n"; 3153 $$tagInfo{NewTagID} = $tag; # save new ID so we can use it in TagLookup 2107 3154 } 2108 3155 # convert scalar Require/Desire entries … … 2111 3158 $$tagInfo{$type} = { 0 => $req } if ref($req) ne 'HASH'; 2112 3159 } 2113 # add this composite tag to our main composite table3160 # add this Composite tag to our main Composite table 2114 3161 $$tagInfo{Table} = \%Image::ExifTool::Composite; 3162 # (use the original TagID, even if we changed it) 3163 # $$tagInfo{TagID} = $tag; 3164 # save new tag ID so we can find entry in Composite table 2115 3165 $Image::ExifTool::Composite{$tag} = $tagInfo; 2116 3166 # set all default groups in tag … … 2127 3177 2128 3178 #------------------------------------------------------------------------------ 3179 # Add tags to TagLookup (used for writing) 3180 # Inputs: 0) source hash of tag definitions, 1) name of destination tag table 3181 sub AddTagsToLookup($$) 3182 { 3183 my ($tagHash, $table) = @_; 3184 if (defined &Image::ExifTool::TagLookup::AddTags) { 3185 Image::ExifTool::TagLookup::AddTags($tagHash, $table); 3186 } elsif (not $Image::ExifTool::pluginTags{$tagHash}) { 3187 # queue these tags until TagLookup is loaded 3188 push @Image::ExifTool::pluginTags, [ $tagHash, $table ]; 3189 # set flag so we don't load same tags twice 3190 $Image::ExifTool::pluginTags{$tagHash} = 1; 3191 } 3192 } 3193 3194 #------------------------------------------------------------------------------ 2129 3195 # Expand tagInfo Flags 2130 3196 # Inputs: 0) tagInfo hash ref … … 2152 3218 # Inputs: 0) Reference to tag table 2153 3219 # Notes: - generates 'Name' field from key if it doesn't exist 2154 # - stores 'Table' pointer 3220 # - stores 'Table' pointer and 'TagID' value 2155 3221 # - expands 'Flags' for quick lookup 2156 3222 sub SetupTagTable($) 2157 3223 { 2158 3224 my $tagTablePtr = shift; 2159 my $tagID;3225 my ($tagID, $tagInfo); 2160 3226 foreach $tagID (TagTableKeys($tagTablePtr)) { 2161 3227 my @infoArray = GetTagInfoList($tagTablePtr,$tagID); 2162 3228 # process conditional tagInfo arrays 2163 my $tagInfo;2164 3229 foreach $tagInfo (@infoArray) { 2165 3230 $$tagInfo{Table} = $tagTablePtr; 3231 $$tagInfo{TagID} = $tagID; 2166 3232 my $tag = $$tagInfo{Name}; 2167 3233 unless (defined $tag) { … … 2172 3238 $$tagInfo{Flags} and ExpandFlags($tagInfo); 2173 3239 } 3240 next unless @infoArray > 1; 3241 # add an "Index" member to each tagInfo in a list 3242 my $index = 0; 3243 foreach $tagInfo (@infoArray) { 3244 $$tagInfo{Index} = $index++; 3245 } 2174 3246 } 2175 3247 } … … 2180 3252 # Notes: May change commas to decimals in floats for use in other locales 2181 3253 sub IsFloat($) { 2182 return 1 if $_[0] =~ /^ ([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/;3254 return 1 if $_[0] =~ /^[+-]?(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/; 2183 3255 # allow comma separators (for other locales) 2184 return 0 unless $_[0] =~ /^ ([+-]?)(?=\d|,\d)\d*(,\d*)?([Ee]([+-]?\d+))?$/;3256 return 0 unless $_[0] =~ /^[+-]?(?=\d|,\d)\d*(,\d*)?([Ee]([+-]?\d+))?$/; 2185 3257 $_[0] =~ tr/,/./; # but translate ',' to '.' 2186 3258 return 1; 2187 3259 } 2188 sub IsInt($) { return scalar($_[0] =~ /^[+-]?\d+$/); } 2189 sub IsHex($) { return scalar($_[0] =~ /^(0x)?[0-9a-f]{1,8}$/i); } 3260 sub IsInt($) { return scalar($_[0] =~ /^[+-]?\d+$/); } 3261 sub IsHex($) { return scalar($_[0] =~ /^(0x)?[0-9a-f]{1,8}$/i); } 3262 sub IsRational($) { return scalar($_[0] =~ m{^[-+]?\d+/\d+$}); } 2190 3263 2191 3264 # round floating point value to specified number of significant digits … … 2201 3274 } 2202 3275 3276 # Convert strings to floating point numbers (or undef) 3277 # Inputs: 0-N) list of strings (may be undef) 3278 # Returns: last value converted 3279 sub ToFloat(@) 3280 { 3281 local $_; 3282 foreach (@_) { 3283 next unless defined $_; 3284 # (add 0 to convert "0.0" to "0" for tests) 3285 $_ = /((?:[+-]?)(?=\d|\.\d)\d*(?:\.\d*)?(?:[Ee](?:[+-]?\d+))?)/ ? $1 + 0 : undef; 3286 } 3287 return $_[-1]; 3288 } 3289 2203 3290 #------------------------------------------------------------------------------ 2204 3291 # Utility routines to for reading binary data values from file 2205 3292 2206 my $swapBytes; # set if EXIF header is not native byte ordering2207 my $swapWords; # swap 32-bit words in doubles (ARM quirk)2208 my $currentByteOrder = 'MM'; # current byte ordering ('II' or 'MM')2209 3293 my %unpackMotorola = ( S => 'n', L => 'N', C => 'C', c => 'c' ); 2210 3294 my %unpackIntel = ( S => 'v', L => 'V', C => 'C', c => 'c' ); 2211 my %unpackStd = %unpackMotorola; 3295 my %unpackRev = ( N => 'V', V => 'N', C => 'C', n => 'v', v => 'n', c => 'c' ); 3296 3297 # the following 4 variables are defined in 'use vars' instead of using 'my' 3298 # because mod_perl 5.6.1 apparently has a problem with setting file-scope 'my' 3299 # variables from within subroutines (ref communication with Pavel Merdin): 3300 # $swapBytes - set if EXIF header is not native byte ordering 3301 # $swapWords - swap 32-bit words in doubles (ARM quirk) 3302 $currentByteOrder = 'MM'; # current byte ordering ('II' or 'MM') 3303 %unpackStd = %unpackMotorola; 2212 3304 2213 3305 # Swap bytes in data if necessary … … 2240 3332 return unpack($unpackStd{$_[0]}, ${$_[1]}); 2241 3333 } 3334 # same, but with reversed byte order 3335 sub DoUnpackRev(@) 3336 { 3337 my $fmt = $unpackRev{$unpackStd{$_[0]}}; 3338 $_[2] and return unpack("x$_[2] $fmt", ${$_[1]}); 3339 return unpack($fmt, ${$_[1]}); 3340 } 2242 3341 # Pack value 2243 3342 # Inputs: 0) template, 1) value, 2) data ref (or undef), 3) offset (if data ref) … … 2246 3345 { 2247 3346 my $val = pack($unpackStd{$_[0]}, $_[1]); 3347 $_[2] and substr(${$_[2]}, $_[3], length($val)) = $val; 3348 return $val; 3349 } 3350 # same, but with reversed byte order 3351 sub DoPackRev(@) 3352 { 3353 my $val = pack($unpackRev{$unpackStd{$_[0]}}, $_[1]); 2248 3354 $_[2] and substr(${$_[2]}, $_[3], length($val)) = $val; 2249 3355 return $val; … … 2289 3395 sub GetFloat($$) { return DoUnpack(4, 'f', @_); } 2290 3396 sub GetDouble($$) { return DoUnpackDbl('d', @_); } 2291 3397 sub Get16uRev($$) { return DoUnpackRev('S', @_); } 3398 3399 # rationals may be a floating point number, 'inf' or 'undef' 2292 3400 sub GetRational32s($$) 2293 3401 { 2294 3402 my ($dataPt, $pos) = @_; 2295 my $denom = Get16s($dataPt, $pos + 2) or return 'inf'; 3403 my $numer = Get16s($dataPt,$pos); 3404 my $denom = Get16s($dataPt, $pos + 2) or return $numer ? 'inf' : 'undef'; 2296 3405 # round off to a reasonable number of significant figures 2297 return RoundFloat( Get16s($dataPt,$pos)/ $denom, 7);3406 return RoundFloat($numer / $denom, 7); 2298 3407 } 2299 3408 sub GetRational32u($$) 2300 3409 { 2301 3410 my ($dataPt, $pos) = @_; 2302 my $denom = Get16u($dataPt, $pos + 2) or return 'inf'; 2303 return RoundFloat(Get16u($dataPt,$pos) / $denom, 7); 3411 my $numer = Get16u($dataPt,$pos); 3412 my $denom = Get16u($dataPt, $pos + 2) or return $numer ? 'inf' : 'undef'; 3413 return RoundFloat($numer / $denom, 7); 2304 3414 } 2305 3415 sub GetRational64s($$) 2306 3416 { 2307 3417 my ($dataPt, $pos) = @_; 2308 my $denom = Get32s($dataPt, $pos + 4) or return 'inf'; 2309 return RoundFloat(Get32s($dataPt,$pos) / $denom, 10); 3418 my $numer = Get32s($dataPt,$pos); 3419 my $denom = Get32s($dataPt, $pos + 4) or return $numer ? 'inf' : 'undef'; 3420 return RoundFloat($numer / $denom, 10); 2310 3421 } 2311 3422 sub GetRational64u($$) 2312 3423 { 2313 3424 my ($dataPt, $pos) = @_; 2314 my $denom = Get32u($dataPt, $pos + 4) or return 'inf'; 2315 return RoundFloat(Get32u($dataPt,$pos) / $denom, 10); 3425 my $numer = Get32u($dataPt,$pos); 3426 my $denom = Get32u($dataPt, $pos + 4) or return $numer ? 'inf' : 'undef'; 3427 return RoundFloat($numer / $denom, 10); 2316 3428 } 2317 3429 sub GetFixed16s($$) … … 2344 3456 sub Set16u(@) { return DoPackStd('S', @_); } 2345 3457 sub Set32u(@) { return DoPackStd('L', @_); } 3458 sub Set16uRev(@) { return DoPackRev('S', @_); } 2346 3459 2347 3460 #------------------------------------------------------------------------------ … … 2351 3464 #------------------------------------------------------------------------------ 2352 3465 # Set byte ordering 2353 # Inputs: 0) ' II'=intel, 'MM'=motorola3466 # Inputs: 0) 'MM'=motorola, 'II'=intel (will translate 'BigEndian', 'LittleEndian') 2354 3467 # Returns: 1 on success 2355 3468 sub SetByteOrder($) … … 2360 3473 %unpackStd = %unpackMotorola; 2361 3474 } elsif ($order eq 'II') { # little endian (Intel) 3475 %unpackStd = %unpackIntel; 3476 } elsif ($order =~ /^Big/i) { 3477 $order = 'MM'; 3478 %unpackStd = %unpackMotorola; 3479 } elsif ($order =~ /^Little/i) { 3480 $order = 'II'; 2362 3481 %unpackStd = %unpackIntel; 2363 3482 } else { … … 2402 3521 int16s => 2, 2403 3522 int16u => 2, 3523 int16uRev => 2, 2404 3524 int32s => 4, 2405 3525 int32u => 4, … … 2417 3537 double => 8, 2418 3538 extended => 10, 3539 unicode => 2, 3540 complex => 8, 2419 3541 string => 1, 2420 3542 binary => 1, 2421 3543 'undef' => 1, 2422 3544 ifd => 4, 2423 ifd 8=> 8,3545 ifd64 => 8, 2424 3546 ); 2425 3547 my %readValueProc = ( … … 2428 3550 int16s => \&Get16s, 2429 3551 int16u => \&Get16u, 3552 int16uRev => \&Get16uRev, 2430 3553 int32s => \&Get32s, 2431 3554 int32u => \&Get32u, … … 2444 3567 extended => \&GetExtended, 2445 3568 ifd => \&Get32u, 2446 ifd 8=> \&Get64u,3569 ifd64 => \&Get64u, 2447 3570 ); 2448 3571 sub FormatSize($) { return $formatSize{$_[0]}; } … … 2482 3605 } 2483 3606 } else { 2484 # handle undef/binary/string 2485 $vals[0] = substr($$dataPt, $offset, $count );3607 # handle undef/binary/string (also unsupported unicode/complex) 3608 $vals[0] = substr($$dataPt, $offset, $count * $len); 2486 3609 # truncate string at null terminator if necessary 2487 3610 $vals[0] =~ s/\0.*//s if $format eq 'string'; 2488 3611 } 2489 if (wantarray) { 2490 return @vals; 2491 } elsif (@vals > 1) { 2492 return join(' ', @vals); 2493 } else { 2494 return $vals[0]; 2495 } 2496 } 2497 2498 #------------------------------------------------------------------------------ 2499 # Convert UTF-8 to current character set 2500 # Inputs: 0) ExifTool ref, 1) UTF-8 string 2501 # Return: Converted string 2502 sub UTF82Charset($$) 2503 { 2504 my ($self, $val) = @_; 2505 if ($self->{OPTIONS}->{Charset} eq 'Latin' and $val =~ /[\x80-\xff]/) { 2506 $val = Image::ExifTool::UTF82Unicode($val,'n',$self); 2507 $val = Image::ExifTool::Unicode2Latin($val,'n',$self); 3612 return @vals if wantarray; 3613 return join(' ', @vals) if @vals > 1; 3614 return $vals[0]; 3615 } 3616 3617 #------------------------------------------------------------------------------ 3618 # Decode string with specified encoding 3619 # Inputs: 0) ExifTool object ref, 1) string to decode 3620 # 2) source character set name (undef for current Charset) 3621 # 3) optional source byte order (2-byte and 4-byte fixed-width sets only) 3622 # 4) optional destination character set (defaults to Charset setting) 3623 # 5) optional destination byte order (2-byte and 4-byte fixed-width only) 3624 # Returns: string in destination encoding 3625 # Note: ExifTool ref may be undef if character both character sets are provided 3626 # (but in this case no warnings will be issued) 3627 sub Decode($$$;$$$) 3628 { 3629 my ($self, $val, $from, $fromOrder, $to, $toOrder) = @_; 3630 $from or $from = $$self{OPTIONS}{Charset}; 3631 $to or $to = $$self{OPTIONS}{Charset}; 3632 if ($from ne $to and length $val) { 3633 require Image::ExifTool::Charset; 3634 my $cs1 = $Image::ExifTool::Charset::csType{$from}; 3635 my $cs2 = $Image::ExifTool::Charset::csType{$to}; 3636 if ($cs1 and $cs2 and not $cs2 & 0x002) { 3637 # treat as straight ASCII if no character will need remapping 3638 if (($cs1 | $cs2) & 0x680 or $val =~ /[\x80-\xff]/) { 3639 my $uni = Image::ExifTool::Charset::Decompose($self, $val, $from, $fromOrder); 3640 $val = Image::ExifTool::Charset::Recompose($self, $uni, $to, $toOrder); 3641 } 3642 } elsif ($self) { 3643 my $set = $cs1 ? $to : $from; 3644 unless ($$self{"DecodeWarn$set"}) { 3645 $self->Warn("Unsupported character set ($set)"); 3646 $$self{"DecodeWarn$set"} = 1; 3647 } 3648 } 2508 3649 } 2509 3650 return $val; … … 2511 3652 2512 3653 #------------------------------------------------------------------------------ 2513 # Convert Latin to current character set 2514 # Inputs: 0) ExifTool ref, 1) Latin string 2515 # Return: Converted string 2516 sub Latin2Charset($$) 2517 { 2518 my ($self, $val) = @_; 2519 if ($self->{OPTIONS}->{Charset} eq 'UTF8' and $val =~ /[\x80-\xff]/) { 2520 $val = Image::ExifTool::Latin2Unicode($val,'n'); 2521 $val = Image::ExifTool::Unicode2UTF8($val,'n'); 2522 } 2523 return $val; 3654 # Encode string with specified encoding 3655 # Inputs: 0) ExifTool object ref, 1) string, 2) destination character set name, 3656 # 3) optional destination byte order (2-byte and 4-byte fixed-width sets only) 3657 # Returns: string in specified encoding 3658 sub Encode($$$;$) 3659 { 3660 my ($self, $val, $to, $toOrder) = @_; 3661 return $self->Decode($val, undef, undef, $to, $toOrder); 2524 3662 } 2525 3663 … … 2554 3692 #------------------------------------------------------------------------------ 2555 3693 # Validate an extracted image and repair if necessary 2556 # Inputs: 0) ExifTool object reference, 1) image reference, 2) tag name 3694 # Inputs: 0) ExifTool object reference, 1) image reference, 2) tag name or key 2557 3695 # Returns: image reference or undef if it wasn't valid 3696 # Note: should be called from RawConv, not ValueConv 2558 3697 sub ValidateImage($$$) 2559 3698 { … … 2563 3702 # the first byte of the preview of some Minolta cameras is wrong, 2564 3703 # so check for this and set it back to 0xff if necessary 2565 $$imagePt =~ s/^.(\xd8\xff\xdb)/\xff$1/ or3704 $$imagePt =~ s/^.(\xd8\xff\xdb)/\xff$1/s or 2566 3705 $self->Options('IgnoreMinorErrors')) 2567 3706 { 2568 3707 # issue warning only if the tag was specifically requested 2569 if ($self->{REQ_TAG_LOOKUP} ->{lc($tag)}) {3708 if ($self->{REQ_TAG_LOOKUP}{lc GetTagName($tag)}) { 2570 3709 $self->Warn("$tag is not a valid JPEG image",1); 2571 3710 return undef; … … 2584 3723 # start with the tag name and force first letter to be upper case 2585 3724 my $desc = ucfirst($tag); 2586 $desc =~ tr/_/ /; # translate underlines to spaces 3725 # translate underlines to spaces 3726 $desc =~ tr/_/ /; 3727 # remove hex TagID from name (to avoid inserting spaces in the number) 3728 $desc =~ s/ (0x[\da-f]+)$//i and $tagID = $1 unless defined $tagID; 2587 3729 # put a space between lower/UPPER case and lower/number combinations 2588 3730 $desc =~ s/([a-z])([A-Z\d])/$1 $2/g; … … 2591 3733 # put spaces after numbers (if more than one character following number) 2592 3734 $desc =~ s/(\d)([A-Z]\S)/$1 $2/g; 2593 # remove space in hex number 2594 $desc =~ s/ 0x ([\dA-Fa-f])/ 0x$1/g; 3735 # add TagID to description 2595 3736 $desc .= ' ' . $tagID if defined $tagID; 2596 3737 return $desc; … … 2600 3741 # Return printable value 2601 3742 # Inputs: 0) ExifTool object reference 2602 # 1) value to print, 2) true for unlimited line length3743 # 1) value to print, 2) line length limit (undef defaults to 60, 0=unlimited) 2603 3744 sub Printable($;$) 2604 3745 { 2605 my ($self, $outStr, $ unlimited) = @_;3746 my ($self, $outStr, $maxLen) = @_; 2606 3747 return '(undef)' unless defined $outStr; 2607 3748 $outStr =~ tr/\x01-\x1f\x7f-\xff/./; 2608 3749 $outStr =~ s/\x00//g; 2609 # limit length if verbose < 4 2610 if (length($outStr) > 60 and not $unlimited and $self->{OPTIONS}->{Verbose} < 4) { 2611 $outStr = substr($outStr,0,54) . '[snip]'; 3750 if (defined $maxLen) { 3751 # minimum length is 20 (0 is unlimited) 3752 $maxLen = 20 if $maxLen and $maxLen < 20; 3753 } else { 3754 $maxLen = 60; # default length is 60 3755 } 3756 # limit length only if verbose < 4 3757 if ($maxLen and length($outStr) > $maxLen and $self->{OPTIONS}{Verbose} < 4) { 3758 $outStr = substr($outStr,0,$maxLen-6) . '[snip]'; 2612 3759 } 2613 3760 return $outStr; … … 2621 3768 { 2622 3769 my ($self, $date) = @_; 2623 my $dateFormat = $self->{OPTIONS} ->{DateFormat};3770 my $dateFormat = $self->{OPTIONS}{DateFormat}; 2624 3771 # only convert date if a format was specified and the date is recognizable 2625 3772 if ($dateFormat) { 2626 if ($date =~ /^(\d+):(\d+):(\d+)\s+(\d+):(\d+):(\d+)/ and eval 'require POSIX') { 2627 $date = POSIX::strftime($dateFormat, $6, $5, $4, $3, $2-1, $1-1900); 2628 } elsif ($self->{OPTIONS}->{StrictDate}) { 3773 # a few cameras use incorrect date/time formatting: 3774 # - slashes instead of colons in date (RolleiD330, ImpressCam) 3775 # - date/time values separated by colon instead of space (Polariod, Sanyo, Sharp, Vivitar) 3776 # - single-digit seconds with leading space (HP scanners) 3777 $date =~ s/[-+]\d{2}:\d{2}$//; # remove timezone if it exists 3778 my @a = ($date =~ /\d+/g); # be very flexible about date/time format 3779 if (@a and $a[0] > 1900 and $a[0] < 3000 and eval 'require POSIX') { 3780 $date = POSIX::strftime($dateFormat, $a[5]||0, $a[4]||0, $a[3]||0, 3781 $a[2]||1, ($a[1]||1)-1, $a[0]-1900); 3782 } elsif ($self->{OPTIONS}{StrictDate}) { 2629 3783 undef $date; 2630 3784 } … … 2634 3788 2635 3789 #------------------------------------------------------------------------------ 3790 # Print conversion for time span value 3791 # Inputs: 0) time ticks, 1) number of seconds per tick (default 1) 3792 # Returns: readable time 3793 sub ConvertTimeSpan($;$) 3794 { 3795 my ($val, $mult) = @_; 3796 if (Image::ExifTool::IsFloat($val) and $val != 0) { 3797 $val *= $mult if $mult; 3798 if ($val < 60) { 3799 $val = "$val seconds"; 3800 } elsif ($val < 3600) { 3801 my $fmt = ($mult and $mult >= 60) ? '%d' : '%.1f'; 3802 my $s = ($val == 60 and $mult) ? '' : 's'; 3803 $val = sprintf("$fmt minute$s", $val / 60); 3804 } elsif ($val < 24 * 3600) { 3805 $val = sprintf("%.1f hours", $val / 3600); 3806 } else { 3807 $val = sprintf("%.1f days", $val / (24 * 3600)); 3808 } 3809 } 3810 return $val; 3811 } 3812 3813 #------------------------------------------------------------------------------ 3814 # Patched timelocal() that fixes ActivePerl timezone bug 3815 # Inputs/Returns: same as timelocal() 3816 # Notes: must 'require Time::Local' before calling this routine 3817 sub TimeLocal(@) 3818 { 3819 my $tm = Time::Local::timelocal(@_); 3820 if ($^O eq 'MSWin32') { 3821 # patch for ActivePerl timezone bug 3822 my @t2 = localtime($tm); 3823 my $t2 = Time::Local::timelocal(@t2); 3824 # adjust timelocal() return value to be consistent with localtime() 3825 $tm += $tm - $t2; 3826 } 3827 return $tm; 3828 } 3829 3830 #------------------------------------------------------------------------------ 3831 # Get time zone in minutes 3832 # Inputs: 0) localtime array ref, 1) gmtime array ref 3833 # Returns: time zone offset in minutes 3834 sub GetTimeZone(;$$) 3835 { 3836 my ($tm, $gm) = @_; 3837 # compute the number of minutes between localtime and gmtime 3838 my $min = $$tm[2] * 60 + $$tm[1] - ($$gm[2] * 60 + $$gm[1]); 3839 if ($$tm[3] != $$gm[3]) { 3840 # account for case where one date wraps to the first of the next month 3841 $$gm[3] = $$tm[3] - ($$tm[3]==1 ? 1 : -1) if abs($$tm[3]-$$gm[3]) != 1; 3842 # adjust for the +/- one day difference 3843 $min += ($$tm[3] - $$gm[3]) * 24 * 60; 3844 } 3845 return $min; 3846 } 3847 3848 #------------------------------------------------------------------------------ 3849 # Get time zone string 3850 # Inputs: 0) time zone offset in minutes 3851 # or 0) localtime array ref, 1) corresponding time value 3852 # Returns: time zone string ("+/-HH:MM") 3853 sub TimeZoneString($;$) 3854 { 3855 my $min = shift; 3856 if (ref $min) { 3857 my @gm = gmtime(shift); 3858 $min = GetTimeZone($min, \@gm); 3859 } 3860 my $sign = '+'; 3861 $min < 0 and $sign = '-', $min = -$min; 3862 my $h = int($min / 60); 3863 return sprintf('%s%.2d:%.2d', $sign, $h, $min - $h * 60); 3864 } 3865 3866 #------------------------------------------------------------------------------ 2636 3867 # Convert Unix time to EXIF date/time string 2637 # Inputs: 0) Unix time value, 1) non-zero to use local instead of GMT time 2638 # Returns: EXIF date/time string 3868 # Inputs: 0) Unix time value, 1) non-zero to convert to local time 3869 # Returns: EXIF date/time string (with timezone for local times) 3870 # Notes: fractional seconds are ignored 2639 3871 sub ConvertUnixTime($;$) 2640 3872 { 2641 my $time = shift;3873 my ($time, $toLocal) = @_; 2642 3874 return '0000:00:00 00:00:00' if $time == 0; 2643 my @tm = shift() ? localtime($time) : gmtime($time); 2644 return sprintf("%4d:%.2d:%.2d %.2d:%.2d:%.2d", $tm[5]+1900, $tm[4]+1, 2645 $tm[3], $tm[2], $tm[1], $tm[0]); 2646 } 2647 2648 #------------------------------------------------------------------------------ 2649 # Get Unix time from EXIF-formatted date/time string 2650 # Inputs: 0) EXIF date/time string, 1) non-zero to use local instead of GMT time 2651 # Returns: Unix time or undefined on error 3875 my (@tm, $tz); 3876 if ($toLocal) { 3877 @tm = localtime($time); 3878 $tz = TimeZoneString(\@tm, $time); 3879 } else { 3880 @tm = gmtime($time); 3881 $tz = ''; 3882 } 3883 my $str = sprintf("%4d:%.2d:%.2d %.2d:%.2d:%.2d%s", 3884 $tm[5]+1900, $tm[4]+1, $tm[3], $tm[2], $tm[1], $tm[0], $tz); 3885 return $str; 3886 } 3887 3888 #------------------------------------------------------------------------------ 3889 # Get Unix time from EXIF-formatted date/time string with optional timezone 3890 # Inputs: 0) EXIF date/time string, 1) non-zero if time is local 3891 # Returns: Unix time (seconds since 0:00 GMT Jan 1, 1970) or undefined on error 2652 3892 sub GetUnixTime($;$) 2653 3893 { 2654 my $timeStr = shift;3894 my ($timeStr, $isLocal) = @_; 2655 3895 return 0 if $timeStr eq '0000:00:00 00:00:00'; 2656 3896 my @tm = ($timeStr =~ /^(\d+):(\d+):(\d+)\s+(\d+):(\d+):(\d+)/); 2657 return undef unless @tm == 6; 2658 return undef unless eval 'require Time::Local'; 3897 return undef unless @tm == 6 and eval 'require Time::Local'; 3898 my $tzsec = 0; 3899 # use specified timezone offset (if given) instead of local system time 3900 # if we are converting a local time value 3901 if ($isLocal and $timeStr =~ /(?:Z|([-+])(\d+):(\d+))$/i) { 3902 # use specified timezone if one exists 3903 $tzsec = ($2 * 60 + $3) * ($1 eq '-' ? -60 : 60) if $1; 3904 undef $isLocal; # convert using GMT corrected for specified timezone 3905 } 2659 3906 $tm[0] -= 1900; # convert year 2660 3907 $tm[1] -= 1; # convert month 2661 3908 @tm = reverse @tm; # change to order required by timelocal() 2662 return shift() ? Time::Local::timelocal(@tm) : Time::Local::timegm(@tm); 3909 return $isLocal ? TimeLocal(@tm) : Time::Local::timegm(@tm) - $tzsec; 3910 } 3911 3912 #------------------------------------------------------------------------------ 3913 # Print conversion for file size 3914 # Inputs: 0) file size in bytes 3915 # Returns: converted file size 3916 sub ConvertFileSize($) 3917 { 3918 my $val = shift; 3919 $val < 2048 and return "$val bytes"; 3920 $val < 10240 and return sprintf('%.1f kB', $val / 1024); 3921 $val < 2097152 and return sprintf('%.0f kB', $val / 1024); 3922 $val < 10485760 and return sprintf('%.1f MB', $val / 1048576); 3923 return sprintf('%.0f MB', $val / 1048576); 3924 } 3925 3926 #------------------------------------------------------------------------------ 3927 # Convert seconds to duration string (handles negative durations) 3928 # Inputs: 0) floating point seconds 3929 # Returns: duration string in form "S.SS s", "MM:SS" or "H:MM:SS" 3930 sub ConvertDuration($) 3931 { 3932 my $time = shift; 3933 return $time unless IsFloat($time); 3934 return '0 s' if $time == 0; 3935 my $sign = ($time > 0 ? '' : (($time = -$time), '-')); 3936 return sprintf("$sign%.2f s", $time) if $time < 30; 3937 my $h = int($time / 3600); 3938 $time -= $h * 3600; 3939 my $m = int($time / 60); 3940 $time -= $m * 60; 3941 return sprintf("$sign%d:%.2d:%.2d", $h, $m, int($time)); 3942 } 3943 3944 #------------------------------------------------------------------------------ 3945 # Print conversion for bitrate values 3946 # Inputs: 0) bitrate in bits per second 3947 # Returns: human-readable bitrate string 3948 # Notes: returns input value without formatting if it isn't numerical 3949 sub ConvertBitrate($) 3950 { 3951 my $bitrate = shift; 3952 IsFloat($bitrate) or return $bitrate; 3953 my @units = ('bps', 'kbps', 'Mbps', 'Gbps'); 3954 for (;;) { 3955 my $units = shift @units; 3956 $bitrate >= 1000 and @units and $bitrate /= 1000, next; 3957 my $fmt = $bitrate < 100 ? '%.3g' : '%.0f'; 3958 return sprintf("$fmt $units", $bitrate); 3959 } 2663 3960 } 2664 3961 … … 2667 3964 # Inputs: 0) ExifTool hash ref, 1) start offset, 2) data size 2668 3965 # 3) comment string, 4) tool tip (or SAME), 5) flags 2669 sub H tmlDump($$$$;$$)3966 sub HDump($$$$;$$) 2670 3967 { 2671 3968 my $self = shift; … … 2752 4049 2753 4050 #------------------------------------------------------------------------------ 4051 # Read/rewrite trailer information (including multiple trailers) 4052 # Inputs: 0) ExifTool object ref, 1) DirInfo ref: 4053 # - requires RAF and DirName 4054 # - OutFile is a scalar reference for writing 4055 # - scans from current file position if ScanForAFCP is set 4056 # Returns: 1 if trailer was processed or couldn't be processed (or written OK) 4057 # 0 if trailer was recognized but offsets need fixing (or write error) 4058 # - DirName, DirLen, DataPos, Offset, Fixup and OutFile are updated 4059 # - preserves current file position and byte order 4060 sub ProcessTrailers($$) 4061 { 4062 my ($self, $dirInfo) = @_; 4063 my $dirName = $$dirInfo{DirName}; 4064 my $outfile = $$dirInfo{OutFile}; 4065 my $offset = $$dirInfo{Offset} || 0; 4066 my $fixup = $$dirInfo{Fixup}; 4067 my $raf = $$dirInfo{RAF}; 4068 my $pos = $raf->Tell(); 4069 my $byteOrder = GetByteOrder(); 4070 my $success = 1; 4071 my $path = $$self{PATH}; 4072 4073 for (;;) { # loop through all trailers 4074 require "Image/ExifTool/$dirName.pm"; 4075 my $proc = "Image::ExifTool::${dirName}::Process$dirName"; 4076 my $outBuff; 4077 if ($outfile) { 4078 # write to local buffer so we can add trailer in proper order later 4079 $$outfile and $$dirInfo{OutFile} = \$outBuff, $outBuff = ''; 4080 # must generate new fixup if necessary so we can shift 4081 # the old fixup separately after we prepend this trailer 4082 delete $$dirInfo{Fixup}; 4083 } 4084 delete $$dirInfo{DirLen}; # reset trailer length 4085 $$dirInfo{Offset} = $offset; # set offset from end of file 4086 $$dirInfo{Trailer} = 1; # set Trailer flag in case proc cares 4087 # add trailer and DirName to SubDirectory PATH 4088 push @$path, 'Trailer', $dirName; 4089 4090 # read or write this trailer 4091 # (proc takes Offset as offset from end of trailer to end of file, 4092 # and returns DataPos and DirLen, and Fixup if applicable) 4093 no strict 'refs'; 4094 my $result = &$proc($self, $dirInfo); 4095 use strict 'refs'; 4096 4097 # restore PATH 4098 pop @$path; 4099 pop @$path; 4100 # check result 4101 if ($outfile) { 4102 if ($result > 0) { 4103 if ($outBuff) { 4104 # write trailers to OutFile in original order 4105 $$outfile = $outBuff . $$outfile; 4106 # must adjust old fixup start if it exists 4107 $$fixup{Start} += length($outBuff) if $fixup; 4108 $outBuff = ''; # free memory 4109 } 4110 if ($fixup) { 4111 # add new fixup information if any 4112 $fixup->AddFixup($$dirInfo{Fixup}) if $$dirInfo{Fixup}; 4113 } else { 4114 $fixup = $$dirInfo{Fixup}; # save fixup 4115 } 4116 } else { 4117 $success = 0 if $self->Error("Error rewriting $dirName trailer", 1); 4118 last; 4119 } 4120 } elsif ($result < 0) { 4121 # can't continue if we must scan for this trailer 4122 $success = 0; 4123 last; 4124 } 4125 last unless $result > 0 and $$dirInfo{DirLen}; 4126 # look for next trailer 4127 $offset += $$dirInfo{DirLen}; 4128 my $nextTrail = IdentifyTrailer($raf, $offset) or last; 4129 $dirName = $$dirInfo{DirName} = $$nextTrail{DirName}; 4130 $raf->Seek($pos, 0); 4131 } 4132 SetByteOrder($byteOrder); # restore original byte order 4133 $raf->Seek($pos, 0); # restore original file position 4134 $$dirInfo{OutFile} = $outfile; # restore original outfile 4135 $$dirInfo{Offset} = $offset; # return offset from EOF to start of first trailer 4136 $$dirInfo{Fixup} = $fixup; # return fixup information 4137 return $success; 4138 } 4139 4140 #------------------------------------------------------------------------------ 2754 4141 # Extract EXIF information from a jpg image 2755 4142 # Inputs: 0) ExifTool object reference, 1) dirInfo ref with RAF set … … 2757 4144 sub ProcessJPEG($$) 2758 4145 { 4146 local $_; 2759 4147 my ($self, $dirInfo) = @_; 2760 my ($ch,$s,$length); 2761 my $verbose = $self->{OPTIONS}->{Verbose}; 2762 my $out = $self->{OPTIONS}->{TextOut}; 4148 my ($ch, $s, $length); 4149 my $verbose = $self->{OPTIONS}{Verbose}; 4150 my $out = $self->{OPTIONS}{TextOut}; 4151 my $fast = $self->{OPTIONS}{FastScan}; 2763 4152 my $raf = $$dirInfo{RAF}; 2764 4153 my $htmlDump = $self->{HTML_DUMP}; 2765 4154 my %dumpParms = ( Out => $out ); 2766 my ($success, $icc_profile, $wantPreview, $trailInfo); 4155 my ($success, $icc_profile, $wantTrailer, $trailInfo, %extendedXMP); 4156 my ($preview, $scalado, @dqt, $subSampling, $dumpEnd); 2767 4157 2768 4158 # check to be sure this is a valid JPG file 2769 4159 return 0 unless $raf->Read($s, 2) == 2 and $s eq "\xff\xd8"; 2770 4160 $dumpParms{MaxLen} = 128 if $verbose < 4; 2771 $self->SetFileType(); # set FileType tag 4161 unless ($self->{VALUE}{FileType}) { 4162 $self->SetFileType(); # set FileType tag 4163 $$self{LOW_PRIORITY_DIR}{IFD1} = 1; # lower priority of IFD1 tags 4164 } 2772 4165 if ($htmlDump) { 2773 my $pos = $raf->Tell() - 2; 2774 $self->HtmlDump(0, $pos, '[unknown header]') if $pos; 2775 $self->HtmlDump($pos, 2, 'JPEG header', 'SOI Marker'); 2776 } 4166 $dumpEnd = $raf->Tell(); 4167 my $pos = $dumpEnd - 2; 4168 $self->HDump(0, $pos, '[unknown header]') if $pos; 4169 $self->HDump($pos, 2, 'JPEG header', 'SOI Marker'); 4170 } 4171 my $path = $$self{PATH}; 4172 my $pn = scalar @$path; 2777 4173 2778 4174 # set input record separator to 0xff (the JPEG marker) to make reading quicker 2779 my $oldsep = $/; 2780 $/ = "\xff"; 2781 2782 my ($nextMarker, $nextSegDataPt, $nextSegPos, $combinedSegData, $dumpEnd); 4175 local $/ = "\xff"; 4176 4177 my ($nextMarker, $nextSegDataPt, $nextSegPos, $combinedSegData); 2783 4178 2784 4179 # read file until we reach an end of image (EOI) or start of scan (SOS) … … 2793 4188 # read ahead to the next segment unless we have reached EOI or SOS 2794 4189 # 2795 unless ($marker and ($marker==0xd9 or ($marker==0xda and not $want Preview))) {4190 unless ($marker and ($marker==0xd9 or ($marker==0xda and not $wantTrailer))) { 2796 4191 # read up to next marker (JPEG markers begin with 0xff) 2797 4192 my $buff; … … 2821 4216 } 2822 4217 # set some useful variables for the current segment 2823 my $hdr = "\xff" . chr($marker); # header for this segment2824 4218 my $markerName = JpegMarkerName($marker); 4219 $$path[$pn] = $markerName; 2825 4220 # 2826 4221 # parse the current segment … … 2845 4240 my ($i, $hmin, $hmax, $vmin, $vmax); 2846 4241 # loop through all components to determine sampling frequency 4242 $subSampling = ''; 2847 4243 for ($i=0; $i<$n; ++$i) { 2848 4244 my $sf = Get8u($segDataPt, 7 + 3 * $i); 4245 $subSampling .= sprintf('%.2x', $sf); 2849 4246 # isolate horizontal and vertical components 2850 4247 my ($hf, $vf) = ($sf >> 4, $sf & 0x0f); … … 2866 4263 next; 2867 4264 } elsif ($marker == 0xd9) { # EOI 4265 pop @$path; 2868 4266 $verbose and print $out "JPEG EOI\n"; 2869 4267 my $pos = $raf->Tell(); 2870 4268 if ($htmlDump and $dumpEnd) { 2871 $self->H tmlDump($dumpEnd, $pos-2-$dumpEnd, '[JPEG Image Data]', undef, 0x08);2872 $self->H tmlDump($pos-2, 2, 'JPEG EOI', undef);4269 $self->HDump($dumpEnd, $pos-2-$dumpEnd, '[JPEG Image Data]', undef, 0x08); 4270 $self->HDump($pos-2, 2, 'JPEG EOI', undef); 2873 4271 $dumpEnd = 0; 2874 4272 } 2875 4273 $success = 1; 2876 4274 # we are here because we are looking for trailer information 2877 if ($wantPreview and $self->{VALUE}->{PreviewImageStart}) { 2878 my $buff; 2879 # most previews start right after the JPEG EOI, but the Olympus E-20 2880 # preview is 508 bytes into the trailer, and the K-M Maxxum 7D preview 2881 # is 979 bytes in, but Minolta previews can have a random first byte... 2882 if ($raf->Read($buff, 1024) and ($buff =~ /\xff\xd8\xff./g or 2883 ($self->{CameraMake} =~ /Minolta/i and $buff =~ /.\xd8\xff\xdb/g))) 2884 { 2885 # adjust PreviewImageStart to this location 2886 my $start = $self->{VALUE}->{PreviewImageStart}; 2887 my $actual = $pos + pos($buff) - 4; 2888 if ($start ne $actual and $verbose > 1) { 2889 print $out "(Fixed PreviewImage location: $start -> $actual)\n"; 4275 if ($wantTrailer) { 4276 my $start = $$self{PreviewImageStart}; 4277 if ($start) { 4278 my $buff; 4279 # most previews start right after the JPEG EOI, but the Olympus E-20 4280 # preview is 508 bytes into the trailer, the K-M Maxxum 7D preview is 4281 # 979 bytes in, and Sony previews can start up to 32 kB into the trailer. 4282 # (and Minolta and Sony previews can have a random first byte...) 4283 my $scanLen = $$self{Make} =~ /Sony/i ? 65536 : 1024; 4284 if ($raf->Read($buff, $scanLen) and ($buff =~ /\xff\xd8\xff./g or 4285 ($self->{Make} =~ /(Minolta|Sony)/i and $buff =~ /.\xd8\xff\xdb/g))) 4286 { 4287 # adjust PreviewImageStart to this location 4288 my $actual = $pos + pos($buff) - 4; 4289 if ($start ne $actual and $verbose > 1) { 4290 print $out "(Fixed PreviewImage location: $start -> $actual)\n"; 4291 } 4292 # update preview image offsets 4293 $self->{VALUE}{PreviewImageStart} = $actual if $self->{VALUE}{PreviewImageStart}; 4294 $$self{PreviewImageStart} = $actual; 4295 # load preview now if we tried and failed earlier 4296 if ($$self{PreviewError} and $$self{PreviewImageLength}) { 4297 if ($raf->Seek($actual, 0) and $raf->Read($buff, $$self{PreviewImageLength})) { 4298 $self->FoundTag('PreviewImage', $buff); 4299 delete $$self{PreviewError}; 4300 } 4301 } 2890 4302 } 2891 $ self->{VALUE}->{PreviewImageStart} = $actual;4303 $raf->Seek($pos, 0); 2892 4304 } 2893 $raf->Seek($pos, 0);2894 4305 } 2895 4306 # process trailer now or finish processing trailers … … 2903 4314 undef $trailInfo; 2904 4315 } 4316 if ($$self{LeicaTrailer}) { 4317 $raf->Seek(0, 2); 4318 $$self{LeicaTrailer}{TrailPos} = $pos; 4319 $$self{LeicaTrailer}{TrailLen} = $raf->Tell() - $pos - $fromEnd; 4320 Image::ExifTool::Panasonic::ProcessLeicaTrailer($self); 4321 } 2905 4322 # finally, dump remaining information in JPEG trailer 2906 4323 if ($verbose or $htmlDump) { 2907 $raf->Seek(0, 2); 2908 my $endPos = $raf->Tell() - $fromEnd; 4324 my $endPos = $$self{LeicaTrailerPos}; 4325 unless ($endPos) { 4326 $raf->Seek(0, 2); 4327 $endPos = $raf->Tell() - $fromEnd; 4328 } 2909 4329 $self->DumpUnknownTrailer({ 2910 4330 RAF => $raf, … … 2915 4335 last; # all done parsing file 2916 4336 } elsif ($marker == 0xda) { # SOS 4337 pop @$path; 2917 4338 # all done with meta information unless we have a trailer 2918 4339 $verbose and print $out "JPEG SOS\n"; 2919 unless ($ self->Options('FastScan')) {4340 unless ($fast) { 2920 4341 $trailInfo = IdentifyTrailer($raf); 2921 4342 # process trailer now unless we are doing verbose dump … … 2925 4346 $self->ProcessTrailers($trailInfo) and undef $trailInfo; 2926 4347 } 2927 if ($want Preview) {4348 if ($wantTrailer) { 2928 4349 # seek ahead and validate preview image 2929 4350 my $buff; 2930 4351 my $curPos = $raf->Tell(); 2931 if ($raf->Seek($ self->GetValue('PreviewImageStart'), 0) and4352 if ($raf->Seek($$self{PreviewImageStart}, 0) and 2932 4353 $raf->Read($buff, 4) == 4 and 2933 4354 $buff =~ /^.\xd8\xff[\xc4\xdb\xe0-\xef]/) 2934 4355 { 2935 undef $want Preview;4356 undef $wantTrailer; 2936 4357 } 2937 4358 $raf->Seek($curPos, 0) or last; 2938 4359 } 2939 next if $trailInfo or $wantPreview or $verbose > 2 or $htmlDump; 4360 # seek ahead and process Leica trailer 4361 if ($$self{LeicaTrailer}) { 4362 require Image::ExifTool::Panasonic; 4363 Image::ExifTool::Panasonic::ProcessLeicaTrailer($self); 4364 $wantTrailer = 1 if $$self{LeicaTrailer}; 4365 } 4366 next if $trailInfo or $wantTrailer or $verbose > 2 or $htmlDump; 2940 4367 } 2941 4368 # nothing interesting to parse after start of scan (SOS) … … 2946 4373 $verbose and $marker and print $out "JPEG $markerName:\n"; 2947 4374 next; 4375 } elsif ($marker == 0xdb and length($$segDataPt) and # DQT 4376 # save the DQT data only if JPEGDigest has been requested 4377 $self->{REQ_TAG_LOOKUP}->{jpegdigest}) 4378 { 4379 my $num = unpack('C',$$segDataPt) & 0x0f; # get table index 4380 $dqt[$num] = $$segDataPt if $num < 4; # save for MD5 calculation 2948 4381 } 2949 4382 # handle all other markers … … 2958 4391 } 2959 4392 } 2960 if ($marker == 0xe0) { # APP0 (JFIF, CIFF)4393 if ($marker == 0xe0) { # APP0 (JFIF, JFXX, CIFF, AVI1, Ocad) 2961 4394 if ($$segDataPt =~ /^JFIF\0/) { 2962 4395 $dumpType = 'JFIF'; … … 2976 4409 $self->FoundTag($tagInfo, substr($$segDataPt, 6)); 2977 4410 } elsif ($$segDataPt =~ /^(II|MM).{4}HEAPJPGM/s) { 4411 next if $fast and $fast > 1; # skip processing for very fast 2978 4412 $dumpType = 'CIFF'; 2979 4413 my %dirInfo = ( … … 2984 4418 Image::ExifTool::CanonRaw::ProcessCRW($self, \%dirInfo); 2985 4419 delete $self->{SET_GROUP1}; 2986 } 2987 } elsif ($marker == 0xe1) { # APP1 (EXIF, XMP) 4420 } elsif ($$segDataPt =~ /^(AVI1|Ocad)/) { 4421 $dumpType = $1; 4422 SetByteOrder('MM'); 4423 my $tagTablePtr = GetTagTable("Image::ExifTool::JPEG::$dumpType"); 4424 my %dirInfo = ( 4425 DataPt => $segDataPt, 4426 DataPos => $segPos, 4427 DirStart => 4, 4428 DirLen => $length - 4, 4429 ); 4430 $self->ProcessDirectory(\%dirInfo, $tagTablePtr); 4431 } 4432 } elsif ($marker == 0xe1) { # APP1 (EXIF, XMP, QVCI) 2988 4433 if ($$segDataPt =~ /^Exif\0/) { # (some Kodak cameras don't put a second \0) 2989 4434 undef $dumpType; # (will be dumped here) … … 2999 4444 ); 3000 4445 if ($htmlDump) { 3001 $self->HtmlDump($segPos-4, 4, 'APP1 header', 3002 "Data size: $length bytes"); 3003 $self->HtmlDump($segPos, $hdrLen, 'Exif header', 3004 'APP1 data type: Exif'); 4446 $self->HDump($segPos-4, 4, 'APP1 header', "Data size: $length bytes"); 4447 $self->HDump($segPos, $hdrLen, 'Exif header', 'APP1 data type: Exif'); 3005 4448 $dumpEnd = $segPos + $length; 3006 4449 } … … 3012 4455 my $start = $self->GetValue('PreviewImageStart'); 3013 4456 my $length = $self->GetValue('PreviewImageLength'); 4457 if (not $start or not $length and $$self{PreviewError}) { 4458 $start = $$self{PreviewImageStart}; 4459 $length = $$self{PreviewImageLength}; 4460 } 3014 4461 if ($start and $length and 3015 4462 $start + $length > $self->{EXIF_POS} + length($self->{EXIF_DATA}) and 3016 $self->{REQ_TAG_LOOKUP} ->{previewimage})4463 $self->{REQ_TAG_LOOKUP}{previewimage}) 3017 4464 { 3018 $wantPreview = 1; 4465 $$self{PreviewImageStart} = $start; 4466 $$self{PreviewImageLength} = $length; 4467 $wantTrailer = 1; 3019 4468 } 4469 } elsif ($$segDataPt =~ /^$xmpExtAPP1hdr/) { 4470 # off len -- extended XMP header (75 bytes total): 4471 # 0 35 bytes - signature 4472 # 35 32 bytes - GUID (MD5 hash of full extended XMP data in ASCII) 4473 # 67 4 bytes - total size of extended XMP data 4474 # 71 4 bytes - offset for this XMP data portion 4475 $dumpType = 'Extended XMP'; 4476 if (length $$segDataPt > 75) { 4477 my ($size, $off) = unpack('x67N2', $$segDataPt); 4478 my $guid = substr($$segDataPt, 35, 32); 4479 my $extXMP = $extendedXMP{$guid}; 4480 $extXMP or $extXMP = $extendedXMP{$guid} = { }; 4481 $$extXMP{Size} = $size; 4482 $$extXMP{$off} = substr($$segDataPt, 75); 4483 # process extended XMP if complete 4484 my @offsets; 4485 for ($off=0; $off<$size; ) { 4486 last unless defined $$extXMP{$off}; 4487 push @offsets, $off; 4488 $off += length $$extXMP{$off}; 4489 } 4490 if ($off == $size) { 4491 my $buff = ''; 4492 # assemble XMP all together 4493 $buff .= $$extXMP{$_} foreach @offsets; 4494 $dumpType = 'Extended XMP'; 4495 my $tagTablePtr = GetTagTable('Image::ExifTool::XMP::Main'); 4496 my %dirInfo = ( 4497 DataPt => \$buff, 4498 Parent => $markerName, 4499 ); 4500 $self->ProcessDirectory(\%dirInfo, $tagTablePtr); 4501 delete $extendedXMP{$guid}; 4502 } 4503 } else { 4504 $self->Warn('Invalid extended XMP segment'); 4505 } 4506 } elsif ($$segDataPt =~ /^QVCI\0/) { 4507 $dumpType = 'QVCI'; 4508 my $tagTablePtr = GetTagTable('Image::ExifTool::Casio::QVCI'); 4509 my %dirInfo = ( 4510 Base => 0, 4511 DataPt => $segDataPt, 4512 DataPos => $segPos, 4513 DataLen => $length, 4514 DirStart => 0, 4515 DirLen => $length, 4516 Parent => $markerName, 4517 ); 4518 $self->ProcessDirectory(\%dirInfo, $tagTablePtr); 3020 4519 } else { 3021 4520 # Hmmm. Could be XMP, let's see … … 3023 4522 if ($$segDataPt =~ /^http/ or $$segDataPt =~ /<exif:/) { 3024 4523 $dumpType = 'XMP'; 4524 # also try to parse XMP with a non-standard header 4525 # (note: this non-standard XMP is ignored when writing) 3025 4526 my $start = ($$segDataPt =~ /^$xmpAPP1hdr/) ? length($xmpAPP1hdr) : 0; 3026 4527 my $tagTablePtr = GetTagTable('Image::ExifTool::XMP::Main'); … … 3035 4536 ); 3036 4537 $processed = $self->ProcessDirectory(\%dirInfo, $tagTablePtr); 4538 if ($processed and not $start) { 4539 $self->Warn('Non-standard header for APP1 XMP segment'); 4540 } 3037 4541 } 3038 4542 if ($verbose and not $processed) { … … 3040 4544 } 3041 4545 } 3042 } elsif ($marker == 0xe2) { # APP2 (ICC Profile, FPXR )4546 } elsif ($marker == 0xe2) { # APP2 (ICC Profile, FPXR, MPF, PreviewImage) 3043 4547 if ($$segDataPt =~ /^ICC_PROFILE\0/) { 3044 4548 $dumpType = 'ICC_Profile'; … … 3064 4568 } 3065 4569 } elsif ($$segDataPt =~ /^FPXR\0/) { 4570 next if $fast and $fast > 1; # skip processing for very fast 3066 4571 $dumpType = 'FPXR'; 3067 4572 my $tagTablePtr = GetTagTable('Image::ExifTool::FlashPix::Main'); … … 3077 4582 ); 3078 4583 $self->ProcessDirectory(\%dirInfo, $tagTablePtr); 3079 } 3080 } elsif ($marker == 0xe3) { # APP3 (Kodak "Meta") 4584 } elsif ($$segDataPt =~ /^MPF\0/) { 4585 undef $dumpType; # (will be dumped here) 4586 my %dirInfo = ( 4587 Parent => $markerName, 4588 DataPt => $segDataPt, 4589 DataPos => $segPos, 4590 DirStart => 4, 4591 Base => $segPos + 4, 4592 Multi => 1, # the MP Attribute IFD will be MPF1 4593 ); 4594 if ($htmlDump) { 4595 $self->HDump($segPos-4, 4, 'APP2 header', "Data size: $length bytes"); 4596 $self->HDump($segPos, 4, 'MPF header', 'APP2 data type: MPF'); 4597 $dumpEnd = $segPos + $length; 4598 } 4599 # extract the MPF information (it is in standard TIFF format) 4600 my $tagTablePtr = GetTagTable('Image::ExifTool::MPF::Main'); 4601 $self->ProcessTIFF(\%dirInfo, $tagTablePtr); 4602 } elsif ($$segDataPt =~ /^\xff\xd8\xff\xdb/) { 4603 $preview = $$segDataPt; 4604 $dumpType = 'Samsung Preview'; 4605 } elsif ($preview) { 4606 $preview .= $$segDataPt; 4607 $dumpType = 'Samsung Preview'; 4608 } 4609 if ($preview and $nextMarker ne $marker) { 4610 $self->FoundTag('PreviewImage', $preview); 4611 undef $preview; 4612 } 4613 } elsif ($marker == 0xe3) { # APP3 (Kodak "Meta", Stim) 3081 4614 if ($$segDataPt =~ /^(Meta|META|Exif)\0\0/) { 3082 4615 undef $dumpType; # (will be dumped here) … … 3089 4622 ); 3090 4623 if ($htmlDump) { 3091 $self->H tmlDump($segPos-4, 10, 'APP3 Meta header');4624 $self->HDump($segPos-4, 10, 'APP3 Meta header'); 3092 4625 $dumpEnd = $segPos + $length; 3093 4626 } 3094 4627 my $tagTablePtr = GetTagTable('Image::ExifTool::Kodak::Meta'); 3095 4628 $self->ProcessTIFF(\%dirInfo, $tagTablePtr); 4629 } elsif ($$segDataPt =~ /^Stim\0/) { 4630 undef $dumpType; # (will be dumped here) 4631 my %dirInfo = ( 4632 Parent => $markerName, 4633 DataPt => $segDataPt, 4634 DataPos => $segPos, 4635 DirStart => 6, 4636 Base => $segPos + 6, 4637 ); 4638 if ($htmlDump) { 4639 $self->HDump($segPos-4, 4, 'APP3 header', "Data size: $length bytes"); 4640 $self->HDump($segPos, 5, 'Stim header', 'APP3 data type: Stim'); 4641 $dumpEnd = $segPos + $length; 4642 } 4643 # extract the Stim information (it is in standard TIFF format) 4644 my $tagTablePtr = GetTagTable('Image::ExifTool::Stim::Main'); 4645 $self->ProcessTIFF(\%dirInfo, $tagTablePtr); 4646 } elsif ($$segDataPt =~ /^\xff\xd8\xff\xdb/) { 4647 $preview = $$segDataPt; 4648 $dumpType = 'Samsung/HP Preview'; 4649 } 4650 # Samsung continues the preview in APP4 4651 if ($preview and $nextMarker ne 0xe4) { 4652 $self->FoundTag('PreviewImage', $preview); 4653 undef $preview; 4654 } 4655 } elsif ($marker == 0xe4) { # APP4 ("SCALADO", FPXR, PreviewImage) 4656 if ($$segDataPt =~ /^SCALADO\0/ and $length >= 16) { 4657 $dumpType = 'SCALADO'; 4658 my ($num, $idx, $len) = unpack('x8n2N', $$segDataPt); 4659 # assume that the segments are in order and just concatinate them 4660 $scalado = '' unless defined $scalado; 4661 $scalado .= substr($$segDataPt, 16); 4662 if ($idx == $num - 1) { 4663 if ($len != length $scalado) { 4664 $self->Warn('Possibly corrupted APP4 SCALADO data', 1); 4665 } 4666 my %dirInfo = ( 4667 Parent => $markerName, 4668 DataPt => \$scalado, 4669 ); 4670 my $tagTablePtr = GetTagTable('Image::ExifTool::JPEG::Scalado'); 4671 $self->ProcessDirectory(\%dirInfo, $tagTablePtr); 4672 undef $scalado; 4673 } 4674 } elsif ($$segDataPt =~ /^FPXR\0/) { 4675 next if $fast and $fast > 1; # skip processing for very fast 4676 $dumpType = 'FPXR'; 4677 my $tagTablePtr = GetTagTable('Image::ExifTool::FlashPix::Main'); 4678 my %dirInfo = ( 4679 DataPt => $segDataPt, 4680 DataPos => $segPos, 4681 DataLen => $length, 4682 DirStart => 0, 4683 DirLen => $length, 4684 Parent => $markerName, 4685 # set flag if this is the last FPXR segment 4686 LastFPXR => not ($nextMarker==$marker and $$nextSegDataPt=~/^FPXR\0/), 4687 ); 4688 $self->ProcessDirectory(\%dirInfo, $tagTablePtr); 4689 } elsif ($preview) { 4690 # continued Samsung S1060 preview from APP3 4691 $preview .= $$segDataPt; 4692 # (not sure if next part would be APP5 or APP4 again, but assume APP4) 4693 if ($nextMarker ne $marker) { 4694 $self->FoundTag('PreviewImage', $preview); 4695 undef $preview; 4696 } 3096 4697 } 3097 4698 } elsif ($marker == 0xe5) { # APP5 (Ricoh "RMETA") … … 3108 4709 $self->ProcessDirectory(\%dirInfo, $tagTablePtr); 3109 4710 } 3110 } elsif ($marker == 0xe6) { # APP6 (Toshiba EPPIM )4711 } elsif ($marker == 0xe6) { # APP6 (Toshiba EPPIM, NITF, HP_TDHD) 3111 4712 if ($$segDataPt =~ /^EPPIM\0/) { 3112 4713 undef $dumpType; # (will be dumped here) … … 3119 4720 ); 3120 4721 if ($htmlDump) { 3121 $self->H tmlDump($segPos-4, 10, 'APP6 EPPIM header');4722 $self->HDump($segPos-4, 10, 'APP6 EPPIM header'); 3122 4723 $dumpEnd = $segPos + $length; 3123 4724 } 3124 4725 my $tagTablePtr = GetTagTable('Image::ExifTool::JPEG::EPPIM'); 3125 4726 $self->ProcessTIFF(\%dirInfo, $tagTablePtr); 4727 } elsif ($$segDataPt =~ /^NITF\0/) { 4728 $dumpType = 'NITF'; 4729 SetByteOrder('MM'); 4730 my $tagTablePtr = GetTagTable('Image::ExifTool::JPEG::NITF'); 4731 my %dirInfo = ( 4732 DataPt => $segDataPt, 4733 DataPos => $segPos, 4734 DirStart => 5, 4735 DirLen => $length - 5, 4736 ); 4737 $self->ProcessDirectory(\%dirInfo, $tagTablePtr); 4738 } elsif ($$segDataPt =~ /^TDHD\x01\0\0\0/ and $length > 12) { 4739 # HP Photosmart R837 APP6 "TDHD" segment 4740 $dumpType = 'TDHD'; 4741 my $tagTablePtr = GetTagTable('Image::ExifTool::HP::TDHD'); 4742 my %dirInfo = ( 4743 DataPt => $segDataPt, 4744 DataPos => $segPos, 4745 DirStart => 12, # (ignore first TDHD element because size includes 12-byte tag header) 4746 DirLen => $length - 12, 4747 ); 4748 $self->ProcessDirectory(\%dirInfo, $tagTablePtr); 3126 4749 } 3127 4750 } elsif ($marker == 0xe8) { # APP8 (SPIFF) … … 3141 4764 if ($$segDataPt =~ /^UNICODE\0/) { 3142 4765 $dumpType = 'PhotoStudio'; 3143 my $comment = $self-> Unicode2Charset(substr($$segDataPt,8), 'MM');4766 my $comment = $self->Decode(substr($$segDataPt,8), 'UCS2', 'MM'); 3144 4767 $self->FoundTag('Comment', $comment); 3145 4768 } … … 3165 4788 $dumpType = 'Photoshop'; 3166 4789 # add this data to the combined data if it exists 4790 my $dataPt = $segDataPt; 3167 4791 if (defined $combinedSegData) { 3168 4792 $combinedSegData .= substr($$segDataPt,length($psAPP13hdr)); 3169 $segDataPt = \$combinedSegData; 3170 $length = length $combinedSegData; # update length 4793 $dataPt = \$combinedSegData; 3171 4794 } 3172 4795 # peek ahead to see if the next segment is photoshop data too … … 3174 4797 # initialize combined data if necessary 3175 4798 $combinedSegData = $$segDataPt unless defined $combinedSegData; 3176 next; # will handle the combined data the next time around 4799 # (will handle the Photoshop data the next time around) 4800 } else { 4801 my $hdrlen = $isOld ? 27 : 14; 4802 # process APP13 Photoshop record 4803 my $tagTablePtr = GetTagTable('Image::ExifTool::Photoshop::Main'); 4804 my %dirInfo = ( 4805 DataPt => $dataPt, 4806 DataPos => $segPos, 4807 DataLen => length $$dataPt, 4808 DirStart => $hdrlen, # directory starts after identifier 4809 DirLen => length($$dataPt) - $hdrlen, 4810 Parent => $markerName, 4811 ); 4812 $self->ProcessDirectory(\%dirInfo, $tagTablePtr); 4813 undef $combinedSegData; 3177 4814 } 3178 my $hdrlen = $isOld ? 27 : 14;3179 # process APP13 Photoshop record3180 my $tagTablePtr = GetTagTable('Image::ExifTool::Photoshop::Main');3181 my %dirInfo = (3182 DataPt => $segDataPt,3183 DataPos => $segPos,3184 DataLen => $length,3185 DirStart => $hdrlen, # directory starts after identifier3186 DirLen => $length - $hdrlen,3187 Parent => $markerName,3188 );3189 $self->ProcessDirectory(\%dirInfo, $tagTablePtr);3190 undef $combinedSegData;3191 4815 } elsif ($$segDataPt =~ /^Adobe_CM/) { 3192 4816 $dumpType = 'Adobe_CM'; … … 3222 4846 } elsif ($marker == 0xfe) { # COM (JPEG comment) 3223 4847 $dumpType = 'Comment'; 4848 $$segDataPt =~ s/\0+$//; # some dumb softwares add null terminators 3224 4849 $self->FoundTag('Comment', $$segDataPt); 3225 4850 } elsif (($marker & 0xf0) != 0xe0) { … … 3227 4852 } 3228 4853 if (defined $dumpType) { 3229 if (not $dumpType and $self->{OPTIONS} ->{Unknown}) {4854 if (not $dumpType and $self->{OPTIONS}{Unknown}) { 3230 4855 $self->Warn("Unknown $markerName segment", 1); 3231 4856 } 3232 4857 if ($htmlDump) { 3233 4858 my $desc = $markerName . ($dumpType ? " $dumpType" : '') . ' segment'; 3234 $self->H tmlDump($segPos-4, $length+4, $desc, undef, 0x08);4859 $self->HDump($segPos-4, $length+4, $desc, undef, 0x08); 3235 4860 $dumpEnd = $segPos + $length; 3236 4861 } … … 3238 4863 undef $$segDataPt; 3239 4864 } 3240 $/ = $oldsep; # restore separator to original value 4865 # calculate JPEGDigest if requested 4866 if (@dqt and $subSampling) { 4867 require Image::ExifTool::JPEGDigest; 4868 Image::ExifTool::JPEGDigest::Calculate($self, \@dqt, $subSampling); 4869 } 4870 $self->Warn('Error reading PreviewImage', 1) if $$self{PreviewError}; 4871 $self->Warn('Invalid extended XMP') if %extendedXMP; 3241 4872 $success or $self->Warn('JPEG format error'); 4873 pop @$path if @$path > $pn; 3242 4874 return 1; 3243 4875 } 3244 4876 3245 4877 #------------------------------------------------------------------------------ 3246 # Process TIFF data 3247 # Inputs: 0) ExifTool object reference, 1) directory information reference 3248 # 2) optional tag table reference 4878 # Process EXIF file 4879 # Inputs/Returns: same as ProcessTIFF 4880 sub ProcessEXIF($$;$) 4881 { 4882 my ($self, $dirInfo, $tagTablePtr) = @_; 4883 return $self->ProcessTIFF($dirInfo, $tagTablePtr); 4884 } 4885 4886 #------------------------------------------------------------------------------ 4887 # Process TIFF data (wrapper for DoProcessTIFF to allow re-entry) 4888 # Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) optional tag table ref 3249 4889 # Returns: 1 if this looked like a valid EXIF block, 0 otherwise, or -1 on write error 3250 4890 sub ProcessTIFF($$;$) 4891 { 4892 my ($self, $dirInfo, $tagTablePtr) = @_; 4893 my $exifData = $$self{EXIF_DATA}; 4894 my $exifPos = $$self{EXIF_POS}; 4895 my $rtnVal = $self->DoProcessTIFF($dirInfo, $tagTablePtr); 4896 # restore original EXIF information (in case ProcessTIFF is nested) 4897 if (defined $exifData) { 4898 $$self{EXIF_DATA} = $exifData; 4899 $$self{EXIF_POS} = $exifPos; 4900 } 4901 return $rtnVal; 4902 } 4903 4904 #------------------------------------------------------------------------------ 4905 # Process TIFF data 4906 # Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) optional tag table ref 4907 # Returns: 1 if this looked like a valid EXIF block, 0 otherwise, or -1 on write error 4908 sub DoProcessTIFF($$;$) 3251 4909 { 3252 4910 my ($self, $dirInfo, $tagTablePtr) = @_; … … 3256 4914 my $base = $$dirInfo{Base} || 0; 3257 4915 my $outfile = $$dirInfo{OutFile}; 3258 my ($length, $err, $canonSig); 3259 3260 # read the image file header and offset to 0th IFD if necessary 4916 my ($err, $canonSig, $otherSig); 4917 4918 # attempt to read TIFF header 4919 $self->{EXIF_DATA} = ''; 3261 4920 if ($raf) { 3262 4921 if ($outfile) { … … 3269 4928 $raf->Seek($base, 0) or return 0; 3270 4929 } 3271 $raf->Read($self->{EXIF_DATA}, 8) == 8 or return 0; 3272 } elsif ($dataPt) { 4930 # extract full EXIF block (for block copy) from EXIF file 4931 my $amount = $fileType eq 'EXIF' ? 65536 * 8 : 8; 4932 my $n = $raf->Read($self->{EXIF_DATA}, $amount); 4933 if ($n < 8) { 4934 return 0 if $n or not $outfile or $fileType ne 'EXIF'; 4935 # create EXIF file from scratch 4936 delete $self->{EXIF_DATA}; 4937 undef $raf; 4938 } 4939 if ($n > 8) { 4940 $raf->Seek(8, 0); 4941 if ($n == $amount) { 4942 $self->{EXIF_DATA} = substr($self->{EXIF_DATA}, 0, 8); 4943 $self->Warn('EXIF too large to extract as a block'); #(shouldn't happen) 4944 } 4945 } 4946 } elsif ($dataPt and length $$dataPt) { 3273 4947 # save a copy of the EXIF data 3274 4948 my $dirStart = $$dirInfo{DirStart} || 0; 3275 $self->{EXIF_DATA} = substr(${$$dirInfo{DataPt}}, $dirStart); 4949 my $dirLen = $$dirInfo{DirLen} || (length($$dataPt) - $dirStart); 4950 $self->{EXIF_DATA} = substr($$dataPt, $dirStart, $dirLen); 4951 $self->VerboseDir('TIFF') if $self->{OPTIONS}{Verbose} and length($$self{INDENT}) > 2; 3276 4952 } elsif ($outfile) { 3277 # create TIFF information from scratch 3278 $self->{EXIF_DATA} = "MM\0\x2a\0\0\0\x08"; 4953 delete $self->{EXIF_DATA}; # create from scratch 3279 4954 } else { 3280 4955 $self->{EXIF_DATA} = ''; 3281 4956 } 4957 unless (defined $self->{EXIF_DATA}) { 4958 # create TIFF information from scratch 4959 if ($self->SetPreferredByteOrder() eq 'MM') { 4960 $self->{EXIF_DATA} = "MM\0\x2a\0\0\0\x08"; 4961 } else { 4962 $self->{EXIF_DATA} = "II\x2a\0\x08\0\0\0"; 4963 } 4964 } 3282 4965 $$self{FIRST_EXIF_POS} = $base + $$self{BASE} unless defined $$self{FIRST_EXIF_POS}; 3283 $$self{EXIF_POS} = $base ;4966 $$self{EXIF_POS} = $base + $$self{BASE}; 3284 4967 $dataPt = \$self->{EXIF_DATA}; 3285 4968 3286 4969 # set byte ordering 3287 SetByteOrder(substr($$dataPt,0,2)) or return 0; 3288 # save EXIF byte ordering 3289 $self->{EXIF_BYTE_ORDER} = GetByteOrder(); 4970 my $byteOrder = substr($$dataPt,0,2); 4971 SetByteOrder($byteOrder) or return 0; 3290 4972 3291 4973 # verify the byte ordering 3292 4974 my $identifier = Get16u($dataPt, 2); 3293 4975 # identifier is 0x2a for TIFF (but 0x4f52, 0x5352 or ?? for ORF) 3294 # no longer do this because ORF files use different values 4976 # no longer do this because various files use different values 4977 # (TIFF=0x2a, RW2/RWL=0x55, HDP=0xbc, BTF=0x2b, ORF=0x4f52/0x5352/0x????) 3295 4978 # return 0 unless $identifier == 0x2a; 3296 4979 … … 3305 4988 $raf->Read($canonSig, 8) == 8 or return 0; 3306 4989 $$dataPt .= $canonSig; 3307 if ($canonSig =~ /^ CR\x02\0/) {3308 $fileType = 'CR2';3309 $self->H tmlDump($base+8, 8, '[CR2 header]') if $self->{HTML_DUMP};4990 if ($canonSig =~ /^(CR\x02\0|\xba\xb0\xac\xbb)/) { 4991 $fileType = $canonSig =~ /^CR/ ? 'CR2' : 'Canon 1D RAW'; 4992 $self->HDump($base+8, 8, "[$fileType header]") if $self->{HTML_DUMP}; 3310 4993 } else { 3311 4994 undef $canonSig; 3312 4995 } 3313 } elsif ($identifier == 0x55 and $fileType =~ /^(RAW|TIFF)$/) { 3314 $fileType = 'RAW'; # Panasonic RAW file 3315 $tagTablePtr = GetTagTable('Image::ExifTool::Panasonic::Raw'); 4996 } elsif ($identifier == 0x55 and $fileType =~ /^(RAW|RW2|RWL|TIFF)$/) { 4997 # panasonic RAW, RW2 or RWL file 4998 my $magic; 4999 # test for RW2/RWL magic number 5000 if ($offset >= 0x18 and $raf->Read($magic, 16) and 5001 $magic eq "\x88\xe7\x74\xd8\xf8\x25\x1d\x4d\x94\x7a\x6e\x77\x82\x2b\x5d\x6a") 5002 { 5003 $fileType = 'RW2' unless $fileType eq 'RWL'; 5004 $self->HDump($base + 8, 16, '[RW2/RWL header]') if $$self{HTML_DUMP}; 5005 $otherSig = $magic; # save signature for writing 5006 } else { 5007 $fileType = 'RAW'; 5008 } 5009 $tagTablePtr = GetTagTable('Image::ExifTool::PanasonicRaw::Main'); 3316 5010 } elsif ($identifier == 0x2b and $fileType eq 'TIFF') { 3317 5011 # this looks like a BigTIFF image … … 3319 5013 require Image::ExifTool::BigTIFF; 3320 5014 return 1 if Image::ExifTool::BigTIFF::ProcessBTF($self, $dirInfo); 3321 } elsif (Get8u($dataPt, 2) == 0xbc and $fileType eq 'TIFF') { 3322 $fileType = 'WDP'; # Windows Media Photo file 5015 } elsif (Get8u($dataPt, 2) == 0xbc and $byteOrder eq 'II' and $fileType eq 'TIFF') { 5016 $fileType = 'HDP'; # Windows HD Photo file 5017 # check version number 5018 my $ver = Get8u($dataPt, 3); 5019 if ($ver > 1) { 5020 $self->Error("Windows HD Photo version $ver files not yet supported"); 5021 return 1; 5022 } 5023 } elsif ($identifier == 0x4352 and $fileType eq 'TIFF') { 5024 $fileType = 'DCP'; 3323 5025 } 3324 5026 # we have a valid TIFF (or whatever) file 3325 if ($fileType and not $self->{VALUE}->{FileType}) { 3326 $self->SetFileType($fileType); 3327 } 3328 } 3329 $self->FoundTag('ExifByteOrder', GetByteOrder()); 5027 if ($fileType and not $self->{VALUE}{FileType}) { 5028 my $lookup = $fileTypeLookup{$fileType}; 5029 $lookup = $fileTypeLookup{$lookup} unless ref $lookup or not $lookup; 5030 # use file extension to pre-determine type if extension is TIFF-based or type is RAW 5031 my $t = (($lookup and $$lookup[0] eq 'TIFF') or $fileType =~ /RAW/) ? $fileType : undef; 5032 $self->SetFileType($t); 5033 } 5034 } 5035 my $ifdName = 'IFD0'; 5036 if (not $tagTablePtr or $$tagTablePtr{GROUPS}{0} eq 'EXIF') { 5037 $self->FoundTag('ExifByteOrder', $byteOrder); 5038 } else { 5039 $ifdName = $$tagTablePtr{GROUPS}{1}; 5040 } 3330 5041 if ($self->{HTML_DUMP}) { 3331 my $ o = (GetByteOrder() eq 'II') ? 'Little' : 'Big';3332 $self->HtmlDump($base, 4, "TIFF header", "Byte order: $o endian", 0);3333 $self->H tmlDump($base+4, 4, "IFD0 pointer", sprintf("Offset: 0x%.4x",$offset), 0);5042 my $tip = sprintf("Byte order: %s endian\nIdentifier: 0x%.4x\n$ifdName offset: 0x%.4x", 5043 ($byteOrder eq 'II') ? 'Little' : 'Big', $identifier, $offset); 5044 $self->HDump($base, 8, 'TIFF header', $tip, 0); 3334 5045 } 3335 5046 # remember where we found the TIFF data (APP1, APP3, TIFF, NEF, etc...) … … 3346 5057 DataPos => 0, 3347 5058 DirStart => $offset, 3348 DirLen => length $$dataPt,5059 DirLen => length($$dataPt) - $offset, 3349 5060 RAF => $raf, 3350 DirName => 'IFD0',5061 DirName => $ifdName, 3351 5062 Parent => $fileType, 3352 ImageData=> 1, # set flag to get information to copy image data later 5063 ImageData=> 'Main', # set flag to get information to copy main image data later 5064 Multi => $$dirInfo{Multi}, 3353 5065 ); 3354 5066 … … 3358 5070 $self->ProcessDirectory(\%dirInfo, $tagTablePtr); 3359 5071 # process GeoTiff information if available 3360 if ($self->{VALUE} ->{GeoTiffDirectory}) {5072 if ($self->{VALUE}{GeoTiffDirectory}) { 3361 5073 require Image::ExifTool::GeoTiff; 3362 5074 Image::ExifTool::GeoTiff::ProcessGeoTiff($self); … … 3369 5081 $self->ProcessTrailers($trailInfo); 3370 5082 } 5083 # dump any other known trailer (ie. A100 RAW Data) 5084 if ($$self{HTML_DUMP} and $$self{KnownTrailer}) { 5085 my $known = $$self{KnownTrailer}; 5086 $raf->Seek(0, 2); 5087 my $len = $raf->Tell() - $$known{Start}; 5088 $len -= $$trailInfo{Offset} if $trailInfo; # account for other trailers 5089 $self->HDump($$known{Start}, $len, "[$$known{Name}]") if $len > 0; 5090 } 5091 } 5092 # update FileType if necessary now that we know more about the file 5093 if ($$self{DNGVersion} and $self->{VALUE}{FileType} ne 'DNG') { 5094 # override whatever FileType we set since we now know it is DNG 5095 $self->OverrideFileType('DNG'); 3371 5096 } 3372 5097 return 1; … … 3388 5113 Image::ExifTool::CanonRaw::WriteCR2($self, \%dirInfo, $tagTablePtr) or $err = 1; 3389 5114 } else { 3390 # write TIFF header (8 bytes to be immediately followed by IFD) 3391 $dirInfo{NewDataPos} = 8; 5115 # write TIFF header (8 bytes [plus optional signature] followed by IFD) 5116 $otherSig = '' unless defined $otherSig; 5117 my $offset = 8 + length($otherSig); 5118 # construct tiff header 5119 my $header = substr($$dataPt, 0, 4) . Set32u($offset) . $otherSig; 5120 $dirInfo{NewDataPos} = $offset; 5121 $dirInfo{HeaderPtr} = \$header; 3392 5122 # preserve padding between image data blocks in ORF images 3393 5123 # (otherwise dcraw has problems because it assumes fixed block spacing) … … 3397 5127 $err = 1; 3398 5128 } elsif (length($newData)) { 3399 my $offset = 8; 3400 my $header = substr($$dataPt, 0, 4) . Set32u($offset); 3401 Write($outfile, $header, $newData) or $err = 1; 5129 # update header length in case more was added 5130 my $hdrLen = length $header; 5131 if ($hdrLen != 8) { 5132 Set32u($hdrLen, \$header, 4); 5133 # also update preview fixup if necessary 5134 my $pi = $$self{PREVIEW_INFO}; 5135 $$pi{Fixup}{Start} += $hdrLen - 8 if $pi and $$pi{Fixup}; 5136 } 5137 if ($$self{TIFF_TYPE} eq 'ARW' and not $err) { 5138 # write any required ARW trailer and patch other ARW quirks 5139 require Image::ExifTool::Sony; 5140 my $errStr = Image::ExifTool::Sony::FinishARW($self, $dirInfo, \$newData, 5141 $dirInfo{ImageData}); 5142 $errStr and $self->Error($errStr); 5143 delete $dirInfo{ImageData}; # (was copied by FinishARW) 5144 } else { 5145 Write($outfile, $header, $newData) or $err = 1; 5146 } 3402 5147 undef $newData; # free memory 3403 5148 } … … 3408 5153 } 3409 5154 } 5155 # make local copy of TIFF_END now (it may be reset when processing trailers) 5156 my $tiffEnd = $self->{TIFF_END}; 5157 delete $self->{TIFF_END}; 5158 3410 5159 # rewrite trailers if they exist 3411 if ($raf and $ self->{TIFF_END}and not $err) {5160 if ($raf and $tiffEnd and not $err) { 3412 5161 my ($buf, $trailInfo); 3413 5162 $raf->Seek(0, 2) or $err = 1; 3414 my $extra = $raf->Tell() - $ self->{TIFF_END};5163 my $extra = $raf->Tell() - $tiffEnd; 3415 5164 # check for trailer and process if possible 3416 5165 for (;;) { 3417 5166 last unless $extra > 12; 3418 $raf->Seek($ self->{TIFF_END}); # seek back to end of image5167 $raf->Seek($tiffEnd); # seek back to end of image 3419 5168 $trailInfo = IdentifyTrailer($raf); 3420 5169 last unless $trailInfo; … … 3429 5178 } 3430 5179 # calculate unused bytes before trailer 3431 $extra = $$trailInfo{DataPos} - $ self->{TIFF_END};5180 $extra = $$trailInfo{DataPos} - $tiffEnd; 3432 5181 last; # yes, the 'for' loop was just a cheap 'goto' 3433 5182 } 3434 5183 # ignore a single zero byte if used for padding 3435 # (note that Photoshop CS adds a trailer with 2 zero bytes 3436 # for some reason, and these will be preserved) 3437 if ($extra > 0 and $self->{TIFF_END} & 0x01) { 3438 $raf->Seek($self->{TIFF_END}, 0) or $err = 1; 5184 if ($extra > 0 and $tiffEnd & 0x01) { 5185 $raf->Seek($tiffEnd, 0) or $err = 1; 3439 5186 $raf->Read($buf, 1) or $err = 1; 3440 $buf eq "\0" and --$extra, ++$self->{TIFF_END};5187 defined $buf and $buf eq "\0" and --$extra, ++$tiffEnd; 3441 5188 } 3442 5189 if ($extra > 0) { 3443 if ($self->{DEL_GROUP}->{Trailer}) { 5190 my $known = $$self{KnownTrailer}; 5191 if ($self->{DEL_GROUP}{Trailer} and not $known) { 3444 5192 $self->VPrint(0, " Deleting unknown trailer ($extra bytes)\n"); 3445 5193 ++$self->{CHANGED}; 5194 } elsif ($known) { 5195 $self->VPrint(0, " Copying $$known{Name} ($extra bytes)\n"); 5196 $raf->Seek($tiffEnd, 0) or $err = 1; 5197 CopyBlock($raf, $outfile, $extra) or $err = 1; 3446 5198 } else { 3447 $self->VPrint(0, " Preserving unknown trailer ($extra bytes)\n"); 3448 $raf->Seek($self->{TIFF_END}, 0) or $err = 1; 3449 while ($extra) { 3450 my $n = $extra < 65536 ? $extra : 65536; 5199 $raf->Seek($tiffEnd, 0) or $err = 1; 5200 # preserve unknown trailer only if it contains non-null data 5201 # (Photoshop CS adds a trailer with 2 null bytes) 5202 my $size = $extra; 5203 for (;;) { 5204 my $n = $size > 65536 ? 65536 : $size; 3451 5205 $raf->Read($buf, $n) == $n or $err = 1, last; 3452 Write($outfile, $buf) or $err = 1, last; 3453 $extra -= $n; 5206 if ($buf =~ /[^\0]/) { 5207 $self->VPrint(0, " Preserving unknown trailer ($extra bytes)\n"); 5208 # copy the trailer since it contains non-null data 5209 Write($outfile, "\0"x($extra-$size)) or $err = 1, last if $size != $extra; 5210 Write($outfile, $buf) or $err = 1, last; 5211 CopyBlock($raf, $outfile, $size-$n) or $err = 1 if $size > $n; 5212 last; 5213 } 5214 $size -= $n; 5215 next if $size > 0; 5216 $self->VPrint(0, " Deleting blank trailer ($extra bytes)\n"); 5217 last; 3454 5218 } 3455 5219 } … … 3461 5225 Write($outfile, $$trailPt) or $err = 1 if $trailPt; 3462 5226 } 3463 delete $self->{TIFF_END}; 5227 # check DNG version 5228 if ($$self{DNGVersion}) { 5229 my $ver = $$self{DNGVersion}; 5230 # currently support up to DNG version 1.2 5231 unless ($ver =~ /^(\d+) (\d+)/ and "$1.$2" <= 1.3) { 5232 $ver =~ tr/ /./; 5233 $self->Error("DNG Version $ver not yet supported", 1); 5234 } 5235 } 3464 5236 return $err ? -1 : 1; 3465 5237 } … … 3495 5267 { 3496 5268 my $tableName = shift or return undef; 3497 3498 5269 my $table = $allTables{$tableName}; 3499 5270 … … 3504 5275 if ($tableName =~ /(.*)::/) { 3505 5276 my $module = $1; 3506 unless (eval "require $module") { 5277 if (eval "require $module") { 5278 # load additional XMP modules if required 5279 if (not %$tableName and $module eq 'Image::ExifTool::XMP') { 5280 require 'Image/ExifTool/XMP2.pl'; 5281 } 5282 } else { 3507 5283 $@ and warn $@; 3508 5284 } … … 3516 5292 $table = \%$tableName; 3517 5293 use strict 'refs'; 5294 $$table{TABLE_NAME} = $tableName; # set table name 5295 ($$table{SHORT_NAME} = $tableName) =~ s/^Image::ExifTool:://; 3518 5296 # set default group 0 and 1 from module name unless already specified 3519 5297 my $defaultGroups = $$table{GROUPS}; … … 3529 5307 } 3530 5308 $$defaultGroups{2} = 'Other' unless $$defaultGroups{2}; 5309 if ($$defaultGroups{0} eq 'XMP' or $$table{NAMESPACE}) { 5310 # initialize some XMP table defaults 5311 require Image::ExifTool::XMP; 5312 Image::ExifTool::XMP::RegisterNamespace($table); # register all table namespaces 5313 # set default write/check procs 5314 $$table{WRITE_PROC} = \&Image::ExifTool::XMP::WriteXMP unless $$table{WRITE_PROC}; 5315 $$table{CHECK_PROC} = \&Image::ExifTool::XMP::CheckXMP unless $$table{CHECK_PROC}; 5316 $$table{LANG_INFO} = \&Image::ExifTool::XMP::GetLangInfo unless $$table{LANG_INFO}; 5317 } 3531 5318 # generate a tag prefix for unknown tags if necessary 3532 5319 unless ($$table{TAG_PREFIX}) { … … 3545 5332 my $tagID; 3546 5333 foreach $tagID (TagTableKeys($UserDefined{$tableName})) { 3547 my $tagInfo = $UserDefined{$tableName} ->{$tagID};5334 my $tagInfo = $UserDefined{$tableName}{$tagID}; 3548 5335 if (ref $tagInfo eq 'HASH') { 3549 5336 $$tagInfo{Name} or $$tagInfo{Name} = ucfirst($tagID); … … 3560 5347 } 3561 5348 } 3562 # generate tag ID's if necessary3563 GenerateTagIDs($table) if $didTagID;3564 5349 # remember order we loaded the tables in 3565 5350 push @tableOrder, $tableName; … … 3577 5362 sub ProcessDirectory($$$;$) 3578 5363 { 3579 my ($self, $dirInfo, $tagTablePtr, $proc essProc) = @_;5364 my ($self, $dirInfo, $tagTablePtr, $proc) = @_; 3580 5365 3581 5366 return 0 unless $tagTablePtr and $dirInfo; 3582 # use default proc from tag table if no proc specified3583 $proc essProc or $processProc = $$tagTablePtr{PROCESS_PROC};5367 # use default proc from tag table or EXIF proc as fallback if no proc specified 5368 $proc or $proc = $$tagTablePtr{PROCESS_PROC} || \&Image::ExifTool::Exif::ProcessExif; 3584 5369 # set directory name from default group0 name if not done already 3585 $$dirInfo{DirName} or $$dirInfo{DirName} = $tagTablePtr->{GROUPS} ->{0};5370 $$dirInfo{DirName} or $$dirInfo{DirName} = $tagTablePtr->{GROUPS}{0}; 3586 5371 # guard against cyclical recursion into the same directory 3587 5372 if (defined $$dirInfo{DirStart} and defined $$dirInfo{DataPos}) { 3588 5373 my $addr = $$dirInfo{DirStart} + $$dirInfo{DataPos} + ($$dirInfo{Base}||0); 3589 if ($self->{PROCESSED} ->{$addr}) {3590 $self->Warn("$$dirInfo{DirName} pointer references previous $self->{PROCESSED} ->{$addr} directory");5374 if ($self->{PROCESSED}{$addr}) { 5375 $self->Warn("$$dirInfo{DirName} pointer references previous $self->{PROCESSED}{$addr} directory"); 3591 5376 return 0; 3592 5377 } 3593 $self->{PROCESSED}->{$addr} = $$dirInfo{DirName}; 3594 } 3595 # otherwise process as an EXIF directory 3596 $processProc or $processProc = \&Image::ExifTool::Exif::ProcessExif; 5378 $self->{PROCESSED}{$addr} = $$dirInfo{DirName}; 5379 } 3597 5380 my $oldOrder = GetByteOrder(); 3598 5381 my $oldIndent = $self->{INDENT}; 3599 5382 my $oldDir = $self->{DIR_NAME}; 5383 $self->{LIST_TAGS} = { }; # don't build lists across different directories 3600 5384 $self->{INDENT} .= '| '; 3601 5385 $self->{DIR_NAME} = $$dirInfo{DirName}; 3602 my $rtnVal = &$processProc($self, $dirInfo, $tagTablePtr); 5386 push @{$self->{PATH}}, $$dirInfo{DirName}; 5387 5388 # process the directory 5389 my $rtnVal = &$proc($self, $dirInfo, $tagTablePtr); 5390 5391 pop @{$self->{PATH}}; 3603 5392 $self->{INDENT} = $oldIndent; 3604 5393 $self->{DIR_NAME} = $oldDir; … … 3608 5397 3609 5398 #------------------------------------------------------------------------------ 5399 # Get Metadata path 5400 # Inputs: 0) Exiftool object ref 5401 # Return: Metadata path string 5402 sub MetadataPath($) 5403 { 5404 my $self = shift; 5405 return join '-', @{$$self{PATH}} 5406 } 5407 5408 #------------------------------------------------------------------------------ 3610 5409 # Get standardized file extension 3611 5410 # Inputs: 0) file name 3612 # Returns: standardized extension (all uppercase) 5411 # Returns: standardized extension (all uppercase), or undefined if no extension 3613 5412 sub GetFileExtension($) 3614 5413 { … … 3669 5468 return '' if $condition =~ /\$(valPt|format|count)\b/ and not defined $valPt; 3670 5469 # set old value for use in condition if needed 3671 my $oldVal = $self->{VALUE}->{$$tagInfo{Name}}; 3672 #### eval Condition ($self, $oldVal, [$valPt, $format, $count]) 5470 local $SIG{'__WARN__'} = \&SetWarning; 5471 undef $evalWarning; 5472 #### eval Condition ($self, [$valPt, $format, $count]) 3673 5473 unless (eval $condition) { 3674 $@ and warn "Condition $$tagInfo{Name}: $@"; 5474 $@ and $evalWarning = $@; 5475 $self->Warn("Condition $$tagInfo{Name}: " . CleanWarning()) if $evalWarning; 3675 5476 next; 3676 5477 } 3677 5478 } 3678 if ($$tagInfo{Unknown} and not $ self->{OPTIONS}->{Unknown}) {5479 if ($$tagInfo{Unknown} and not $$self{OPTIONS}{Unknown} and not $$self{OPTIONS}{Verbose}) { 3679 5480 # don't return Unknown tags unless that option is set 3680 5481 return undef; … … 3684 5485 } 3685 5486 # generate information for unknown tags (numerical only) if required 3686 if (not $tagInfo and $self->{OPTIONS}->{Unknown} and $tagID =~ /^\d+$/and3687 not $$self{NO_UNKNOWN})5487 if (not $tagInfo and ($$self{OPTIONS}{Unknown} or $$self{OPTIONS}{Verbose}) and 5488 $tagID =~ /^\d+$/ and not $$self{NO_UNKNOWN}) 3688 5489 { 3689 5490 my $printConv; … … 3714 5515 # Add new tag to table (must use this routine to add new tags to a table) 3715 5516 # Inputs: 0) reference to tag table, 1) tag ID 3716 # 2) reference to tag information hash5517 # 2) [optional] reference to tag information hash 3717 5518 # Notes: - will not overwrite existing entry in table 3718 5519 # - info need contain no entries when this routine is called 3719 sub AddTagToTable($$ $)5520 sub AddTagToTable($$;$) 3720 5521 { 3721 5522 my ($tagTablePtr, $tagID, $tagInfo) = @_; 5523 $tagInfo or $tagInfo = { }; 3722 5524 3723 5525 # define necessary entries in information hash … … 3725 5527 # fill in default groups from table GROUPS 3726 5528 foreach (keys %{$$tagTablePtr{GROUPS}}) { 3727 next if $tagInfo->{Groups} ->{$_};3728 $tagInfo->{Groups} ->{$_} = $tagTablePtr->{GROUPS}->{$_};5529 next if $tagInfo->{Groups}{$_}; 5530 $tagInfo->{Groups}{$_} = $tagTablePtr->{GROUPS}{$_}; 3729 5531 } 3730 5532 } else { 3731 $$tagInfo{Groups} = $$tagTablePtr{GROUPS};5533 $$tagInfo{Groups} = { %{$$tagTablePtr{GROUPS}} }; 3732 5534 } 3733 5535 $$tagInfo{Flags} and ExpandFlags($tagInfo); … … 3736 5538 $$tagInfo{TagID} = $tagID; 3737 5539 3738 unless ($$tagInfo{Name}) { 5540 my $name = $$tagInfo{Name}; 5541 if (defined $name) { 5542 $name =~ tr/-_a-zA-Z0-9//dc; # remove illegal characters 5543 } else { 5544 # construct a name from the tag ID 5545 $name = $tagID; 5546 $name =~ tr/-_a-zA-Z0-9//dc; # remove illegal characters 5547 $name = ucfirst $name; # start with uppercase 5548 # make sure name is a reasonable length 3739 5549 my $prefix = $$tagTablePtr{TAG_PREFIX}; 3740 $$tagInfo{Name} = "${prefix}_$tagID"; 3741 # make description to prevent tagID from getting mangled by MakeDescription() 3742 $$tagInfo{Description} = MakeDescription($prefix, $tagID); 3743 } 5550 if ($prefix) { 5551 # make description to prevent tagID from getting mangled by MakeDescription() 5552 $$tagInfo{Description} = MakeDescription($prefix, $name); 5553 $name = "${prefix}_$name"; 5554 } 5555 } 5556 # tag names must be at least 2 characters long and begin with a letter 5557 $name = "Tag$name" if length($name) <= 1 or $name !~ /^[A-Z]/i; 5558 $$tagInfo{Name} = $name; 3744 5559 # add tag to table, but never overwrite existing entries (could potentially happen 3745 5560 # if someone thinks there isn't any tagInfo because a condition wasn't satisfied) … … 3751 5566 # Inputs: 0) ExifTool object ref, 1) tag table reference, 2) tagID, 3) value, 3752 5567 # 4-N) parameters hash: Index, DataPt, DataPos, Start, Size, Parent, 3753 # TagInfo, ProcessProc 5568 # TagInfo, ProcessProc, RAF 3754 5569 # Returns: tag key or undef if tag not found 5570 # Notes: if value is not defined, it is extracted from DataPt using TagInfo 5571 # Format and Count if provided 3755 5572 sub HandleTag($$$$;%) 3756 5573 { 3757 5574 my ($self, $tagTablePtr, $tag, $val, %parms) = @_; 3758 my $verbose = $self->{OPTIONS} ->{Verbose};3759 my $tagInfo = $parms{TagInfo} || $self->GetTagInfo($tagTablePtr, $tag );5575 my $verbose = $self->{OPTIONS}{Verbose}; 5576 my $tagInfo = $parms{TagInfo} || $self->GetTagInfo($tagTablePtr, $tag, \$val); 3760 5577 my $dataPt = $parms{DataPt}; 3761 my $subdir;5578 my ($subdir, $format, $count, $size, $noTagInfo); 3762 5579 3763 5580 if ($tagInfo) { … … 3765 5582 } else { 3766 5583 return undef unless $verbose; 5584 $tagInfo = { Name => "tag $tag" }; # create temporary tagInfo hash 5585 $noTagInfo = 1; 3767 5586 } 3768 5587 # read value if not done already (not necessary for subdir) 3769 unless (defined $val or $subdir) {5588 unless (defined $val or ($subdir and not $$tagInfo{Writable})) { 3770 5589 my $start = $parms{Start} || 0; 3771 5590 my $size = $parms{Size} || 0; 3772 5591 # read from data in memory if possible 3773 5592 if ($dataPt and $start >= 0 and $start + $size <= length($$dataPt)) { 3774 $val = substr($$dataPt, $start, $size); 5593 $format = $$tagInfo{Format} || $$tagTablePtr{FORMAT}; 5594 if ($format) { 5595 $val = ReadValue($dataPt, $start, $format, $$tagInfo{Count}, $size); 5596 } else { 5597 $val = substr($$dataPt, $start, $size); 5598 } 3775 5599 } else { 3776 my $name = $tagInfo ? $$tagInfo{Name} : "tag $tag"; 3777 $self->Warn("Error extracting value for $name"); 5600 $self->Warn("Error extracting value for $$tagInfo{Name}"); 3778 5601 return undef; 3779 5602 } … … 3781 5604 # do verbose print if necessary 3782 5605 if ($verbose) { 5606 undef $tagInfo if $noTagInfo; 3783 5607 $parms{Value} = $val; 3784 5608 $parms{Table} = $tagTablePtr; 5609 if ($format) { 5610 $count or $count = int(($parms{Size} || 0) / ($formatSize{$format} || 1)); 5611 $parms{Format} = $format . "[$count]"; 5612 } 3785 5613 $self->VerboseInfo($tag, $tagInfo, %parms); 3786 5614 } 3787 5615 if ($tagInfo) { 3788 5616 if ($subdir) { 5617 my $subdirStart = $parms{Start}; 5618 my $subdirLen = $parms{Size}; 5619 if ($$subdir{Start}) { 5620 my $valuePtr = 0; 5621 #### eval Start ($valuePtr) 5622 my $off = eval $$subdir{Start}; 5623 $subdirStart += $off; 5624 $subdirLen -= $off; 5625 } 3789 5626 $dataPt or $dataPt = \$val; 3790 5627 # process subdirectory information 3791 5628 my %dirInfo = ( 3792 DirName => $$ tagInfo{Name},5629 DirName => $$subdir{DirName} || $$tagInfo{Name}, 3793 5630 DataPt => $dataPt, 3794 5631 DataLen => length $$dataPt, 3795 5632 DataPos => $parms{DataPos}, 3796 DirStart => $ parms{Start},3797 DirLen => $ parms{Size},5633 DirStart => $subdirStart, 5634 DirLen => $subdirLen, 3798 5635 Parent => $parms{Parent}, 5636 Base => $parms{Base}, 5637 Multi => $$subdir{Multi}, 5638 TagInfo => $tagInfo, 5639 RAF => $parms{RAF}, 3799 5640 ); 5641 my $oldOrder = GetByteOrder(); 5642 SetByteOrder($$subdir{ByteOrder}) if $$subdir{ByteOrder}; 3800 5643 my $subTablePtr = GetTagTable($$subdir{TagTable}) || $tagTablePtr; 3801 $self->ProcessDirectory(\%dirInfo, $subTablePtr, $$subdir{ProcessProc}); 3802 } else { 3803 return $self->FoundTag($tagInfo, $val); 3804 } 5644 $self->ProcessDirectory(\%dirInfo, $subTablePtr, $$subdir{ProcessProc} || $parms{ProcessProc}); 5645 SetByteOrder($oldOrder); 5646 # return now unless directory is writable as a block 5647 return undef unless $$tagInfo{Writable}; 5648 } 5649 return $self->FoundTag($tagInfo, $val); 3805 5650 } 3806 5651 return undef; … … 3809 5654 #------------------------------------------------------------------------------ 3810 5655 # Add tag to hash of extracted information 3811 # Inputs: 0) reference to ExifTool object5656 # Inputs: 0) ExifTool object reference 3812 5657 # 1) reference to tagInfo hash or tag name 3813 # 2) data value (or reference to require hash if composite)5658 # 2) data value (or reference to require hash if Composite) 3814 5659 # Returns: tag key or undef if no value 3815 5660 sub FoundTag($$$) … … 3828 5673 # (not advised to do this since the tag won't show in list) 3829 5674 $tagInfo or $tagInfo = { Name => $tag, Groups => \%allGroupsExifTool }; 3830 $self->{OPTIONS} ->{Verbose} and $self->VerboseInfo(undef, $tagInfo, Value => $value);5675 $self->{OPTIONS}{Verbose} and $self->VerboseInfo(undef, $tagInfo, Value => $value); 3831 5676 } 3832 5677 my $rawValueHash = $self->{VALUE}; 3833 5678 if ($$tagInfo{RawConv}) { 3834 my $conv = $$tagInfo{RawConv};3835 my $val = $value; # must do this in case eval references $val3836 5679 # initialize @val for use in Composite RawConv expressions 3837 5680 my @val; 3838 if (ref $val eq 'HASH') { 3839 foreach (keys %$val) { $val[$_] = $$rawValueHash{$$val{$_}}; } 3840 } 5681 if (ref $value eq 'HASH') { 5682 foreach (keys %$value) { $val[$_] = $$rawValueHash{$$value{$_}}; } 5683 } 5684 &