home *** CD-ROM | disk | FTP | other *** search
/ Clickx 115 / Clickx 115.iso / software / tools / windows / tails-i386-0.16.iso / live / filesystem.squashfs / usr / share / perl5 / Image / ExifTool.pm next >
Encoding:
Perl POD Document  |  2010-03-17  |  227.5 KB  |  5,690 lines

  1. #------------------------------------------------------------------------------
  2. # File:         ExifTool.pm
  3. #
  4. # Description:  Read and write meta information
  5. #
  6. # URL:          http://owl.phy.queensu.ca/~phil/exiftool/
  7. #
  8. # Revisions:    Nov. 12/2003 - P. Harvey Created
  9. #               (See html/history.html for revision history)
  10. #
  11. # Legal:        Copyright (c) 2003-2010, Phil Harvey (phil at owl.phy.queensu.ca)
  12. #               This library is free software; you can redistribute it and/or
  13. #               modify it under the same terms as Perl itself.
  14. #------------------------------------------------------------------------------
  15.  
  16. package Image::ExifTool;
  17.  
  18. use strict;
  19. require 5.004;  # require 5.004 for UNIVERSAL::isa (otherwise 5.002 would do)
  20. require Exporter;
  21. use File::RandomAccess;
  22.  
  23. use vars qw($VERSION $RELEASE @ISA %EXPORT_TAGS $AUTOLOAD @fileTypes %allTables
  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.15';
  30. $RELEASE = '';
  31. @ISA = qw(Exporter);
  32. %EXPORT_TAGS = (
  33.     # all public non-object-oriented functions:
  34.     Public => [qw(
  35.         ImageInfo GetTagName GetShortcuts GetAllTags GetWritableTags
  36.         GetAllGroups GetDeleteGroups GetFileType CanWrite CanCreate
  37.     )],
  38.     # exports not part of the public API, but used by ExifTool modules:
  39.     DataAccess => [qw(
  40.         ReadValue GetByteOrder SetByteOrder ToggleByteOrder Get8u Get8s Get16u
  41.         Get16s Get32u Get32s Get64u GetFloat GetDouble GetFixed32s Write
  42.         WriteValue Tell Set8u Set8s Set16u Set32u
  43.     )],
  44.     Utils => [qw(
  45.         GetTagTable TagTableKeys GetTagInfoList
  46.     )],
  47.     Vars => [qw(
  48.         %allTables @tableOrder @fileTypes
  49.     )],
  50. );
  51. # set all of our EXPORT_TAGS in EXPORT_OK
  52. Exporter::export_ok_tags(keys %EXPORT_TAGS);
  53.  
  54. # test for problems that can arise if encoding.pm is used
  55. { my $t = "\xff"; die "Incompatible encoding!\n" if ord($t) != 0xff; }
  56.  
  57. # The following functions defined in Image::ExifTool::Writer are declared
  58. # here so their prototypes will be available.  These Writer routines will be
  59. # autoloaded when any of them is called.
  60. sub SetNewValue($;$$%);
  61. sub SetNewValuesFromFile($$;@);
  62. sub GetNewValues($;$$);
  63. sub CountNewValues($);
  64. sub SaveNewValues($);
  65. sub RestoreNewValues($);
  66. sub WriteInfo($$;$$);
  67. sub SetFileModifyDate($$;$);
  68. sub SetFileName($$;$);
  69. sub GetAllTags(;$);
  70. sub GetWritableTags(;$);
  71. sub GetAllGroups($);
  72. sub GetNewGroups($);
  73. sub GetDeleteGroups();
  74. # non-public routines below
  75. sub InsertTagValues($$$;$);
  76. sub IsWritable($);
  77. sub GetNewFileName($$);
  78. sub NextTagKey($$);
  79. sub LoadAllTables();
  80. sub GetNewTagInfoList($;$);
  81. sub GetNewTagInfoHash($@);
  82. sub GetLangInfo($$);
  83. sub Get64s($$);
  84. sub Get64u($$);
  85. sub GetExtended($$);
  86. sub DecodeBits($$;$);
  87. sub EncodeBits($$;$$);
  88. sub HexDump($;$%);
  89. sub DumpTrailer($$);
  90. sub DumpUnknownTrailer($$);
  91. sub VerboseInfo($$$%);
  92. sub VerboseDir($$;$$);
  93. sub VerboseValue($$$;$);
  94. sub VPrint($$@);
  95. sub Rationalize($;$);
  96. sub Write($@);
  97. sub ProcessTrailers($$);
  98. sub WriteTrailerBuffer($$$);
  99. sub AddNewTrailers($;@);
  100. sub Tell($);
  101. sub WriteValue($$;$$$$);
  102. sub WriteDirectory($$$;$);
  103. sub WriteBinaryData($$$);
  104. sub CheckBinaryData($$$);
  105. sub WriteTIFF($$$);
  106. sub PackUTF8(@);
  107. sub UnpackUTF8($);
  108. sub SetPreferredByteOrder($);
  109. sub CopyBlock($$$);
  110.  
  111. # other subroutine definitions
  112. sub DoEscape($$);
  113. sub ConvertFileSize($);
  114. sub ParseArguments($;@); #(defined in attempt to avoid mod_perl problem)
  115.  
  116. # list of main tag tables to load in LoadAllTables() (sub-tables are recursed
  117. # automatically).  Note: They will appear in this order in the documentation
  118. # unless tweaked in BuildTagLookup::GetTableOrder().
  119. @loadAllTables = qw(
  120.     PhotoMechanic Exif GeoTiff CanonRaw KyoceraRaw MinoltaRaw PanasonicRaw
  121.     SigmaRaw JPEG Jpeg2000 GIF BMP BMP::OS2 PICT PNG MNG MIFF PSP PDF PostScript
  122.     Photoshop::Header FujiFilm::RAF Sony::SRF2 Sony::SR2SubIFD ITC ID3 Vorbis
  123.     FLAC APE APE::NewHeader APE::OldHeader MPC MPEG::Audio MPEG::Video MPEG::VBR
  124.     M2TS QuickTime QuickTime::ImageFile Flash Flash::FLV Real::Media Real::Audio
  125.     Real::Metafile RIFF AIFF ASF DICOM DjVu MIE HTML XMP::SVG EXE EXE::PEVersion
  126.     EXE::PEString EXE::MachO EXE::PEF EXE::ELF LNK Font RSRC Rawzor ZIP
  127.     ZIP::GZIP OOXML iWork
  128. );
  129.  
  130. # alphabetical list of current Lang modules
  131. @langs = qw(cs de en en_ca en_gb es fr it ja ko nl pl ru sv tr zh_cn zh_tw);
  132.  
  133. $defaultLang = 'en';    # default language
  134.  
  135. # language names
  136. %langName = (
  137.     cs => 'Czech (─îe┼ítina)',
  138.     de => 'German (Deutsch)',
  139.     en => 'English',
  140.     en_ca => 'Canadian English',
  141.     en_gb => 'British English',
  142.     es => 'Spanish (Espa├▒ol)',
  143.     fr => 'French (Fran├ºais)',
  144.     it => 'Italian (Italiano)',
  145.     ja => 'Japanese (µùѵ£¼Φ¬₧)',
  146.     ko => 'Korean (φò£Ω╡¡∞û┤)',
  147.     nl => 'Dutch (Nederlands)',
  148.     pl => 'Polish (Polski)',
  149.     ru => 'Russian (╨á╤â╤ü╤ü╨║╨╕╨╣)',
  150.     sv => 'Swedish (Svenska)',
  151.    'tr'=> 'Turkish (T├╝rk├ºe)',
  152.     zh_cn => 'Simplified Chinese (τ«ÇΣ╜ôΣ╕¡µûç)',
  153.     zh_tw => 'Traditional Chinese (τ╣üΘ½öΣ╕¡µûç)',
  154. );
  155.  
  156. # recognized file types, in the order we test unknown files
  157. # Notes: 1) There is no need to test for like types separately here
  158. # 2) Put types with weak file signatures at end of list to avoid false matches
  159. @fileTypes = qw(JPEG CRW TIFF GIF MRW RAF X3F JP2 PNG MIE MIFF PS PDF PSD XMP
  160.                 BMP PPM RIFF AIFF ASF MOV MPEG Real SWF PSP FLV OGG FLAC APE MPC
  161.                 IND ICC ITC HTML VRD QTIF FPX PICT ZIP GZIP RWZ EXE LNK RAW Font
  162.                 RSRC M2TS MP3 DICM);
  163.  
  164. # file types that we can write (edit)
  165. my @writeTypes = qw(JPEG TIFF GIF CRW MRW ORF RAF RAW PNG MIE PSD XMP PPM
  166.                     EPS PS PDF ICC VRD JP2 EXIF AI IND);
  167.  
  168. # file extensions that we can't write for various base types
  169. %noWriteFile = (
  170.     TIFF => [ qw(3FR DCR K25 KDC SRF IIQ) ],
  171.     XMP  => [ 'SVG' ],
  172. );
  173.  
  174. # file types that we can create from scratch
  175. # - must update CanCreate() documentation if this list is changed!
  176. my %createTypes = (XMP=>1, ICC=>1, MIE=>1, VRD=>1, EXIF=>1);
  177.  
  178. # file type lookup for all recognized file extensions
  179. my %fileTypeLookup = (
  180.    '3FR' => ['TIFF', 'Hasselblad RAW format'],
  181.    '3G2' => ['MOV',  '3rd Gen. Partnership Project 2 audio/video'],
  182.    '3GP' => ['MOV',  '3rd Gen. Partnership Project audio/video'],
  183.     ACR  => ['DICM', 'American College of Radiology ACR-NEMA'],
  184.     ACFM => ['Font', 'Adobe Composite Font Metrics'],
  185.     AFM  => ['Font', 'Adobe Font Metrics'],
  186.     AMFM => ['Font', 'Adobe Multiple Master Font Metrics'],
  187.     AI   => [['PDF','PS'], 'Adobe Illustrator'],
  188.     AIF  =>  'AIFF',
  189.     AIFC => ['AIFF', 'Audio Interchange File Format Compressed'],
  190.     AIFF => ['AIFF', 'Audio Interchange File Format'],
  191.     APE  => ['APE',  "Monkey's Audio format"],
  192.     ARW  => ['TIFF', 'Sony Alpha RAW format'],
  193.     ASF  => ['ASF',  'Microsoft Advanced Systems Format'],
  194.     AVI  => ['RIFF', 'Audio Video Interleaved'],
  195.     BMP  => ['BMP',  'Windows BitMaP'],
  196.     BTF  => ['BTF',  'Big Tagged Image File Format'], #(unofficial)
  197.     CIFF => ['CRW',  'Camera Image File Format'],
  198.     COS  => ['COS',  'Capture One Settings'],
  199.     CR2  => ['TIFF', 'Canon RAW 2 format'],
  200.     CRW  => ['CRW',  'Canon RAW format'],
  201.     CS1  => ['PSD',  'Sinar CaptureShop 1-Shot RAW'],
  202.     DC3  =>  'DICM',
  203.     DCM  =>  'DICM',
  204.     DCP  => ['TIFF', 'DNG Camera Profile'],
  205.     DCR  => ['TIFF', 'Kodak Digital Camera RAW'],
  206.     DFONT=> ['Font', 'Macintosh Data fork Font'],
  207.     DIB  => ['BMP',  'Device Independent Bitmap'],
  208.     DIC  =>  'DICM',
  209.     DICM => ['DICM', 'Digital Imaging and Communications in Medicine'],
  210.     DIVX => ['ASF',  'DivX media format'],
  211.     DJV  =>  'DJVU',
  212.     DJVU => ['AIFF', 'DjVu image'],
  213.     DLL  => ['EXE',  'Windows Dynamic Link Library'],
  214.     DNG  => ['TIFF', 'Digital Negative'],
  215.     DOC  => ['FPX',  'Microsoft Word Document'],
  216.     DOCM => [['ZIP','FPX'], 'Office Open XML Document Macro-enabled'],
  217.     # Note: I have seen a password-protected DOCX file which was FPX-like, so I assume
  218.     # that any other MS Office file could be like this too.  The only difference is
  219.     # that the ZIP and FPX formats are checked first, so if this is wrong, no biggie.
  220.     DOCX => [['ZIP','FPX'], 'Office Open XML Document'],
  221.     DOT  => ['FPX',  'Microsoft Word Template'],
  222.     DOTM => [['ZIP','FPX'], 'Office Open XML Document Template Macro-enabled'],
  223.     DOTX => [['ZIP','FPX'], 'Office Open XML Document Template'],
  224.     DVB  => ['MOV',  'Digital Video Broadcasting'],
  225.     DYLIB=> ['EXE',  'Mach-O Dynamic Link Library'],
  226.     EIP  => ['ZIP',  'Capture One Enhanced Image Package'],
  227.     EPS  => ['EPS',  'Encapsulated PostScript Format'],
  228.     EPSF =>  'EPS',
  229.     ERF  => ['TIFF', 'Epson Raw Format'],
  230.     EXE  => ['EXE',  'Windows executable file'],
  231.     EXIF => ['EXIF', 'Exchangable Image File Metadata'],
  232.     F4A  => ['MOV',  'Adobe Flash Player 9+ Audio'],
  233.     F4B  => ['MOV',  'Adobe Flash Player 9+ audio Book'],
  234.     F4P  => ['MOV',  'Adobe Flash Player 9+ Protected'],
  235.     F4V  => ['MOV',  'Adobe Flash Player 9+ Video'],
  236.     FLAC => ['FLAC', 'Free Lossless Audio Codec'],
  237.     FLA  => ['FPX',  'Macromedia/Adobe Flash project'],
  238.     FLV  => ['FLV',  'Flash Video'],
  239.     FPX  => ['FPX',  'FlashPix'],
  240.     GIF  => ['GIF',  'Compuserve Graphics Interchange Format'],
  241.     GZ   =>  'GZIP',
  242.     GZIP => ['GZIP', 'GNU ZIP compressed archive'],
  243.     HDP  => ['TIFF', 'Windows HD Photo'],
  244.     HTM  =>  'HTML',
  245.     HTML => ['HTML', 'HyperText Markup Language'],
  246.     ICC  => ['ICC',  'International Color Consortium'],
  247.     ICM  =>  'ICC',
  248.     IIQ  => ['TIFF', 'Phase One Intelligent Image Quality RAW'],
  249.     IND  => ['IND',  'Adobe InDesign'],
  250.     INDD => ['IND',  'Adobe InDesign Document'],
  251.     INDT => ['IND',  'Adobe InDesign Template'],
  252.     ITC  => ['ITC',  'iTunes Cover Flow'],
  253.     JNG  => ['PNG',  'JPG Network Graphics'],
  254.     JP2  => ['JP2',  'JPEG 2000 file'],
  255.     JPEG =>  'JPG',
  256.     JPG  => ['JPEG', 'Joint Photographic Experts Group'],
  257.     JPM  => ['JP2',  'JPEG 2000 compound image'],
  258.     JPX  => ['JP2',  'JPEG 2000 with extensions'],
  259.     K25  => ['TIFF', 'Kodak DC25 RAW'],
  260.     KDC  => ['TIFF', 'Kodak Digital Camera RAW'],
  261.     KEY  => ['ZIP',  'Apple Keynote presentation'],
  262.     KTH  => ['ZIP',  'Apple Keynote Theme'],
  263.     LNK  => ['LNK',  'Windows shortcut'],
  264.     M2T  =>  'M2TS',
  265.     M2TS => ['M2TS', 'MPEG-2 Transport Stream'],
  266.     M2V  => ['MPEG', 'MPEG-2 Video'],
  267.     M4A  => ['MOV',  'MPEG-4 Audio'],
  268.     M4B  => ['MOV',  'MPEG-4 audio Book'],
  269.     M4P  => ['MOV',  'MPEG-4 Protected'],
  270.     M4V  => ['MOV',  'MPEG-4 Video'],
  271.     MEF  => ['TIFF', 'Mamiya (RAW) Electronic Format'],
  272.     MIE  => ['MIE',  'Meta Information Encapsulation format'],
  273.     MIF  =>  'MIFF',
  274.     MIFF => ['MIFF', 'Magick Image File Format'],
  275.     MNG  => ['PNG',  'Multiple-image Network Graphics'],
  276.   # MODD => ['PLIST','Sony Picture Motion Metadata'],
  277.     MOS  => ['TIFF', 'Creo Leaf Mosaic'],
  278.     MOV  => ['MOV',  'Apple QuickTime movie'],
  279.     MP3  => ['MP3',  'MPEG-1 Layer 3 audio'],
  280.     MP4  => ['MOV',  'MPEG-4 video'],
  281.     MPC  => ['MPC',  'Musepack Audio'],
  282.     MPEG => ['MPEG', 'MPEG-1 or MPEG-2 audio/video'],
  283.     MPG  =>  'MPEG',
  284.     MPO  => ['JPEG', 'Extended Multi-Picture format'],
  285.     MQV  => ['MOV',  'Sony Mobile Quicktime Video'],
  286.     MRW  => ['MRW',  'Minolta RAW format'],
  287.     MTS  => ['M2TS', 'MPEG-2 Transport Stream'],
  288.     NEF  => ['TIFF', 'Nikon (RAW) Electronic Format'],
  289.     NMBTEMPLATE => ['ZIP','Apple Numbers Template'],
  290.     NRW  => ['TIFF', 'Nikon RAW (2)'],
  291.     NUMBERS => ['ZIP','Apple Numbers spreadsheet'],
  292.     OGG  => ['OGG',  'Ogg Vorbis audio file'],
  293.     ORF  => ['ORF',  'Olympus RAW format'],
  294.     OTF  => ['Font', 'Open Type Font'],
  295.     PAGES => ['ZIP', 'Apple Pages document'],
  296.     PBM  => ['PPM',  'Portable BitMap'],
  297.     PCT  =>  'PICT',
  298.     PDF  => ['PDF',  'Adobe Portable Document Format'],
  299.     PEF  => ['TIFF', 'Pentax (RAW) Electronic Format'],
  300.     PFA  => ['Font', 'PostScript Font ASCII'],
  301.     PFB  => ['Font', 'PostScript Font Binary'],
  302.     PFM  => ['Font', 'Printer Font Metrics'],
  303.     PGM  => ['PPM',  'Portable Gray Map'],
  304.     PICT => ['PICT', 'Apple PICTure'],
  305.   # PLIST=> ['PLIST','Apple Property List'],
  306.     PNG  => ['PNG',  'Portable Network Graphics'],
  307.     POT  => ['FPX',  'Microsoft PowerPoint Template'],
  308.     POTM => [['ZIP','FPX'], 'Office Open XML Presentation Template Macro-enabled'],
  309.     POTX => [['ZIP','FPX'], 'Office Open XML Presentation Template'],
  310.     PPM  => ['PPM',  'Portable Pixel Map'],
  311.     PPS  => ['FPX',  'Microsoft PowerPoint Slideshow'],
  312.     PPSM => [['ZIP','FPX'], 'Office Open XML Presentation Slideshow Macro-enabled'],
  313.     PPSX => [['ZIP','FPX'], 'Office Open XML Presentation Slideshow'],
  314.     PPT  => ['FPX',  'Microsoft PowerPoint Presentation'],
  315.     PPTM => [['ZIP','FPX'], 'Office Open XML Presentation Macro-enabled'],
  316.     PPTX => [['ZIP','FPX'], 'Office Open XML Presentation'],
  317.     PS   => ['PS',   'PostScript'],
  318.     PSB  => ['PSD',  'Photoshop Large Document'],
  319.     PSD  => ['PSD',  'Photoshop Drawing'],
  320.     PSP  => ['PSP',  'Paint Shop Pro'],
  321.     PSPFRAME => 'PSP',
  322.     PSPIMAGE => 'PSP',
  323.     PSPSHAPE => 'PSP',
  324.     PSPTUBE  => 'PSP',
  325.     QIF  =>  'QTIF',
  326.     QT   => ['MOV',  'QuickTime movie'],
  327.     QTI  =>  'QTIF',
  328.     QTIF => ['QTIF', 'QuickTime Image File'],
  329.     RA   => ['Real', 'Real Audio'],
  330.     RAF  => ['RAF',  'FujiFilm RAW Format'],
  331.     RAM  => ['Real', 'Real Audio Metafile'],
  332.     RAW  => [['RAW','TIFF'], 'Kyocera Contax N Digital RAW or Panasonic RAW'],
  333.     RIF  =>  'RIFF',
  334.     RIFF => ['RIFF', 'Resource Interchange File Format'],
  335.     RM   => ['Real', 'Real Media'],
  336.     RMVB => ['Real', 'Real Media Variable Bitrate'],
  337.     RPM  => ['Real', 'Real Media Plug-in Metafile'],
  338.     RSRC => ['RSRC', 'Mac OS Resource'],
  339.     RV   => ['Real', 'Real Video'],
  340.     RW2  => ['TIFF', 'Panasonic RAW 2'],
  341.     RWL  => ['TIFF', 'Leica RAW'],
  342.     RWZ  => ['RWZ',  'Rawzor compressed image'],
  343.     SO   => ['EXE',  'Shared Object file'],
  344.     SR2  => ['TIFF', 'Sony RAW Format 2'],
  345.     SRF  => ['TIFF', 'Sony RAW Format'],
  346.     SRW  => ['TIFF', 'Samsung RAW format'],
  347.     SVG  => ['XMP',  'Scalable Vector Graphics'],
  348.     SWF  => ['SWF',  'Shockwave Flash'],
  349.     THM  => ['JPEG', 'Canon Thumbnail'],
  350.     THMX => [['ZIP','FPX'], 'Office Open XML Theme'],
  351.     TIF  =>  'TIFF',
  352.     TIFF => ['TIFF', 'Tagged Image File Format'],
  353.     TTC  => ['Font', 'Trye Type Font Collection'],
  354.     TTF  => ['Font', 'True Type Font'],
  355.     TUB  => 'PSP',
  356.     VOB  => ['MPEG', 'Video Object'],
  357.     VRD  => ['VRD',  'Canon VRD Recipe Data'],
  358.     WAV  => ['RIFF', 'WAVeform (Windows digital audio)'],
  359.     WDP  => ['TIFF', 'Windows Media Photo'],
  360.     WMA  => ['ASF',  'Windows Media Audio'],
  361.     WMV  => ['ASF',  'Windows Media Video'],
  362.     X3F  => ['X3F',  'Sigma RAW format'],
  363.     XHTML=> ['HTML', 'Extensible HyperText Markup Language'],
  364.     XLA  => ['FPX',  'Microsoft Excel Add-in'],
  365.     XLAM => [['ZIP','FPX'], 'Office Open XML Spreadsheet Add-in Macro-enabled'],
  366.     XLS  => ['FPX',  'Microsoft Excel Spreadsheet'],
  367.     XLSB => [['ZIP','FPX'], 'Office Open XML Spreadsheet Binary'],
  368.     XLSM => [['ZIP','FPX'], 'Office Open XML Spreadsheet Macro-enabled'],
  369.     XLSX => [['ZIP','FPX'], 'Office Open XML Spreadsheet'],
  370.     XLT  => ['FPX',  'Microsoft Excel Template'],
  371.     XLTM => [['ZIP','FPX'], 'Office Open XML Spreadsheet Template Macro-enabled'],
  372.     XLTX => [['ZIP','FPX'], 'Office Open XML Spreadsheet Template'],
  373.     XMP  => ['XMP',  'Extensible Metadata Platform'],
  374.     ZIP  => ['ZIP',  'ZIP archive'],
  375. );
  376.  
  377. # descriptions for file types not found in above file extension lookup
  378. my %fileDescription = (
  379.     DICOM => 'Digital Imaging and Communications in Medicine',
  380.     PLIST => 'Property List',
  381.     XML   => 'Extensible Markup Language',
  382.     'DJVU (multi-page)' => 'DjVu multi-page image',
  383.     'Win32 EXE' => 'Windows 32-bit Executable',
  384.     'Win32 DLL' => 'Windows 32-bit Dynamic Link Library',
  385. );
  386.  
  387. # MIME types for applicable file types above
  388. # (missing entries default to 'application/unknown', but note that
  389. #  other mime types may be specified by some modules, ie. QuickTime.pm)
  390. %mimeType = (
  391.    '3FR' => 'image/x-raw',
  392.     AIFF => 'audio/aiff',
  393.     APE  => 'audio/x-monkeys-audio',
  394.     ASF  => 'video/x-ms-asf',
  395.     ARW  => 'image/x-raw',
  396.     AVI  => 'video/avi',
  397.     BMP  => 'image/bmp',
  398.     BTF  => 'image/x-tiff-big', #(NC) (ref http://www.asmail.be/msg0055371937.html)
  399.    'Canon 1D RAW' => 'image/x-raw', # (uses .TIF file extension)
  400.     CR2  => 'image/x-raw',
  401.     CRW  => 'image/x-raw',
  402.     DCR  => 'image/x-raw',
  403.     DFONT=> 'application/x-dfont',
  404.     DICM => 'application/dicom',
  405.     DIVX => 'video/divx',
  406.     DJVU => 'image/vnd.djvu',
  407.     DNG  => 'image/x-raw',
  408.     DOC  => 'application/msword',
  409.     DOCM => 'application/vnd.ms-word.document.macroEnabled',
  410.     DOCX => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
  411.     DOT  => 'application/msword',
  412.     DOTM => 'application/vnd.ms-word.template.macroEnabledTemplate',
  413.     DOTX => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
  414.     EIP  => 'application/x-captureone', #(NC)
  415.     EPS  => 'application/postscript',
  416.     ERF  => 'image/x-raw',
  417.     EXE  => 'application/octet-stream',
  418.     FLA  => 'application/vnd.adobe.fla',
  419.     FLAC => 'audio/flac',
  420.     FLV  => 'video/x-flv',
  421.     Font => 'application/x-font-type1', # covers PFA, PFB and PFM (not sure about PFM)
  422.     FPX  => 'image/vnd.fpx',
  423.     GIF  => 'image/gif',
  424.     GZIP => 'application/x-gzip',
  425.     HDP  => 'image/vnd.ms-photo',
  426.     HTML => 'text/html',
  427.     ICC  => 'application/vnd.iccprofile',
  428.     IIQ  => 'image/x-raw',
  429.     IND  => 'application/x-indesign',
  430.     ITC  => 'application/itunes',
  431.     JNG  => 'image/jng',
  432.     JP2  => 'image/jp2',
  433.     JPEG => 'image/jpeg',
  434.     JPM  => 'image/jpm',
  435.     JPX  => 'image/jpx',
  436.     K25  => 'image/x-raw',
  437.     KDC  => 'image/x-raw',
  438.     LNK  => 'application/octet-stream',
  439.     M2T  => 'video/mpeg',
  440.     M2TS => 'video/m2ts',
  441.     MEF  => 'image/x-raw',
  442.     MIE  => 'application/x-mie',
  443.     MIFF => 'application/x-magick-image',
  444.     MNG  => 'video/mng',
  445.     MOS  => 'image/x-raw',
  446.     MOV  => 'video/quicktime',
  447.     MP3  => 'audio/mpeg',
  448.     MP4  => 'video/mp4',
  449.     MPC  => 'audio/x-musepack',
  450.     MPEG => 'video/mpeg',
  451.     MRW  => 'image/x-raw',
  452.     NEF  => 'image/x-raw',
  453.     NRW  => 'image/x-raw',
  454.     OGG  => 'audio/x-ogg',
  455.     ORF  => 'image/x-raw',
  456.     OTF  => 'application/x-font-otf',
  457.     PBM  => 'image/x-portable-bitmap',
  458.     PDF  => 'application/pdf',
  459.     PEF  => 'image/x-raw',
  460.     PGM  => 'image/x-portable-graymap',
  461.     PICT => 'image/pict',
  462.     PLIST=> 'application/xml',
  463.     PNG  => 'image/png',
  464.     POT  => 'application/vnd.ms-powerpoint',
  465.     POTM => 'application/vnd.ms-powerpoint.template.macroEnabled',
  466.     POTX => 'application/vnd.openxmlformats-officedocument.presentationml.template',
  467.     PPM  => 'image/x-portable-pixmap',
  468.     PPS  => 'application/vnd.ms-powerpoint',
  469.     PPSM => 'application/vnd.ms-powerpoint.slideshow.macroEnabled',
  470.     PPSX => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow',
  471.     PPT  => 'application/vnd.ms-powerpoint',
  472.     PPTM => 'application/vnd.ms-powerpoint.presentation.macroEnabled',
  473.     PPTX => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
  474.     PS   => 'application/postscript',
  475.     PSD  => 'application/photoshop',
  476.     PSP  => 'image/x-paintshoppro', #(NC)
  477.     QTIF => 'image/x-quicktime',
  478.     RA   => 'audio/x-pn-realaudio',
  479.     RAF  => 'image/x-raw',
  480.     RAM  => 'audio/x-pn-realaudio',
  481.     RAW  => 'image/x-raw',
  482.     RM   => 'application/vnd.rn-realmedia',
  483.     RMVB => 'application/vnd.rn-realmedia-vbr',
  484.     RPM  => 'audio/x-pn-realaudio-plugin',
  485.     RSRC => 'application/ResEdit',
  486.     RV   => 'video/vnd.rn-realvideo',
  487.     RW2  => 'image/x-raw',
  488.     RWL  => 'image/x-raw',
  489.     RWZ  => 'image/x-rawzor', #(duplicated in Rawzor.pm)
  490.     SR2  => 'image/x-raw',
  491.     SRF  => 'image/x-raw',
  492.     SRW  => 'image/x-raw',
  493.     SVG  => 'image/svg+xml',
  494.     SWF  => 'application/x-shockwave-flash',
  495.     THMX => 'application/vnd.ms-officetheme',
  496.     TIFF => 'image/tiff',
  497.     TTC  => 'application/x-font-ttf',
  498.     TTF  => 'application/x-font-ttf',
  499.     WAV  => 'audio/x-wav',
  500.     WDP  => 'image/vnd.ms-photo',
  501.     WMA  => 'audio/x-ms-wma',
  502.     WMV  => 'video/x-ms-wmv',
  503.     X3F  => 'image/x-raw',
  504.     XLA  => 'application/vnd.ms-excel',
  505.     XLAM => 'application/vnd.ms-excel.addin.macroEnabled',
  506.     XLS  => 'application/vnd.ms-excel',
  507.     XLSB => 'application/vnd.ms-excel.sheet.binary.macroEnabled',
  508.     XLSM => 'application/vnd.ms-excel.sheet.macroEnabled',
  509.     XLSX => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
  510.     XLT  => 'application/vnd.ms-excel',
  511.     XLTM => 'application/vnd.ms-excel.template.macroEnabled',
  512.     XLTX => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
  513.     XML  => 'application/xml',
  514.     XMP  => 'application/rdf+xml',
  515.     ZIP  => 'application/zip',
  516. );
  517.  
  518. # module names for processing routines of each file type
  519. # (missing entries have same module name as file type)
  520. my %moduleName = (
  521.     BTF  => 'BigTIFF',
  522.     CRW  => 'CanonRaw',
  523.     DICM => 'DICOM',
  524.     COS  => 'CaptureOne',
  525.     DOCX => 'OOXML',
  526.     EPS  => 'PostScript',
  527.     EXIF => '',     # (Image::ExifTool)
  528.     ICC  => 'ICC_Profile',
  529.     IND  => 'InDesign',
  530.     FLV  => 'Flash',
  531.     FPX  => 'FlashPix',
  532.     GZIP => 'ZIP',
  533.     JP2  => 'Jpeg2000',
  534.     JPEG => '',     # (Image::ExifTool)
  535.   # MODD => 'XML',
  536.     MOV  => 'QuickTime',
  537.     MP3  => 'ID3',
  538.     MRW  => 'MinoltaRaw',
  539.     OGG  => 'Vorbis',
  540.     ORF  => 'Olympus',
  541.   # PLIST=> 'XML',
  542.     PS   => 'PostScript',
  543.     PSD  => 'Photoshop',
  544.     QTIF => 'QuickTime',
  545.     RAF  => 'FujiFilm',
  546.     RAW  => 'KyoceraRaw',
  547.     RWZ  => 'Rawzor',
  548.     SWF  => 'Flash',
  549.     TIFF => '',     # (Image::ExifTool)
  550.     VRD  => 'CanonVRD',
  551.     X3F  => 'SigmaRaw',
  552. );
  553.  
  554. # quick "magic number" file test used to avoid loading module unnecessarily:
  555. # - regular expression evaluated on first 1024 bytes of file
  556. # - must match beginning at first byte in file
  557. # - this test must not be more stringent than module logic
  558. %magicNumber = (
  559.     AIFF => '(FORM....AIF[FC]|AT&TFORM)',
  560.     APE  => '(MAC |APETAGEX|ID3)',
  561.     ASF  => '\x30\x26\xb2\x75\x8e\x66\xcf\x11\xa6\xd9\x00\xaa\x00\x62\xce\x6c',
  562.     BMP  => 'BM',
  563.     BTF  => '(II\x2b\0|MM\0\x2b)',
  564.     CRW  => '(II|MM).{4}HEAP(CCDR|JPGM)',
  565.     DICM => '(.{128}DICM|\0[\x02\x04\x06\x08]\0[\0-\x20]|[\x02\x04\x06\x08]\0[\0-\x20]\0)',
  566.     DOCX => 'PK\x03\x04',
  567.     EPS  => '(%!PS|%!Ad|\xc5\xd0\xd3\xc6)',
  568.     EXE  => '(MZ|\xca\xfe\xba\xbe|\xfe\xed\xfa[\xce\xcf]|[\xce\xcf]\xfa\xed\xfe|Joy!peff|\x7fELF|#!\s*/\S*bin/|!<arch>\x0a)',
  569.     EXIF => '(II\x2a\0|MM\0\x2a)',
  570.     FLAC => '(fLaC|ID3)',
  571.     FLV  => 'FLV\x01',
  572.     Font => '((\0\x01\0\0|OTTO|true|typ1)[\0\x01]|ttcf\0[\x01\x02]\0\0|' .
  573.              '(.{6})?%!(PS-AdobeFont-|FontType1-)|Start(Comp|Master)?FontMetrics|\0[\x01\x02])',
  574.     FPX  => '\xd0\xcf\x11\xe0\xa1\xb1\x1a\xe1',
  575.     GIF  => 'GIF8[79]a',
  576.     GZIP => '\x1f\x8b\x08',
  577.     HTML => '(?i)<(!DOCTYPE\s+HTML|HTML|\?xml)', # (case insensitive)
  578.     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}',
  579.     IND  => '\x06\x06\xed\xf5\xd8\x1d\x46\xe5\xbd\x31\xef\xe7\xfe\x74\xb7\x1d',
  580.     ITC  => '.{4}itch',
  581.     JP2  => '\0\0\0\x0cjP(  |\x1a\x1a)\x0d\x0a\x87\x0a',
  582.     JPEG => '\xff\xd8\xff',
  583.     LNK  => '.{4}\x01\x14\x02\0{5}\xc0\0{6}\x46',
  584.     M2TS => '(....)?\x47',
  585.     MIE  => '~[\x10\x18]\x04.0MIE',
  586.     MIFF => 'id=ImageMagick',
  587.     MOV  => '.{4}(free|skip|wide|ftyp|pnot|PICT|pict|moov|mdat|junk|uuid)',
  588.   # MP3  =>  difficult to rule out
  589.     MPC  => '(MP\+|ID3)',
  590.     MPEG => '\0\0\x01[\xb0-\xbf]',
  591.     MRW  => '\0MR[MI]',
  592.     OGG  => '(OggS|ID3)',
  593.     ORF  => '(II|MM)',
  594.     PDF  => '%PDF-\d+\.\d+',
  595.     PICT => '(.{10}|.{522})(\x11\x01|\x00\x11)',
  596.     PNG  => '(\x89P|\x8aM|\x8bJ)NG\r\n\x1a\n',
  597.     PPM  => 'P[1-6]\s+',
  598.     PS   => '(%!PS|%!Ad|\xc5\xd0\xd3\xc6)',
  599.     PSP  => 'Paint Shop Pro Image File\x0a\x1a\0{5}',
  600.     PSD  => '8BPS\0[\x01\x02]',
  601.     QTIF => '.{4}(idsc|idat|iicc)',
  602.     RAF  => 'FUJIFILM',
  603.     RAW  => '(.{25}ARECOYK|II|MM)',
  604.     Real => '(\.RMF|\.ra\xfd|pnm://|rtsp://|http://)',
  605.     RIFF => 'RIFF',
  606.     RSRC => '(....)?\0\0\x01\0',
  607.     # (don't be too restrictive for RW2/RWL -- how does magic number change for big-endian?)
  608.     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)
  609.     RWL  => '(II|MM)', #(ditto)
  610.     RWZ  => 'rawzor',
  611.     SWF  => '[FC]WS[^\0]',
  612.     TIFF => '(II|MM)', # don't test magic number (some raw formats are different)
  613.     VRD  => 'CANON OPTIONAL DATA\0',
  614.     X3F  => 'FOVb',
  615.     XMP  => '\0{0,3}(\xfe\xff|\xff\xfe|\xef\xbb\xbf)?\0{0,3}<',
  616.     ZIP  => 'PK\x03\x04',
  617. );
  618.  
  619. # default group priority for writing
  620. my @defaultWriteGroups = qw(EXIF IPTC XMP MakerNotes Photoshop ICC_Profile CanonVRD);
  621.  
  622. # group hash for ExifTool-generated tags
  623. my %allGroupsExifTool = ( 0 => 'ExifTool', 1 => 'ExifTool', 2 => 'ExifTool' );
  624.  
  625. # special tag names (not used for tag info)
  626. my %specialTags = (
  627.     TABLE_NAME=>1, SHORT_NAME=>1, PROCESS_PROC=>1, WRITE_PROC=>1, CHECK_PROC=>1,
  628.     GROUPS=>1, FORMAT=>1, FIRST_ENTRY=>1, TAG_PREFIX=>1, PRINT_CONV=>1,
  629.     WRITABLE=>1, TABLE_DESC=>1, NOTES=>1, IS_OFFSET=>1, EXTRACT_UNKNOWN=>1,
  630.     NAMESPACE=>1, PREFERRED=>1, SRC_TABLE=>1, PRIORITY=>1, WRITE_GROUP=>1,
  631.     LANG_INFO=>1, VARS=>1, DATAMEMBER=>1, SET_GROUP1=>1,
  632. );
  633.  
  634. # lookup for valid character set names (keys are all lower case)
  635. %charsetName = (
  636.     #   Charset setting                       alias(es)
  637.     # -------------------------   --------------------------------------------
  638.     utf8        => 'UTF8',        cp65001 => 'UTF8', 'utf-8' => 'UTF8',
  639.     latin       => 'Latin',       cp1252  => 'Latin', latin1 => 'Latin',
  640.     latin2      => 'Latin2',      cp1250  => 'Latin2',
  641.     cyrillic    => 'Cyrillic',    cp1251  => 'Cyrillic', russian => 'Cyrillic',
  642.     greek       => 'Greek',       cp1253  => 'Greek',
  643.     turkish     => 'Turkish',     cp1254  => 'Turkish',
  644.     hebrew      => 'Hebrew',      cp1255  => 'Hebrew',
  645.     arabic      => 'Arabic',      cp1256  => 'Arabic',
  646.     baltic      => 'Baltic',      cp1257  => 'Baltic',
  647.     vietnam     => 'Vietnam',     cp1258  => 'Vietnam',
  648.     thai        => 'Thai',        cp874   => 'Thai',
  649.     macroman    => 'MacRoman',    cp10000 => 'MacRoman', mac => 'MacRoman', roman => 'MacRoman',
  650.     maclatin2   => 'MacLatin2',   cp10029 => 'MacLatin2',
  651.     maccyrillic => 'MacCyrillic', cp10007 => 'MacCyrillic',
  652.     macgreek    => 'MacGreek',    cp10006 => 'MacGreek',
  653.     macturkish  => 'MacTurkish',  cp10081 => 'MacTurkish',
  654.     macromanian => 'MacRomanian', cp10010 => 'MacRomanian',
  655.     maciceland  => 'MacIceland',  cp10079 => 'MacIceland',
  656.     maccroatian => 'MacCroatian', cp10082 => 'MacCroatian',
  657. );
  658.  
  659. # headers for various segment types
  660. $exifAPP1hdr = "Exif\0\0";
  661. $xmpAPP1hdr = "http://ns.adobe.com/xap/1.0/\0";
  662. $xmpExtAPP1hdr = "http://ns.adobe.com/xmp/extension/\0";
  663. $psAPP13hdr = "Photoshop 3.0\0";
  664. $psAPP13old = 'Adobe_Photoshop2.5:';
  665.  
  666. sub DummyWriteProc { return 1; }
  667.  
  668. # lookup for user lenses defined in @Image::ExifTool::UserDefined::Lenses
  669. %Image::ExifTool::userLens = ( );
  670.  
  671. # queued plug-in tags to add to lookup
  672. @Image::ExifTool::pluginTags = ( );
  673. %Image::ExifTool::pluginTags = ( );
  674.  
  675. # tag information for preview image -- this should be used for all
  676. # PreviewImage tags so they are handled properly when reading/writing
  677. %Image::ExifTool::previewImageTagInfo = (
  678.     Name => 'PreviewImage',
  679.     Writable => 'undef',
  680.     # a value of 'none' is ok...
  681.     WriteCheck => '$val eq "none" ? undef : $self->CheckImage(\$val)',
  682.     DataTag => 'PreviewImage',
  683.     # accept either scalar or scalar reference
  684.     RawConv => '$self->ValidateImage(ref $val ? $val : \$val, $tag)',
  685.     # we allow preview image to be set to '', but we don't want a zero-length value
  686.     # in the IFD, so set it temorarily to 'none'.  Note that the length is <= 4,
  687.     # so this value will fit in the IFD so the preview fixup won't be generated.
  688.     ValueConvInv => '$val eq "" and $val="none"; $val',
  689. );
  690.  
  691. # extra tags that aren't truly EXIF tags, but are generated by the script
  692. # Note: any tag in this list with a name corresponding to a Group0 name is
  693. #       used to write the entire corresponding directory as a block.
  694. %Image::ExifTool::Extra = (
  695.     GROUPS => { 0 => 'File', 1 => 'File', 2 => 'Image' },
  696.     VARS => { NO_ID => 1 }, # tag ID's aren't meaningful for these tags
  697.     WRITE_PROC => \&DummyWriteProc,
  698.     Error   => { Priority => 0, Groups => \%allGroupsExifTool },
  699.     Warning => { Priority => 0, Groups => \%allGroupsExifTool },
  700.     Comment => {
  701.         Notes => 'comment embedded in JPEG, GIF89a or PPM/PGM/PBM image',
  702.         Writable => 1,
  703.         WriteGroup => 'Comment',
  704.         Priority => 0,  # to preserve order of JPEG COM segments
  705.     },
  706.     Directory => {
  707.         Groups => { 1 => 'System' },
  708.         Writable => 1,
  709.         Protected => 1,
  710.         # translate backslashes in directory names and add trailing '/'
  711.         ValueConvInv => '$_=$val; tr/\\\\/\//; m{[^/]$} and $_ .= "/"; $_',
  712.     },
  713.     FileName => {
  714.         Groups => { 1 => 'System' },
  715.         Writable => 1,
  716.         Protected => 1,
  717.         Notes => q{
  718.             may be written with a full path name to set FileName and Directory in one
  719.             operation
  720.         },
  721.         ValueConvInv => '$val=~tr/\\\\/\//; $val',
  722.     },
  723.     FileSize => {
  724.         Groups => { 1 => 'System' },
  725.         PrintConv => \&ConvertFileSize,
  726.     },
  727.     ResourceForkSize => {
  728.         Groups => { 1 => 'System' },
  729.         Notes => q{
  730.             [Mac OS only] size of the file's resource fork if it contains data.  If this
  731.             tag is generated the ExtractEmbedded option may be used to extract
  732.             resource-fork information as a sub-document
  733.         },
  734.         PrintConv => \&ConvertFileSize,
  735.     },
  736.     FileType    => { },
  737.     FileModifyDate => {
  738.         Description => 'File Modification Date/Time',
  739.         Notes => 'the filesystem modification time',
  740.         Groups => { 1 => 'System', 2 => 'Time' },
  741.         Writable => 1,
  742.         # all pseudo-tags must be protected so -tagsfromfile fails with
  743.         # unrecognized files unless a pseudo tag is specified explicitly
  744.         Protected => 1,
  745.         Shift => 'Time',
  746.         ValueConv => 'ConvertUnixTime($val,1)',
  747.         ValueConvInv => 'GetUnixTime($val,1)',
  748.         PrintConv => '$self->ConvertDateTime($val)',
  749.         PrintConvInv => '$self->InverseDateTime($val)',
  750.     },
  751.     FilePermissions => {
  752.         Groups => { 1 => 'System' },
  753.         Notes => q{
  754.             r=read, w=write and x=execute permissions for the file owner, group and
  755.             others.  The ValueConv value is an octal number so bit test operations on
  756.             this value should be done in octal, ie. "oct($filePermissions) & 0200"
  757.         },
  758.         ValueConv => 'sprintf("%.3o", $val & 0777)',
  759.         PrintConv => sub {
  760.             my ($mask, $str, $val) = (0400, '', oct(shift));
  761.             while ($mask) {
  762.                 foreach (qw(r w x)) {
  763.                     $str .= $val & $mask ? $_ : '-';
  764.                     $mask >>= 1;
  765.                 }
  766.             }
  767.             return $str;
  768.         },
  769.     },
  770.     MIMEType    => { },
  771.     ImageWidth  => { },
  772.     ImageHeight => { },
  773.     XResolution => { },
  774.     YResolution => { },
  775.     MaxVal      => { }, # max pixel value in PPM or PGM image
  776.     EXIF => {
  777.         Notes => 'the full EXIF data block from JPEG, PNG, JP2, MIE and MIFF images',
  778.         Groups => { 0 => 'EXIF', 1 => 'EXIF' },
  779.         Flags => ['Writable' ,'Protected', 'Binary'],
  780.         WriteCheck => q{
  781.             return undef if $val =~ /^(II\x2a\0|MM\0\x2a)/;
  782.             return 'Invalid EXIF data';
  783.         },
  784.     },
  785.     ICC_Profile => {
  786.         Notes => 'the full ICC_Profile data block',
  787.         Groups => { 0 => 'ICC_Profile', 1 => 'ICC_Profile' },
  788.         Flags => ['Writable' ,'Protected', 'Binary'],
  789.         WriteCheck => q{
  790.             require Image::ExifTool::ICC_Profile;
  791.             return Image::ExifTool::ICC_Profile::ValidateICC(\$val);
  792.         },
  793.     },
  794.     XMP => {
  795.         Notes => 'the full XMP data block',
  796.         Groups => { 0 => 'XMP', 1 => 'XMP' },
  797.         Flags => ['Writable', 'Protected', 'Binary'],
  798.         Priority => 0,  # so main xmp (which usually comes first) takes priority
  799.         WriteCheck => q{
  800.             require Image::ExifTool::XMP;
  801.             return Image::ExifTool::XMP::CheckXMP($self, $tagInfo, \$val);
  802.         },
  803.     },
  804.     CanonVRD => {
  805.         Notes => 'the full Canon DPP VRD trailer block',
  806.         Groups => { 0 => 'CanonVRD', 1 => 'CanonVRD' },
  807.         Flags => ['Writable' ,'Protected', 'Binary'],
  808.         Permanent => 0, # (this is 1 by default for MakerNotes tags)
  809.         WriteCheck => q{
  810.             return undef if $val =~ /^CANON OPTIONAL DATA\0/;
  811.             return 'Invalid CanonVRD data';
  812.         },
  813.     },
  814.     CurrentIPTCDigest => {
  815.         Notes => q{
  816.             MD5 digest of IPTC data.  All zeros if IPTC exists but Digest::MD5 is not
  817.             installed.  Only calculated for IPTC in the standard location as specified
  818.             by the L<MWG|http://www.metadataworkinggroup.org/>
  819.         },
  820.         ValueConv => 'unpack("H*", $val)',
  821.     },
  822.     PreviewImage => {
  823.         Writable => 1,
  824.         WriteCheck => '$self->CheckImage(\$val)',
  825.         # accept either scalar or scalar reference
  826.         RawConv => '$self->ValidateImage(ref $val ? $val : \$val, $tag)',
  827.     },
  828.     Encryption  => { }, # PDF encryption filter
  829.     ExifByteOrder => {
  830.         Writable => 1,
  831.         Notes => 'only writable for newly created EXIF segments',
  832.         PrintConv => {
  833.             II => 'Little-endian (Intel, II)',
  834.             MM => 'Big-endian (Motorola, MM)',
  835.         },
  836.     },
  837.     ExifUnicodeByteOrder => {
  838.         Writable => 1,
  839.         Notes => q{
  840.             the EXIF specification is particularly vague about the byte ordering for
  841.             Unicode text, and different applications use different conventions.  By
  842.             default ExifTool writes Unicode text in EXIF byte order, but this write-only
  843.             tag may be used to force a specific byte order
  844.         },
  845.         PrintConv => {
  846.             II => 'Little-endian (Intel, II)',
  847.             MM => 'Big-endian (Motorola, MM)',
  848.         },
  849.     },
  850.     ExifToolVersion => {
  851.         Description => 'ExifTool Version Number',
  852.         Groups => \%allGroupsExifTool,
  853.     },
  854.     RAFVersion => { },
  855.     JPEGDigest => {
  856.         Notes => q{
  857.             an MD5 digest of the JPEG quantization tables is combined with the component
  858.             sub-sampling values to generate the value of this tag.  The result is
  859.             compared to known values in an attempt to deduce the originating software
  860.             based only on the JPEG image data.  For performance reasons, this tag is
  861.             generated only if specifically requested
  862.         },
  863.     },
  864.     Now => {
  865.         Groups => { 0 => 'ExifTool', 1 => 'ExifTool', 2 => 'Time' },
  866.         Notes => q{
  867.             the current date/time.  Useful when setting the tag values, ie.
  868.             C<"-modifydate<now">.  Not generated unless specifically requested
  869.         },
  870.         ValueConv => sub {
  871.             my $time = shift;
  872.             my @tm = localtime $time;
  873.             my $tz = Image::ExifTool::TimeZoneString(\@tm, $time);
  874.             sprintf("%4d:%.2d:%.2d %.2d:%.2d:%.2d%s", $tm[5]+1900, $tm[4]+1, $tm[3],
  875.                     $tm[2], $tm[1], $tm[0], $tz);
  876.         },
  877.         PrintConv => '$self->ConvertDateTime($val)',
  878.     },
  879.     ID3Size     => { },
  880.     Geotag => {
  881.         Writable => 1,
  882.         AllowGroup => '(exif|gps|xmp|xmp-exif)',
  883.         Notes => q{
  884.             this write-only tag is used to define the GPS track log data or track log
  885.             file name.  Currently supported track log formats are GPX, NMEA RMC/GGA/GLL,
  886.             KML, IGC, Garmin XML and TCX, and Magellan PMGNTRK
  887.         },
  888.         ValueConvInv => q{
  889.             require Image::ExifTool::Geotag;
  890.             # always warn because this tag is never set (warning is "\n" on success)
  891.             my $result = Image::ExifTool::Geotag::LoadTrackLog($self, $val);
  892.             return '' if not defined $result;   # deleting geo tags
  893.             return $result if ref $result;      # geotag data hash reference
  894.             warn "$result\n";                   # error string
  895.         },
  896.     },
  897.     Geotime => {
  898.         Writable => 1,
  899.         AllowGroup => '(exif|gps|xmp|xmp-exif)',
  900.         Notes => q{
  901.             this write-only tag is used to define a date/time for interpolating a
  902.             position in the GPS track specified by the Geotag tag.  Writing this tag
  903.             causes the following 8 tags to be written:  GPSLatitude, GPSLatitudeRef,
  904.             GPSLongitude, GPSLongitudeRef, GPSAltitude, GPSAltitudeRef, GPSDateStamp and
  905.             GPSTimeStamp.  The local system timezone is assumed if the date/time value
  906.             does not contain a timezone.  May be deleted to delete associated GPS tags.
  907.             A group name of 'EXIF' or 'XMP' may be specified to write or delete only
  908.             EXIF or XMP GPS tags.  The value of Geotag must be assigned before this tag
  909.         },
  910.         DelCheck => q{
  911.             require Image::ExifTool::Geotag;
  912.             # delete associated tags
  913.             Image::ExifTool::Geotag::SetGeoValues($self, undef, $wantGroup);
  914.         },
  915.         ValueConvInv => q{
  916.             require Image::ExifTool::Geotag;
  917.             warn Image::ExifTool::Geotag::SetGeoValues($self, $val, $wantGroup) . "\n";
  918.             return undef;
  919.         },
  920.     },
  921.     Geosync => {
  922.         Writable => 1,
  923.         AllowGroup => '(exif|gps|xmp|xmp-exif)',
  924.         Shift => 'Time', # enables "+=" syntax as well as "=+"
  925.         Notes => q{
  926.             this write-only tag specifies a time difference to add to Geotime for
  927.             synchronization with the GPS clock.  For example, set this to "-12" if the
  928.             camera clock is 12 seconds faster than GPS time.  Input format is
  929.             "[+-][[[DD ]HH:]MM:]SS[.ss]".  Must be set before Geotime to be effective.
  930.             Additional features allow calculation of time differences and time drifts,
  931.             and extraction of synchronization times from image files. See the
  932.             L<geotagging documentation|../geotag.html> for details
  933.         },
  934.         ValueConvInv => q{
  935.             require Image::ExifTool::Geotag;
  936.             return Image::ExifTool::Geotag::ConvertGeosync($self, $val);
  937.         },
  938.     },
  939. );
  940.  
  941. # YCbCrSubSampling values (used by JPEG SOF, EXIF and XMP)
  942. %Image::ExifTool::JPEG::yCbCrSubSampling = (
  943.     '1 1' => 'YCbCr4:4:4 (1 1)', #PH
  944.     '2 1' => 'YCbCr4:2:2 (2 1)', #14 in Exif.pm
  945.     '2 2' => 'YCbCr4:2:0 (2 2)', #14 in Exif.pm
  946.     '4 1' => 'YCbCr4:1:1 (4 1)', #14 in Exif.pm
  947.     '4 2' => 'YCbCr4:1:0 (4 2)', #PH
  948.     '1 2' => 'YCbCr4:4:0 (1 2)', #PH
  949.     '1 4' => 'YCbCr4:4:1 (1 4)', #JD
  950.     '2 4' => 'YCbCr4:2:1 (2 4)', #JD
  951. );
  952.  
  953. # define common JPEG segments here to avoid overhead of loading JPEG module
  954.  
  955. # JPEG SOF (start of frame) tags
  956. # (ref http://www.w3.org/Graphics/JPEG/itu-t81.pdf)
  957. %Image::ExifTool::JPEG::SOF = (
  958.     GROUPS => { 0 => 'File', 1 => 'File', 2 => 'Image' },
  959.     NOTES => 'This information is extracted from the JPEG Start Of Frame segment.',
  960.     VARS => { NO_ID => 1 }, # tag ID's aren't meaningful for these tags
  961.     EncodingProcess => {
  962.         PrintHex => 1,
  963.         PrintConv => {
  964.             0x0 => 'Baseline DCT, Huffman coding',
  965.             0x1 => 'Extended sequential DCT, Huffman coding',
  966.             0x2 => 'Progressive DCT, Huffman coding',
  967.             0x3 => 'Lossless, Huffman coding',
  968.             0x5 => 'Sequential DCT, differential Huffman coding',
  969.             0x6 => 'Progressive DCT, differential Huffman coding',
  970.             0x7 => 'Lossless, Differential Huffman coding',
  971.             0x9 => 'Extended sequential DCT, arithmetic coding',
  972.             0xa => 'Progressive DCT, arithmetic coding',
  973.             0xb => 'Lossless, arithmetic coding',
  974.             0xd => 'Sequential DCT, differential arithmetic coding',
  975.             0xe => 'Progressive DCT, differential arithmetic coding',
  976.             0xf => 'Lossless, differential arithmetic coding',
  977.         }
  978.     },
  979.     BitsPerSample    => { },
  980.     ImageHeight      => { },
  981.     ImageWidth       => { },
  982.     ColorComponents  => { },
  983.     YCbCrSubSampling => {
  984.         Notes => 'calculated from components table',
  985.         PrintConv => \%Image::ExifTool::JPEG::yCbCrSubSampling,
  986.     },
  987. );
  988.  
  989. # JPEG JFIF APP0 definitions
  990. %Image::ExifTool::JFIF::Main = (
  991.     PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
  992.     WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
  993.     CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
  994.     GROUPS => { 0 => 'JFIF', 1 => 'JFIF', 2 => 'Image' },
  995.     DATAMEMBER => [ 2, 3, 5 ],
  996.     0 => {
  997.         Name => 'JFIFVersion',
  998.         Format => 'int8u[2]',
  999.         PrintConv => 'sprintf("%d.%.2d", split(" ",$val))',
  1000.     },
  1001.     2 => {
  1002.         Name => 'ResolutionUnit',
  1003.         Writable => 1,
  1004.         RawConv => '$$self{JFIFResolutionUnit} = $val',
  1005.         PrintConv => {
  1006.             0 => 'None',
  1007.             1 => 'inches',
  1008.             2 => 'cm',
  1009.         },
  1010.         Priority => -1,
  1011.     },
  1012.     3 => {
  1013.         Name => 'XResolution',
  1014.         Format => 'int16u',
  1015.         Writable => 1,
  1016.         Priority => -1,
  1017.         RawConv => '$$self{JFIFXResolution} = $val',
  1018.     },
  1019.     5 => {
  1020.         Name => 'YResolution',
  1021.         Format => 'int16u',
  1022.         Writable => 1,
  1023.         Priority => -1,
  1024.         RawConv => '$$self{JFIFYResolution} = $val',
  1025.     },
  1026. );
  1027. %Image::ExifTool::JFIF::Extension = (
  1028.     GROUPS => { 0 => 'JFIF', 1 => 'JFIF', 2 => 'Image' },
  1029.     0x10 => {
  1030.         Name => 'ThumbnailImage',
  1031.         RawConv => '$self->ValidateImage(\$val,$tag)',
  1032.     },
  1033. );
  1034.  
  1035. # composite tags (accumulation of all Composite tag tables)
  1036. %Image::ExifTool::Composite = (
  1037.     GROUPS => { 0 => 'Composite', 1 => 'Composite' },
  1038.     TABLE_NAME => 'Image::ExifTool::Composite',
  1039.     SHORT_NAME => 'Composite',
  1040.     VARS => { NO_ID => 1 }, # want empty tagID's for composite tags
  1041.     WRITE_PROC => \&DummyWriteProc,
  1042. );
  1043.  
  1044. # static private ExifTool variables
  1045.  
  1046. %allTables = ( );   # list of all tables loaded (except composite tags)
  1047. @tableOrder = ( );  # order the tables were loaded
  1048.  
  1049. #------------------------------------------------------------------------------
  1050. # Warning handler routines (warning string stored in $evalWarning)
  1051. #
  1052. # Set warning message
  1053. # Inputs: 0) warning string (undef to reset warning)
  1054. sub SetWarning($) { $evalWarning = $_[0]; }
  1055.  
  1056. # Get warning message
  1057. sub GetWarning()  { return $evalWarning; }
  1058.  
  1059. # Clean unnecessary information (line number, LF) from warning
  1060. # Inputs: 0) warning string or undef to use current warning
  1061. # Returns: cleaned warning
  1062. sub CleanWarning(;$)
  1063. {
  1064.     my $str = shift;
  1065.     unless (defined $str) {
  1066.         return undef unless defined $evalWarning;
  1067.         $str = $evalWarning;
  1068.     }
  1069.     $str = $1 if $str =~ /(.*) at /s;
  1070.     $str =~ s/\s+$//s;
  1071.     return $str;
  1072. }
  1073.  
  1074. #==============================================================================
  1075. # New - create new ExifTool object
  1076. # Inputs: 0) reference to exiftool object or ExifTool class name
  1077. sub new
  1078. {
  1079.     local $_;
  1080.     my $that = shift;
  1081.     my $class = ref($that) || $that || 'Image::ExifTool';
  1082.     my $self = bless {}, $class;
  1083.  
  1084.     # make sure our main Exif tag table has been loaded
  1085.     GetTagTable("Image::ExifTool::Exif::Main");
  1086.  
  1087.     $self->ClearOptions();      # create default options hash
  1088.     $self->{VALUE} = { };       # must initialize this for warning messages
  1089.     $self->{DEL_GROUP} = { };   # list of groups to delete when writing
  1090.  
  1091.     # initialize our new groups for writing
  1092.     $self->SetNewGroups(@defaultWriteGroups);
  1093.  
  1094.     return $self;
  1095. }
  1096.  
  1097. #------------------------------------------------------------------------------
  1098. # ImageInfo - return specified information from image file
  1099. # Inputs: 0) [optional] ExifTool object reference
  1100. #         1) filename, file reference, or scalar data reference
  1101. #         2-N) list of tag names to find (or tag list reference or options reference)
  1102. # Returns: reference to hash of tag/value pairs (with "Error" entry on error)
  1103. # Notes:
  1104. #   - if no tags names are specified, the values of all tags are returned
  1105. #   - tags may be specified with leading '-' to exclude, or trailing '#' for ValueConv
  1106. #   - can pass a reference to list of tags to find, in which case the list will
  1107. #     be updated with the tags found in the proper case and in the specified order.
  1108. #   - can pass reference to hash specifying options
  1109. #   - returned tag values may be scalar references indicating binary data
  1110. #   - see ClearOptions() below for a list of options and their default values
  1111. # Examples:
  1112. #   use Image::ExifTool 'ImageInfo';
  1113. #   my $info = ImageInfo($file, 'DateTimeOriginal', 'ImageSize');
  1114. #    - or -
  1115. #   my $exifTool = new Image::ExifTool;
  1116. #   my $info = $exifTool->ImageInfo($file, \@tagList, {Sort=>'Group0'} );
  1117. sub ImageInfo($;@)
  1118. {
  1119.     local $_;
  1120.     # get our ExifTool object ($self) or create one if necessary
  1121.     my $self;
  1122.     if (ref $_[0] and UNIVERSAL::isa($_[0],'Image::ExifTool')) {
  1123.         $self = shift;
  1124.     } else {
  1125.         $self = new Image::ExifTool;
  1126.     }
  1127.     my %saveOptions = %{$self->{OPTIONS}};  # save original options
  1128.  
  1129.     # initialize file information
  1130.     $self->{FILENAME} = $self->{RAF} = undef;
  1131.  
  1132.     $self->ParseArguments(@_);              # parse our function arguments
  1133.     $self->ExtractInfo(undef);              # extract meta information from image
  1134.     my $info = $self->GetInfo(undef);       # get requested information
  1135.  
  1136.     $self->{OPTIONS} = \%saveOptions;       # restore original options
  1137.  
  1138.     return $info;   # return requested information
  1139. }
  1140.  
  1141. #------------------------------------------------------------------------------
  1142. # Get/set ExifTool options
  1143. # Inputs: 0) ExifTool object reference,
  1144. #         1) Parameter name, 2) Value to set the option
  1145. #         3-N) More parameter/value pairs
  1146. # Returns: original value of last option specified
  1147. sub Options($$;@)
  1148. {
  1149.     local $_;
  1150.     my $self = shift;
  1151.     my $options = $$self{OPTIONS};
  1152.     my $oldVal;
  1153.  
  1154.     while (@_) {
  1155.         my $param = shift;
  1156.         $oldVal = $$options{$param};
  1157.         last unless @_;
  1158.         my $newVal = shift;
  1159.         if ($param eq 'Lang') {
  1160.             # allow this to be set to undef to select the default language
  1161.             $newVal = $defaultLang unless defined $newVal;
  1162.             if ($newVal eq $defaultLang) {
  1163.                 $$options{$param} = $newVal;
  1164.                 delete $$self{CUR_LANG};
  1165.             # make sure the language is available
  1166.             } elsif (eval "require Image::ExifTool::Lang::$newVal") {
  1167.                 my $xlat = "Image::ExifTool::Lang::${newVal}::Translate";
  1168.                 no strict 'refs';
  1169.                 if (%$xlat) {
  1170.                     $$self{CUR_LANG} = \%$xlat;
  1171.                     $$options{$param} = $newVal;
  1172.                 }
  1173.             } # else don't change Lang
  1174.         } elsif ($param eq 'Exclude' and defined $newVal) {
  1175.             # clone Exclude list and expand shortcuts
  1176.             my @exclude;
  1177.             if (ref $newVal eq 'ARRAY') {
  1178.                 @exclude = @$newVal;
  1179.             } else {
  1180.                 @exclude = ($newVal);
  1181.             }
  1182.             ExpandShortcuts(\@exclude, 1);  # (also remove '#' suffix)
  1183.             $$options{$param} = \@exclude;
  1184.         } elsif ($param =~ /^Charset/ or $param eq 'IPTCCharset') {
  1185.             # only allow valid character sets to be set
  1186.             if ($newVal) {
  1187.                 my $charset = $charsetName{lc $newVal};
  1188.                 if ($charset) {
  1189.                     $$options{$param} = $charset;
  1190.                     # maintain backward-compatibility with old IPTCCharset option
  1191.                     $$options{CharsetIPTC} = $charset if $param eq 'IPTCCharset';
  1192.                 } else {
  1193.                     warn "Invalid Charset $newVal\n";
  1194.                 }
  1195.             }
  1196.         } else {
  1197.             if ($param eq 'Escape') {
  1198.                 # set ESCAPE_PROC
  1199.                 if (defined $newVal and $newVal eq 'XML') {
  1200.                     require Image::ExifTool::XMP;
  1201.                     $$self{ESCAPE_PROC} = \&Image::ExifTool::XMP::EscapeXML;
  1202.                 } elsif (defined $newVal and $newVal eq 'HTML') {
  1203.                     require Image::ExifTool::HTML;
  1204.                     $$self{ESCAPE_PROC} = \&Image::ExifTool::HTML::EscapeHTML;
  1205.                 } else {
  1206.                     delete $$self{ESCAPE_PROC};
  1207.                 }
  1208.             }
  1209.             $$options{$param} = $newVal;
  1210.         }
  1211.     }
  1212.     return $oldVal;
  1213. }
  1214.  
  1215. #------------------------------------------------------------------------------
  1216. # ClearOptions - set options to default values
  1217. # Inputs: 0) ExifTool object reference
  1218. sub ClearOptions($)
  1219. {
  1220.     local $_;
  1221.     my $self = shift;
  1222.  
  1223.     # create options hash with default values
  1224.     # (commented out options don't need initializing)
  1225.     # +-----------------------------------------------------+
  1226.     # ! DON'T FORGET!!  When adding any new option, must    !
  1227.     # ! decide how it is handled in SetNewValuesFromFile()  !
  1228.     # +-----------------------------------------------------+
  1229.     $self->{OPTIONS} = {
  1230.     #   Binary      => undef,   # flag to extract binary values even if tag not specified
  1231.     #   ByteOrder   => undef,   # default byte order when creating EXIF information
  1232.         Charset     => 'UTF8',  # character set for converting Unicode characters
  1233.         CharsetID3  => 'Latin', # internal ID3v1 character set
  1234.         CharsetIPTC => 'Latin', # fallback IPTC character set if no CodedCharacterSet
  1235.     #   Compact     => undef,   # compact XMP and IPTC data
  1236.         Composite   => 1,       # flag to calculate Composite tags
  1237.     #   Compress    => undef,   # flag to write new values as compressed if possible
  1238.     #   CoordFormat => undef,   # GPS lat/long coordinate format
  1239.     #   DateFormat  => undef,   # format for date/time
  1240.         Duplicates  => 1,       # flag to save duplicate tag values
  1241.     #   Escape      => undef,   # escape special characters
  1242.     #   Exclude     => undef,   # tags to exclude
  1243.     #   ExtractEmbedded =>undef,# flag to extract information from embedded documents
  1244.     #   FastScan    => undef,   # flag to avoid scanning for trailer
  1245.     #   FixBase     => undef,   # fix maker notes base offsets
  1246.     #   GeoMaxIntSecs => undef, # geotag maximum interpolation time (secs)
  1247.     #   GeoMaxExtSecs => undef, # geotag maximum extrapolation time (secs)
  1248.     #   GeoMaxHDOP  => undef,   # geotag maximum HDOP
  1249.     #   GeoMaxPDOP  => undef,   # geotag maximum PDOP
  1250.     #   GeoMinSats  => undef,   # geotag minimum satellites
  1251.     #   Group#      => undef,   # return tags for specified groups in family #
  1252.         HtmlDump    => 0,       # HTML dump (0-3, higher # = bigger limit)
  1253.     #   HtmlDumpBase => undef,  # base address for HTML dump
  1254.     #   IgnoreMinorErrors => undef, # ignore minor errors when reading/writing
  1255.         Lang        => $defaultLang,# localized language for descriptions etc
  1256.     #   LargeFileSupport => undef,  # flag indicating support of 64-bit file offsets
  1257.     #   List        => undef,   # extract lists of PrintConv values into arrays
  1258.         ListSep     => ', ',    # list item separator
  1259.     #   ListSplit   => undef,   # regex for splitting list-type tag values when writing
  1260.     #   MakerNotes  => undef,   # extract maker notes as a block
  1261.     #   MissingTagValue =>undef,# value for missing tags when expanded in expressions
  1262.         PrintConv   => 1,       # flag to enable print conversion
  1263.     #   ScanForXMP  => undef,   # flag to scan for XMP information in all files
  1264.         Sort        => 'Input', # order to sort found tags (Input, File, Alpha, Group#)
  1265.     #   StrictDate  => undef,   # flag to return undef for invalid date conversions
  1266.     #   Struct      => undef,   # return structures as hash references
  1267.         TextOut     => \*STDOUT,# file for Verbose/HtmlDump output
  1268.         Unknown     => 0,       # flag to get values of unknown tags (0-2)
  1269.         Verbose     => 0,       # print verbose messages (0-5, higher # = more verbose)
  1270.     };
  1271.     # keep necessary member variables in sync with options
  1272.     delete $$self{CUR_LANG};
  1273.     delete $$self{ESCAPE_PROC};
  1274.  
  1275.     # load user-defined default options
  1276.     if (%Image::ExifTool::UserDefined::Options) {
  1277.         foreach (keys %Image::ExifTool::UserDefined::Options) {
  1278.             $self->Options($_, $Image::ExifTool::UserDefined::Options{$_});
  1279.         }
  1280.     }
  1281. }
  1282.  
  1283. #------------------------------------------------------------------------------
  1284. # Extract meta information from image
  1285. # Inputs: 0) ExifTool object reference
  1286. #         1-N) Same as ImageInfo()
  1287. # Returns: 1 if this was a valid image, 0 otherwise
  1288. # Notes: pass an undefined value to avoid parsing arguments
  1289. # Internal 'ReEntry' option allows this routine to be called recursively
  1290. sub ExtractInfo($;@)
  1291. {
  1292.     local $_;
  1293.     my $self = shift;
  1294.     my $options = $self->{OPTIONS};     # pointer to current options
  1295.     my (%saveOptions, $reEntry, $rsize);
  1296.  
  1297.     # check for internal ReEntry option to allow recursive calls to ExtractInfo
  1298.     if (ref $_[1] eq 'HASH' and $_[1]{ReEntry} and
  1299.        (ref $_[0] eq 'SCALAR' or ref $_[0] eq 'GLOB'))
  1300.     {
  1301.         # save necessary members for restoring later
  1302.         $reEntry = {
  1303.             RAF       => $$self{RAF},
  1304.             PROCESSED => $$self{PROCESSED},
  1305.             EXIF_DATA => $$self{EXIF_DATA},
  1306.             EXIF_POS  => $$self{EXIF_POS},
  1307.             FILE_TYPE => $$self{FILE_TYPE},
  1308.         };
  1309.         $self->{RAF} = new File::RandomAccess($_[0]);
  1310.         $$self{PROCESSED} = { };
  1311.         delete $$self{EXIF_DATA};
  1312.         delete $$self{EXIF_POS};
  1313.     } else {
  1314.         if (defined $_[0] or $options->{HtmlDump}) {
  1315.             %saveOptions = %$options;       # save original options
  1316.     
  1317.             # require duplicates for html dump
  1318.             $self->Options(Duplicates => 1) if $options->{HtmlDump};
  1319.     
  1320.             if (defined $_[0]) {
  1321.                 # only initialize filename if called with arguments
  1322.                 $self->{FILENAME} = undef;  # name of file (or '' if we didn't open it)
  1323.                 $self->{RAF} = undef;       # RandomAccess object reference
  1324.     
  1325.                 $self->ParseArguments(@_);  # initialize from our arguments
  1326.             }
  1327.         }
  1328.         # initialize ExifTool object members
  1329.         $self->Init();
  1330.  
  1331.         delete $self->{MAKER_NOTE_FIXUP};   # fixup information for extracted maker notes
  1332.         delete $self->{MAKER_NOTE_BYTE_ORDER};
  1333.  
  1334.         # return our version number
  1335.         $self->FoundTag('ExifToolVersion', "$VERSION$RELEASE");
  1336.         $self->FoundTag('Now', time()) if $self->{REQ_TAG_LOOKUP}{now} or $self->{TAGS_FROM_FILE};
  1337.     }
  1338.     my $filename = $self->{FILENAME};   # image file name ('' if already open)
  1339.     my $raf = $self->{RAF};             # RandomAccess object
  1340.  
  1341.     local *EXIFTOOL_FILE;   # avoid clashes with global namespace
  1342.  
  1343.     my $realname = $filename;
  1344.     unless ($raf) {
  1345.         # save file name
  1346.         if (defined $filename and $filename ne '') {
  1347.             unless ($filename eq '-') {
  1348.                 # extract file name from pipe if necessary
  1349.                 $realname =~ /\|$/ and $realname =~ s/.*?"(.*?)".*/$1/;
  1350.                 my ($dir, $name);
  1351.                 if (eval 'require File::Basename') {
  1352.                     $dir = File::Basename::dirname($realname);
  1353.                     $name = File::Basename::basename($realname);
  1354.                 } else {
  1355.                     ($name = $realname) =~ tr/\\/\//;
  1356.                     # remove path
  1357.                     $dir = length($1) ? $1 : '/' if $name =~ s/(.*)\///;
  1358.                 }
  1359.                 $self->FoundTag('FileName', $name);
  1360.                 $self->FoundTag('Directory', $dir) if defined $dir and length $dir;
  1361.                 # get size of resource fork on Mac OS
  1362.                 $rsize = -s "$filename/rsrc" if $^O eq 'darwin' and not $$self{IN_RESOURCE};
  1363.             }
  1364.             # open the file
  1365.             if (open(EXIFTOOL_FILE, $filename)) {
  1366.                 my $filePt = \*EXIFTOOL_FILE;
  1367.                 # create random access file object
  1368.                 $raf = new File::RandomAccess($filePt);
  1369.                 # patch to force pipe to be buffered because seek returns success
  1370.                 # in Windows cmd shell pipe even though it really failed
  1371.                 $raf->{TESTED} = -1 if $filename eq '-' or $filename =~ /\|$/;
  1372.                 $self->{RAF} = $raf;
  1373.             } else {
  1374.                 $self->Error('Error opening file');
  1375.             }
  1376.         } else {
  1377.             $self->Error('No file specified');
  1378.         }
  1379.     }
  1380.  
  1381.     if ($raf) {
  1382.         if ($reEntry) {
  1383.             # we already set these tags
  1384.         } elsif (not $raf->{FILE_PT}) {
  1385.             # get file size from image in memory
  1386.             $self->FoundTag('FileSize', length ${$raf->{BUFF_PT}});
  1387.         } elsif (-f $raf->{FILE_PT}) {
  1388.             # get file size and last modified time if this is a plain file
  1389.             my $fileSize = -s _;
  1390.             my $fileTime = -M _;
  1391.             my @stat = stat _;
  1392.             $self->FoundTag('FileSize', $fileSize) if defined $fileSize;
  1393.             $self->FoundTag('ResourceForkSize', $rsize) if $rsize;
  1394.             $self->FoundTag('FileModifyDate', $^T - $fileTime*(24*3600)) if defined $fileTime;
  1395.             $self->FoundTag('FilePermissions', $stat[2])if defined $stat[2];
  1396.         }
  1397.  
  1398.         # get list of file types to check
  1399.         my $tiffType;
  1400.         $self->{FILE_EXT} = GetFileExtension($realname);
  1401.         my @fileTypeList = GetFileType($realname);
  1402.         if (@fileTypeList) {
  1403.             # add remaining types to end of list so we test them all
  1404.             my $pat = join '|', @fileTypeList;
  1405.             push @fileTypeList, grep(!/^($pat)$/, @fileTypes);
  1406.             $tiffType = $self->{FILE_EXT};
  1407.         } else {
  1408.             # scan through all recognized file types
  1409.             @fileTypeList = @fileTypes;
  1410.             $tiffType = 'TIFF';
  1411.         }
  1412.         push @fileTypeList, ''; # end of list marker
  1413.         # initialize the input file for seeking in binary data
  1414.         $raf->BinMode();    # set binary mode before we start reading
  1415.         my $pos = $raf->Tell(); # get file position so we can rewind
  1416.         my %dirInfo = ( RAF => $raf, Base => $pos );
  1417.         # loop through list of file types to test
  1418.         my ($type, $buff, $seekErr);
  1419.         # read first 1024 bytes of file for testing
  1420.         $raf->Read($buff, 1024) or $buff = '';
  1421.         $raf->Seek($pos, 0) or $seekErr = 1;
  1422.         until ($seekErr) {
  1423.             $type = shift @fileTypeList;
  1424.             if ($type) {
  1425.                 # do quick test for this file type to avoid loading module unnecessarily
  1426.                 next if $magicNumber{$type} and $buff !~ /^$magicNumber{$type}/s;
  1427.             } else {
  1428.                 last unless defined $type;
  1429.                 # last ditch effort to scan past unknown header for JPEG/TIFF
  1430.                 next unless $buff =~ /(\xff\xd8\xff|MM\0\x2a|II\x2a\0)/g;
  1431.                 $type = ($1 eq "\xff\xd8\xff") ? 'JPEG' : 'TIFF';
  1432.                 my $skip = pos($buff) - length($1);
  1433.                 $dirInfo{Base} = $pos + $skip;
  1434.                 $raf->Seek($pos + $skip, 0) or $seekErr = 1, last;
  1435.                 $self->Warn("Skipped unknown $skip byte header");
  1436.             }
  1437.             # save file type in member variable
  1438.             $self->{FILE_TYPE} = $self->{PATH}[0] = $type;
  1439.             $dirInfo{Parent} = ($type eq 'TIFF') ? $tiffType : $type;
  1440.             my $module = $moduleName{$type};
  1441.             $module = $type unless defined $module;
  1442.             my $func = "Process$type";
  1443.  
  1444.             # load module if necessary
  1445.             if ($module) {
  1446.                 require "Image/ExifTool/$module.pm";
  1447.                 $func = "Image::ExifTool::${module}::$func";
  1448.             }
  1449.             # process the file
  1450.             no strict 'refs';
  1451.             &$func($self, \%dirInfo) and last;
  1452.             use strict 'refs';
  1453.  
  1454.             # seek back to try again from the same position in the file
  1455.             $raf->Seek($pos, 0) or $seekErr = 1, last;
  1456.         }
  1457.         if ($seekErr) {
  1458.             $self->Error('Error seeking in file');
  1459.         } elsif ($self->Options('ScanForXMP') and (not defined $type or
  1460.             (not $self->Options('FastScan') and not $$self{FoundXMP})))
  1461.         {
  1462.             # scan for XMP
  1463.             $raf->Seek($pos, 0);
  1464.             require Image::ExifTool::XMP;
  1465.             Image::ExifTool::XMP::ScanForXMP($self, $raf) and $type = '';
  1466.         }
  1467.         unless (defined $type) {
  1468.             # if we were given a single image with a known type there
  1469.             # must be a format error since we couldn't read it, otherwise
  1470.             # it is likely we don't support images of this type
  1471.             my $fileType = GetFileType($realname);
  1472.             my $err;
  1473.             if (not $fileType) {
  1474.                 $err = 'Unknown file type';
  1475.             } elsif ($fileType eq 'RAW') {
  1476.                 $err = 'Unsupported RAW file type';
  1477.             } else {
  1478.                 $err = 'File format error';
  1479.             }
  1480.             $self->Error($err);
  1481.         }
  1482.         # extract binary EXIF data block only if requested
  1483.         if (defined $self->{EXIF_DATA} and length $$self{EXIF_DATA} > 16 and
  1484.             ($self->{REQ_TAG_LOOKUP}{exif} or $self->{OPTIONS}{Binary}))
  1485.         {
  1486.             $self->FoundTag('EXIF', $self->{EXIF_DATA});
  1487.         }
  1488.         unless ($reEntry) {
  1489.             # calculate composite tags
  1490.             $self->BuildCompositeTags() if $options->{Composite};
  1491.             # do our HTML dump if requested
  1492.             if ($self->{HTML_DUMP}) {
  1493.                 $raf->Seek(0, 2);   # seek to end of file
  1494.                 $self->{HTML_DUMP}->FinishTiffDump($self, $raf->Tell());
  1495.                 my $pos = $options->{HtmlDumpBase};
  1496.                 $pos = ($self->{FIRST_EXIF_POS} || 0) unless defined $pos;
  1497.                 my $dataPt = defined $self->{EXIF_DATA} ? \$self->{EXIF_DATA} : undef;
  1498.                 undef $dataPt if defined $self->{EXIF_POS} and $pos != $self->{EXIF_POS};
  1499.                 my $success = $self->{HTML_DUMP}->Print($raf, $dataPt, $pos,
  1500.                     $options->{TextOut}, $options->{HtmlDump},
  1501.                     $self->{FILENAME} ? "HTML Dump ($self->{FILENAME})" : 'HTML Dump');
  1502.                 $self->Warn("Error reading $self->{HTML_DUMP}{ERROR}") if $success < 0;
  1503.             }
  1504.         }
  1505.         if ($filename) {
  1506.             $raf->Close();  # close the file if we opened it
  1507.             # process the resource fork as an embedded file on Mac filesystems
  1508.             if ($rsize and $options->{ExtractEmbedded}) {
  1509.                 local *RESOURCE_FILE;
  1510.                 if (open(RESOURCE_FILE, "$filename/rsrc")) {
  1511.                     $$self{DOC_NUM} = $$self{DOC_COUNT} + 1;
  1512.                     $$self{IN_RESOURCE} = 1;
  1513.                     $self->ExtractInfo(\*RESOURCE_FILE, { ReEntry => 1 });
  1514.                     close RESOURCE_FILE;
  1515.                     delete $$self{IN_RESOURCE};
  1516.                 } else {
  1517.                     $self->Warn('Error opening resource fork');
  1518.                 }
  1519.             }
  1520.         }
  1521.     }
  1522.  
  1523.     # restore original options
  1524.     %saveOptions and $self->{OPTIONS} = \%saveOptions;
  1525.  
  1526.     if ($reEntry) {
  1527.         # restore necessary members when exiting re-entrant code
  1528.         $$self{$_} = $$reEntry{$_} foreach keys %$reEntry;
  1529.     }
  1530.  
  1531.     return exists $self->{VALUE}{Error} ? 0 : 1;
  1532. }
  1533.  
  1534. #------------------------------------------------------------------------------
  1535. # Get hash of extracted meta information
  1536. # Inputs: 0) ExifTool object reference
  1537. #         1-N) options hash reference, tag list reference or tag names
  1538. # Returns: Reference to information hash
  1539. # Notes: - pass an undefined value to avoid parsing arguments
  1540. #        - If groups are specified, first groups take precedence if duplicate
  1541. #          tags found but Duplicates option not set.
  1542. #        - tag names may end in '#' to extract ValueConv value
  1543. sub GetInfo($;@)
  1544. {
  1545.     local $_;
  1546.     my $self = shift;
  1547.     my %saveOptions;
  1548.  
  1549.     unless (@_ and not defined $_[0]) {
  1550.         %saveOptions = %{$self->{OPTIONS}}; # save original options
  1551.         # must set FILENAME so it isn't parsed from the arguments
  1552.         $self->{FILENAME} = '' unless defined $self->{FILENAME};
  1553.         $self->ParseArguments(@_);
  1554.     }
  1555.  
  1556.     # get reference to list of tags for which we will return info
  1557.     my ($rtnTags, $byValue) = $self->SetFoundTags();
  1558.  
  1559.     # build hash of tag information
  1560.     my (%info, %ignored);
  1561.     my $conv = $self->{OPTIONS}{PrintConv} ? 'PrintConv' : 'ValueConv';
  1562.     foreach (@$rtnTags) {
  1563.         my $val = $self->GetValue($_, $conv);
  1564.         defined $val or $ignored{$_} = 1, next;
  1565.         $info{$_} = $val;
  1566.     }
  1567.  
  1568.     # override specified tags with ValueConv value if necessary
  1569.     if (@$byValue and $conv ne 'ValueConv') {
  1570.         # first determine the number of times each non-ValueConv value is used
  1571.         my %nonVal;
  1572.         $nonVal{$_} = ($nonVal{$_} || 0) + 1 foreach @$rtnTags;
  1573.         --$nonVal{$$rtnTags[$_]} foreach @$byValue;
  1574.         # then loop through ValueConv tags, updating return values as necessary
  1575.         foreach (@$byValue) {
  1576.             my $tag = $$rtnTags[$_];
  1577.             my $val = $self->GetValue($tag, 'ValueConv');
  1578.             next unless defined $val;
  1579.             # must generate new tag key if the non-ValueConv value is also used
  1580.             # (otherwise we just override the returned value for the existing key)
  1581.             if ($nonVal{$tag}) {
  1582.                 my $vtag = $tag;
  1583.                 # generate a new tag key like "Tag #" or "Tag #(1)"
  1584.                 $vtag =~ s/( |$)/ #/;
  1585.                 unless (defined $self->{VALUE}->{$vtag}) {
  1586.                     $self->{VALUE}{$vtag} = $self->{VALUE}{$tag};
  1587.                     $self->{TAG_INFO}{$vtag} = $self->{TAG_INFO}{$tag};
  1588.                     $self->{TAG_EXTRA}{$vtag} = $self->{TAG_EXTRA}{$tag};
  1589.                     $self->{FILE_ORDER}{$vtag} = $self->{FILE_ORDER}{$tag};
  1590.                 }
  1591.                 # store ValueConv value with new tag key
  1592.                 $$rtnTags[$_] = $tag = $vtag;
  1593.             }
  1594.             $info{$tag} = $val;     # return ValueConv value
  1595.         }
  1596.     }
  1597.  
  1598.     # remove ignored tags from the list
  1599.     my $reqTags = $self->{REQUESTED_TAGS} || [ ];
  1600.     if (%ignored and not @$reqTags) {
  1601.         my @goodTags;
  1602.         foreach (@$rtnTags) {
  1603.             push @goodTags, $_ unless $ignored{$_};
  1604.         }
  1605.         $rtnTags = $self->{FOUND_TAGS} = \@goodTags;
  1606.     }
  1607.  
  1608.     # return sorted tag list if provided with a list reference
  1609.     if ($self->{IO_TAG_LIST}) {
  1610.         # use file order by default if no tags specified
  1611.         # (no such thing as 'Input' order in this case)
  1612.         my $sortOrder = $self->{OPTIONS}{Sort};
  1613.         unless (@$reqTags or ($sortOrder and $sortOrder ne 'Input')) {
  1614.             $sortOrder = 'File';
  1615.         }
  1616.         # return tags in specified sort order
  1617.         @{$self->{IO_TAG_LIST}} = $self->GetTagList($rtnTags, $sortOrder);
  1618.     }
  1619.  
  1620.     # restore original options
  1621.     %saveOptions and $self->{OPTIONS} = \%saveOptions;
  1622.  
  1623.     return \%info;
  1624. }
  1625.  
  1626. #------------------------------------------------------------------------------
  1627. # Combine information from a list of info hashes
  1628. # Unless Duplicates is enabled, first entry found takes priority
  1629. # Inputs: 0) ExifTool object reference, 1-N) list of info hash references
  1630. # Returns: Combined information hash reference
  1631. sub CombineInfo($;@)
  1632. {
  1633.     local $_;
  1634.     my $self = shift;
  1635.     my (%combinedInfo, $info, $tag, %haveInfo);
  1636.  
  1637.     if ($self->{OPTIONS}{Duplicates}) {
  1638.         while ($info = shift) {
  1639.             foreach $tag (keys %$info) {
  1640.                 $combinedInfo{$tag} = $$info{$tag};
  1641.             }
  1642.         }
  1643.     } else {
  1644.         while ($info = shift) {
  1645.             foreach $tag (keys %$info) {
  1646.                 my $tagName = GetTagName($tag);
  1647.                 next if $haveInfo{$tagName};
  1648.                 $haveInfo{$tagName} = 1;
  1649.                 $combinedInfo{$tag} = $$info{$tag};
  1650.             }
  1651.         }
  1652.     }
  1653.     return \%combinedInfo;
  1654. }
  1655.  
  1656. #------------------------------------------------------------------------------
  1657. # Inputs: 0) ExifTool object reference
  1658. #         1) [optional] reference to info hash or tag list ref (default is found tags)
  1659. #         2) [optional] sort order ('File', 'Input', ...)
  1660. # Returns: List of tags in specified order
  1661. sub GetTagList($;$$)
  1662. {
  1663.     local $_;
  1664.     my ($self, $info, $sortOrder) = @_;
  1665.  
  1666.     my $foundTags;
  1667.     if (ref $info eq 'HASH') {
  1668.         my @tags = keys %$info;
  1669.         $foundTags = \@tags;
  1670.     } elsif (ref $info eq 'ARRAY') {
  1671.         $foundTags = $info;
  1672.     }
  1673.     my $fileOrder = $self->{FILE_ORDER};
  1674.  
  1675.     if ($foundTags) {
  1676.         # make sure a FILE_ORDER entry exists for all tags
  1677.         # (note: already generated bogus entries for FOUND_TAGS case below)
  1678.         foreach (@$foundTags) {
  1679.             next if defined $$fileOrder{$_};
  1680.             $$fileOrder{$_} = 999;
  1681.         }
  1682.     } else {
  1683.         $sortOrder = $info if $info and not $sortOrder;
  1684.         $foundTags = $self->{FOUND_TAGS} || $self->SetFoundTags() or return undef;
  1685.     }
  1686.     $sortOrder or $sortOrder = $self->{OPTIONS}{Sort};
  1687.  
  1688.     # return original list if no sort order specified
  1689.     return @$foundTags unless $sortOrder and $sortOrder ne 'Input';
  1690.  
  1691.     if ($sortOrder eq 'Alpha') {
  1692.         return sort @$foundTags;
  1693.     } elsif ($sortOrder =~ /^Group(\d*(:\d+)*)/) {
  1694.         my $family = $1 || 0;
  1695.         # want to maintain a basic file order with the groups
  1696.         # ordered in the way they appear in the file
  1697.         my (%groupCount, %groupOrder);
  1698.         my $numGroups = 0;
  1699.         my $tag;
  1700.         foreach $tag (sort { $$fileOrder{$a} <=> $$fileOrder{$b} } @$foundTags) {
  1701.             my $group = $self->GetGroup($tag, $family);
  1702.             my $num = $groupCount{$group};
  1703.             $num or $num = $groupCount{$group} = ++$numGroups;
  1704.             $groupOrder{$tag} = $num;
  1705.         }
  1706.         return sort { $groupOrder{$a} <=> $groupOrder{$b} or
  1707.                       $$fileOrder{$a} <=> $$fileOrder{$b} } @$foundTags;
  1708.     } else {
  1709.         return sort { $$fileOrder{$a} <=> $$fileOrder{$b} } @$foundTags;
  1710.     }
  1711. }
  1712.  
  1713. #------------------------------------------------------------------------------
  1714. # Get list of found tags in specified sort order
  1715. # Inputs: 0) ExifTool object reference, 1) sort order ('File', 'Input', ...)
  1716. # Returns: List of tag keys in specified order
  1717. # Notes: If not specified, sort order is taken from OPTIONS
  1718. sub GetFoundTags($;$)
  1719. {
  1720.     local $_;
  1721.     my ($self, $sortOrder) = @_;
  1722.     my $foundTags = $self->{FOUND_TAGS} || $self->SetFoundTags() or return undef;
  1723.     return $self->GetTagList($foundTags, $sortOrder);
  1724. }
  1725.  
  1726. #------------------------------------------------------------------------------
  1727. # Get list of requested tags
  1728. # Inputs: 0) ExifTool object reference
  1729. # Returns: List of requested tag keys
  1730. sub GetRequestedTags($)
  1731. {
  1732.     local $_;
  1733.     return @{$_[0]{REQUESTED_TAGS}};
  1734. }
  1735.  
  1736. #------------------------------------------------------------------------------
  1737. # Get tag value
  1738. # Inputs: 0) ExifTool object reference, 1) tag key
  1739. #         2) [optional] Value type: PrintConv, ValueConv, Both or Raw, the default
  1740. #            is PrintConv or ValueConv, depending on the PrintConv option setting
  1741. # Returns: Scalar context: tag value or undefined
  1742. #          List context: list of values or empty list
  1743. sub GetValue($$;$)
  1744. {
  1745.     local $_;
  1746.     my ($self, $tag, $type) = @_;
  1747.  
  1748.     # start with the raw value
  1749.     my $value = $self->{VALUE}{$tag};
  1750.     return wantarray ? () : undef unless defined $value;
  1751.  
  1752.     # figure out what conversions to do
  1753.     my (@convTypes, $tagInfo);
  1754.     $type or $type = $self->{OPTIONS}{PrintConv} ? 'PrintConv' : 'ValueConv';
  1755.     unless ($type eq 'Raw') {
  1756.         $tagInfo = $self->{TAG_INFO}{$tag};
  1757.         push @convTypes, 'ValueConv';
  1758.         push @convTypes, 'PrintConv' unless $type eq 'ValueConv';
  1759.     }
  1760.  
  1761.     # do the conversions
  1762.     my (@val, @prt, @raw, $convType, $valueConv);
  1763.     foreach $convType (@convTypes) {
  1764.         last if ref $value eq 'SCALAR'; # don't convert a scalar reference
  1765.         my $conv = $$tagInfo{$convType};
  1766.         unless (defined $conv) {
  1767.             if ($convType eq 'ValueConv') {
  1768.                 next unless $$tagInfo{Binary};
  1769.                 $conv = '\$val';  # return scalar reference for binary values
  1770.             } else {
  1771.                 # use PRINT_CONV from tag table if PrintConv doesn't exist
  1772.                 next unless defined($conv = $tagInfo->{Table}{PRINT_CONV});
  1773.                 next if exists $$tagInfo{$convType};
  1774.             }
  1775.         }
  1776.         # save old ValueConv value if we want Both
  1777.         $valueConv = $value if $type eq 'Both' and $convType eq 'PrintConv';
  1778.         my ($i, $val, $vals, @values, $convList);
  1779.         # split into list if conversion is an array
  1780.         if (ref $conv eq 'ARRAY') {
  1781.             $convList = $conv;
  1782.             $conv = $$convList[0];
  1783.             my @valList = split ' ', $value;
  1784.             # reorganize list if specified (Note: The writer currently doesn't
  1785.             # relist values, so they may be grouped but the order must not change)
  1786.             my $relist = $$tagInfo{Relist};
  1787.             if ($relist) {
  1788.                 my (@newList, $oldIndex);
  1789.                 foreach $oldIndex (@$relist) {
  1790.                     my ($newVal, @join);
  1791.                     if (ref $oldIndex) {
  1792.                         foreach (@$oldIndex) {
  1793.                             push @join, $valList[$_] if defined $valList[$_];
  1794.                         }
  1795.                         $newVal = join(' ', @join) if @join;
  1796.                     } else {
  1797.                         $newVal = $valList[$oldIndex];
  1798.                     }
  1799.                     push @newList, $newVal if defined $newVal;
  1800.                 }
  1801.                 $value = \@newList;
  1802.             } else {
  1803.                 $value = \@valList;
  1804.             }
  1805.         }
  1806.         # initialize array so we can iterate over values in list
  1807.         if (ref $value eq 'ARRAY') {
  1808.             $i = 0;
  1809.             $vals = $value;
  1810.             $val = $$vals[0];
  1811.         } else {
  1812.             $val = $value;
  1813.         }
  1814.         # loop through all values in list
  1815.         for (;;) {
  1816.             if (defined $conv) {
  1817.                 # get values of required tags if this is a composite tag
  1818.                 if (ref $val eq 'HASH' and not @val) {
  1819.                     foreach (keys %$val) {
  1820.                         $raw[$_] = $self->{VALUE}{$$val{$_}};
  1821.                         ($val[$_], $prt[$_]) = $self->GetValue($$val{$_}, 'Both');
  1822.                         next if defined $val[$_] or not $tagInfo->{Require}{$_};
  1823.                         return wantarray ? () : undef;
  1824.                     }
  1825.                     # set $val to $val[0], or \@val for a CODE ref conversion
  1826.                     $val = ref $conv eq 'CODE' ? \@val : $val[0];
  1827.                 }
  1828.                 if (ref $conv eq 'HASH') {
  1829.                     # look up converted value in hash
  1830.                     my $lc;
  1831.                     if (defined($value = $$conv{$val})) {
  1832.                         # override with our localized language PrintConv if available
  1833.                         if ($$self{CUR_LANG} and $convType eq 'PrintConv' and
  1834.                             # (no need to check for lang-alt tag names -- they won't have a PrintConv)
  1835.                             ref($lc = $self->{CUR_LANG}{$$tagInfo{Name}}) eq 'HASH' and
  1836.                             ($lc = $$lc{PrintConv}) and ($lc = $$lc{$value}))
  1837.                         {
  1838.                             $value = $self->Decode($lc, 'UTF8');
  1839.                         }
  1840.                     } else {
  1841.                         if ($$conv{BITMASK}) {
  1842.                             $value = DecodeBits($val, $$conv{BITMASK});
  1843.                             # override with localized language strings
  1844.                             if (defined $value and $$self{CUR_LANG} and $convType eq 'PrintConv' and
  1845.                                 ref($lc = $self->{CUR_LANG}{$$tagInfo{Name}}) eq 'HASH' and
  1846.                                 ($lc = $$lc{PrintConv}))
  1847.                             {
  1848.                                 my @vals = split ', ', $value;
  1849.                                 foreach (@vals) {
  1850.                                     $_ = $$lc{$_} if defined $$lc{$_};
  1851.                                 }
  1852.                                 $value = join ', ', @vals;
  1853.                             }
  1854.                         } elsif (not $$conv{OTHER} or
  1855.                                  # use alternate conversion routine if available
  1856.                                  not defined($value = &{$$conv{OTHER}}($val, undef, $conv)))
  1857.                         {
  1858.                             if (($$tagInfo{PrintHex} or
  1859.                                 ($$tagInfo{Mask} and not defined $$tagInfo{PrintHex}))
  1860.                                 and $val and IsInt($val) and $convType eq 'PrintConv')
  1861.                             {
  1862.                                 $val = sprintf('0x%x',$val);
  1863.                             }
  1864.                             $value = "Unknown ($val)";
  1865.                         }
  1866.                     }
  1867.                 } else {
  1868.                     # call subroutine or do eval to convert value
  1869.                     local $SIG{'__WARN__'} = \&SetWarning;
  1870.                     undef $evalWarning;
  1871.                     if (ref $conv eq 'CODE') {
  1872.                         $value = &$conv($val, $self);
  1873.                     } else {
  1874.                         #### eval ValueConv/PrintConv ($val, $self, @val, @prt, @raw)
  1875.                         $value = eval $conv;
  1876.                         $@ and $evalWarning = $@;
  1877.                     }
  1878.                     $self->Warn("$convType $tag: " . CleanWarning()) if $evalWarning;
  1879.                 }
  1880.             } else {
  1881.                 $value = $val;
  1882.             }
  1883.             last unless $vals;
  1884.             # save this converted value and step to next value in list
  1885.             push @values, $value if defined $value;
  1886.             if (++$i >= scalar(@$vals)) {
  1887.                 $value = \@values if @values;
  1888.                 last;
  1889.             }
  1890.             $val = $$vals[$i];
  1891.             $conv = $$convList[$i] if $convList;
  1892.         }
  1893.         # return undefined now if no value
  1894.         return wantarray ? () : undef unless defined $value;
  1895.         # join back into single value if split for conversion list
  1896.         if ($convList and ref $value eq 'ARRAY') {
  1897.             $value = join($convType eq 'PrintConv' ? '; ' : ' ', @$value);
  1898.         }
  1899.     }
  1900.     # escape values if necessary
  1901.     if ($$self{ESCAPE_PROC}) {
  1902.         DoEscape($value, $$self{ESCAPE_PROC});
  1903.         DoEscape($valueConv, $$self{ESCAPE_PROC}) if defined $valueConv;
  1904.     }
  1905.     if ($type eq 'Both') {
  1906.         # $valueConv is undefined if there was no print conversion done
  1907.         $valueConv = $value unless defined $valueConv;
  1908.         # return Both values as a list (ValueConv, PrintConv)
  1909.         return ($valueConv, $value);
  1910.     }
  1911.     if (ref $value eq 'ARRAY') {
  1912.         # return array if requested
  1913.         return @$value if wantarray;
  1914.         # return list reference for Raw, ValueConv or if List or not a list of scalars
  1915.         return $value if @convTypes < 2 or $self->{OPTIONS}{List} or ref $$value[0];
  1916.         # otherwise join in comma-separated string
  1917.         $value = join $self->{OPTIONS}{ListSep}, @$value;
  1918.     }
  1919.     return $value;
  1920. }
  1921.  
  1922. #------------------------------------------------------------------------------
  1923. # Get tag identification number
  1924. # Inputs: 0) ExifTool object reference, 1) tag key
  1925. # Returns: Scalar context: Tag ID if available, otherwise ''
  1926. #          List context: 0) Tag ID (or ''), 1) language code (or undef)
  1927. sub GetTagID($$)
  1928. {
  1929.     my ($self, $tag) = @_;
  1930.     my $tagInfo = $self->{TAG_INFO}{$tag};
  1931.     return '' unless $tagInfo and defined $$tagInfo{TagID};
  1932.     return ($$tagInfo{TagID}, $$tagInfo{LangCode}) if wantarray;
  1933.     return $$tagInfo{TagID};
  1934. }
  1935.  
  1936. #------------------------------------------------------------------------------
  1937. # Get tag table name
  1938. # Inputs: 0) ExifTool object reference, 1) tag key
  1939. # Returns: Table name if available, otherwise ''
  1940. sub GetTableName($$)
  1941. {
  1942.     my ($self, $tag) = @_;
  1943.     my $tagInfo = $self->{TAG_INFO}{$tag} or return '';
  1944.     return $tagInfo->{Table}{SHORT_NAME};
  1945. }
  1946.  
  1947. #------------------------------------------------------------------------------
  1948. # Get tag index number
  1949. # Inputs: 0) ExifTool object reference, 1) tag key
  1950. # Returns: Table index number, or undefined if this tag isn't indexed
  1951. sub GetTagIndex($$)
  1952. {
  1953.     my ($self, $tag) = @_;
  1954.     my $tagInfo = $self->{TAG_INFO}{$tag} or return undef;
  1955.     return $$tagInfo{Index};
  1956. }
  1957.  
  1958. #------------------------------------------------------------------------------
  1959. # Get description for specified tag
  1960. # Inputs: 0) ExifTool object reference, 1) tag key
  1961. # Returns: Tag description
  1962. # Notes: Will always return a defined value, even if description isn't available
  1963. sub GetDescription($$)
  1964. {
  1965.     local $_;
  1966.     my ($self, $tag) = @_;
  1967.     my ($desc, $name);
  1968.     my $tagInfo = $self->{TAG_INFO}{$tag};
  1969.     # ($tagInfo won't be defined for missing tags extracted with -f)
  1970.     if ($tagInfo) {
  1971.         # use alternate language description if available
  1972.         while ($$self{CUR_LANG}) {
  1973.             $desc = $self->{CUR_LANG}{$$tagInfo{Name}};
  1974.             if ($desc) {
  1975.                 # must look up Description if this tag also has a PrintConv
  1976.                 $desc = $$desc{Description} or last if ref $desc;
  1977.             } else {
  1978.                 # look up default language of lang-alt tag
  1979.                 last unless $$tagInfo{LangCode} and
  1980.                     ($name = $$tagInfo{Name}) =~ s/-$$tagInfo{LangCode}$// and
  1981.                     $desc = $self->{CUR_LANG}{$name};
  1982.                 $desc = $$desc{Description} or last if ref $desc;
  1983.                 $desc .= " ($$tagInfo{LangCode})";
  1984.             }
  1985.             # escape description if necessary
  1986.             DoEscape($desc, $$self{ESCAPE_PROC}) if $$self{ESCAPE_PROC};
  1987.             # return description in proper Charset
  1988.             return $self->Decode($desc, 'UTF8');
  1989.         }
  1990.         $desc = $$tagInfo{Description};
  1991.     }
  1992.     # just make the tag more readable if description doesn't exist
  1993.     unless ($desc) {
  1994.         $desc = MakeDescription(GetTagName($tag));
  1995.         # save description in tag information
  1996.         $$tagInfo{Description} = $desc if $tagInfo;
  1997.     }
  1998.     return $desc;
  1999. }
  2000.  
  2001. #------------------------------------------------------------------------------
  2002. # Get group name for specified tag
  2003. # Inputs: 0) ExifTool object reference
  2004. #         1) tag key (or reference to tagInfo hash, not part of the public API)
  2005. #         2) [optional] group family (-1 to get extended group list)
  2006. # Returns: Scalar context: Group name (for family 0 if not otherwise specified)
  2007. #          Array context: Group name if family specified, otherwise list of
  2008. #          group names for each family.  Returns '' for undefined tag.
  2009. # Notes: Mutiple families may be specified with ':' in family argument (ie. '1:2')
  2010. sub GetGroup($$;$)
  2011. {
  2012.     local $_;
  2013.     my ($self, $tag, $family) = @_;
  2014.     my ($tagInfo, @groups, @families, $simplify);
  2015.     if (ref $tag eq 'HASH') {
  2016.         $tagInfo = $tag;
  2017.         $tag = $tagInfo->{Name};
  2018.     } else {
  2019.         $tagInfo = $self->{TAG_INFO}{$tag} or return '';
  2020.     }
  2021.     my $groups = $$tagInfo{Groups};
  2022.     # fill in default groups unless already done
  2023.     # (after this, Groups 0-2 in tagInfo are guaranteed to be defined)
  2024.     unless ($$tagInfo{GotGroups}) {
  2025.         my $tagTablePtr = $$tagInfo{Table};
  2026.         if ($tagTablePtr) {
  2027.             # construct our group list
  2028.             $groups or $groups = $$tagInfo{Groups} = { };
  2029.             # fill in default groups
  2030.             foreach (keys %{$$tagTablePtr{GROUPS}}) {
  2031.                 $$groups{$_} or $$groups{$_} = $tagTablePtr->{GROUPS}{$_};
  2032.             }
  2033.         }
  2034.         # set flag indicating group list was built
  2035.         $$tagInfo{GotGroups} = 1;
  2036.     }
  2037.     if (defined $family and $family ne '-1') {
  2038.         if ($family =~ /[^\d]/) {
  2039.             @families = ($family =~ /\d+/g);
  2040.             return $$groups{0} unless @families;
  2041.             $simplify = 1 unless $family =~ /^:/;
  2042.             undef $family;
  2043.             foreach (0..2) { $groups[$_] = $$groups{$_}; }
  2044.         } else {
  2045.             return $$groups{$family} if $family == 0 or $family == 2;
  2046.             $groups[1] = $$groups{1};
  2047.         }
  2048.     } else {
  2049.         return $$groups{0} unless wantarray;
  2050.         foreach (0..2) { $groups[$_] = $$groups{$_}; }
  2051.     }
  2052.     $groups[3] = 'Main';
  2053.     $groups[4] = "Copy$1" if $tag =~ /\((\d+)\)$/;
  2054.     # handle dynamic group names if necessary
  2055.     my $ex = $self->{TAG_EXTRA}{$tag};
  2056.     if ($ex) {
  2057.         $groups[0] = $$ex{G0} if $$ex{G0};
  2058.         $groups[1] = $$ex{G1} =~ /^\+(.*)/ ? "$groups[1]$1" : $$ex{G1} if $$ex{G1};
  2059.         $groups[3] = 'Doc' . $$ex{G3} if $$ex{G3};
  2060.     }
  2061.     if ($family) {
  2062.         return $groups[$family] || '' if $family > 0;
  2063.         # add additional matching group names to list
  2064.         # ie) for MIE-Doc, also add MIE1, MIE1-Doc, MIE-Doc1 and MIE1-Doc1
  2065.         # and for MIE2-Doc3, also add MIE2, MIE-Doc3, MIE2-Doc and MIE-Doc
  2066.         if ($groups[1] =~ /^MIE(\d*)-(.+?)(\d*)$/) {
  2067.             push @groups, 'MIE' . ($1 || '1');
  2068.             push @groups, 'MIE' . ($1 ? '' : '1') . "-$2$3";
  2069.             push @groups, "MIE$1-$2" . ($3 ? '' : '1');
  2070.             push @groups, 'MIE' . ($1 ? '' : '1') . "-$2" . ($3 ? '' : '1');
  2071.         }
  2072.     }
  2073.     if (@families) {
  2074.         my @grps;
  2075.         # create list of group names (without identical adjacent groups if simplifying)
  2076.         foreach (@families) {
  2077.             my $grp = $groups[$_] or next;
  2078.             push @grps, $grp unless $simplify and @grps and $grp eq $grps[-1];
  2079.         }
  2080.         # remove leading "Main:" if simplifying
  2081.         shift @grps if $simplify and @grps > 1 and $grps[0] eq 'Main';
  2082.         # return colon-separated string of group names
  2083.         return join ':', @grps;
  2084.     }
  2085.     return @groups;
  2086. }
  2087.  
  2088. #------------------------------------------------------------------------------
  2089. # Get group names for specified tags
  2090. # Inputs: 0) ExifTool object reference
  2091. #         1) [optional] information hash reference (default all extracted info)
  2092. #         2) [optional] group family (default 0)
  2093. # Returns: List of group names in alphabetical order
  2094. sub GetGroups($;$$)
  2095. {
  2096.     local $_;
  2097.     my $self = shift;
  2098.     my $info = shift;
  2099.     my $family;
  2100.  
  2101.     # figure out our arguments
  2102.     if (ref $info ne 'HASH') {
  2103.         $family = $info;
  2104.         $info = $self->{VALUE};
  2105.     } else {
  2106.         $family = shift;
  2107.     }
  2108.     $family = 0 unless defined $family;
  2109.  
  2110.     # get a list of all groups in specified information
  2111.     my ($tag, %groups);
  2112.     foreach $tag (keys %$info) {
  2113.         $groups{ $self->GetGroup($tag, $family) } = 1;
  2114.     }
  2115.     return sort keys %groups;
  2116. }
  2117.  
  2118. #------------------------------------------------------------------------------
  2119. # Set priority for group where new values are written
  2120. # Inputs: 0) ExifTool object reference,
  2121. #         1-N) group names (reset to default if no groups specified)
  2122. sub SetNewGroups($;@)
  2123. {
  2124.     local $_;
  2125.     my ($self, @groups) = @_;
  2126.     @groups or @groups = @defaultWriteGroups;
  2127.     my $count = @groups;
  2128.     my %priority;
  2129.     foreach (@groups) {
  2130.         $priority{lc($_)} = $count--;
  2131.     }
  2132.     $priority{file} = 10;       # 'File' group is always written (Comment)
  2133.     $priority{composite} = 10;  # 'Composite' group is always written
  2134.     # set write priority (higher # is higher priority)
  2135.     $self->{WRITE_PRIORITY} = \%priority;
  2136.     $self->{WRITE_GROUPS} = \@groups;
  2137. }
  2138.  
  2139. #------------------------------------------------------------------------------
  2140. # Build composite tags from required tags
  2141. # Inputs: 0) ExifTool object reference
  2142. # Note: Tag values are calculated in alphabetical order unless a tag Require's
  2143. #       or Desire's another composite tag, in which case the calculation is
  2144. #       deferred until after the other tag is calculated.
  2145. sub BuildCompositeTags($)
  2146. {
  2147.     local $_;
  2148.     my $self = shift;
  2149.  
  2150.     $$self{BuildingComposite} = 1;
  2151.     # first, add user-defined composite tags if necessary
  2152.     if (%UserDefined and $UserDefined{'Image::ExifTool::Composite'}) {
  2153.         AddCompositeTags($UserDefined{'Image::ExifTool::Composite'}, 1);
  2154.         delete $UserDefined{'Image::ExifTool::Composite'};
  2155.     }
  2156.     my @tagList = sort keys %Image::ExifTool::Composite;
  2157.     my %tagsUsed;
  2158.  
  2159.     my $rawValue = $self->{VALUE};
  2160.     for (;;) {
  2161.         my %notBuilt;
  2162.         $notBuilt{$_} = 1 foreach @tagList;
  2163.         my @deferredTags;
  2164.         my $tag;
  2165. COMPOSITE_TAG:
  2166.         foreach $tag (@tagList) {
  2167.             next if $specialTags{$tag};
  2168.             my $tagInfo = $self->GetTagInfo(\%Image::ExifTool::Composite, $tag);
  2169.             next unless $tagInfo;
  2170.             # put required tags into array and make sure they all exist
  2171.             my $subDoc = ($$tagInfo{SubDoc} and $$self{DOC_COUNT});
  2172.             my $require = $$tagInfo{Require} || { };
  2173.             my $desire = $$tagInfo{Desire} || { };
  2174.             # loop through sub-documents if necessary
  2175.             my $doc;
  2176.             for (;;) {
  2177.                 my (%tagKey, $found, $index);
  2178.                 # save Require'd and Desire'd tag values in list
  2179.                 for ($index=0; ; ++$index) {
  2180.                     my $reqTag = $$require{$index} || $$desire{$index} or last;
  2181.                     # add family 3 group if generating composite tags for sub-documents
  2182.                     # (unless tag already begins with family 3 group name)
  2183.                     if ($subDoc and $reqTag !~ /^(Main|Doc\d+):/) {
  2184.                         $reqTag = ($doc ? "Doc$doc:" : 'Main:') . $reqTag;
  2185.                     }
  2186.                     # allow tag group to be specified
  2187.                     if ($reqTag =~ /(.+):(.+)/) {
  2188.                         my ($reqGroup, $name) = ($1, $2);
  2189.                         if ($reqGroup eq 'Composite' and $notBuilt{$name}) {
  2190.                             push @deferredTags, $tag;
  2191.                             next COMPOSITE_TAG;
  2192.                         }
  2193.                         my ($i, $key, @keys);
  2194.                         for ($i=0; ; ++$i) {
  2195.                             $key = $name;
  2196.                             $key .= " ($i)" if $i;
  2197.                             last unless defined $$rawValue{$key};
  2198.                             push @keys, $key;
  2199.                         }
  2200.                         # find first matching tag
  2201.                         $key = $self->GroupMatches($reqGroup, \@keys);
  2202.                         $reqTag = $key if $key;
  2203.                     } elsif ($notBuilt{$reqTag}) {
  2204.                         # calculate this tag later if it relies on another
  2205.                         # Composite tag which hasn't been calculated yet
  2206.                         push @deferredTags, $tag;
  2207.                         next COMPOSITE_TAG;
  2208.                     }
  2209.                     if (defined $$rawValue{$reqTag}) {
  2210.                         $found = 1;
  2211.                     } elsif ($$require{$index}) {
  2212.                         $found = 0;
  2213.                         last;   # don't continue since we require this tag
  2214.                     }
  2215.                     $tagKey{$index} = $reqTag;
  2216.                 }
  2217.                 if ($doc) {
  2218.                     if ($found) {
  2219.                         $self->{DOC_NUM} = $doc;
  2220.                         $self->FoundTag($tagInfo, \%tagKey);
  2221.                         delete $self->{DOC_NUM};
  2222.                     }
  2223.                     next if ++$doc <= $self->{DOC_COUNT};
  2224.                     last;
  2225.                 } elsif ($found) {
  2226.                     delete $notBuilt{$tag}; # this tag is OK to build now
  2227.                     # keep track of all Require'd tag keys
  2228.                     foreach (keys %tagKey) {
  2229.                         # only tag keys with same name as a composite tag
  2230.                         # can be replaced (also eliminates keys with
  2231.                         # instance numbers which can't be replaced either)
  2232.                         next unless $Image::ExifTool::Composite{$tagKey{$_}};
  2233.                         my $keyRef = \$tagKey{$_};
  2234.                         $tagsUsed{$$keyRef} or $tagsUsed{$$keyRef} = [ ];
  2235.                         push @{$tagsUsed{$$keyRef}}, $keyRef;
  2236.                     }
  2237.                     # save reference to tag key lookup as value for composite tag
  2238.                     my $key = $self->FoundTag($tagInfo, \%tagKey);
  2239.                     # check to see if we just replaced one of the tag keys we Require'd
  2240.                     if (defined $key and $tagsUsed{$key}) {
  2241.                         foreach (@{$tagsUsed{$key}}) {
  2242.                             $$_ = $self->{MOVED_KEY};   # replace with new tag key
  2243.                         }
  2244.                         delete $tagsUsed{$key};         # can't be replaced again
  2245.                     }
  2246.                 } elsif (not defined $found) {
  2247.                     delete $notBuilt{$tag}; # tag can't be built anyway
  2248.                 }
  2249.                 last unless $subDoc;
  2250.                 $doc = 1;   # continue to process the 1st sub-document
  2251.             }
  2252.         }
  2253.         last unless @deferredTags;
  2254.         if (@deferredTags == @tagList) {
  2255.             # everything was deferred in the last pass,
  2256.             # must be a circular dependency
  2257.             warn "Circular dependency in Composite tags\n";
  2258.             last;
  2259.         }
  2260.         @tagList = @deferredTags; # calculate deferred tags now
  2261.     }
  2262.     delete $$self{BuildingComposite};
  2263. }
  2264.  
  2265. #------------------------------------------------------------------------------
  2266. # Get tag name (removes copy index)
  2267. # Inputs: 0) Tag key
  2268. # Returns: Tag name
  2269. sub GetTagName($)
  2270. {
  2271.     local $_;
  2272.     $_[0] =~ /^(\S+)/;
  2273.     return $1;
  2274. }
  2275.  
  2276. #------------------------------------------------------------------------------
  2277. # Get list of shortcuts
  2278. # Returns: Shortcut list (sorted alphabetically)
  2279. sub GetShortcuts()
  2280. {
  2281.     local $_;
  2282.     require Image::ExifTool::Shortcuts;
  2283.     return sort keys %Image::ExifTool::Shortcuts::Main;
  2284. }
  2285.  
  2286. #------------------------------------------------------------------------------
  2287. # Get file type for specified extension
  2288. # Inputs: 0) file name or extension (case is not significant),
  2289. #            or FileType value if a description is requested
  2290. #         1) flag to return long description instead of type
  2291. # Returns: File type (or desc) or undef if extension not supported or if
  2292. #          description is the same as the input FileType.  In array
  2293. #          context, may return more than one file type if the file may be
  2294. #          different formats.  Returns list of all recognized extensions if no
  2295. #          file specified
  2296. sub GetFileType(;$$)
  2297. {
  2298.     local $_;
  2299.     my ($file, $desc) = @_;
  2300.     return sort keys %fileTypeLookup unless defined $file;
  2301.     my $fileType;
  2302.     my $fileExt = GetFileExtension($file);
  2303.     $fileExt = uc($file) unless $fileExt;
  2304.     $fileExt and $fileType = $fileTypeLookup{$fileExt}; # look up the file type
  2305.     $fileType = $fileTypeLookup{$fileType} unless ref $fileType or not $fileType;
  2306.     # return description if specified
  2307.     # (allow input $file to be a FileType for this purpose)
  2308.     $desc and return $fileType ? $$fileType[1] : $fileDescription{$file};
  2309.     $fileType or return wantarray ? () : undef;
  2310.     $fileType = $$fileType[0];      # get file type (or list of types)
  2311.     if (wantarray) {
  2312.         return @$fileType if ref $fileType eq 'ARRAY';
  2313.     } elsif ($fileType) {
  2314.         $fileType = $fileExt if ref $fileType eq 'ARRAY';
  2315.     }
  2316.     return $fileType;
  2317. }
  2318.  
  2319. #------------------------------------------------------------------------------
  2320. # Return true if we can write the specified file type
  2321. # Inputs: 0) file name or ext
  2322. # Returns: true if writable, 0 if not writable, undef if unrecognized
  2323. sub CanWrite($)
  2324. {
  2325.     local $_;
  2326.     my $file = shift or return undef;
  2327.     my $type = GetFileType($file) or return undef;
  2328.     if ($noWriteFile{$type}) {
  2329.         # can't write TIFF files with certain extensions (various RAW formats)
  2330.         my $ext = GetFileExtension($file) || uc($file);
  2331.         return grep(/^$ext$/, @{$noWriteFile{$type}}) ? 0 : 1 if $ext;
  2332.     }
  2333.     return scalar(grep /^$type$/, @writeTypes);
  2334. }
  2335.  
  2336. #------------------------------------------------------------------------------
  2337. # Return true if we can create the specified file type
  2338. # Inputs: 0) file name or ext
  2339. # Returns: true if creatable, 0 if not writable, undef if unrecognized
  2340. sub CanCreate($)
  2341. {
  2342.     local $_;
  2343.     my $file = shift or return undef;
  2344.     my $ext = GetFileExtension($file) || uc($file);
  2345.     my $type = GetFileType($file) or return undef;
  2346.     return 1 if $createTypes{$ext} or $createTypes{$type};
  2347.     return 0;
  2348. }
  2349.  
  2350. #==============================================================================
  2351. # Functions below this are not part of the public API
  2352.  
  2353. # Initialize member variables
  2354. # Inputs: 0) ExifTool object reference
  2355. sub Init($)
  2356. {
  2357.     local $_;
  2358.     my $self = shift;
  2359.     # delete all DataMember variables (lower-case names)
  2360.     foreach (keys %$self) {
  2361.         /[a-z]/ and delete $self->{$_};
  2362.     }
  2363.     delete $self->{FOUND_TAGS};     # list of found tags
  2364.     delete $self->{EXIF_DATA};      # the EXIF data block
  2365.     delete $self->{EXIF_POS};       # EXIF position in file
  2366.     delete $self->{FIRST_EXIF_POS}; # position of first EXIF in file
  2367.     delete $self->{HTML_DUMP};      # html dump information
  2368.     delete $self->{SET_GROUP1};     # group1 name override
  2369.     delete $self->{DOC_NUM};        # current embedded document number
  2370.     $self->{DOC_COUNT}  = 0;        # count of embedded documents processed
  2371.     $self->{BASE}       = 0;        # base for offsets from start of file
  2372.     $self->{FILE_ORDER} = { };      # hash of tag order in file
  2373.     $self->{VALUE}      = { };      # hash of raw tag values
  2374.     $self->{TAG_INFO}   = { };      # hash of tag information
  2375.     $self->{TAG_EXTRA}  = { };      # hash of extra tag information (dynamic group names)
  2376.     $self->{PRIORITY}   = { };      # priority of current tags
  2377.     $self->{LIST_TAGS}  = { };      # hash of tagInfo refs for active List-type tags
  2378.     $self->{PROCESSED}  = { };      # hash of processed directory start positions
  2379.     $self->{DIR_COUNT}  = { };      # count various types of directories
  2380.     $self->{DUPL_TAG}   = { };      # last-used index for duplicate-tag keys
  2381.     $self->{PATH}       = [ ];      # current subdirectory path in file when reading
  2382.     $self->{NUM_FOUND}  = 0;        # total number of tags found (incl. duplicates)
  2383.     $self->{CHANGED}    = 0;        # number of tags changed (writer only)
  2384.     $self->{INDENT}     = '  ';     # initial indent for verbose messages
  2385.     $self->{PRIORITY_DIR} = '';     # the priority directory name
  2386.     $self->{LOW_PRIORITY_DIR} = { PreviewIFD => 1 }; # names of priority 0 directories
  2387.     $self->{TIFF_TYPE}  = '';       # type of TIFF data (APP1, TIFF, NEF, etc...)
  2388.     $self->{Make}       = '';       # camera make
  2389.     $self->{Model}      = '';       # camera model
  2390.     $self->{CameraType} = '';       # Olympus camera type
  2391.     if ($self->Options('HtmlDump')) {
  2392.         require Image::ExifTool::HtmlDump;
  2393.         $self->{HTML_DUMP} = new Image::ExifTool::HtmlDump;
  2394.     }
  2395.     # make sure our TextOut is a file reference
  2396.     $self->{OPTIONS}{TextOut} = \*STDOUT unless ref $self->{OPTIONS}{TextOut};
  2397. }
  2398.  
  2399. #------------------------------------------------------------------------------
  2400. # Parse function arguments and set member variables accordingly
  2401. # Inputs: Same as ImageInfo()
  2402. # - sets REQUESTED_TAGS, REQ_TAG_LOOKUP, IO_TAG_LIST, FILENAME, RAF, OPTIONS
  2403. sub ParseArguments($;@)
  2404. {
  2405.     my $self = shift;
  2406.     my $options = $self->{OPTIONS};
  2407.     my @exclude;
  2408.     my @oldGroupOpts = grep /^Group/, keys %{$self->{OPTIONS}};
  2409.     my $wasExcludeOpt;
  2410.  
  2411.     $self->{REQUESTED_TAGS} = [ ];
  2412.     $self->{REQ_TAG_LOOKUP} = { };
  2413.     $self->{IO_TAG_LIST} = undef;
  2414.  
  2415.     # handle our input arguments
  2416.     while (@_) {
  2417.         my $arg = shift;
  2418.         if (ref $arg) {
  2419.             if (ref $arg eq 'ARRAY') {
  2420.                 $self->{IO_TAG_LIST} = $arg;
  2421.                 foreach (@$arg) {
  2422.                     if (/^-(.*)/) {
  2423.                         push @exclude, $1;
  2424.                     } else {
  2425.                         push @{$self->{REQUESTED_TAGS}}, $_;
  2426.                     }
  2427.                 }
  2428.             } elsif (ref $arg eq 'HASH') {
  2429.                 my $opt;
  2430.                 foreach $opt (keys %$arg) {
  2431.                     # a single new group option overrides all old group options
  2432.                     if (@oldGroupOpts and $opt =~ /^Group/) {
  2433.                         foreach (@oldGroupOpts) {
  2434.                             delete $options->{$_};
  2435.                         }
  2436.                         undef @oldGroupOpts;
  2437.                     }
  2438.                     $self->Options($opt, $$arg{$opt});
  2439.                     $opt eq 'Exclude' and $wasExcludeOpt = 1;
  2440.                 }
  2441.             } elsif (ref $arg eq 'SCALAR' or UNIVERSAL::isa($arg,'GLOB')) {
  2442.                 next if defined $self->{RAF};
  2443.                 # convert image data from UTF-8 to character stream if necessary
  2444.                 # (patches RHEL 3 UTF8 LANG problem)
  2445.                 if (ref $arg eq 'SCALAR' and $] >= 5.006 and
  2446.                     (eval 'require Encode; Encode::is_utf8($$arg)' or $@))
  2447.                 {
  2448.                     # repack by hand if Encode isn't available
  2449.                     my $buff = $@ ? pack('C*',unpack('U0C*',$$arg)) : Encode::encode('utf8',$$arg);
  2450.                     $arg = \$buff;
  2451.                 }
  2452.                 $self->{RAF} = new File::RandomAccess($arg);
  2453.                 # set filename to empty string to indicate that
  2454.                 # we have a file but we didn't open it
  2455.                 $self->{FILENAME} = '';
  2456.             } elsif (UNIVERSAL::isa($arg, 'File::RandomAccess')) {
  2457.                 $self->{RAF} = $arg;
  2458.                 $self->{FILENAME} = '';
  2459.             } else {
  2460.                 warn "Don't understand ImageInfo argument $arg\n";
  2461.             }
  2462.         } elsif (defined $self->{FILENAME}) {
  2463.             if ($arg =~ /^-(.*)/) {
  2464.                 push @exclude, $1;
  2465.             } else {
  2466.                 push @{$self->{REQUESTED_TAGS}}, $arg;
  2467.             }
  2468.         } else {
  2469.             $self->{FILENAME} = $arg;
  2470.         }
  2471.     }
  2472.     # expand shortcuts in tag arguments if provided
  2473.     if (@{$self->{REQUESTED_TAGS}}) {
  2474.         ExpandShortcuts($self->{REQUESTED_TAGS});
  2475.         # initialize lookup for requested tags
  2476.         foreach (@{$self->{REQUESTED_TAGS}}) {
  2477.             $self->{REQ_TAG_LOOKUP}{lc(/.+:(.+)/ ? $1 : $_)} = 1;
  2478.         }
  2479.     }
  2480.  
  2481.     if (@exclude or $wasExcludeOpt) {
  2482.         # must add existing excluded tags
  2483.         if ($options->{Exclude}) {
  2484.             if (ref $options->{Exclude} eq 'ARRAY') {
  2485.                 push @exclude, @{$options->{Exclude}};
  2486.             } else {
  2487.                 push @exclude, $options->{Exclude};
  2488.             }
  2489.         }
  2490.         $options->{Exclude} = \@exclude;
  2491.         # expand shortcuts in new exclude list
  2492.         ExpandShortcuts($options->{Exclude}, 1); # (also remove '#' suffix)
  2493.     }
  2494. }
  2495.  
  2496. #------------------------------------------------------------------------------
  2497. # Get list of tags in specified group
  2498. # Inputs: 0) ExifTool ref, 1) group spec, 2) tag key or reference to list of tag keys
  2499. # Returns: list of matching tags in list context, or first match in scalar context
  2500. # Notes: Group spec may contain multiple groups separated by colons, each
  2501. #        possibly with a leading family number
  2502. sub GroupMatches($$$)
  2503. {
  2504.     my ($self, $group, $tagList) = @_;
  2505.     $tagList = [ $tagList ] unless ref $tagList;
  2506.     my ($tag, @matches);
  2507.     if ($group =~ /:/) {
  2508.         # check each group name individually (ie. "Author:1IPTC")
  2509.         my @grps = split ':', lc $group;
  2510.         my (@fmys, $g);
  2511.         for ($g=0; $g<@grps; ++$g) {
  2512.             $fmys[$g] = $1 if $grps[$g] =~ s/^(\d+)//;
  2513.         }
  2514.         foreach $tag (@$tagList) {
  2515.             my @groups = $self->GetGroup($tag, -1);
  2516.             for ($g=0; $g<@grps; ++$g) {
  2517.                 my $grp = $grps[$g];
  2518.                 next if $grp eq '*' or $grp eq 'all';
  2519.                 if (defined $fmys[$g]) {
  2520.                     my $f = $fmys[$g];
  2521.                     last unless $groups[$f] and $grps[$g] eq lc $groups[$f];
  2522.                 } else {
  2523.                     last unless grep /^$grps[$g]$/i, @groups;
  2524.                 }
  2525.             }
  2526.             push @matches, $tag if $g == @grps;
  2527.         }
  2528.     } else {
  2529.         my $family = ($group =~ s/^(\d+)//) ? $1 : -1;
  2530.         foreach $tag (@$tagList) {
  2531.             my @groups = $self->GetGroup($tag, $family);
  2532.             push @matches, $tag if grep(/^$group$/i, @groups);
  2533.         }
  2534.     }
  2535.     return wantarray ? @matches : $matches[0];
  2536. }
  2537.  
  2538. #------------------------------------------------------------------------------
  2539. # Set list of found tags from previously requested tags
  2540. # Inputs: 0) ExifTool object reference
  2541. # Returns: 0) Reference to list of found tag keys (in order of requested tags)
  2542. #          1) Reference to list of indices for tags requested by value
  2543. sub SetFoundTags($)
  2544. {
  2545.     my $self = shift;
  2546.     my $options = $self->{OPTIONS};
  2547.     my $reqTags = $self->{REQUESTED_TAGS} || [ ];
  2548.     my $duplicates = $options->{Duplicates};
  2549.     my $exclude = $options->{Exclude};
  2550.     my $fileOrder = $self->{FILE_ORDER};
  2551.     my @groupOptions = sort grep /^Group/, keys %$options;
  2552.     my $doDups = $duplicates || $exclude || @groupOptions;
  2553.     my ($tag, $rtnTags, @byValue);
  2554.  
  2555.     # only return requested tags if specified
  2556.     if (@$reqTags) {
  2557.         $rtnTags or $rtnTags = [ ];
  2558.         # scan through the requested tags and generate a list of tags we found
  2559.         my $tagHash = $self->{VALUE};
  2560.         my $reqTag;
  2561.         foreach $reqTag (@$reqTags) {
  2562.             my (@matches, $group, $allGrp, $allTag, $byValue);
  2563.             if ($reqTag =~ /^(.+):(.+)/) {
  2564.                 ($group, $tag) = ($1, $2, $3);
  2565.                 if ($group =~ /^(\*|all)$/i) {
  2566.                     $allGrp = 1;
  2567.                 } elsif ($group !~ /^[-\w:]+$/) {
  2568.                     $self->Warn("Invalid group name '$group'");
  2569.                     $group = 'invalid';
  2570.                 }
  2571.             } else {
  2572.                 $tag = $reqTag;
  2573.             }
  2574.             $byValue = 1 if $tag =~ s/#$//;
  2575.             if (defined $tagHash->{$reqTag} and not $doDups) {
  2576.                 $matches[0] = $tag;
  2577.             } elsif ($tag =~ /^(\*|all)$/i) {
  2578.                 # tag name of '*' or 'all' matches all tags
  2579.                 if ($doDups or $allGrp) {
  2580.                     @matches = keys %$tagHash;
  2581.                 } else {
  2582.                     @matches = grep(!/ /, keys %$tagHash);
  2583.                 }
  2584.                 next unless @matches;   # don't want entry in list for '*' tag
  2585.                 $allTag = 1;
  2586.             } elsif ($doDups or defined $group) {
  2587.                 # must also look for tags like "Tag (1)"
  2588.                 @matches = grep(/^$tag(\s|$)/i, keys %$tagHash);
  2589.             } elsif ($tag =~ /^[-\w]+$/) {
  2590.                 # find first matching value
  2591.                 # (use in list context to return value instead of count)
  2592.                 ($matches[0]) = grep /^$tag$/i, keys %$tagHash;
  2593.                 defined $matches[0] or undef @matches;
  2594.             } else {
  2595.                 $self->Warn("Invalid tag name '$tag'");
  2596.             }
  2597.             if (defined $group and not $allGrp) {
  2598.                 # keep only specified group
  2599.                 @matches = $self->GroupMatches($group, \@matches);
  2600.                 next unless @matches or not $allTag;
  2601.             }
  2602.             if (@matches > 1) {
  2603.                 # maintain original file order for multiple tags
  2604.                 @matches = sort { $$fileOrder{$a} <=> $$fileOrder{$b} } @matches;
  2605.                 # return only the highest priority tag unless duplicates wanted
  2606.                 unless ($doDups or $allTag or $allGrp) {
  2607.                     $tag = shift @matches;
  2608.                     my $oldPriority = $self->{PRIORITY}{$tag} || 1;
  2609.                     foreach (@matches) {
  2610.                         my $priority = $self->{PRIORITY}{$_};
  2611.                         $priority = 1 unless defined $priority;
  2612.                         next unless $priority >= $oldPriority;
  2613.                         $tag = $_;
  2614.                         $oldPriority = $priority || 1;
  2615.                     }
  2616.                     @matches = ( $tag );
  2617.                 }
  2618.             } elsif (not @matches) {
  2619.                 # put entry in return list even without value (value is undef)
  2620.                 $matches[0] = "$tag (0)";
  2621.                 # bogus file order entry to avoid warning if sorting in file order
  2622.                 $self->{FILE_ORDER}{$matches[0]} = 999;
  2623.             }
  2624.             # save indices of tags extracted by value
  2625.             push @byValue, scalar(@$rtnTags) .. (scalar(@$rtnTags)+scalar(@matches)-1) if $byValue;
  2626.             push @$rtnTags, @matches;
  2627.         }
  2628.     } else {
  2629.         # no requested tags, so we want all tags
  2630.         my @allTags;
  2631.         if ($doDups) {
  2632.             @allTags = keys %{$self->{VALUE}};
  2633.         } else {
  2634.             foreach (keys %{$self->{VALUE}}) {
  2635.                 # only include tag if it doesn't end in a copy number
  2636.                 push @allTags, $_ unless / /;
  2637.             }
  2638.         }
  2639.         $rtnTags = \@allTags;
  2640.     }
  2641.  
  2642.     # filter excluded tags and group options
  2643.     while (($exclude or @groupOptions) and @$rtnTags) {
  2644.         if ($exclude) {
  2645.             my @filteredTags;
  2646. EX_TAG:     foreach $tag (@$rtnTags) {
  2647.                 my $tagName = GetTagName($tag);
  2648.                 my @matches = grep /(^|:)($tagName|\*|all)$/i, @$exclude;
  2649.                 foreach (@matches) {
  2650.                     next EX_TAG unless /^(.+):/;
  2651.                     my $group = $1;
  2652.                     next EX_TAG if $group =~ /^(\*|all)$/i;
  2653.                     next EX_TAG if $self->GroupMatches($group, $tag);
  2654.                 }
  2655.                 push @filteredTags, $tag;
  2656.             }
  2657.             $rtnTags = \@filteredTags;      # use new filtered tag list
  2658.             last if $duplicates and not @groupOptions;
  2659.         }
  2660.         # filter groups if requested, or to remove duplicates
  2661.         my (%keepTags, %wantGroup, $family, $groupOpt);
  2662.         my $allGroups = 1;
  2663.         # build hash of requested/excluded group names for each group family
  2664.         my $wantOrder = 0;
  2665.         foreach $groupOpt (@groupOptions) {
  2666.             $groupOpt =~ /^Group(\d*(:\d+)*)/ or next;
  2667.             $family = $1 || 0;
  2668.             $wantGroup{$family} or $wantGroup{$family} = { };
  2669.             my $groupList;
  2670.             if (ref $options->{$groupOpt} eq 'ARRAY') {
  2671.                 $groupList = $options->{$groupOpt};
  2672.             } else {
  2673.                 $groupList = [ $options->{$groupOpt} ];
  2674.             }
  2675.             foreach (@$groupList) {
  2676.                 # groups have priority in order they were specified
  2677.                 ++$wantOrder;
  2678.                 my ($groupName, $want);
  2679.                 if (/^-(.*)/) {
  2680.                     # excluded group begins with '-'
  2681.                     $groupName = $1;
  2682.                     $want = 0;          # we don't want tags in this group
  2683.                 } else {
  2684.                     $groupName = $_;
  2685.                     $want = $wantOrder; # we want tags in this group
  2686.                     $allGroups = 0;     # don't want all groups if we requested one
  2687.                 }
  2688.                 $wantGroup{$family}{$groupName} = $want;
  2689.             }
  2690.         }
  2691.         # loop through all tags and decide which ones we want
  2692.         my (@tags, %bestTag);
  2693. GR_TAG: foreach $tag (@$rtnTags) {
  2694.             my $wantTag = $allGroups;   # want tag by default if want all groups
  2695.             foreach $family (keys %wantGroup) {
  2696.                 my $group = $self->GetGroup($tag, $family);
  2697.                 my $wanted = $wantGroup{$family}{$group};
  2698.                 next unless defined $wanted;
  2699.                 next GR_TAG unless $wanted;     # skip tag if group excluded
  2700.                 # take lowest non-zero want flag
  2701.                 next if $wantTag and $wantTag < $wanted;
  2702.                 $wantTag = $wanted;
  2703.             }
  2704.             next unless $wantTag;
  2705.             if ($duplicates) {
  2706.                 push @tags, $tag;
  2707.             } else {
  2708.                 my $tagName = GetTagName($tag);
  2709.                 my $bestTag = $bestTag{$tagName};
  2710.                 if (defined $bestTag) {
  2711.                     next if $wantTag > $keepTags{$bestTag};
  2712.                     if ($wantTag == $keepTags{$bestTag}) {
  2713.                         # want two tags with the same name -- keep the latest one
  2714.                         if ($tag =~ / \((\d+)\)$/) {
  2715.                             my $tagNum = $1;
  2716.                             next if $bestTag !~ / \((\d+)\)$/ or $1 > $tagNum;
  2717.                         }
  2718.                     }
  2719.                     # this tag is better, so delete old best tag
  2720.                     delete $keepTags{$bestTag};
  2721.                 }
  2722.                 $keepTags{$tag} = $wantTag;    # keep this tag (for now...)
  2723.                 $bestTag{$tagName} = $tag;      # this is our current best tag
  2724.             }
  2725.         }
  2726.         unless ($duplicates) {
  2727.             # construct new tag list with no duplicates, preserving order
  2728.             foreach $tag (@$rtnTags) {
  2729.                 push @tags, $tag if $keepTags{$tag};
  2730.             }
  2731.         }
  2732.         $rtnTags = \@tags;
  2733.         last;
  2734.     }
  2735.     $self->{FOUND_TAGS} = $rtnTags;     # save found tags 
  2736.  
  2737.     # return reference to found tag keys (and list of indices of tags to extract by value)
  2738.     return wantarray ? ($rtnTags, \@byValue) : $rtnTags;
  2739. }
  2740.  
  2741. #------------------------------------------------------------------------------
  2742. # Utility to load our write routines if required (called via AUTOLOAD)
  2743. # Inputs: 0) autoload function, 1-N) function arguments
  2744. # Returns: result of function or dies if function not available
  2745. # To Do: Generalize this routine so it works on systems that don't use '/'
  2746. #        as a path name separator.
  2747. sub DoAutoLoad(@)
  2748. {
  2749.     my $autoload = shift;
  2750.     my @callInfo = split(/::/, $autoload);
  2751.     my $file = 'Image/ExifTool/Write';
  2752.  
  2753.     return if $callInfo[$#callInfo] eq 'DESTROY';
  2754.     if (@callInfo == 4) {
  2755.         # load Image/ExifTool/WriteMODULE.pl
  2756.         $file .= "$callInfo[2].pl";
  2757.     } else {
  2758.         # load Image/ExifTool/Writer.pl
  2759.         $file .= 'r.pl';
  2760.     }
  2761.     # attempt to load the package
  2762.     eval "require '$file'" or die "Error while attempting to call $autoload\n$@\n";
  2763.     unless (defined &$autoload) {
  2764.         my @caller = caller(0);
  2765.         # reproduce Perl's standard 'undefined subroutine' message:
  2766.         die "Undefined subroutine $autoload called at $caller[1] line $caller[2]\n";
  2767.     }
  2768.     no strict 'refs';
  2769.     return &$autoload(@_);     # call the function
  2770. }
  2771.  
  2772. #------------------------------------------------------------------------------
  2773. # AutoLoad our writer routines when necessary
  2774. #
  2775. sub AUTOLOAD
  2776. {
  2777.     return DoAutoLoad($AUTOLOAD, @_);
  2778. }
  2779.  
  2780. #------------------------------------------------------------------------------
  2781. # Add warning tag
  2782. # Inputs: 0) ExifTool object reference, 1) warning message, 2) true if minor
  2783. # Returns: true if warning tag was added
  2784. sub Warn($$;$)
  2785. {
  2786.     my ($self, $str, $ignorable) = @_;
  2787.     if ($ignorable) {
  2788.         return 0 if $self->{OPTIONS}{IgnoreMinorErrors};
  2789.         $str = "[minor] $str";
  2790.     }
  2791.     $self->FoundTag('Warning', $str);
  2792.     return 1;
  2793. }
  2794.  
  2795. #------------------------------------------------------------------------------
  2796. # Add error tag
  2797. # Inputs: 0) ExifTool object reference, 1) error message, 2) true if minor
  2798. # Returns: true if error tag was added, otherwise warning was added
  2799. sub Error($$;$)
  2800. {
  2801.     my ($self, $str, $ignorable) = @_;
  2802.     if ($ignorable) {
  2803.         if ($self->{OPTIONS}{IgnoreMinorErrors}) {
  2804.             $self->Warn($str);
  2805.             return 0;
  2806.         }
  2807.         $str = "[minor] $str";
  2808.     }
  2809.     $self->FoundTag('Error', $str);
  2810.     return 1;
  2811. }
  2812.  
  2813. #------------------------------------------------------------------------------
  2814. # Expand shortcuts
  2815. # Inputs: 0) reference to list of tags, 1) set to remove trailing '#'
  2816. # Notes: Handles leading '-' for excluded tags, trailing '#' for ValueConv,
  2817. #        multiple group names, and redirected tags
  2818. sub ExpandShortcuts($;$)
  2819. {
  2820.     my ($tagList, $removeSuffix) = @_;
  2821.     return unless $tagList and @$tagList;
  2822.  
  2823.     require Image::ExifTool::Shortcuts;
  2824.  
  2825.     # expand shortcuts
  2826.     my $suffix = $removeSuffix ? '' : '#';
  2827.     my @expandedTags;
  2828.     my ($entry, $tag, $excl);
  2829.     foreach $entry (@$tagList) {
  2830.         # skip things like options hash references in list
  2831.         if (ref $entry) {
  2832.             push @expandedTags, $entry;
  2833.             next;
  2834.         }
  2835.         # remove leading '-'
  2836.         ($excl, $tag) = $entry =~ /^(-?)(.*)/s;
  2837.         my ($post, @post, $pre, $v);
  2838.         # handle redirection
  2839.         if (not $excl and $tag =~ /(.+?)([-+]?[<>].+)/s) {
  2840.             ($tag, $post) = ($1, $2);
  2841.             if ($post =~ /^[-+]?>/ or $post !~ /\$/) {
  2842.                 # expand shortcuts in postfix (rhs of redirection)
  2843.                 my ($op, $p2, $t2) = ($post =~ /([-+]?[<>])(.+:)?(.+)/);
  2844.                 $p2 = '' unless defined $p2;
  2845.                 $v = ($t2 =~ s/#$//) ? $suffix : ''; # ValueConv suffix
  2846.                 my ($match) = grep /^\Q$t2\E$/i, keys %Image::ExifTool::Shortcuts::Main;
  2847.                 if ($match) {
  2848.                     foreach (@{$Image::ExifTool::Shortcuts::Main{$match}}) {
  2849.                         /^-/ and next;  # ignore excluded tags
  2850.                         if ($p2 and /(.+:)(.+)/) {
  2851.                             push @post, "$op$_$v";
  2852.                         } else {
  2853.                             push @post, "$op$p2$_$v";
  2854.                         }
  2855.                     }
  2856.                     next unless @post;
  2857.                     $post = shift @post;
  2858.                 }
  2859.             }
  2860.         } else {
  2861.             $post = '';
  2862.         }
  2863.         # handle group names
  2864.         if ($tag =~ /(.+:)(.+)/) {
  2865.             ($pre, $tag) = ($1, $2);
  2866.         } else {
  2867.             $pre = '';
  2868.         }
  2869.         $v = ($tag =~ s/#$//) ? $suffix : '';   # ValueConv suffix
  2870.         # loop over all postfixes
  2871.         for (;;) {
  2872.             # expand the tag name
  2873.             my ($match) = grep /^\Q$tag\E$/i, keys %Image::ExifTool::Shortcuts::Main;
  2874.             if ($match) {
  2875.                 if ($excl) {
  2876.                     # entry starts with '-', so exclude all tags in this shortcut
  2877.                     foreach (@{$Image::ExifTool::Shortcuts::Main{$match}}) {
  2878.                         /^-/ and next;  # ignore excluded exclude tags
  2879.                         # group of expanded tag takes precedence
  2880.                         if ($pre and /(.+:)(.+)/) {
  2881.                             push @expandedTags, "$excl$_";
  2882.                         } else {
  2883.                             push @expandedTags, "$excl$pre$_";
  2884.                         }
  2885.                     }
  2886.                 } elsif (length $pre or length $post or $v) {
  2887.                     foreach (@{$Image::ExifTool::Shortcuts::Main{$match}}) {
  2888.                         /(-?)(.+:)?(.+)/;
  2889.                         if ($2) {
  2890.                             # group from expanded tag takes precedence
  2891.                             push @expandedTags, "$_$v$post";
  2892.                         } else {
  2893.                             push @expandedTags, "$1$pre$3$v$post";
  2894.                         }
  2895.                     }
  2896.                 } else {
  2897.                     push @expandedTags, @{$Image::ExifTool::Shortcuts::Main{$match}};
  2898.                 }
  2899.             } else {
  2900.                 push @expandedTags, "$excl$pre$tag$v$post";
  2901.             }
  2902.             last unless @post;
  2903.             $post = shift @post;
  2904.         }
  2905.     }
  2906.     @$tagList = @expandedTags;
  2907. }
  2908.  
  2909. #------------------------------------------------------------------------------
  2910. # Add hash of composite tags to our composites
  2911. # Inputs: 0) hash reference to table of composite tags to add or module name,
  2912. #         1) overwrite existing tag
  2913. sub AddCompositeTags($;$)
  2914. {
  2915.     local $_;
  2916.     my ($add, $overwrite) = @_;
  2917.     my $module;
  2918.     unless (ref $add) {
  2919.         $module = $add;
  2920.         $add .= '::Composite';
  2921.         no strict 'refs';
  2922.         $add = \%$add;
  2923.     }
  2924.     my $defaultGroups = $$add{GROUPS};
  2925.  
  2926.     # make sure default groups are defined in families 0 and 1
  2927.     if ($defaultGroups) {
  2928.         $defaultGroups->{0} or $defaultGroups->{0} = 'Composite';
  2929.         $defaultGroups->{1} or $defaultGroups->{1} = 'Composite';
  2930.         $defaultGroups->{2} or $defaultGroups->{2} = 'Other';
  2931.     } else {
  2932.         $defaultGroups = $$add{GROUPS} = { 0 => 'Composite', 1 => 'Composite', 2 => 'Other' };
  2933.     }
  2934.     SetupTagTable($add);    # generate tag Name, etc
  2935.     my $tagID;
  2936.     foreach $tagID (sort keys %$add) {
  2937.         next if $specialTags{$tagID};   # must skip special tags
  2938.         my $tagInfo = $$add{$tagID};
  2939.         # tagID's MUST be the exact tag name for logic in BuildCompositeTags()
  2940.         my $tag = $$tagInfo{Name};
  2941.         $$tagInfo{Module} = $module if $$tagInfo{Writable};
  2942.         # allow composite tags with the same name
  2943.         my ($t, $n, $type);
  2944.         while ($Image::ExifTool::Composite{$tag} and not $overwrite) {
  2945.             $n ? $n += 1 : ($n = 2, $t = $tag);
  2946.             $tag = "${t}_$n";
  2947.             $$tagInfo{NewTagID} = $tag; # save new ID so we can use it in TagLookup
  2948.         }
  2949.         # convert scalar Require/Desire entries
  2950.         foreach $type ('Require','Desire') {
  2951.             my $req = $$tagInfo{$type} or next;
  2952.             $$tagInfo{$type} = { 0 => $req } if ref($req) ne 'HASH';
  2953.         }
  2954.         # add this composite tag to our main composite table
  2955.         $$tagInfo{Table} = \%Image::ExifTool::Composite;
  2956.         # (use the original TagID, even if we changed it)
  2957.         # $$tagInfo{TagID} = $tag;
  2958.         # save new tag ID so we can find entry in Composite table
  2959.         $Image::ExifTool::Composite{$tag} = $tagInfo;
  2960.         # set all default groups in tag
  2961.         my $groups = $$tagInfo{Groups};
  2962.         $groups or $groups = $$tagInfo{Groups} = { };
  2963.         # fill in default groups
  2964.         foreach (keys %$defaultGroups) {
  2965.             $$groups{$_} or $$groups{$_} = $$defaultGroups{$_};
  2966.         }
  2967.         # set flag indicating group list was built
  2968.         $$tagInfo{GotGroups} = 1;
  2969.     }
  2970. }
  2971.  
  2972. #------------------------------------------------------------------------------
  2973. # Add tags to TagLookup (used for writing)
  2974. # Inputs: 0) source hash of tag definitions, 1) name of destination tag table
  2975. sub AddTagsToLookup($$)
  2976. {
  2977.     my ($tagHash, $table) = @_;
  2978.     if (defined &Image::ExifTool::TagLookup::AddTags) {
  2979.         Image::ExifTool::TagLookup::AddTags($tagHash, $table);
  2980.     } elsif (not $Image::ExifTool::pluginTags{$tagHash}) {
  2981.         # queue these tags until TagLookup is loaded
  2982.         push @Image::ExifTool::pluginTags, [ $tagHash, $table ];
  2983.         # set flag so we don't load same tags twice
  2984.         $Image::ExifTool::pluginTags{$tagHash} = 1;
  2985.     }
  2986. }
  2987.  
  2988. #------------------------------------------------------------------------------
  2989. # Expand tagInfo Flags
  2990. # Inputs: 0) tagInfo hash ref
  2991. # Notes: $$tagInfo{Flags} must be defined to call this routine
  2992. sub ExpandFlags($)
  2993. {
  2994.     my $tagInfo = shift;
  2995.     my $flags = $$tagInfo{Flags};
  2996.     if (ref $flags eq 'ARRAY') {
  2997.         foreach (@$flags) {
  2998.             $$tagInfo{$_} = 1;
  2999.         }
  3000.     } elsif (ref $flags eq 'HASH') {
  3001.         my $key;
  3002.         foreach $key (keys %$flags) {
  3003.             $$tagInfo{$key} = $$flags{$key};
  3004.         }
  3005.     } else {
  3006.         $$tagInfo{$flags} = 1;
  3007.     }
  3008. }
  3009.  
  3010. #------------------------------------------------------------------------------
  3011. # Set up tag table (must be done once for each tag table used)
  3012. # Inputs: 0) Reference to tag table
  3013. # Notes: - generates 'Name' field from key if it doesn't exist
  3014. #        - stores 'Table' pointer
  3015. #        - expands 'Flags' for quick lookup
  3016. sub SetupTagTable($)
  3017. {
  3018.     my $tagTablePtr = shift;
  3019.     my ($tagID, $tagInfo);
  3020.     foreach $tagID (TagTableKeys($tagTablePtr)) {
  3021.         my @infoArray = GetTagInfoList($tagTablePtr,$tagID);
  3022.         # process conditional tagInfo arrays
  3023.         foreach $tagInfo (@infoArray) {
  3024.             $$tagInfo{Table} = $tagTablePtr;
  3025.             $$tagInfo{TagID} = $tagID;
  3026.             my $tag = $$tagInfo{Name};
  3027.             unless (defined $tag) {
  3028.                 # generate name equal to tag ID if 'Name' doesn't exist
  3029.                 $tag = $tagID;
  3030.                 $$tagInfo{Name} = ucfirst($tag); # make first char uppercase
  3031.             }
  3032.             $$tagInfo{Flags} and ExpandFlags($tagInfo);
  3033.         }
  3034.         next unless @infoArray > 1;
  3035.         # add an "Index" member to each tagInfo in a list
  3036.         my $index = 0;
  3037.         foreach $tagInfo (@infoArray) {
  3038.             $$tagInfo{Index} = $index++;
  3039.         }
  3040.     }
  3041. }
  3042.  
  3043. #------------------------------------------------------------------------------
  3044. # Utilities to check for numerical types
  3045. # Inputs: 0) value;  Returns: true if value is a numerical type
  3046. # Notes: May change commas to decimals in floats for use in other locales
  3047. sub IsFloat($) {
  3048.     return 1 if $_[0] =~ /^[+-]?(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/;
  3049.     # allow comma separators (for other locales)
  3050.     return 0 unless $_[0] =~ /^[+-]?(?=\d|,\d)\d*(,\d*)?([Ee]([+-]?\d+))?$/;
  3051.     $_[0] =~ tr/,/./;   # but translate ',' to '.'
  3052.     return 1;
  3053. }
  3054. sub IsInt($)   { return scalar($_[0] =~ /^[+-]?\d+$/); }
  3055. sub IsHex($)   { return scalar($_[0] =~ /^(0x)?[0-9a-f]{1,8}$/i); }
  3056.  
  3057. # round floating point value to specified number of significant digits
  3058. # Inputs: 0) value, 1) number of sig digits;  Returns: rounded number
  3059. sub RoundFloat($$)
  3060. {
  3061.     my ($val, $sig) = @_;
  3062.     $val == 0 and return 0;
  3063.     my $sign = $val < 0 ? ($val=-$val, -1) : 1;
  3064.     my $log = log($val) / log(10);
  3065.     my $exp = int($log) - $sig + ($log > 0 ? 1 : 0);
  3066.     return $sign * int(10 ** ($log - $exp) + 0.5) * 10 ** $exp;
  3067. }
  3068.  
  3069. # Convert strings to floating point numbers (or undef)
  3070. # Inputs: 0-N) list of strings (may be undef)
  3071. # Returns: last value converted
  3072. sub ToFloat(@)
  3073. {
  3074.     local $_;
  3075.     foreach (@_) {
  3076.         next unless defined $_;
  3077.         # (add 0 to convert "0.0" to "0" for tests)
  3078.         $_ = /((?:[+-]?)(?=\d|\.\d)\d*(?:\.\d*)?(?:[Ee](?:[+-]?\d+))?)/ ? $1 + 0 : undef;
  3079.     }
  3080.     return $_[-1];
  3081. }
  3082.  
  3083. #------------------------------------------------------------------------------
  3084. # Utility routines to for reading binary data values from file
  3085.  
  3086. my %unpackMotorola = ( S => 'n', L => 'N', C => 'C', c => 'c' );
  3087. my %unpackIntel    = ( S => 'v', L => 'V', C => 'C', c => 'c' );
  3088. my %unpackRev = ( N => 'V', V => 'N', C => 'C', n => 'v', v => 'n', c => 'c' );
  3089.  
  3090. # the following 4 variables are defined in 'use vars' instead of using 'my'
  3091. # because mod_perl 5.6.1 apparently has a problem with setting file-scope 'my'
  3092. # variables from within subroutines (ref communication with Pavel Merdin):
  3093. # $swapBytes - set if EXIF header is not native byte ordering
  3094. # $swapWords - swap 32-bit words in doubles (ARM quirk)
  3095. $currentByteOrder = 'MM'; # current byte ordering ('II' or 'MM')
  3096. %unpackStd = %unpackMotorola;
  3097.  
  3098. # Swap bytes in data if necessary
  3099. # Inputs: 0) data, 1) number of bytes
  3100. # Returns: swapped data
  3101. sub SwapBytes($$)
  3102. {
  3103.     return $_[0] unless $swapBytes;
  3104.     my ($val, $bytes) = @_;
  3105.     my $newVal = '';
  3106.     $newVal .= substr($val, $bytes, 1) while $bytes--;
  3107.     return $newVal;
  3108. }
  3109. # Swap words.  Inputs: 8 bytes of data, Returns: swapped data
  3110. sub SwapWords($)
  3111. {
  3112.     return $_[0] unless $swapWords and length($_[0]) == 8;
  3113.     return substr($_[0],4,4) . substr($_[0],0,4)
  3114. }
  3115.  
  3116. # Unpack value, letting unpack() handle byte swapping
  3117. # Inputs: 0) unpack template, 1) data reference, 2) offset
  3118. # Returns: unpacked number
  3119. # - uses value of %unpackStd to determine the unpack template
  3120. # - can only be called for 'S' or 'L' templates since these are the only
  3121. #   templates for which you can specify the byte ordering.
  3122. sub DoUnpackStd(@)
  3123. {
  3124.     $_[2] and return unpack("x$_[2] $unpackStd{$_[0]}", ${$_[1]});
  3125.     return unpack($unpackStd{$_[0]}, ${$_[1]});
  3126. }
  3127. # same, but with reversed byte order
  3128. sub DoUnpackRev(@)
  3129. {
  3130.     my $fmt = $unpackRev{$unpackStd{$_[0]}};
  3131.     $_[2] and return unpack("x$_[2] $fmt", ${$_[1]});
  3132.     return unpack($fmt, ${$_[1]});
  3133. }
  3134. # Pack value
  3135. # Inputs: 0) template, 1) value, 2) data ref (or undef), 3) offset (if data ref)
  3136. # Returns: packed value
  3137. sub DoPackStd(@)
  3138. {
  3139.     my $val = pack($unpackStd{$_[0]}, $_[1]);
  3140.     $_[2] and substr(${$_[2]}, $_[3], length($val)) = $val;
  3141.     return $val;
  3142. }
  3143. # same, but with reversed byte order
  3144. sub DoPackRev(@)
  3145. {
  3146.     my $val = pack($unpackRev{$unpackStd{$_[0]}}, $_[1]);
  3147.     $_[2] and substr(${$_[2]}, $_[3], length($val)) = $val;
  3148.     return $val;
  3149. }
  3150.  
  3151. # Unpack value, handling the byte swapping manually
  3152. # Inputs: 0) # bytes, 1) unpack template, 2) data reference, 3) offset
  3153. # Returns: unpacked number
  3154. # - uses value of $swapBytes to determine byte ordering
  3155. sub DoUnpack(@)
  3156. {
  3157.     my ($bytes, $template, $dataPt, $pos) = @_;
  3158.     my $val;
  3159.     if ($swapBytes) {
  3160.         $val = '';
  3161.         $val .= substr($$dataPt,$pos+$bytes,1) while $bytes--;
  3162.     } else {
  3163.         $val = substr($$dataPt,$pos,$bytes);
  3164.     }
  3165.     defined($val) or return undef;
  3166.     return unpack($template,$val);
  3167. }
  3168.  
  3169. # Unpack double value
  3170. # Inputs: 0) unpack template, 1) data reference, 2) offset
  3171. # Returns: unpacked number
  3172. sub DoUnpackDbl(@)
  3173. {
  3174.     my ($template, $dataPt, $pos) = @_;
  3175.     my $val = substr($$dataPt,$pos,8);
  3176.     defined($val) or return undef;
  3177.     # swap bytes and 32-bit words (ARM quirk) if necessary, then unpack value
  3178.     return unpack($template, SwapWords(SwapBytes($val, 8)));
  3179. }
  3180.  
  3181. # Inputs: 0) data reference, 1) offset into data
  3182. sub Get8s($$)     { return DoUnpackStd('c', @_); }
  3183. sub Get8u($$)     { return DoUnpackStd('C', @_); }
  3184. sub Get16s($$)    { return DoUnpack(2, 's', @_); }
  3185. sub Get16u($$)    { return DoUnpackStd('S', @_); }
  3186. sub Get32s($$)    { return DoUnpack(4, 'l', @_); }
  3187. sub Get32u($$)    { return DoUnpackStd('L', @_); }
  3188. sub GetFloat($$)  { return DoUnpack(4, 'f', @_); }
  3189. sub GetDouble($$) { return DoUnpackDbl('d', @_); }
  3190. sub Get16uRev($$) { return DoUnpackRev('S', @_); }
  3191.  
  3192. # rationals may be a floating point number, 'inf' or 'undef'
  3193. sub GetRational32s($$)
  3194. {
  3195.     my ($dataPt, $pos) = @_;
  3196.     my $numer = Get16s($dataPt,$pos);
  3197.     my $denom = Get16s($dataPt, $pos + 2) or return $numer ? 'inf' : 'undef';
  3198.     # round off to a reasonable number of significant figures
  3199.     return RoundFloat($numer / $denom, 7);
  3200. }
  3201. sub GetRational32u($$)
  3202. {
  3203.     my ($dataPt, $pos) = @_;
  3204.     my $numer = Get16u($dataPt,$pos);
  3205.     my $denom = Get16u($dataPt, $pos + 2) or return $numer ? 'inf' : 'undef';
  3206.     return RoundFloat($numer / $denom, 7);
  3207. }
  3208. sub GetRational64s($$)
  3209. {
  3210.     my ($dataPt, $pos) = @_;
  3211.     my $numer = Get32s($dataPt,$pos);
  3212.     my $denom = Get32s($dataPt, $pos + 4) or return $numer ? 'inf' : 'undef';
  3213.     return RoundFloat($numer / $denom, 10);
  3214. }
  3215. sub GetRational64u($$)
  3216. {
  3217.     my ($dataPt, $pos) = @_;
  3218.     my $numer = Get32u($dataPt,$pos);
  3219.     my $denom = Get32u($dataPt, $pos + 4) or return $numer ? 'inf' : 'undef';
  3220.     return RoundFloat($numer / $denom, 10);
  3221. }
  3222. sub GetFixed16s($$)
  3223. {
  3224.     my ($dataPt, $pos) = @_;
  3225.     my $val = Get16s($dataPt, $pos) / 0x100;
  3226.     return int($val * 1000 + ($val<0 ? -0.5 : 0.5)) / 1000;
  3227. }
  3228. sub GetFixed16u($$)
  3229. {
  3230.     my ($dataPt, $pos) = @_;
  3231.     return int((Get16u($dataPt, $pos) / 0x100) * 1000 + 0.5) / 1000;
  3232. }
  3233. sub GetFixed32s($$)
  3234. {
  3235.     my ($dataPt, $pos) = @_;
  3236.     my $val = Get32s($dataPt, $pos) / 0x10000;
  3237.     # remove insignificant digits
  3238.     return int($val * 1e5 + ($val>0 ? 0.5 : -0.5)) / 1e5;
  3239. }
  3240. sub GetFixed32u($$)
  3241. {
  3242.     my ($dataPt, $pos) = @_;
  3243.     # remove insignificant digits
  3244.     return int((Get32u($dataPt, $pos) / 0x10000) * 1e5 + 0.5) / 1e5;
  3245. }
  3246. # Inputs: 0) value, 1) data ref, 2) offset
  3247. sub Set8s(@)  { return DoPackStd('c', @_); }
  3248. sub Set8u(@)  { return DoPackStd('C', @_); }
  3249. sub Set16u(@) { return DoPackStd('S', @_); }
  3250. sub Set32u(@) { return DoPackStd('L', @_); }
  3251. sub Set16uRev(@) { return DoPackRev('S', @_); }
  3252.  
  3253. #------------------------------------------------------------------------------
  3254. # Get current byte order ('II' or 'MM')
  3255. sub GetByteOrder() { return $currentByteOrder; }
  3256.  
  3257. #------------------------------------------------------------------------------
  3258. # Set byte ordering
  3259. # Inputs: 0) 'MM'=motorola, 'II'=intel (will translate 'BigEndian', 'LittleEndian')
  3260. # Returns: 1 on success
  3261. sub SetByteOrder($)
  3262. {
  3263.     my $order = shift;
  3264.  
  3265.     if ($order eq 'MM') {       # big endian (Motorola)
  3266.         %unpackStd = %unpackMotorola;
  3267.     } elsif ($order eq 'II') {  # little endian (Intel)
  3268.         %unpackStd = %unpackIntel;
  3269.     } elsif ($order =~ /^Big/i) {
  3270.         $order = 'MM';
  3271.         %unpackStd = %unpackMotorola;
  3272.     } elsif ($order =~ /^Little/i) {
  3273.         $order = 'II';
  3274.         %unpackStd = %unpackIntel;
  3275.     } else {
  3276.         return 0;
  3277.     }
  3278.     my $val = unpack('S','A ');
  3279.     my $nativeOrder;
  3280.     if ($val == 0x4120) {       # big endian
  3281.         $nativeOrder = 'MM';
  3282.     } elsif ($val == 0x2041) {  # little endian
  3283.         $nativeOrder = 'II';
  3284.     } else {
  3285.         warn sprintf("Unknown native byte order! (pattern %x)\n",$val);
  3286.         return 0;
  3287.     }
  3288.     $currentByteOrder = $order;  # save current byte order
  3289.  
  3290.     # swap bytes if our native CPU byte ordering is not the same as the EXIF
  3291.     $swapBytes = ($order ne $nativeOrder);
  3292.  
  3293.     # little-endian ARM has big-endian words for doubles (thanks Riku Voipio)
  3294.     # (Note: Riku's patch checked for '0ff3', but I think it should be 'f03f' since
  3295.     # 1 is '000000000000f03f' on an x86 -- so check for both, but which is correct?)
  3296.     my $pack1d = pack('d', 1);
  3297.     $swapWords = ($pack1d eq "\0\0\x0f\xf3\0\0\0\0" or
  3298.                   $pack1d eq "\0\0\xf0\x3f\0\0\0\0");
  3299.     return 1;
  3300. }
  3301.  
  3302. #------------------------------------------------------------------------------
  3303. # Change byte order
  3304. sub ToggleByteOrder()
  3305. {
  3306.     SetByteOrder(GetByteOrder() eq 'II' ? 'MM' : 'II');
  3307. }
  3308.  
  3309. #------------------------------------------------------------------------------
  3310. # hash lookups for reading values from data
  3311. my %formatSize = (
  3312.     int8s => 1,
  3313.     int8u => 1,
  3314.     int16s => 2,
  3315.     int16u => 2,
  3316.     int16uRev => 2,
  3317.     int32s => 4,
  3318.     int32u => 4,
  3319.     int64s => 8,
  3320.     int64u => 8,
  3321.     rational32s => 4,
  3322.     rational32u => 4,
  3323.     rational64s => 8,
  3324.     rational64u => 8,
  3325.     fixed16s => 2,
  3326.     fixed16u => 2,
  3327.     fixed32s => 4,
  3328.     fixed32u => 4,
  3329.     float => 4,
  3330.     double => 8,
  3331.     extended => 10,
  3332.     string => 1,
  3333.     binary => 1,
  3334.    'undef' => 1,
  3335.     ifd => 4,
  3336.     ifd64 => 8,
  3337. );
  3338. my %readValueProc = (
  3339.     int8s => \&Get8s,
  3340.     int8u => \&Get8u,
  3341.     int16s => \&Get16s,
  3342.     int16u => \&Get16u,
  3343.     int16uRev => \&Get16uRev,
  3344.     int32s => \&Get32s,
  3345.     int32u => \&Get32u,
  3346.     int64s => \&Get64s,
  3347.     int64u => \&Get64u,
  3348.     rational32s => \&GetRational32s,
  3349.     rational32u => \&GetRational32u,
  3350.     rational64s => \&GetRational64s,
  3351.     rational64u => \&GetRational64u,
  3352.     fixed16s => \&GetFixed16s,
  3353.     fixed16u => \&GetFixed16u,
  3354.     fixed32s => \&GetFixed32s,
  3355.     fixed32u => \&GetFixed32u,
  3356.     float => \&GetFloat,
  3357.     double => \&GetDouble,
  3358.     extended => \&GetExtended,
  3359.     ifd => \&Get32u,
  3360.     ifd64 => \&Get64u,
  3361. );
  3362. sub FormatSize($) { return $formatSize{$_[0]}; }
  3363.  
  3364. #------------------------------------------------------------------------------
  3365. # Read value from binary data (with current byte ordering)
  3366. # Inputs: 0) data reference, 1) value offset, 2) format string,
  3367. #         3) number of values (or undef to use all data)
  3368. #         4) valid data length relative to offset
  3369. # Returns: converted value, or undefined if data isn't there
  3370. #          or list of values in list context
  3371. sub ReadValue($$$$$)
  3372. {
  3373.     my ($dataPt, $offset, $format, $count, $size) = @_;
  3374.  
  3375.     my $len = $formatSize{$format};
  3376.     unless ($len) {
  3377.         warn "Unknown format $format";
  3378.         $len = 1;
  3379.     }
  3380.     unless ($count) {
  3381.         return '' if defined $count or $size < $len;
  3382.         $count = int($size / $len);
  3383.     }
  3384.     # make sure entry is inside data
  3385.     if ($len * $count > $size) {
  3386.         $count = int($size / $len);     # shorten count if necessary
  3387.         $count < 1 and return undef;    # return undefined if no data
  3388.     }
  3389.     my @vals;
  3390.     my $proc = $readValueProc{$format};
  3391.     if ($proc) {
  3392.         for (;;) {
  3393.             push @vals, &$proc($dataPt, $offset);
  3394.             last if --$count <= 0;
  3395.             $offset += $len;
  3396.         }
  3397.     } else {
  3398.         # handle undef/binary/string
  3399.         $vals[0] = substr($$dataPt, $offset, $count);
  3400.         # truncate string at null terminator if necessary
  3401.         $vals[0] =~ s/\0.*//s if $format eq 'string';
  3402.     }
  3403.     return @vals if wantarray;
  3404.     return join(' ', @vals) if @vals > 1;
  3405.     return $vals[0];
  3406. }
  3407.  
  3408. #------------------------------------------------------------------------------
  3409. # Decode string with specified encoding
  3410. # Inputs: 0) ExifTool object ref, 1) string to decode
  3411. #         2) source character set name (undef for current Charset)
  3412. #         3) optional source byte order (2-byte and 4-byte fixed-width sets only)
  3413. #         4) optional destination character set (defaults to Charset setting)
  3414. #         5) optional destination byte order (2-byte and 4-byte fixed-width only)
  3415. # Returns: string in destination encoding
  3416. # Note: ExifTool ref may be undef if character both character sets are provided
  3417. #       (but in this case no warnings will be issued)
  3418. sub Decode($$$;$$$)
  3419. {
  3420.     my ($self, $val, $from, $fromOrder, $to, $toOrder) = @_;
  3421.     $from or $from = $$self{OPTIONS}{Charset};
  3422.     $to or $to = $$self{OPTIONS}{Charset};
  3423.     if ($from ne $to and length $val) {
  3424.         require Image::ExifTool::Charset;
  3425.         my $cs1 = $Image::ExifTool::Charset::csType{$from};
  3426.         my $cs2 = $Image::ExifTool::Charset::csType{$to};
  3427.         if ($cs1 and $cs2 and not $cs2 & 0x002) {
  3428.             # treat as straight ASCII if no character will need remapping
  3429.             if (($cs1 | $cs2) & 0x680 or $val =~ /[\x80-\xff]/) {
  3430.                 my $uni = Image::ExifTool::Charset::Decompose($self, $val, $from, $fromOrder);
  3431.                 $val = Image::ExifTool::Charset::Recompose($self, $uni, $to, $toOrder);
  3432.             }
  3433.         } elsif ($self) {
  3434.             my $set = $cs1 ? $from : $to;
  3435.             unless ($$self{"DecodeWarn$set"}) {
  3436.                 $self->Warn("Unsupported character set ($set)");
  3437.                 $$self{"DecodeWarn$set"} = 1;
  3438.             }
  3439.         }
  3440.     }
  3441.     return $val;
  3442. }
  3443.  
  3444. #------------------------------------------------------------------------------
  3445. # Encode string with specified encoding
  3446. # Inputs: 0) ExifTool object ref, 1) string, 2) destination character set name,
  3447. #         3) optional destination byte order (2-byte and 4-byte fixed-width sets only)
  3448. # Returns: string in specified encoding
  3449. sub Encode($$$;$)
  3450. {
  3451.     my ($self, $val, $to, $toOrder) = @_;
  3452.     return $self->Decode($val, undef, undef, $to, $toOrder);
  3453. }
  3454.  
  3455. #------------------------------------------------------------------------------
  3456. # Decode bit mask
  3457. # Inputs: 0) value to decode, 1) Reference to hash for decoding (or undef)
  3458. #         2) optional bits per word (defaults to 32)
  3459. sub DecodeBits($$;$)
  3460. {
  3461.     my ($vals, $lookup, $bits) = @_;
  3462.     $bits or $bits = 32;
  3463.     my ($val, $i, @bitList);
  3464.     my $num = 0;
  3465.     foreach $val (split ' ', $vals) {
  3466.         for ($i=0; $i<$bits; ++$i) {
  3467.             next unless $val & (1 << $i);
  3468.             my $n = $i + $num;
  3469.             if (not $lookup) {
  3470.                 push @bitList, $n;
  3471.             } elsif ($$lookup{$n}) {
  3472.                 push @bitList, $$lookup{$n};
  3473.             } else {
  3474.                 push @bitList, "[$n]";
  3475.             }
  3476.         }
  3477.         $num += $bits;
  3478.     }
  3479.     return '(none)' unless @bitList;
  3480.     return join($lookup ? ', ' : ',', @bitList);
  3481. }
  3482.  
  3483. #------------------------------------------------------------------------------
  3484. # Validate an extracted image and repair if necessary
  3485. # Inputs: 0) ExifTool object reference, 1) image reference, 2) tag name or key
  3486. # Returns: image reference or undef if it wasn't valid
  3487. # Note: should be called from RawConv, not ValueConv
  3488. sub ValidateImage($$$)
  3489. {
  3490.     my ($self, $imagePt, $tag) = @_;
  3491.     return undef if $$imagePt eq 'none';
  3492.     unless ($$imagePt =~ /^(Binary data|\xff\xd8\xff)/ or
  3493.             # the first byte of the preview of some Minolta cameras is wrong,
  3494.             # so check for this and set it back to 0xff if necessary
  3495.             $$imagePt =~ s/^.(\xd8\xff\xdb)/\xff$1/s or
  3496.             $self->Options('IgnoreMinorErrors'))
  3497.     {
  3498.         # issue warning only if the tag was specifically requested
  3499.         if ($self->{REQ_TAG_LOOKUP}{lc GetTagName($tag)}) {
  3500.             $self->Warn("$tag is not a valid JPEG image",1);
  3501.             return undef;
  3502.         }
  3503.     }
  3504.     return $imagePt;
  3505. }
  3506.  
  3507. #------------------------------------------------------------------------------
  3508. # Make description from a tag name
  3509. # Inputs: 0) tag name 1) optional tagID to add at end of description
  3510. # Returns: description
  3511. sub MakeDescription($;$)
  3512. {
  3513.     my ($tag, $tagID) = @_;
  3514.     # start with the tag name and force first letter to be upper case
  3515.     my $desc = ucfirst($tag);
  3516.     $desc =~ tr/_/ /;       # translate underlines to spaces
  3517.     # put a space between lower/UPPER case and lower/number combinations
  3518.     $desc =~ s/([a-z])([A-Z\d])/$1 $2/g;
  3519.     # put a space between acronyms and words
  3520.     $desc =~ s/([A-Z])([A-Z][a-z])/$1 $2/g;
  3521.     # put spaces after numbers (if more than one character following number)
  3522.     $desc =~ s/(\d)([A-Z]\S)/$1 $2/g;
  3523.     # remove space in hex number
  3524.     $desc =~ s/ 0x ([\dA-Fa-f])/ 0x$1/g;
  3525.     $desc .= ' ' . $tagID if defined $tagID;
  3526.     return $desc;
  3527. }
  3528.  
  3529. #------------------------------------------------------------------------------
  3530. # Return printable value
  3531. # Inputs: 0) ExifTool object reference
  3532. #         1) value to print, 2) true for unlimited line length
  3533. sub Printable($;$)
  3534. {
  3535.     my ($self, $outStr, $unlimited) = @_;
  3536.     return '(undef)' unless defined $outStr;
  3537.     $outStr =~ tr/\x01-\x1f\x7f-\xff/./;
  3538.     $outStr =~ s/\x00//g;
  3539.     # limit length if verbose < 4
  3540.     if (length($outStr) > 60 and not $unlimited and $self->{OPTIONS}{Verbose} < 4) {
  3541.         $outStr = substr($outStr,0,54) . '[snip]';
  3542.     }
  3543.     return $outStr;
  3544. }
  3545.  
  3546. #------------------------------------------------------------------------------
  3547. # Convert date/time from Exif format
  3548. # Inputs: 0) ExifTool object reference, 1) Date/time in EXIF format
  3549. # Returns: Formatted date/time string
  3550. sub ConvertDateTime($$)
  3551. {
  3552.     my ($self, $date) = @_;
  3553.     my $dateFormat = $self->{OPTIONS}{DateFormat};
  3554.     # only convert date if a format was specified and the date is recognizable
  3555.     if ($dateFormat) {
  3556.         # a few cameras use incorrect date/time formatting:
  3557.         # - slashes instead of colons in date (RolleiD330, ImpressCam)
  3558.         # - date/time values separated by colon instead of space (Polariod, Sanyo, Sharp, Vivitar)
  3559.         # - single-digit seconds with leading space (HP scanners)
  3560.         $date =~ s/[-+]\d{2}:\d{2}$//;  # remove timezone if it exists
  3561.         my @a = ($date =~ /\d+/g);      # be very flexible about date/time format
  3562.         if (@a and $a[0] > 1900 and $a[0] < 3000 and eval 'require POSIX') {
  3563.             $date = POSIX::strftime($dateFormat, $a[5]||0, $a[4]||0, $a[3]||0,
  3564.                                                  $a[2]||1, ($a[1]||1)-1, $a[0]-1900);
  3565.         } elsif ($self->{OPTIONS}{StrictDate}) {
  3566.             undef $date;
  3567.         }
  3568.     }
  3569.     return $date;
  3570. }
  3571.  
  3572. #------------------------------------------------------------------------------
  3573. # Patched timelocal() that fixes ActivePerl timezone bug
  3574. # Inputs/Returns: same as timelocal()
  3575. # Notes: must 'require Time::Local' before calling this routine
  3576. sub TimeLocal(@)
  3577. {
  3578.     my $tm = Time::Local::timelocal(@_);
  3579.     if ($^O eq 'MSWin32') {
  3580.         # patch for ActivePerl timezone bug
  3581.         my @t2 = localtime($tm);
  3582.         my $t2 = Time::Local::timelocal(@t2);
  3583.         # adjust timelocal() return value to be consistent with localtime()
  3584.         $tm += $tm - $t2;
  3585.     }
  3586.     return $tm;
  3587. }
  3588.  
  3589. #------------------------------------------------------------------------------
  3590. # Get time zone in minutes
  3591. # Inputs: 0) localtime array ref, 1) gmtime array ref
  3592. # Returns: time zone offset in minutes
  3593. sub GetTimeZone(;$$)
  3594. {
  3595.     my ($tm, $gm) = @_;
  3596.     # compute the number of minutes between localtime and gmtime
  3597.     my $min = $$tm[2] * 60 + $$tm[1] - ($$gm[2] * 60 + $$gm[1]);
  3598.     if ($$tm[3] != $$gm[3]) {
  3599.         # account for case where one date wraps to the first of the next month
  3600.         $$gm[3] = $$tm[3] - ($$tm[3]==1 ? 1 : -1) if abs($$tm[3]-$$gm[3]) != 1;
  3601.         # adjust for the +/- one day difference
  3602.         $min += ($$tm[3] - $$gm[3]) * 24 * 60;
  3603.     }
  3604.     return $min;
  3605. }
  3606.  
  3607. #------------------------------------------------------------------------------
  3608. # Get time zone string
  3609. # Inputs: 0) time zone offset in minutes
  3610. #     or  0) localtime array ref, 1) corresponding time value
  3611. # Returns: time zone string ("+/-HH:MM")
  3612. sub TimeZoneString($;$)
  3613. {
  3614.     my $min = shift;
  3615.     if (ref $min) {
  3616.         my @gm = gmtime(shift);
  3617.         $min = GetTimeZone($min, \@gm);
  3618.     }
  3619.     my $sign = '+';
  3620.     $min < 0 and $sign = '-', $min = -$min;
  3621.     my $h = int($min / 60);
  3622.     return sprintf('%s%.2d:%.2d', $sign, $h, $min - $h * 60);
  3623. }
  3624.  
  3625. #------------------------------------------------------------------------------
  3626. # Convert Unix time to EXIF date/time string
  3627. # Inputs: 0) Unix time value, 1) non-zero to convert to local time
  3628. # Returns: EXIF date/time string (with timezone for local times)
  3629. # Notes: fractional seconds are ignored
  3630. sub ConvertUnixTime($;$)
  3631. {
  3632.     my ($time, $toLocal) = @_;
  3633.     return '0000:00:00 00:00:00' if $time == 0;
  3634.     my (@tm, $tz);
  3635.     if ($toLocal) {
  3636.         @tm = localtime($time);
  3637.         $tz = TimeZoneString(\@tm, $time);
  3638.     } else {
  3639.         @tm = gmtime($time);
  3640.         $tz = '';
  3641.     }
  3642.     my $str = sprintf("%4d:%.2d:%.2d %.2d:%.2d:%.2d%s",
  3643.                       $tm[5]+1900, $tm[4]+1, $tm[3], $tm[2], $tm[1], $tm[0], $tz);
  3644.     return $str;
  3645. }
  3646.  
  3647. #------------------------------------------------------------------------------
  3648. # Print conversion for file size
  3649. # Inputs: 0) file size in bytes
  3650. # Returns: converted file size
  3651. sub ConvertFileSize($)
  3652. {
  3653.     my $val = shift;
  3654.     $val < 2048 and return "$val bytes";
  3655.     $val < 10240 and return sprintf('%.1f kB', $val / 1024);
  3656.     $val < 2097152 and return sprintf('%.0f kB', $val / 1024);
  3657.     $val < 10485760 and return sprintf('%.1f MB', $val / 1048576);
  3658.     return sprintf('%.0f MB', $val / 1048576);
  3659. }
  3660.  
  3661. #------------------------------------------------------------------------------
  3662. # Get Unix time from EXIF-formatted date/time string with optional timezone
  3663. # Inputs: 0) EXIF date/time string, 1) non-zero if time is local
  3664. # Returns: Unix time (seconds since 0:00 GMT Jan 1, 1970) or undefined on error
  3665. sub GetUnixTime($;$)
  3666. {
  3667.     my ($timeStr, $isLocal) = @_;
  3668.     return 0 if $timeStr eq '0000:00:00 00:00:00';
  3669.     my @tm = ($timeStr =~ /^(\d+):(\d+):(\d+)\s+(\d+):(\d+):(\d+)/);
  3670.     return undef unless @tm == 6 and eval 'require Time::Local';
  3671.     my $tzsec = 0;
  3672.     # use specified timezone offset (if given) instead of local system time
  3673.     # if we are converting a local time value
  3674.     if ($isLocal and $timeStr =~ /(?:Z|([-+])(\d+):(\d+))$/i) {
  3675.         # use specified timezone if one exists
  3676.         $tzsec = ($2 * 60 + $3) * ($1 eq '-' ? -60 : 60) if $1;
  3677.         undef $isLocal; # convert using GMT corrected for specified timezone
  3678.     }
  3679.     $tm[0] -= 1900;     # convert year
  3680.     $tm[1] -= 1;        # convert month
  3681.     @tm = reverse @tm;  # change to order required by timelocal()
  3682.     return $isLocal ? TimeLocal(@tm) : Time::Local::timegm(@tm) - $tzsec;
  3683. }
  3684.  
  3685. #------------------------------------------------------------------------------
  3686. # Convert seconds to duration string
  3687. # Inputs: 0) floating point seconds
  3688. # Returns: duration string in form "S.SS s", "MM:SS" or "H:MM:SS"
  3689. sub ConvertDuration($)
  3690. {
  3691.     my $time = shift;
  3692.     return $time unless IsFloat($time);
  3693.     return '0 s' if $time == 0;
  3694.     return sprintf('%.2f s', $time) if $time < 30;
  3695.     my $str = '';
  3696.     if ($time >= 3600) {
  3697.         my $h = int($time / 3600);
  3698.         $str = "$h:";
  3699.         $time -= $h * 3600;
  3700.     }
  3701.     my $m = int($time / 60);
  3702.     $time -= $m * 60;
  3703.     return sprintf('%s%.2d:%.2d', $str, $m, int($time));
  3704. }
  3705.  
  3706. #------------------------------------------------------------------------------
  3707. # Save information for HTML dump
  3708. # Inputs: 0) ExifTool hash ref, 1) start offset, 2) data size
  3709. #         3) comment string, 4) tool tip (or SAME), 5) flags
  3710. sub HDump($$$$;$$)
  3711. {
  3712.     my $self = shift;
  3713.     my $pos = shift;
  3714.     $pos += $$self{BASE} if $$self{BASE};
  3715.     $$self{HTML_DUMP} and $self->{HTML_DUMP}->Add($pos, @_);
  3716. }
  3717.  
  3718. #------------------------------------------------------------------------------
  3719. # JPEG constants
  3720. my %jpegMarker = (
  3721.     0x01 => 'TEM',
  3722.     0xc0 => 'SOF0', # to SOF15, with a few exceptions below
  3723.     0xc4 => 'DHT',
  3724.     0xc8 => 'JPGA',
  3725.     0xcc => 'DAC',
  3726.     0xd0 => 'RST0',
  3727.     0xd8 => 'SOI',
  3728.     0xd9 => 'EOI',
  3729.     0xda => 'SOS',
  3730.     0xdb => 'DQT',
  3731.     0xdc => 'DNL',
  3732.     0xdd => 'DRI',
  3733.     0xde => 'DHP',
  3734.     0xdf => 'EXP',
  3735.     0xe0 => 'APP0', # to APP15
  3736.     0xf0 => 'JPG0',
  3737.     0xfe => 'COM',
  3738. );
  3739.  
  3740. #------------------------------------------------------------------------------
  3741. # Get JPEG marker name
  3742. # Inputs: 0) Jpeg number
  3743. # Returns: marker name
  3744. sub JpegMarkerName($)
  3745. {
  3746.     my $marker = shift;
  3747.     my $markerName = $jpegMarker{$marker};
  3748.     unless ($markerName) {
  3749.         $markerName = $jpegMarker{$marker & 0xf0};
  3750.         if ($markerName and $markerName =~ /^([A-Z]+)\d+$/) {
  3751.             $markerName = $1 . ($marker & 0x0f);
  3752.         } else {
  3753.             $markerName = sprintf("marker 0x%.2x", $marker);
  3754.         }
  3755.     }
  3756.     return $markerName;
  3757. }
  3758.  
  3759. #------------------------------------------------------------------------------
  3760. # Identify trailer ending at specified offset from end of file
  3761. # Inputs: 0) RAF reference, 1) offset from end of file (0 by default)
  3762. # Returns: Trailer info hash (with RAF and DirName set),
  3763. #          or undef if no recognized trailer was found
  3764. # Notes: leaves file position unchanged
  3765. sub IdentifyTrailer($;$)
  3766. {
  3767.     my $raf = shift;
  3768.     my $offset = shift || 0;
  3769.     my $pos = $raf->Tell();
  3770.     my ($buff, $type, $len);
  3771.     while ($raf->Seek(-$offset, 2) and ($len = $raf->Tell()) > 0) {
  3772.         # read up to 64 bytes before specified offset from end of file
  3773.         $len = 64 if $len > 64;
  3774.         $raf->Seek(-$len, 1) and $raf->Read($buff, $len) == $len or last;
  3775.         if ($buff =~ /AXS(!|\*).{8}$/s) {
  3776.             $type = 'AFCP';
  3777.         } elsif ($buff =~ /\xa1\xb2\xc3\xd4$/) {
  3778.             $type = 'FotoStation';
  3779.         } elsif ($buff =~ /cbipcbbl$/) {
  3780.             $type = 'PhotoMechanic';
  3781.         } elsif ($buff =~ /^CANON OPTIONAL DATA\0/) {
  3782.             $type = 'CanonVRD';
  3783.         } elsif ($buff =~ /~\0\x04\0zmie~\0\0\x06.{4}[\x10\x18]\x04$/s or
  3784.                  $buff =~ /~\0\x04\0zmie~\0\0\x0a.{8}[\x10\x18]\x08$/s)
  3785.         {
  3786.             $type = 'MIE';
  3787.         }
  3788.         last;
  3789.     }
  3790.     $raf->Seek($pos, 0);    # restore original file position
  3791.     return $type ? { RAF => $raf, DirName => $type } : undef;
  3792. }
  3793.  
  3794. #------------------------------------------------------------------------------
  3795. # Extract EXIF information from a jpg image
  3796. # Inputs: 0) ExifTool object reference, 1) dirInfo ref with RAF set
  3797. # Returns: 1 on success, 0 if this wasn't a valid JPEG file
  3798. sub ProcessJPEG($$)
  3799. {
  3800.     local $_;
  3801.     my ($self, $dirInfo) = @_;
  3802.     my ($ch, $s, $length);
  3803.     my $verbose = $self->{OPTIONS}{Verbose};
  3804.     my $out = $self->{OPTIONS}{TextOut};
  3805.     my $fast = $self->{OPTIONS}{FastScan};
  3806.     my $raf = $$dirInfo{RAF};
  3807.     my $htmlDump = $self->{HTML_DUMP};
  3808.     my %dumpParms = ( Out => $out );
  3809.     my ($success, $icc_profile, $wantTrailer, $trailInfo, %extendedXMP);
  3810.     my ($app2Preview, $scalado, @dqt, $subSampling, $dumpEnd);
  3811.  
  3812.     # check to be sure this is a valid JPG file
  3813.     return 0 unless $raf->Read($s, 2) == 2 and $s eq "\xff\xd8";
  3814.     $dumpParms{MaxLen} = 128 if $verbose < 4;
  3815.     unless ($self->{VALUE}{FileType}) {
  3816.         $self->SetFileType();               # set FileType tag
  3817.         $$self{LOW_PRIORITY_DIR}{IFD1} = 1; # lower priority of IFD1 tags
  3818.     }
  3819.     if ($htmlDump) {
  3820.         my $pos = $raf->Tell() - 2;
  3821.         $self->HDump(0, $pos, '[unknown header]') if $pos;
  3822.         $self->HDump($pos, 2, 'JPEG header', 'SOI Marker');
  3823.         $dumpEnd = 2;
  3824.     }
  3825.     my $path = $$self{PATH};
  3826.     my $pn = scalar @$path;
  3827.  
  3828.     # set input record separator to 0xff (the JPEG marker) to make reading quicker
  3829.     local $/ = "\xff";
  3830.  
  3831.     my ($nextMarker, $nextSegDataPt, $nextSegPos, $combinedSegData);
  3832.  
  3833.     # read file until we reach an end of image (EOI) or start of scan (SOS)
  3834.     Marker: for (;;) {
  3835.         # set marker and data pointer for current segment
  3836.         my $marker = $nextMarker;
  3837.         my $segDataPt = $nextSegDataPt;
  3838.         my $segPos = $nextSegPos;
  3839.         undef $nextMarker;
  3840.         undef $nextSegDataPt;
  3841. #
  3842. # read ahead to the next segment unless we have reached EOI or SOS
  3843. #
  3844.         unless ($marker and ($marker==0xd9 or ($marker==0xda and not $wantTrailer))) {
  3845.             # read up to next marker (JPEG markers begin with 0xff)
  3846.             my $buff;
  3847.             $raf->ReadLine($buff) or last;
  3848.             # JPEG markers can be padded with unlimited 0xff's
  3849.             for (;;) {
  3850.                 $raf->Read($ch, 1) or last Marker;
  3851.                 $nextMarker = ord($ch);
  3852.                 last unless $nextMarker == 0xff;
  3853.             }
  3854.             # read data for all markers except 0xd9 (EOI) and stand-alone
  3855.             # markers 0x00, 0x01 and 0xd0-0xd7 (NULL, TEM, RST0-RST7)
  3856.             if ($nextMarker!=0xd9 and $nextMarker!=0x00 and $nextMarker!=0x01 and
  3857.                 ($nextMarker<0xd0 or $nextMarker>0xd7))
  3858.             {
  3859.                 # read record length word
  3860.                 last unless $raf->Read($s, 2) == 2;
  3861.                 my $len = unpack('n',$s);   # get data length
  3862.                 last unless defined($len) and $len >= 2;
  3863.                 $nextSegPos = $raf->Tell();
  3864.                 $len -= 2;  # subtract size of length word
  3865.                 last unless $raf->Read($buff, $len) == $len;
  3866.                 $nextSegDataPt = \$buff;    # set pointer to our next data
  3867.             }
  3868.             # read second segment too if this was the first
  3869.             next unless defined $marker;
  3870.         }
  3871.         # set some useful variables for the current segment
  3872.         my $hdr = "\xff" . chr($marker);    # header for this segment
  3873.         my $markerName = JpegMarkerName($marker);
  3874.         $$path[$pn] = $markerName;
  3875. #
  3876. # parse the current segment
  3877. #
  3878.         # handle SOF markers: SOF0-SOF15, except DHT(0xc4), JPGA(0xc8) and DAC(0xcc)
  3879.         if (($marker & 0xf0) == 0xc0 and ($marker == 0xc0 or $marker & 0x03)) {
  3880.             $length = length $$segDataPt;
  3881.             if ($verbose) {
  3882.                 print $out "JPEG $markerName ($length bytes):\n";
  3883.                 HexDump($segDataPt, undef, %dumpParms, Addr=>$segPos) if $verbose>2;
  3884.             }
  3885.             next unless $length >= 6;
  3886.             # extract some useful information
  3887.             my ($p, $h, $w, $n) = unpack('Cn2C', $$segDataPt);
  3888.             my $sof = GetTagTable('Image::ExifTool::JPEG::SOF');
  3889.             $self->FoundTag($$sof{ImageWidth}, $w);
  3890.             $self->FoundTag($$sof{ImageHeight}, $h);
  3891.             $self->FoundTag($$sof{EncodingProcess}, $marker - 0xc0);
  3892.             $self->FoundTag($$sof{BitsPerSample}, $p);
  3893.             $self->FoundTag($$sof{ColorComponents}, $n);
  3894.             next unless $n == 3 and $length >= 15;
  3895.             my ($i, $hmin, $hmax, $vmin, $vmax);
  3896.             # loop through all components to determine sampling frequency
  3897.             $subSampling = '';
  3898.             for ($i=0; $i<$n; ++$i) {
  3899.                 my $sf = Get8u($segDataPt, 7 + 3 * $i);
  3900.                 $subSampling .= sprintf('%.2x', $sf);
  3901.                 # isolate horizontal and vertical components
  3902.                 my ($hf, $vf) = ($sf >> 4, $sf & 0x0f);
  3903.                 unless ($i) {
  3904.                     $hmin = $hmax = $hf;
  3905.                     $vmin = $vmax = $vf;
  3906.                     next;
  3907.                 }
  3908.                 # determine min/max frequencies
  3909.                 $hmin = $hf if $hf < $hmin;
  3910.                 $hmax = $hf if $hf > $hmax;
  3911.                 $vmin = $vf if $vf < $vmin;
  3912.                 $vmax = $vf if $vf > $vmax;
  3913.             }
  3914.             if ($hmin and $vmin) {
  3915.                 my ($hs, $vs) = ($hmax / $hmin, $vmax / $vmin);
  3916.                 $self->FoundTag($$sof{YCbCrSubSampling}, "$hs $vs");
  3917.             }
  3918.             next;
  3919.         } elsif ($marker == 0xd9) {         # EOI
  3920.             pop @$path;
  3921.             $verbose and print $out "JPEG EOI\n";
  3922.             my $pos = $raf->Tell();
  3923.             if ($htmlDump and $dumpEnd) {
  3924.                 $self->HDump($dumpEnd, $pos-2-$dumpEnd, '[JPEG Image Data]', undef, 0x08);
  3925.                 $self->HDump($pos-2, 2, 'JPEG EOI', undef);
  3926.                 $dumpEnd = 0;
  3927.             }
  3928.             $success = 1;
  3929.             # we are here because we are looking for trailer information
  3930.             if ($wantTrailer) {
  3931.                 my $start = $$self{PreviewImageStart};
  3932.                 if ($start) {
  3933.                     my $buff;
  3934.                     # most previews start right after the JPEG EOI, but the Olympus E-20
  3935.                     # preview is 508 bytes into the trailer, the K-M Maxxum 7D preview is
  3936.                     # 979 bytes in, and Sony previews can start up to 32 kB into the trailer.
  3937.                     # (and Minolta and Sony previews can have a random first byte...)
  3938.                     my $scanLen = $$self{Make} =~ /Sony/i ? 65536 : 1024;
  3939.                     if ($raf->Read($buff, $scanLen) and ($buff =~ /\xff\xd8\xff./g or
  3940.                         ($self->{Make} =~ /(Minolta|Sony)/i and $buff =~ /.\xd8\xff\xdb/g)))
  3941.                     {
  3942.                         # adjust PreviewImageStart to this location
  3943.                         my $actual = $pos + pos($buff) - 4;
  3944.                         if ($start ne $actual and $verbose > 1) {
  3945.                             print $out "(Fixed PreviewImage location: $start -> $actual)\n";
  3946.                         }
  3947.                         # update preview image offsets
  3948.                         $self->{VALUE}{PreviewImageStart} = $actual if $self->{VALUE}{PreviewImageStart};
  3949.                         $$self{PreviewImageStart} = $actual;
  3950.                         # load preview now if we tried and failed earlier
  3951.                         if ($$self{PreviewError} and $$self{PreviewImageLength}) {
  3952.                             if ($raf->Seek($actual, 0) and $raf->Read($buff, $$self{PreviewImageLength})) {
  3953.                                 $self->FoundTag('PreviewImage', $buff);
  3954.                                 delete $$self{PreviewError};
  3955.                             }
  3956.                         }
  3957.                     }
  3958.                     $raf->Seek($pos, 0);
  3959.                 }
  3960.             }
  3961.             # process trailer now or finish processing trailers
  3962.             # and scan for AFCP if necessary
  3963.             my $fromEnd = 0;
  3964.             if ($trailInfo) {
  3965.                 $$trailInfo{ScanForAFCP} = 1;   # scan now if necessary
  3966.                 $self->ProcessTrailers($trailInfo);
  3967.                 # save offset from end of file to start of first trailer
  3968.                 $fromEnd = $$trailInfo{Offset};
  3969.                 undef $trailInfo;
  3970.             }
  3971.             if ($$self{LeicaTrailer}) {
  3972.                 $raf->Seek(0, 2);
  3973.                 $$self{LeicaTrailer}{TrailPos} = $pos;
  3974.                 $$self{LeicaTrailer}{TrailLen} = $raf->Tell() - $pos - $fromEnd;
  3975.                 Image::ExifTool::Panasonic::ProcessLeicaTrailer($self);
  3976.             }
  3977.             # finally, dump remaining information in JPEG trailer
  3978.             if ($verbose or $htmlDump) {
  3979.                 my $endPos = $$self{LeicaTrailerPos};
  3980.                 unless ($endPos) {
  3981.                     $raf->Seek(0, 2);
  3982.                     $endPos = $raf->Tell() - $fromEnd;
  3983.                 }
  3984.                 $self->DumpUnknownTrailer({
  3985.                     RAF => $raf,
  3986.                     DataPos => $pos,
  3987.                     DirLen => $endPos - $pos
  3988.                 }) if $endPos > $pos;
  3989.             }
  3990.             last;       # all done parsing file
  3991.         } elsif ($marker == 0xda) {         # SOS
  3992.             pop @$path;
  3993.             # all done with meta information unless we have a trailer
  3994.             $verbose and print $out "JPEG SOS\n";
  3995.             unless ($fast) {
  3996.                 $trailInfo = IdentifyTrailer($raf);
  3997.                 # process trailer now unless we are doing verbose dump
  3998.                 if ($trailInfo and $verbose < 3 and not $htmlDump) {
  3999.                     # process trailers (keep trailInfo to finish processing later
  4000.                     # only if we can't finish without scanning from end of file)
  4001.                     $self->ProcessTrailers($trailInfo) and undef $trailInfo;
  4002.                 }
  4003.                 if ($wantTrailer) {
  4004.                     # seek ahead and validate preview image
  4005.                     my $buff;
  4006.                     my $curPos = $raf->Tell();
  4007.                     if ($raf->Seek($$self{PreviewImageStart}, 0) and
  4008.                         $raf->Read($buff, 4) == 4 and
  4009.                         $buff =~ /^.\xd8\xff[\xc4\xdb\xe0-\xef]/)
  4010.                     {
  4011.                         undef $wantTrailer;
  4012.                     }
  4013.                     $raf->Seek($curPos, 0) or last;
  4014.                 }
  4015.                 # seek ahead and process Leica trailer
  4016.                 if ($$self{LeicaTrailer}) {
  4017.                     require Image::ExifTool::Panasonic;
  4018.                     Image::ExifTool::Panasonic::ProcessLeicaTrailer($self);
  4019.                     $wantTrailer = 1 if $$self{LeicaTrailer};
  4020.                 }
  4021.                 next if $trailInfo or $wantTrailer or $verbose > 2 or $htmlDump;
  4022.             }
  4023.             # nothing interesting to parse after start of scan (SOS)
  4024.             $success = 1;
  4025.             last;   # all done parsing file
  4026.         } elsif ($marker==0x00 or $marker==0x01 or ($marker>=0xd0 and $marker<=0xd7)) {
  4027.             # handle stand-alone markers 0x00, 0x01 and 0xd0-0xd7 (NULL, TEM, RST0-RST7)
  4028.             $verbose and $marker and print $out "JPEG $markerName:\n";
  4029.             next;
  4030.         } elsif ($marker == 0xdb and length($$segDataPt) and    # DQT
  4031.             # save the DQT data only if JPEGDigest has been requested
  4032.             $self->{REQ_TAG_LOOKUP}->{jpegdigest})
  4033.         {
  4034.             my $num = unpack('C',$$segDataPt) & 0x0f;   # get table index
  4035.             $dqt[$num] = $$segDataPt if $num < 4;       # save for MD5 calculation
  4036.         }
  4037.         # handle all other markers
  4038.         my $dumpType = '';
  4039.         $length = length $$segDataPt;
  4040.         if ($verbose) {
  4041.             print $out "JPEG $markerName ($length bytes):\n";
  4042.             if ($verbose > 2) {
  4043.                 my %extraParms = ( Addr => $segPos );
  4044.                 $extraParms{MaxLen} = 128 if $verbose == 4;
  4045.                 HexDump($segDataPt, undef, %dumpParms, %extraParms);
  4046.             }
  4047.         }
  4048.         if ($marker == 0xe0) {              # APP0 (JFIF, CIFF, AVI1)
  4049.             if ($$segDataPt =~ /^JFIF\0/) {
  4050.                 $dumpType = 'JFIF';
  4051.                 my %dirInfo = (
  4052.                     DataPt => $segDataPt,
  4053.                     DataPos  => $segPos,
  4054.                     DirStart => 5,
  4055.                     DirLen => $length - 5,
  4056.                 );
  4057.                 SetByteOrder('MM');
  4058.                 my $tagTablePtr = GetTagTable('Image::ExifTool::JFIF::Main');
  4059.                 $self->ProcessDirectory(\%dirInfo, $tagTablePtr);
  4060.             } elsif ($$segDataPt =~ /^JFXX\0\x10/) {
  4061.                 $dumpType = 'JFXX';
  4062.                 my $tagTablePtr = GetTagTable('Image::ExifTool::JFIF::Extension');
  4063.                 my $tagInfo = $self->GetTagInfo($tagTablePtr, 0x10);
  4064.                 $self->FoundTag($tagInfo, substr($$segDataPt, 6));
  4065.             } elsif ($$segDataPt =~ /^(II|MM).{4}HEAPJPGM/s) {
  4066.                 next if $fast and $fast > 1;    # skip processing for very fast
  4067.                 $dumpType = 'CIFF';
  4068.                 my %dirInfo = (
  4069.                     RAF => new File::RandomAccess($segDataPt),
  4070.                 );
  4071.                 $self->{SET_GROUP1} = 'CIFF';
  4072.                 require Image::ExifTool::CanonRaw;
  4073.                 Image::ExifTool::CanonRaw::ProcessCRW($self, \%dirInfo);
  4074.                 delete $self->{SET_GROUP1};
  4075.             } elsif ($$segDataPt =~ /^AVI1/) {
  4076.                 $dumpType = 'AVI1';
  4077.                 SetByteOrder('MM');
  4078.                 my $tagTablePtr = GetTagTable('Image::ExifTool::JPEG::AVI1');
  4079.                 my %dirInfo = (
  4080.                     DataPt   => $segDataPt,
  4081.                     DataPos  => $segPos,
  4082.                     DirStart => 4,
  4083.                     DirLen   => $length - 4,
  4084.                 );
  4085.                 $self->ProcessDirectory(\%dirInfo, $tagTablePtr);
  4086.             }
  4087.         } elsif ($marker == 0xe1) {         # APP1 (EXIF, XMP)
  4088.             if ($$segDataPt =~ /^Exif\0/) { # (some Kodak cameras don't put a second \0)
  4089.                 undef $dumpType;    # (will be dumped here)
  4090.                 # this is EXIF data --
  4091.                 # get the data block (into a common variable)
  4092.                 my $hdrLen = length($exifAPP1hdr);
  4093.                 my %dirInfo = (
  4094.                     Parent => $markerName,
  4095.                     DataPt => $segDataPt,
  4096.                     DataPos => $segPos,
  4097.                     DirStart => $hdrLen,
  4098.                     Base => $segPos + $hdrLen,
  4099.                 );
  4100.                 if ($htmlDump) {
  4101.                     $self->HDump($segPos-4, 4, 'APP1 header', "Data size: $length bytes");
  4102.                     $self->HDump($segPos, $hdrLen, 'Exif header', 'APP1 data type: Exif');
  4103.                     $dumpEnd = $segPos + $length;
  4104.                 }
  4105.                 # extract the EXIF information (it is in standard TIFF format)
  4106.                 $self->ProcessTIFF(\%dirInfo);
  4107.                 # avoid looking for preview unless necessary because it really slows
  4108.                 # us down -- only look for it if we found pointer, and preview is
  4109.                 # outside EXIF, and PreviewImage is specifically requested
  4110.                 my $start = $self->GetValue('PreviewImageStart');
  4111.                 my $length = $self->GetValue('PreviewImageLength');
  4112.                 if (not $start or not $length and $$self{PreviewError}) {
  4113.                     $start = $$self{PreviewImageStart};
  4114.                     $length = $$self{PreviewImageLength};
  4115.                 }
  4116.                 if ($start and $length and
  4117.                     $start + $length > $self->{EXIF_POS} + length($self->{EXIF_DATA}) and
  4118.                     $self->{REQ_TAG_LOOKUP}{previewimage})
  4119.                 {
  4120.                     $$self{PreviewImageStart} = $start;
  4121.                     $$self{PreviewImageLength} = $length;
  4122.                     $wantTrailer = 1;
  4123.                 }
  4124.             } elsif ($$segDataPt =~ /^$xmpExtAPP1hdr/) {
  4125.                 # off len -- extended XMP header (75 bytes total):
  4126.                 #   0  35 bytes - signature
  4127.                 #  35  32 bytes - GUID (MD5 hash of full extended XMP data in ASCII)
  4128.                 #  67   4 bytes - total size of extended XMP data
  4129.                 #  71   4 bytes - offset for this XMP data portion
  4130.                 $dumpType = 'Extended XMP';
  4131.                 if (length $$segDataPt > 75) {
  4132.                     my ($size, $off) = unpack('x67N2', $$segDataPt);
  4133.                     my $guid = substr($$segDataPt, 35, 32);
  4134.                     my $extXMP = $extendedXMP{$guid};
  4135.                     $extXMP or $extXMP = $extendedXMP{$guid} = { };
  4136.                     $$extXMP{Size} = $size;
  4137.                     $$extXMP{$off} = substr($$segDataPt, 75);
  4138.                     # process extended XMP if complete
  4139.                     my @offsets;
  4140.                     for ($off=0; $off<$size; ) {
  4141.                         last unless defined $$extXMP{$off};
  4142.                         push @offsets, $off;
  4143.                         $off += length $$extXMP{$off};
  4144.                     }
  4145.                     if ($off == $size) {
  4146.                         my $buff = '';
  4147.                         # assemble XMP all together
  4148.                         $buff .= $$extXMP{$_} foreach @offsets;
  4149.                         $dumpType = 'Extended XMP';
  4150.                         my $tagTablePtr = GetTagTable('Image::ExifTool::XMP::Main');
  4151.                         my %dirInfo = (
  4152.                             DataPt   => \$buff,
  4153.                             Parent   => $markerName,
  4154.                         );
  4155.                         $self->ProcessDirectory(\%dirInfo, $tagTablePtr);
  4156.                         delete $extendedXMP{$guid};
  4157.                     }
  4158.                 } else {
  4159.                     $self->Warn('Invalid extended XMP segment');
  4160.                 }
  4161.             } else {
  4162.                 # Hmmm.  Could be XMP, let's see
  4163.                 my $processed;
  4164.                 if ($$segDataPt =~ /^http/ or $$segDataPt =~ /<exif:/) {
  4165.                     $dumpType = 'XMP';
  4166.                     my $start = ($$segDataPt =~ /^$xmpAPP1hdr/) ? length($xmpAPP1hdr) : 0;
  4167.                     my $tagTablePtr = GetTagTable('Image::ExifTool::XMP::Main');
  4168.                     my %dirInfo = (
  4169.                         Base     => 0,
  4170.                         DataPt   => $segDataPt,
  4171.                         DataPos  => $segPos,
  4172.                         DataLen  => $length,
  4173.                         DirStart => $start,
  4174.                         DirLen   => $length - $start,
  4175.                         Parent   => $markerName,
  4176.                     );
  4177.                     $processed = $self->ProcessDirectory(\%dirInfo, $tagTablePtr);
  4178.                 }
  4179.                 if ($verbose and not $processed) {
  4180.                     $self->Warn("Ignored EXIF block length $length (bad header)");
  4181.                 }
  4182.             }
  4183.         } elsif ($marker == 0xe2) {         # APP2 (ICC Profile, FPXR, MPF, PreviewImage)
  4184.             if ($$segDataPt =~ /^ICC_PROFILE\0/) {
  4185.                 $dumpType = 'ICC_Profile';
  4186.                 # must concatenate blocks of profile
  4187.                 my $block_num = Get8u($segDataPt, 12);
  4188.                 my $blocks_tot = Get8u($segDataPt, 13);
  4189.                 $icc_profile = '' if $block_num == 1;
  4190.                 if (defined $icc_profile) {
  4191.                     $icc_profile .= substr($$segDataPt, 14);
  4192.                     if ($block_num == $blocks_tot) {
  4193.                         my $tagTablePtr = GetTagTable('Image::ExifTool::ICC_Profile::Main');
  4194.                         my %dirInfo = (
  4195.                             DataPt   => \$icc_profile,
  4196.                             DataPos  => $segPos + 14,
  4197.                             DataLen  => length($icc_profile),
  4198.                             DirStart => 0,
  4199.                             DirLen   => length($icc_profile),
  4200.                             Parent   => $markerName,
  4201.                         );
  4202.                         $self->ProcessDirectory(\%dirInfo, $tagTablePtr);
  4203.                         undef $icc_profile;
  4204.                     }
  4205.                 }
  4206.             } elsif ($$segDataPt =~ /^FPXR\0/) {
  4207.                 next if $fast and $fast > 1;    # skip processing for very fast
  4208.                 $dumpType = 'FPXR';
  4209.                 my $tagTablePtr = GetTagTable('Image::ExifTool::FlashPix::Main');
  4210.                 my %dirInfo = (
  4211.                     DataPt   => $segDataPt,
  4212.                     DataPos  => $segPos,
  4213.                     DataLen  => $length,
  4214.                     DirStart => 0,
  4215.                     DirLen   => $length,
  4216.                     Parent   => $markerName,
  4217.                     # set flag if this is the last FPXR segment
  4218.                     LastFPXR => not ($nextMarker==$marker and $$nextSegDataPt=~/^FPXR\0/),
  4219.                 );
  4220.                 $self->ProcessDirectory(\%dirInfo, $tagTablePtr);
  4221.             } elsif ($$segDataPt =~ /^MPF\0/) {
  4222.                 undef $dumpType;    # (will be dumped here)
  4223.                 my %dirInfo = (
  4224.                     Parent => $markerName,
  4225.                     DataPt => $segDataPt,
  4226.                     DataPos => $segPos,
  4227.                     DirStart => 4,
  4228.                     Base => $segPos + 4,
  4229.                     Multi => 1, # the MP Attribute IFD will be MPF1
  4230.                 );
  4231.                 if ($htmlDump) {
  4232.                     $self->HDump($segPos-4, 4, 'APP2 header', "Data size: $length bytes");
  4233.                     $self->HDump($segPos, 4, 'MPF header', 'APP2 data type: MPF');
  4234.                     $dumpEnd = $segPos + $length;
  4235.                 }
  4236.                 # extract the MPF information (it is in standard TIFF format)
  4237.                 my $tagTablePtr = GetTagTable('Image::ExifTool::MPF::Main');
  4238.                 $self->ProcessTIFF(\%dirInfo, $tagTablePtr);
  4239.             } elsif ($$segDataPt =~ /^\xff\xd8\xff\xdb/) {
  4240.                 $app2Preview = $$segDataPt;
  4241.                 $dumpType = 'Samsung Preview';
  4242.             } elsif ($app2Preview) {
  4243.                 $app2Preview .= $$segDataPt;
  4244.                 $dumpType = 'Samsung Preview';
  4245.             }
  4246.             if ($app2Preview and $nextMarker ne $marker) {
  4247.                 $self->FoundTag('PreviewImage', $app2Preview);
  4248.                 undef $app2Preview;
  4249.             }
  4250.         } elsif ($marker == 0xe3) {         # APP3 (Kodak "Meta", Stim)
  4251.             if ($$segDataPt =~ /^(Meta|META|Exif)\0\0/) {
  4252.                 undef $dumpType;    # (will be dumped here)
  4253.                 my %dirInfo = (
  4254.                     Parent => $markerName,
  4255.                     DataPt => $segDataPt,
  4256.                     DataPos => $segPos,
  4257.                     DirStart => 6,
  4258.                     Base => $segPos + 6,
  4259.                 );
  4260.                 if ($htmlDump) {
  4261.                     $self->HDump($segPos-4, 10, 'APP3 Meta header');
  4262.                     $dumpEnd = $segPos + $length;
  4263.                 }
  4264.                 my $tagTablePtr = GetTagTable('Image::ExifTool::Kodak::Meta');
  4265.                 $self->ProcessTIFF(\%dirInfo, $tagTablePtr);
  4266.             } elsif ($$segDataPt =~ /^Stim\0/) {
  4267.                 undef $dumpType;    # (will be dumped here)
  4268.                 my %dirInfo = (
  4269.                     Parent => $markerName,
  4270.                     DataPt => $segDataPt,
  4271.                     DataPos => $segPos,
  4272.                     DirStart => 6,
  4273.                     Base => $segPos + 6,
  4274.                 );
  4275.                 if ($htmlDump) {
  4276.                     $self->HDump($segPos-4, 4, 'APP3 header', "Data size: $length bytes");
  4277.                     $self->HDump($segPos, 5, 'Stim header', 'APP3 data type: Stim');
  4278.                     $dumpEnd = $segPos + $length;
  4279.                 }
  4280.                 # extract the Stim information (it is in standard TIFF format)
  4281.                 my $tagTablePtr = GetTagTable('Image::ExifTool::Stim::Main');
  4282.                 $self->ProcessTIFF(\%dirInfo, $tagTablePtr);
  4283.             }
  4284.         } elsif ($marker == 0xe4) {         # APP4 ("SCALADO")
  4285.             while ($$segDataPt =~ /^SCALADO\0/) {
  4286.                 $dumpType = 'SCALADO';
  4287.                 last if $length < 16;
  4288.                 my ($num, $idx, $len) = unpack('x8n2N', $$segDataPt);
  4289.                 # assume that the segments are in order and just concatinate them
  4290.                 $scalado = '' unless defined $scalado;
  4291.                 $scalado .= substr($$segDataPt, 16);
  4292.                 last unless $idx == $num - 1;
  4293.                 if ($len != length $scalado) {
  4294.                     $self->Warn('Possibly corrupted APP4 SCALADO data', 1);
  4295.                 }
  4296.                 my %dirInfo = (
  4297.                     Parent => $markerName,
  4298.                     DataPt => \$scalado,
  4299.                 );
  4300.                 my $tagTablePtr = GetTagTable('Image::ExifTool::JPEG::Scalado');
  4301.                 $self->ProcessDirectory(\%dirInfo, $tagTablePtr);
  4302.                 undef $scalado;
  4303.                 last;
  4304.             }
  4305.         } elsif ($marker == 0xe5) {         # APP5 (Ricoh "RMETA")
  4306.             if ($$segDataPt =~ /^RMETA\0/) {
  4307.                 $dumpType = 'Ricoh RMETA';
  4308.                 my %dirInfo = (
  4309.                     Parent => $markerName,
  4310.                     DataPt => $segDataPt,
  4311.                     DataPos => $segPos,
  4312.                     DirStart => 6,
  4313.                     Base => $segPos + 6,
  4314.                 );
  4315.                 my $tagTablePtr = GetTagTable('Image::ExifTool::Ricoh::RMETA');
  4316.                 $self->ProcessDirectory(\%dirInfo, $tagTablePtr);
  4317.             }
  4318.         } elsif ($marker == 0xe6) {         # APP6 (Toshiba EPPIM)
  4319.             if ($$segDataPt =~ /^EPPIM\0/) {
  4320.                 undef $dumpType;    # (will be dumped here)
  4321.                 my %dirInfo = (
  4322.                     Parent => $markerName,
  4323.                     DataPt => $segDataPt,
  4324.                     DataPos => $segPos,
  4325.                     DirStart => 6,
  4326.                     Base => $segPos + 6,
  4327.                 );
  4328.                 if ($htmlDump) {
  4329.                     $self->HDump($segPos-4, 10, 'APP6 EPPIM header');
  4330.                     $dumpEnd = $segPos + $length;
  4331.                 }
  4332.                 my $tagTablePtr = GetTagTable('Image::ExifTool::JPEG::EPPIM');
  4333.                 $self->ProcessTIFF(\%dirInfo, $tagTablePtr);
  4334.             } elsif ($$segDataPt =~ /^NITF\0/) {
  4335.                 $dumpType = 'NITF';
  4336.                 SetByteOrder('MM');
  4337.                 my $tagTablePtr = GetTagTable('Image::ExifTool::JPEG::NITF');
  4338.                 my %dirInfo = (
  4339.                     DataPt   => $segDataPt,
  4340.                     DataPos  => $segPos,
  4341.                     DirStart => 5,
  4342.                     DirLen   => $length - 5,
  4343.                 );
  4344.                 $self->ProcessDirectory(\%dirInfo, $tagTablePtr);
  4345.             }
  4346.         } elsif ($marker == 0xe8) {         # APP8 (SPIFF)
  4347.             # my sample SPIFF has 32 bytes of data, but spec states 30
  4348.             if ($$segDataPt =~ /^SPIFF\0/ and $length == 32) {
  4349.                 $dumpType = 'SPIFF';
  4350.                 my %dirInfo = (
  4351.                     DataPt => $segDataPt,
  4352.                     DataPos  => $segPos,
  4353.                     DirStart => 6,
  4354.                     DirLen => $length - 6,
  4355.                 );
  4356.                 my $tagTablePtr = GetTagTable('Image::ExifTool::JPEG::SPIFF');
  4357.                 $self->ProcessDirectory(\%dirInfo, $tagTablePtr);
  4358.             }
  4359.         } elsif ($marker == 0xea) {         # APP10 (PhotoStudio Unicode comments)
  4360.             if ($$segDataPt =~ /^UNICODE\0/) {
  4361.                 $dumpType = 'PhotoStudio';
  4362.                 my $comment = $self->Decode(substr($$segDataPt,8), 'UCS2', 'MM');
  4363.                 $self->FoundTag('Comment', $comment);
  4364.             }
  4365.         } elsif ($marker == 0xec) {         # APP12 (Ducky, Picture Info)
  4366.             if ($$segDataPt =~ /^Ducky/) {
  4367.                 $dumpType = 'Ducky';
  4368.                 my %dirInfo = (
  4369.                     DataPt => $segDataPt,
  4370.                     DataPos => $segPos,
  4371.                     DirStart => 5,
  4372.                     DirLen => $length - 5,
  4373.                 );
  4374.                 my $tagTablePtr = GetTagTable('Image::ExifTool::APP12::Ducky');
  4375.                 $self->ProcessDirectory(\%dirInfo, $tagTablePtr);
  4376.             } else {
  4377.                 my %dirInfo = ( DataPt => $segDataPt );
  4378.                 my $tagTablePtr = GetTagTable('Image::ExifTool::APP12::PictureInfo');
  4379.                 $self->ProcessDirectory(\%dirInfo, $tagTablePtr) and $dumpType = 'Picture Info';
  4380.             }
  4381.         } elsif ($marker == 0xed) {         # APP13 (Photoshop, Adobe_CM)
  4382.             my $isOld;
  4383.             if ($$segDataPt =~ /^$psAPP13hdr/ or ($$segDataPt =~ /^$psAPP13old/ and $isOld=1)) {
  4384.                 $dumpType = 'Photoshop';
  4385.                 # add this data to the combined data if it exists
  4386.                 my $dataPt = $segDataPt;
  4387.                 if (defined $combinedSegData) {
  4388.                     $combinedSegData .= substr($$segDataPt,length($psAPP13hdr));
  4389.                     $dataPt = \$combinedSegData;
  4390.                 }
  4391.                 # peek ahead to see if the next segment is photoshop data too
  4392.                 if ($nextMarker == $marker and $$nextSegDataPt =~ /^$psAPP13hdr/) {
  4393.                     # initialize combined data if necessary
  4394.                     $combinedSegData = $$segDataPt unless defined $combinedSegData;
  4395.                     # (will handle the Photoshop data the next time around)
  4396.                 } else {
  4397.                     my $hdrlen = $isOld ? 27 : 14;
  4398.                     # process APP13 Photoshop record
  4399.                     my $tagTablePtr = GetTagTable('Image::ExifTool::Photoshop::Main');
  4400.                     my %dirInfo = (
  4401.                         DataPt   => $dataPt,
  4402.                         DataPos  => $segPos,
  4403.                         DataLen  => length $$dataPt,
  4404.                         DirStart => $hdrlen,    # directory starts after identifier
  4405.                         DirLen   => length($$dataPt) - $hdrlen,
  4406.                         Parent   => $markerName,
  4407.                     );
  4408.                     $self->ProcessDirectory(\%dirInfo, $tagTablePtr);
  4409.                     undef $combinedSegData;
  4410.                 }
  4411.             } elsif ($$segDataPt =~ /^Adobe_CM/) {
  4412.                 $dumpType = 'Adobe_CM';
  4413.                 SetByteOrder('MM');
  4414.                 my $tagTablePtr = GetTagTable('Image::ExifTool::JPEG::AdobeCM');
  4415.                 my %dirInfo = (
  4416.                     DataPt   => $segDataPt,
  4417.                     DataPos  => $segPos,
  4418.                     DirStart => 8,
  4419.                     DirLen   => $length - 8,
  4420.                 );
  4421.                 $self->ProcessDirectory(\%dirInfo, $tagTablePtr);
  4422.             }
  4423.         } elsif ($marker == 0xee) {         # APP14 (Adobe)
  4424.             if ($$segDataPt =~ /^Adobe/) {
  4425.                 $dumpType = 'Adobe';
  4426.                 SetByteOrder('MM');
  4427.                 my $tagTablePtr = GetTagTable('Image::ExifTool::JPEG::Adobe');
  4428.                 my %dirInfo = (
  4429.                     DataPt   => $segDataPt,
  4430.                     DataPos  => $segPos,
  4431.                     DirStart => 5,
  4432.                     DirLen   => $length - 5,
  4433.                 );
  4434.                 $self->ProcessDirectory(\%dirInfo, $tagTablePtr);
  4435.             }
  4436.         } elsif ($marker == 0xef) {         # APP15 (GraphicConverter)
  4437.             if ($$segDataPt =~ /^Q\s*(\d+)/ and $length == 4) {
  4438.                 $dumpType = 'GraphicConverter';
  4439.                 my $tagTablePtr = GetTagTable('Image::ExifTool::JPEG::GraphConv');
  4440.                 $self->HandleTag($tagTablePtr, 'Q', $1);
  4441.             }
  4442.         } elsif ($marker == 0xfe) {         # COM (JPEG comment)
  4443.             $dumpType = 'Comment';
  4444.             $$segDataPt =~ s/\0+$//;    # some dumb softwares add null terminators
  4445.             $self->FoundTag('Comment', $$segDataPt);
  4446.         } elsif (($marker & 0xf0) != 0xe0) {
  4447.             undef $dumpType;    # only dump unknown APP segments
  4448.         }
  4449.         if (defined $dumpType) {
  4450.             if (not $dumpType and $self->{OPTIONS}{Unknown}) {
  4451.                 $self->Warn("Unknown $markerName segment", 1);
  4452.             }
  4453.             if ($htmlDump) {
  4454.                 my $desc = $markerName . ($dumpType ? " $dumpType" : '') . ' segment';
  4455.                 $self->HDump($segPos-4, $length+4, $desc, undef, 0x08);
  4456.                 $dumpEnd = $segPos + $length;
  4457.             }
  4458.         }
  4459.         undef $$segDataPt;
  4460.     }
  4461.     # calculate JPEGDigest if requested
  4462.     if (@dqt and $subSampling) {
  4463.         require Image::ExifTool::JPEGDigest;
  4464.         Image::ExifTool::JPEGDigest::Calculate($self, \@dqt, $subSampling);
  4465.     }
  4466.     $self->Warn('Error reading PreviewImage', 1) if $$self{PreviewError};
  4467.     $self->Warn('Invalid extended XMP') if %extendedXMP;
  4468.     $success or $self->Warn('JPEG format error');
  4469.     pop @$path if @$path > $pn;
  4470.     return 1;
  4471. }
  4472.  
  4473. #------------------------------------------------------------------------------
  4474. # Process EXIF file
  4475. # Inputs/Returns: same as ProcessTIFF
  4476. sub ProcessEXIF($$;$)
  4477. {
  4478.     my ($self, $dirInfo, $tagTablePtr) = @_;
  4479.     return $self->ProcessTIFF($dirInfo, $tagTablePtr);
  4480. }
  4481.  
  4482. #------------------------------------------------------------------------------
  4483. # Process TIFF data (wrapper for DoProcessTIFF to allow re-entry)
  4484. # Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) optional tag table ref
  4485. # Returns: 1 if this looked like a valid EXIF block, 0 otherwise, or -1 on write error
  4486. sub ProcessTIFF($$;$)
  4487. {
  4488.     my ($self, $dirInfo, $tagTablePtr) = @_;
  4489.     my $exifData = $$self{EXIF_DATA};
  4490.     my $exifPos = $$self{EXIF_POS};
  4491.     my $rtnVal = $self->DoProcessTIFF($dirInfo, $tagTablePtr);
  4492.     # restore original EXIF information (in case ProcessTIFF is nested)
  4493.     if (defined $exifData) {
  4494.         $$self{EXIF_DATA} = $exifData;
  4495.         $$self{EXIF_POS} = $exifPos;
  4496.     }
  4497.     return $rtnVal;
  4498. }
  4499.  
  4500. #------------------------------------------------------------------------------
  4501. # Process TIFF data
  4502. # Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) optional tag table ref
  4503. # Returns: 1 if this looked like a valid EXIF block, 0 otherwise, or -1 on write error
  4504. sub DoProcessTIFF($$;$)
  4505. {
  4506.     my ($self, $dirInfo, $tagTablePtr) = @_;
  4507.     my $dataPt = $$dirInfo{DataPt};
  4508.     my $fileType = $$dirInfo{Parent} || '';
  4509.     my $raf = $$dirInfo{RAF};
  4510.     my $base = $$dirInfo{Base} || 0;
  4511.     my $outfile = $$dirInfo{OutFile};
  4512.     my ($length, $err, $canonSig, $otherSig);
  4513.  
  4514.     # attempt to read TIFF header
  4515.     $self->{EXIF_DATA} = '';
  4516.     if ($raf) {
  4517.         if ($outfile) {
  4518.             $raf->Seek(0, 0) or return 0;
  4519.             if ($base) {
  4520.                 $raf->Read($$dataPt, $base) == $base or return 0;
  4521.                 Write($outfile, $$dataPt) or $err = 1;
  4522.             }
  4523.         } else {
  4524.             $raf->Seek($base, 0) or return 0;
  4525.         }
  4526.         # extract full EXIF block (for block copy) from EXIF file
  4527.         my $amount = $fileType eq 'EXIF' ? 65536 * 8 : 8;
  4528.         my $n = $raf->Read($self->{EXIF_DATA}, $amount);
  4529.         if ($n < 8) {
  4530.             return 0 if $n or not $outfile or $fileType ne 'EXIF';
  4531.             # create EXIF file from scratch
  4532.             delete $self->{EXIF_DATA};
  4533.             undef $raf;
  4534.         }
  4535.         if ($n > 8 and $n == $amount) {
  4536.             $raf->Seek(8, 0);
  4537.             $self->{EXIF_DATA} = substr($self->{EXIF_DATA}, 0, 8);
  4538.             $self->Warn('EXIF too large to extract as a block'); #(shouldn't happen)
  4539.         }
  4540.     } elsif ($dataPt and length $$dataPt) {
  4541.         # save a copy of the EXIF data
  4542.         my $dirStart = $$dirInfo{DirStart} || 0;
  4543.         $self->{EXIF_DATA} = substr($$dataPt, $dirStart);
  4544.         $self->VerboseDir('TIFF') if $self->{OPTIONS}{Verbose} and length($$self{INDENT}) > 2;
  4545.     } elsif ($outfile) {
  4546.         delete $self->{EXIF_DATA};  # create from scratch
  4547.     } else {
  4548.         $self->{EXIF_DATA} = '';
  4549.     }
  4550.     unless (defined $self->{EXIF_DATA}) {
  4551.         # create TIFF information from scratch
  4552.         if ($self->SetPreferredByteOrder() eq 'MM') {
  4553.             $self->{EXIF_DATA} = "MM\0\x2a\0\0\0\x08";
  4554.         } else {
  4555.             $self->{EXIF_DATA} = "II\x2a\0\x08\0\0\0";
  4556.         }
  4557.     }
  4558.     $$self{FIRST_EXIF_POS} = $base + $$self{BASE} unless defined $$self{FIRST_EXIF_POS};
  4559.     $$self{EXIF_POS} = $base;
  4560.     $dataPt = \$self->{EXIF_DATA};
  4561.  
  4562.     # set byte ordering
  4563.     my $byteOrder = substr($$dataPt,0,2);
  4564.     SetByteOrder($byteOrder) or return 0;
  4565.  
  4566.     # verify the byte ordering
  4567.     my $identifier = Get16u($dataPt, 2);
  4568.     # identifier is 0x2a for TIFF (but 0x4f52, 0x5352 or ?? for ORF)
  4569.   # no longer do this because various files use different values
  4570.   # (TIFF=0x2a, RW2/RWL=0x55, HDP=0xbc, BTF=0x2b, ORF=0x4f52/0x5352/0x????)
  4571.   #  return 0 unless $identifier == 0x2a;
  4572.  
  4573.     # get offset to IFD0
  4574.     my $offset = Get32u($dataPt, 4);
  4575.     $offset >= 8 or return 0;
  4576.  
  4577.     if ($raf) {
  4578.         # Canon CR2 images usually have an offset of 16, but it may be
  4579.         # greater if edited by PhotoMechanic, so check the 4-byte signature
  4580.         if ($identifier == 0x2a and $offset >= 16) {
  4581.             $raf->Read($canonSig, 8) == 8 or return 0;
  4582.             $$dataPt .= $canonSig;
  4583.             if ($canonSig =~ /^(CR\x02\0|\xba\xb0\xac\xbb)/) {
  4584.                 $fileType = $canonSig =~ /^CR/ ? 'CR2' : 'Canon 1D RAW';
  4585.                 $self->HDump($base+8, 8, "[$fileType header]") if $self->{HTML_DUMP};
  4586.             } else {
  4587.                 undef $canonSig;
  4588.             }
  4589.         } elsif ($identifier == 0x55 and $fileType =~ /^(RAW|RW2|RWL|TIFF)$/) {
  4590.             # panasonic RAW, RW2 or RWL file
  4591.             my $magic;
  4592.             # test for RW2/RWL magic number
  4593.             if ($offset >= 0x18 and $raf->Read($magic, 16) and
  4594.                 $magic eq "\x88\xe7\x74\xd8\xf8\x25\x1d\x4d\x94\x7a\x6e\x77\x82\x2b\x5d\x6a")
  4595.             {
  4596.                 $fileType = 'RW2' unless $fileType eq 'RWL';
  4597.                 $self->HDump($base + 8, 16, '[RW2/RWL header]') if $$self{HTML_DUMP};
  4598.                 $otherSig = $magic; # save signature for writing
  4599.             } else {
  4600.                 $fileType = 'RAW';
  4601.             }
  4602.             $tagTablePtr = GetTagTable('Image::ExifTool::PanasonicRaw::Main');
  4603.         } elsif ($identifier == 0x2b and $fileType eq 'TIFF') {
  4604.             # this looks like a BigTIFF image
  4605.             $raf->Seek(0);
  4606.             require Image::ExifTool::BigTIFF;
  4607.             return 1 if Image::ExifTool::BigTIFF::ProcessBTF($self, $dirInfo);
  4608.         } elsif (Get8u($dataPt, 2) == 0xbc and $byteOrder eq 'II' and $fileType eq 'TIFF') {
  4609.             $fileType = 'HDP';  # Windows HD Photo file
  4610.             # check version number
  4611.             my $ver = Get8u($dataPt, 3);
  4612.             if ($ver > 1) {
  4613.                 $self->Error("Windows HD Photo version $ver files not yet supported");
  4614.                 return 1;
  4615.             }
  4616.         } elsif ($identifier == 0x4352 and $fileType eq 'TIFF') {
  4617.             $fileType = 'DCP';
  4618.         }
  4619.         # we have a valid TIFF (or whatever) file
  4620.         if ($fileType and not $self->{VALUE}{FileType}) {
  4621.             my $lookup = $fileTypeLookup{$fileType};
  4622.             $lookup = $fileTypeLookup{$lookup} unless ref $lookup or not $lookup;
  4623.             # use file extension to pre-determine type if extension is TIFF-based or type is RAW
  4624.             my $t = (($lookup and $$lookup[0] eq 'TIFF') or $fileType =~ /RAW/) ? $fileType : undef;
  4625.             $self->SetFileType($t);
  4626.         }
  4627.     }
  4628.     my $ifdName = 'IFD0';
  4629.     if (not $tagTablePtr or $$tagTablePtr{GROUPS}{0} eq 'EXIF') {
  4630.         $self->FoundTag('ExifByteOrder', $byteOrder);
  4631.     } else {
  4632.         $ifdName = $$tagTablePtr{GROUPS}{1};
  4633.     }
  4634.     if ($self->{HTML_DUMP}) {
  4635.         my $tip = sprintf("Byte order: %s endian\nIdentifier: 0x%.4x\n$ifdName offset: 0x%.4x",
  4636.                           ($byteOrder eq 'II') ? 'Little' : 'Big', $identifier, $offset);
  4637.         $self->HDump($base, 8, 'TIFF header', $tip, 0);
  4638.     }
  4639.     # remember where we found the TIFF data (APP1, APP3, TIFF, NEF, etc...)
  4640.     $self->{TIFF_TYPE} = $fileType;
  4641.  
  4642.     # get reference to the main EXIF table
  4643.     $tagTablePtr or $tagTablePtr = GetTagTable('Image::ExifTool::Exif::Main');
  4644.  
  4645.     # build directory information hash
  4646.     my %dirInfo = (
  4647.         Base     => $base,
  4648.         DataPt   => $dataPt,
  4649.         DataLen  => length $$dataPt,
  4650.         DataPos  => 0,
  4651.         DirStart => $offset,
  4652.         DirLen   => length($$dataPt) - $offset,
  4653.         RAF      => $raf,
  4654.         DirName  => $ifdName,
  4655.         Parent   => $fileType,
  4656.         ImageData=> 'Main', # set flag to get information to copy main image data later
  4657.         Multi    => $$dirInfo{Multi},
  4658.     );
  4659.  
  4660.     # extract information from the image
  4661.     unless ($outfile) {
  4662.         # process the directory
  4663.         $self->ProcessDirectory(\%dirInfo, $tagTablePtr);
  4664.         # process GeoTiff information if available
  4665.         if ($self->{VALUE}{GeoTiffDirectory}) {
  4666.             require Image::ExifTool::GeoTiff;
  4667.             Image::ExifTool::GeoTiff::ProcessGeoTiff($self);
  4668.         }
  4669.         # process information in recognized trailers
  4670.         if ($raf) {
  4671.             my $trailInfo = IdentifyTrailer($raf);
  4672.             if ($trailInfo) {
  4673.                 $$trailInfo{ScanForAFCP} = 1;   # scan to find AFCP if necessary
  4674.                 $self->ProcessTrailers($trailInfo);
  4675.             }
  4676.             # dump any other known trailer (ie. A100 RAW Data)
  4677.             if ($$self{HTML_DUMP} and $$self{KnownTrailer}) {
  4678.                 my $known = $$self{KnownTrailer};
  4679.                 $raf->Seek(0, 2);
  4680.                 my $len = $raf->Tell() - $$known{Start};
  4681.                 $len -= $$trailInfo{Offset} if $trailInfo;  # account for other trailers
  4682.                 $self->HDump($$known{Start}, $len, "[$$known{Name}]") if $len > 0;
  4683.            }
  4684.         }
  4685.         # update FileType if necessary now that we know more about the file
  4686.         if ($$self{DNGVersion} and $self->{VALUE}{FileType} ne 'DNG') {
  4687.             # override whatever FileType we set since we now know it is DNG
  4688.             $self->OverrideFileType('DNG');
  4689.         }
  4690.         return 1;
  4691.     }
  4692. #
  4693. # rewrite the image
  4694. #
  4695.     if ($$dirInfo{NoTiffEnd}) {
  4696.         delete $self->{TIFF_END};
  4697.     } else {
  4698.         # initialize TIFF_END so it will be updated by WriteExif()
  4699.         $self->{TIFF_END} = 0;
  4700.     }
  4701.     if ($canonSig) {
  4702.         # write Canon CR2 specially because it has a header we want to preserve,
  4703.         # and possibly trailers added by the Canon utilities and/or PhotoMechanic
  4704.         $dirInfo{OutFile} = $outfile;
  4705.         require Image::ExifTool::CanonRaw;
  4706.         Image::ExifTool::CanonRaw::WriteCR2($self, \%dirInfo, $tagTablePtr) or $err = 1;
  4707.     } else {
  4708.         # write TIFF header (8 bytes [plus optional signature] followed by IFD)
  4709.         $otherSig = '' unless defined $otherSig;
  4710.         my $offset = 8 + length($otherSig);
  4711.         # must get header now in case EXIF_DATA is changed by re-entrant call in WriteDirectory
  4712.         my $header = substr($$dataPt, 0, 4);
  4713.         $dirInfo{NewDataPos} = $offset;
  4714.         # preserve padding between image data blocks in ORF images
  4715.         # (otherwise dcraw has problems because it assumes fixed block spacing)
  4716.         $dirInfo{PreserveImagePadding} = 1 if $fileType eq 'ORF' or $identifier != 0x2a;
  4717.         my $newData = $self->WriteDirectory(\%dirInfo, $tagTablePtr);
  4718.         if (not defined $newData) {
  4719.             $err = 1;
  4720.         } elsif (length($newData)) {
  4721.             if ($$self{TIFF_TYPE} eq 'ARW' and not $err) {
  4722.                 # write any required ARW trailer and patch other ARW quirks
  4723.                 require Image::ExifTool::Sony;
  4724.                 my $errStr = Image::ExifTool::Sony::FinishARW($self, $dirInfo, \$newData, 
  4725.                                                               $dirInfo{ImageData});
  4726.                 $errStr and $self->Error($errStr);
  4727.                 delete $dirInfo{ImageData}; # (was copied by FinishARW)
  4728.             } else {
  4729.                 $header .= Set32u($offset);
  4730.                 Write($outfile, $header, $otherSig, $newData) or $err = 1;
  4731.             }
  4732.             undef $newData; # free memory
  4733.         }
  4734.         # copy over image data now if necessary
  4735.         if (ref $dirInfo{ImageData} and not $err) {
  4736.             $self->CopyImageData($dirInfo{ImageData}, $outfile) or $err = 1;
  4737.             delete $dirInfo{ImageData};
  4738.         }
  4739.     }
  4740.     # make local copy of TIFF_END now (it may be reset when processing trailers)
  4741.     my $tiffEnd = $self->{TIFF_END};
  4742.     delete $self->{TIFF_END};
  4743.  
  4744.     # rewrite trailers if they exist
  4745.     if ($raf and $tiffEnd and not $err) {
  4746.         my ($buf, $trailInfo);
  4747.         $raf->Seek(0, 2) or $err = 1;
  4748.         my $extra = $raf->Tell() - $tiffEnd;
  4749.         # check for trailer and process if possible
  4750.         for (;;) {
  4751.             last unless $extra > 12;
  4752.             $raf->Seek($tiffEnd);  # seek back to end of image
  4753.             $trailInfo = IdentifyTrailer($raf);
  4754.             last unless $trailInfo;
  4755.             my $tbuf = '';
  4756.             $$trailInfo{OutFile} = \$tbuf;  # rewrite trailer(s)
  4757.             $$trailInfo{ScanForAFCP} = 1;   # scan for AFCP if necessary
  4758.             # rewrite all trailers to buffer
  4759.             unless ($self->ProcessTrailers($trailInfo)) {
  4760.                 undef $trailInfo;
  4761.                 $err = 1;
  4762.                 last;
  4763.             }
  4764.             # calculate unused bytes before trailer
  4765.             $extra = $$trailInfo{DataPos} - $tiffEnd;
  4766.             last; # yes, the 'for' loop was just a cheap 'goto'
  4767.         }
  4768.         # ignore a single zero byte if used for padding
  4769.         # (note that Photoshop CS adds a trailer with 2 zero bytes
  4770.         #  for some reason, and these will be preserved)
  4771.         if ($extra > 0 and $tiffEnd & 0x01) {
  4772.             $raf->Seek($tiffEnd, 0) or $err = 1;
  4773.             $raf->Read($buf, 1) or $err = 1;
  4774.             $buf eq "\0" and --$extra, ++$tiffEnd;
  4775.         }
  4776.         if ($extra > 0) {
  4777.             my $known = $$self{KnownTrailer};
  4778.             if ($self->{DEL_GROUP}{Trailer} and not $known) {
  4779.                 $self->VPrint(0, "  Deleting unknown trailer ($extra bytes)\n");
  4780.                 ++$self->{CHANGED};
  4781.             } else {
  4782.                 my $str = $known ? "Copying $$known{Name}" : 'Preserving unknown trailer';
  4783.                 $self->VPrint(0, "  $str ($extra bytes)\n");
  4784.                 $raf->Seek($tiffEnd, 0) or $err = 1;
  4785.                 CopyBlock($raf, $outfile, $extra) or $err = 1;
  4786.             }
  4787.         }
  4788.         # write trailer buffer if necessary
  4789.         $self->WriteTrailerBuffer($trailInfo, $outfile) or $err = 1 if $trailInfo;
  4790.         # add any new trailers we are creating
  4791.         my $trailPt = $self->AddNewTrailers();
  4792.         Write($outfile, $$trailPt) or $err = 1 if $trailPt;
  4793.     }
  4794.     # check DNG version
  4795.     if ($$self{DNGVersion}) {
  4796.         my $ver = $$self{DNGVersion};
  4797.         # currently support up to DNG version 1.2
  4798.         unless ($ver =~ /^(\d+) (\d+)/ and "$1.$2" <= 1.3) {
  4799.             $ver =~ tr/ /./;
  4800.             $self->Error("DNG Version $ver not yet supported", 1);
  4801.         }
  4802.     }
  4803.     return $err ? -1 : 1;
  4804. }
  4805.  
  4806. #------------------------------------------------------------------------------
  4807. # Return list of tag table keys (ignoring special keys)
  4808. # Inputs: 0) reference to tag table
  4809. # Returns: List of table keys (unsorted)
  4810. sub TagTableKeys($)
  4811. {
  4812.     local $_;
  4813.     my $tagTablePtr = shift;
  4814.     my @keyList;
  4815.     foreach (keys %$tagTablePtr) {
  4816.         push(@keyList, $_) unless $specialTags{$_};
  4817.     }
  4818.     return @keyList;
  4819. }
  4820.  
  4821. #------------------------------------------------------------------------------
  4822. # GetTagTable
  4823. # Inputs: 0) table name
  4824. # Returns: tag table reference, or undefined if not found
  4825. # Notes: Always use this function instead of requiring module and using table
  4826. # directly since this function also does the following the first time the table
  4827. # is loaded:
  4828. # - requires new module if necessary
  4829. # - generates default GROUPS hash and Group 0 name from module name
  4830. # - registers Composite tags if Composite table found
  4831. # - saves descriptions for tags in specified table
  4832. # - generates default TAG_PREFIX to be used for unknown tags
  4833. sub GetTagTable($)
  4834. {
  4835.     my $tableName = shift or return undef;
  4836.     my $table = $allTables{$tableName};
  4837.  
  4838.     unless ($table) {
  4839.         no strict 'refs';
  4840.         unless (%$tableName) {
  4841.             # try to load module for this table
  4842.             if ($tableName =~ /(.*)::/) {
  4843.                 my $module = $1;
  4844.                 if (eval "require $module") {
  4845.                     # load additional XMP modules if required
  4846.                     if (not %$tableName and $module eq 'Image::ExifTool::XMP') {
  4847.                         require Image::ExifTool::XMP2;
  4848.                     }
  4849.                 } else {
  4850.                     $@ and warn $@;
  4851.                 }
  4852.             }
  4853.             unless (%$tableName) {
  4854.                 warn "Can't find table $tableName\n";
  4855.                 return undef;
  4856.             }
  4857.         }
  4858.         no strict 'refs';
  4859.         $table = \%$tableName;
  4860.         use strict 'refs';
  4861.         $$table{TABLE_NAME} = $tableName;   # set table name
  4862.         ($$table{SHORT_NAME} = $tableName) =~ s/^Image::ExifTool:://;
  4863.         # set default group 0 and 1 from module name unless already specified
  4864.         my $defaultGroups = $$table{GROUPS};
  4865.         $defaultGroups or $defaultGroups = $$table{GROUPS} = { };
  4866.         unless ($$defaultGroups{0} and $$defaultGroups{1}) {
  4867.             if ($tableName =~ /Image::.*?::([^:]*)/) {
  4868.                 $$defaultGroups{0} = $1 unless $$defaultGroups{0};
  4869.                 $$defaultGroups{1} = $1 unless $$defaultGroups{1};
  4870.             } else {
  4871.                 $$defaultGroups{0} = $tableName unless $$defaultGroups{0};
  4872.                 $$defaultGroups{1} = $tableName unless $$defaultGroups{1};
  4873.             }
  4874.         }
  4875.         $$defaultGroups{2} = 'Other' unless $$defaultGroups{2};
  4876.         # generate a tag prefix for unknown tags if necessary
  4877.         unless ($$table{TAG_PREFIX}) {
  4878.             my $tagPrefix;
  4879.             if ($tableName =~ /Image::.*?::(.*)::Main/ || $tableName =~ /Image::.*?::(.*)/) {
  4880.                 ($tagPrefix = $1) =~ s/::/_/g;
  4881.             } else {
  4882.                 $tagPrefix = $tableName;
  4883.             }
  4884.             $$table{TAG_PREFIX} = $tagPrefix;
  4885.         }
  4886.         # set up the new table
  4887.         SetupTagTable($table);
  4888.         # add any user-defined tags
  4889.         if (%UserDefined and $UserDefined{$tableName}) {
  4890.             my $tagID;
  4891.             foreach $tagID (TagTableKeys($UserDefined{$tableName})) {
  4892.                 my $tagInfo = $UserDefined{$tableName}{$tagID};
  4893.                 if (ref $tagInfo eq 'HASH') {
  4894.                     $$tagInfo{Name} or $$tagInfo{Name} = ucfirst($tagID);
  4895.                 } else {
  4896.                     $tagInfo = { Name => $tagInfo };
  4897.                 }
  4898.                 if ($$table{WRITABLE} and not defined $$tagInfo{Writable} and
  4899.                     not $$tagInfo{SubDirectory})
  4900.                 {
  4901.                     $$tagInfo{Writable} = $$table{WRITABLE};
  4902.                 }
  4903.                 delete $$table{$tagID}; # replace any existing entry
  4904.                 AddTagToTable($table, $tagID, $tagInfo);
  4905.             }
  4906.         }
  4907.         # remember order we loaded the tables in
  4908.         push @tableOrder, $tableName;
  4909.         # insert newly loaded table into list
  4910.         $allTables{$tableName} = $table;
  4911.     }
  4912.     return $table;
  4913. }
  4914.  
  4915. #------------------------------------------------------------------------------
  4916. # Process an image directory
  4917. # Inputs: 0) ExifTool object reference, 1) directory information reference
  4918. #         2) tag table reference, 3) optional reference to processing procedure
  4919. # Returns: Result from processing (1=success)
  4920. sub ProcessDirectory($$$;$)
  4921. {
  4922.     my ($self, $dirInfo, $tagTablePtr, $proc) = @_;
  4923.  
  4924.     return 0 unless $tagTablePtr and $dirInfo;
  4925.     # use default proc from tag table or EXIF proc as fallback if no proc specified
  4926.     $proc or $proc = $$tagTablePtr{PROCESS_PROC} || \&Image::ExifTool::Exif::ProcessExif;
  4927.     # set directory name from default group0 name if not done already
  4928.     $$dirInfo{DirName} or $$dirInfo{DirName} = $tagTablePtr->{GROUPS}{0};
  4929.     # guard against cyclical recursion into the same directory
  4930.     if (defined $$dirInfo{DirStart} and defined $$dirInfo{DataPos}) {
  4931.         my $addr = $$dirInfo{DirStart} + $$dirInfo{DataPos} + ($$dirInfo{Base}||0);
  4932.         if ($self->{PROCESSED}{$addr}) {
  4933.             $self->Warn("$$dirInfo{DirName} pointer references previous $self->{PROCESSED}{$addr} directory");
  4934.             return 0;
  4935.         }
  4936.         $self->{PROCESSED}{$addr} = $$dirInfo{DirName};
  4937.     }
  4938.     my $oldOrder = GetByteOrder();
  4939.     my $oldIndent = $self->{INDENT};
  4940.     my $oldDir = $self->{DIR_NAME};
  4941.     $self->{LIST_TAGS} = { };  # don't build lists across different directories
  4942.     $self->{INDENT} .= '| ';
  4943.     $self->{DIR_NAME} = $$dirInfo{DirName};
  4944.     push @{$self->{PATH}}, $$dirInfo{DirName};
  4945.  
  4946.     # process the directory
  4947.     my $rtnVal = &$proc($self, $dirInfo, $tagTablePtr);
  4948.  
  4949.     pop @{$self->{PATH}};
  4950.     $self->{INDENT} = $oldIndent;
  4951.     $self->{DIR_NAME} = $oldDir;
  4952.     SetByteOrder($oldOrder);
  4953.     return $rtnVal;
  4954. }
  4955.  
  4956. #------------------------------------------------------------------------------
  4957. # Get standardized file extension
  4958. # Inputs: 0) file name
  4959. # Returns: standardized extension (all uppercase), or undefined if no extension
  4960. sub GetFileExtension($)
  4961. {
  4962.     my $filename = shift;
  4963.     my $fileExt;
  4964.     if ($filename and $filename =~ /.*\.(.+)$/) {
  4965.         $fileExt = uc($1);   # change extension to upper case
  4966.         # convert TIF extension to TIFF because we use the
  4967.         # extension for the file type tag of TIFF images
  4968.         $fileExt eq 'TIF' and $fileExt = 'TIFF';
  4969.     }
  4970.     return $fileExt;
  4971. }
  4972.  
  4973. #------------------------------------------------------------------------------
  4974. # Get list of tag information hashes for given tag ID
  4975. # Inputs: 0) Tag table reference, 1) tag ID
  4976. # Returns: Array of tag information references
  4977. # Notes: Generates tagInfo hash if necessary
  4978. sub GetTagInfoList($$)
  4979. {
  4980.     my ($tagTablePtr, $tagID) = @_;
  4981.     my $tagInfo = $$tagTablePtr{$tagID};
  4982.  
  4983.     if (ref $tagInfo eq 'HASH') {
  4984.         return ($tagInfo);
  4985.     } elsif (ref $tagInfo eq 'ARRAY') {
  4986.         return @$tagInfo;
  4987.     } elsif ($tagInfo) {
  4988.         # create hash with name
  4989.         $tagInfo = $$tagTablePtr{$tagID} = { Name => $tagInfo };
  4990.         return ($tagInfo);
  4991.     }
  4992.     return ();
  4993. }
  4994.  
  4995. #------------------------------------------------------------------------------
  4996. # Find tag information, processing conditional tags
  4997. # Inputs: 0) ExifTool object reference, 1) tagTable pointer, 2) tag ID
  4998. #         3) optional value reference, 4) optional format type, 5) optional value count
  4999. # Returns: pointer to tagInfo hash, undefined if none found, or '' if $valPt needed
  5000. # Notes: You should always call this routine to find a tag in a table because
  5001. # this routine will evaluate conditional tags.
  5002. # Arguments 3-5 are only required if the information type allows $valPt, $format and/or
  5003. # $count in a Condition, and if not given when needed this routine returns ''.
  5004. sub GetTagInfo($$$;$$$)
  5005. {
  5006.     my ($self, $tagTablePtr, $tagID) = @_;
  5007.     my ($valPt, $format, $count);
  5008.  
  5009.     my @infoArray = GetTagInfoList($tagTablePtr, $tagID);
  5010.     # evaluate condition
  5011.     my $tagInfo;
  5012.     foreach $tagInfo (@infoArray) {
  5013.         my $condition = $$tagInfo{Condition};
  5014.         if ($condition) {
  5015.             ($valPt, $format, $count) = splice(@_, 3) if @_ > 3;
  5016.             return '' if $condition =~ /\$(valPt|format|count)\b/ and not defined $valPt;
  5017.             # set old value for use in condition if needed
  5018.             local $SIG{'__WARN__'} = \&SetWarning;
  5019.             undef $evalWarning;
  5020.             #### eval Condition ($self, [$valPt, $format, $count])
  5021.             unless (eval $condition) {
  5022.                 $@ and $evalWarning = $@;
  5023.                 $self->Warn("Condition $$tagInfo{Name}: " . CleanWarning()) if $evalWarning;
  5024.                 next;
  5025.             }
  5026.         }
  5027.         if ($$tagInfo{Unknown} and not $$self{OPTIONS}{Unknown} and not $$self{OPTIONS}{Verbose}) {
  5028.             # don't return Unknown tags unless that option is set
  5029.             return undef;
  5030.         }
  5031.         # return the tag information we found
  5032.         return $tagInfo;
  5033.     }
  5034.     # generate information for unknown tags (numerical only) if required
  5035.     if (not $tagInfo and $self->{OPTIONS}{Unknown} and $tagID =~ /^\d+$/ and
  5036.         not $$self{NO_UNKNOWN})
  5037.     {
  5038.         my $printConv;
  5039.         if (defined $$tagTablePtr{PRINT_CONV}) {
  5040.             $printConv = $$tagTablePtr{PRINT_CONV};
  5041.         } else {
  5042.             # limit length of printout (can be very long)
  5043.             $printConv = 'length($val) > 60 ? substr($val,0,55) . "[...]" : $val';
  5044.         }
  5045.         my $hex = sprintf("0x%.4x", $tagID);
  5046.         my $prefix = $$tagTablePtr{TAG_PREFIX};
  5047.         $tagInfo = {
  5048.             Name => "${prefix}_$hex",
  5049.             Description => MakeDescription($prefix, $hex),
  5050.             Unknown => 1,
  5051.             Writable => 0,  # can't write unknown tags
  5052.             PrintConv => $printConv,
  5053.         };
  5054.         # add tag information to table
  5055.         AddTagToTable($tagTablePtr, $tagID, $tagInfo);
  5056.     } else {
  5057.         undef $tagInfo;
  5058.     }
  5059.     return $tagInfo;
  5060. }
  5061.  
  5062. #------------------------------------------------------------------------------
  5063. # Add new tag to table (must use this routine to add new tags to a table)
  5064. # Inputs: 0) reference to tag table, 1) tag ID
  5065. #         2) [optional] reference to tag information hash
  5066. # Notes: - will not overwrite existing entry in table
  5067. # - info need contain no entries when this routine is called
  5068. sub AddTagToTable($$;$)
  5069. {
  5070.     my ($tagTablePtr, $tagID, $tagInfo) = @_;
  5071.     $tagInfo or $tagInfo = { };
  5072.  
  5073.     # define necessary entries in information hash
  5074.     if ($$tagInfo{Groups}) {
  5075.         # fill in default groups from table GROUPS
  5076.         foreach (keys %{$$tagTablePtr{GROUPS}}) {
  5077.             next if $tagInfo->{Groups}{$_};
  5078.             $tagInfo->{Groups}{$_} = $tagTablePtr->{GROUPS}{$_};
  5079.         }
  5080.     } else {
  5081.         $$tagInfo{Groups} = $$tagTablePtr{GROUPS};
  5082.     }
  5083.     $$tagInfo{Flags} and ExpandFlags($tagInfo);
  5084.     $$tagInfo{GotGroups} = 1,
  5085.     $$tagInfo{Table} = $tagTablePtr;
  5086.     $$tagInfo{TagID} = $tagID;
  5087.  
  5088.     my $name = $$tagInfo{Name};
  5089.     if ($name) {
  5090.         $name =~ tr/-_a-zA-Z0-9//dc;    # remove illegal characters
  5091.     } else {
  5092.         # construct a name from the tag ID
  5093.         $name = $tagID;
  5094.         $name =~ tr/-_a-zA-Z0-9//dc;    # remove illegal characters
  5095.         $name = ucfirst $name;          # start with uppercase
  5096.         # make sure name is a reasonable length
  5097.         my $prefix = $$tagTablePtr{TAG_PREFIX};
  5098.         if ($prefix) {
  5099.             # make description to prevent tagID from getting mangled by MakeDescription()
  5100.             $$tagInfo{Description} = MakeDescription($prefix, $name);
  5101.             $name = "${prefix}_$name";
  5102.         }
  5103.     }
  5104.     # don't allow 1-character tag names, or tags that don't start with a letter
  5105.     $name = "Tag$name" if length($name) <= 1 or $name !~ /^[A-Z]/i;
  5106.     # don't allow single-character tag names
  5107.     $$tagInfo{Name} = length($name) > 1 ? $name : "Tag$name";
  5108.     # add tag to table, but never overwrite existing entries (could potentially happen
  5109.     # if someone thinks there isn't any tagInfo because a condition wasn't satisfied)
  5110.     $$tagTablePtr{$tagID} = $tagInfo unless defined $$tagTablePtr{$tagID};
  5111. }
  5112.  
  5113. #------------------------------------------------------------------------------
  5114. # Handle simple extraction of new tag information
  5115. # Inputs: 0) ExifTool object ref, 1) tag table reference, 2) tagID, 3) value,
  5116. #         4-N) parameters hash: Index, DataPt, DataPos, Start, Size, Parent,
  5117. #              TagInfo, ProcessProc
  5118. # Returns: tag key or undef if tag not found
  5119. # Notes: if value is not defined, it is extracted from DataPt using TagInfo
  5120. #        Format and Count if provided
  5121. sub HandleTag($$$$;%)
  5122. {
  5123.     my ($self, $tagTablePtr, $tag, $val, %parms) = @_;
  5124.     my $verbose = $self->{OPTIONS}{Verbose};
  5125.     my $tagInfo = $parms{TagInfo} || $self->GetTagInfo($tagTablePtr, $tag, \$val);
  5126.     my $dataPt = $parms{DataPt};
  5127.     my ($subdir, $format, $count, $size, $noTagInfo);
  5128.  
  5129.     if ($tagInfo) {
  5130.         $subdir = $$tagInfo{SubDirectory}
  5131.     } else {
  5132.         return undef unless $verbose;
  5133.         $tagInfo = { Name => "tag $tag" };  # create temporary tagInfo hash
  5134.         $noTagInfo = 1;
  5135.     }
  5136.     # read value if not done already (not necessary for subdir)
  5137.     unless (defined $val or ($subdir and not $$tagInfo{Writable})) {
  5138.         my $start = $parms{Start} || 0;
  5139.         my $size = $parms{Size} || 0;
  5140.         # read from data in memory if possible
  5141.         if ($dataPt and $start >= 0 and $start + $size <= length($$dataPt)) {
  5142.             $format = $$tagInfo{Format} || $$tagTablePtr{FORMAT};
  5143.             if ($format) {
  5144.                 $val = ReadValue($dataPt, $start, $format, $$tagInfo{Count}, $size);
  5145.             } else {
  5146.                 $val = substr($$dataPt, $start, $size);
  5147.             }
  5148.         } else {
  5149.             $self->Warn("Error extracting value for $$tagInfo{Name}");
  5150.             return undef;
  5151.         }
  5152.     }
  5153.     # do verbose print if necessary
  5154.     if ($verbose) {
  5155.         undef $tagInfo if $noTagInfo;
  5156.         $parms{Value} = $val;
  5157.         $parms{Table} = $tagTablePtr;
  5158.         if ($format) {
  5159.             $count or $count = int(($parms{Size} || 0) / ($formatSize{$format} || 1));
  5160.             $parms{Format} = $format . "[$count]";
  5161.         }
  5162.         $self->VerboseInfo($tag, $tagInfo, %parms);
  5163.     }
  5164.     if ($tagInfo) {
  5165.         if ($subdir) {
  5166.             my $subdirStart = $parms{Start};
  5167.             if ($$subdir{Start}) {
  5168.                 my $valuePtr = 0;
  5169.                 #### eval Start ($valuePtr)
  5170.                 $subdirStart += eval $$subdir{Start};
  5171.             }
  5172.             $dataPt or $dataPt = \$val;
  5173.             # process subdirectory information
  5174.             my %dirInfo = (
  5175.                 DirName  => $$subdir{DirName} || $$tagInfo{Name},
  5176.                 DataPt   => $dataPt,
  5177.                 DataLen  => length $$dataPt,
  5178.                 DataPos  => $parms{DataPos},
  5179.                 DirStart => $subdirStart,
  5180.                 DirLen   => $parms{Size},
  5181.                 Parent   => $parms{Parent},
  5182.                 Base     => $parms{Base},
  5183.                 Multi    => $$subdir{Multi},
  5184.                 TagInfo  => $tagInfo,
  5185.             );
  5186.             my $oldOrder = GetByteOrder();
  5187.             SetByteOrder($$subdir{ByteOrder}) if $$subdir{ByteOrder};
  5188.             my $subTablePtr = GetTagTable($$subdir{TagTable}) || $tagTablePtr;
  5189.             $self->ProcessDirectory(\%dirInfo, $subTablePtr, $$subdir{ProcessProc});
  5190.             SetByteOrder($oldOrder);
  5191.             # return now unless directory is writable as a block
  5192.             return undef unless $$tagInfo{Writable};
  5193.         }
  5194.         return $self->FoundTag($tagInfo, $val);
  5195.     }
  5196.     return undef;
  5197. }
  5198.  
  5199. #------------------------------------------------------------------------------
  5200. # Add tag to hash of extracted information
  5201. # Inputs: 0) ExifTool object reference
  5202. #         1) reference to tagInfo hash or tag name
  5203. #         2) data value (or reference to require hash if composite)
  5204. # Returns: tag key or undef if no value
  5205. sub FoundTag($$$)
  5206. {
  5207.     local $_;
  5208.     my ($self, $tagInfo, $value) = @_;
  5209.     my $tag;
  5210.  
  5211.     if (ref $tagInfo eq 'HASH') {
  5212.         $tag = $$tagInfo{Name} or warn("No tag name\n"), return undef;
  5213.     } else {
  5214.         $tag = $tagInfo;
  5215.         # look for tag in Extra
  5216.         $tagInfo = $self->GetTagInfo(GetTagTable('Image::ExifTool::Extra'), $tag);
  5217.         # make temporary hash if tag doesn't exist in Extra
  5218.         # (not advised to do this since the tag won't show in list)
  5219.         $tagInfo or $tagInfo = { Name => $tag, Groups => \%allGroupsExifTool };
  5220.         $self->{OPTIONS}{Verbose} and $self->VerboseInfo(undef, $tagInfo, Value => $value);
  5221.     }
  5222.     my $rawValueHash = $self->{VALUE};
  5223.     if ($$tagInfo{RawConv}) {
  5224.         # initialize @val for use in Composite RawConv expressions
  5225.         my @val;
  5226.         if (ref $value eq 'HASH') {
  5227.             foreach (keys %$value) { $val[$_] = $$rawValueHash{$$value{$_}}; }
  5228.         }
  5229.         my $conv = $$tagInfo{RawConv};
  5230.         local $SIG{'__WARN__'} = \&SetWarning;
  5231.         undef $evalWarning;
  5232.         if (ref $conv eq 'CODE') {
  5233.             $value = &$conv($value, $self);
  5234.         } else {
  5235.             my $val = $value;   # must do this in case eval references $val
  5236.             #### eval RawConv ($self, $val, $tag)
  5237.             $value = eval $conv;
  5238.             $@ and $evalWarning = $@;
  5239.         }
  5240.         $self->Warn("RawConv $tag: " . CleanWarning()) if $evalWarning;
  5241.         return undef unless defined $value;
  5242.     }
  5243.     # get tag priority
  5244.     my $priority = $$tagInfo{Priority};
  5245.     defined $priority or $priority = $tagInfo->{Table}{PRIORITY};
  5246.     # handle duplicate tag names
  5247.     if (defined $$rawValueHash{$tag}) {
  5248.         # add to list if there is an active list for this tag
  5249.         if ($self->{LIST_TAGS}{$tagInfo}) {
  5250.             $tag = $self->{LIST_TAGS}{$tagInfo};  # use key from previous list tag
  5251.             if (ref $$rawValueHash{$tag} ne 'ARRAY') {
  5252.                 $$rawValueHash{$tag} = [ $$rawValueHash{$tag} ];
  5253.             }
  5254.             push @{$$rawValueHash{$tag}}, $value;
  5255.             return $tag;    # return without creating a new entry
  5256.         }
  5257.         # get next available tag key
  5258.         my $nextInd = $self->{DUPL_TAG}{$tag} = ($self->{DUPL_TAG}{$tag} || 0) + 1;
  5259.         my $nextTag = "$tag ($nextInd)";
  5260. #
  5261. # take tag with highest priority
  5262. #
  5263.         # promote existing 0-priority tag so it takes precedence over a new 0-tag
  5264.         my $oldPriority = $self->{PRIORITY}{$tag} || 1;
  5265.         # set priority for this tag
  5266.         if (defined $priority) {
  5267.             # increase 0-priority tags if this is the priority directory
  5268.             $priority = 1 if not $priority and $$self{DIR_NAME} and
  5269.                              $$self{DIR_NAME} eq $$self{PRIORITY_DIR};
  5270.         } elsif ($$self{DIR_NAME} and $$self{LOW_PRIORITY_DIR}{$$self{DIR_NAME}}) {
  5271.             $priority = 0;  # default is 0 for a LOW_PRIORITY_DIR
  5272.         } else {
  5273.             $priority = 1;  # the normal default
  5274.         }
  5275.         if ($priority >= $oldPriority and not $self->{DOC_NUM}) {
  5276.             # move existing tag out of the way since this tag is higher priority
  5277.             $self->{MOVED_KEY} = $nextTag;  # used in BuildCompositeTags()
  5278.             $self->{PRIORITY}{$nextTag} = $self->{PRIORITY}{$tag};
  5279.             $$rawValueHash{$nextTag} = $$rawValueHash{$tag};
  5280.             $self->{FILE_ORDER}{$nextTag} = $self->{FILE_ORDER}{$tag};
  5281.             my $oldInfo = $self->{TAG_INFO}{$nextTag} = $self->{TAG_INFO}{$tag};
  5282.             if ($self->{TAG_EXTRA}{$tag}) {
  5283.                 $self->{TAG_EXTRA}{$nextTag} = $self->{TAG_EXTRA}{$tag};
  5284.                 delete $self->{TAG_EXTRA}{$tag};
  5285.             }
  5286.             # update tag key for list if necessary
  5287.             $self->{LIST_TAGS}{$oldInfo} = $nextTag if $self->{LIST_TAGS}{$oldInfo};
  5288.         } else {
  5289.             $tag = $nextTag;        # don't override the existing tag
  5290.         }
  5291.         $self->{PRIORITY}{$tag} = $priority;
  5292.     } elsif ($priority) {
  5293.         # set tag priority (only if exists and non-zero)
  5294.         $self->{PRIORITY}{$tag} = $priority;
  5295.     }
  5296.  
  5297.     # save the raw value, file order, tagInfo ref, group1 name,
  5298.     # and tag key for lists if necessary
  5299.     $$rawValueHash{$tag} = $value;
  5300.     $self->{FILE_ORDER}{$tag} = ++$self->{NUM_FOUND};
  5301.     $self->{TAG_INFO}{$tag} = $tagInfo;
  5302.     # set dynamic groups 1 and 3 if necessary
  5303.     $self->{TAG_EXTRA}{$tag}{G1} = $self->{SET_GROUP1} if $self->{SET_GROUP1};
  5304.     if ($self->{DOC_NUM}) {
  5305.         $self->{TAG_EXTRA}{$tag}{G3} = $self->{DOC_NUM};
  5306.         if ($self->{DOC_NUM} =~ /^(\d+)/) {
  5307.             # keep track of maximum 1st-level sub-document number
  5308.             $self->{DOC_COUNT} = $1 unless $self->{DOC_COUNT} >= $1;
  5309.         }
  5310.     }
  5311.     # remember this tagInfo if we will be accumulating values in a list
  5312.     $self->{LIST_TAGS}{$tagInfo} = $tag if $$tagInfo{List} and not $$self{NO_LIST};
  5313.  
  5314.     return $tag;
  5315. }
  5316.  
  5317. #------------------------------------------------------------------------------
  5318. # Make current directory the priority directory if not set already
  5319. # Inputs: 0) ExifTool object reference
  5320. sub SetPriorityDir($)
  5321. {
  5322.     my $self = shift;
  5323.     $self->{PRIORITY_DIR} = $self->{DIR_NAME} unless $self->{PRIORITY_DIR};
  5324. }
  5325.  
  5326. #------------------------------------------------------------------------------
  5327. # Set family 0 or 1 group name specific to this tag instance
  5328. # Inputs: 0) ExifTool ref, 1) tag key, 2) group name, 3) family (default 1)
  5329. sub SetGroup($$$;$)
  5330. {
  5331.     my ($self, $tagKey, $extra, $fam) = @_;
  5332.     $self->{TAG_EXTRA}{$tagKey}{defined $fam ? "G$fam" : 'G1'} = $extra;
  5333. }
  5334.  
  5335. #------------------------------------------------------------------------------
  5336. # Delete specified tag
  5337. # Inputs: 0) ExifTool object ref, 1) tag key
  5338. sub DeleteTag($$)
  5339. {
  5340.     my ($self, $tag) = @_;
  5341.     delete $self->{VALUE}{$tag};
  5342.     delete $self->{FILE_ORDER}{$tag};
  5343.     delete $self->{TAG_INFO}{$tag};
  5344.     delete $self->{TAG_EXTRA}{$tag};
  5345. }
  5346.  
  5347. #------------------------------------------------------------------------------
  5348. # Escape all elements of a value
  5349. # Inputs: 0) value, 1) escape proc
  5350. sub DoEscape($$)
  5351. {
  5352.     my $val;
  5353.     if (not ref $_[0]) {
  5354.         $_[0] = &{$_[1]}($_[0]);
  5355.     } elsif (ref $_[0] eq 'ARRAY') {
  5356.         foreach $val (@{$_[0]}) {
  5357.             DoEscape($val, $_[1]);
  5358.         }
  5359.     } elsif (ref $_[0] eq 'HASH') {
  5360.         foreach $val (keys %{$_[0]}) {
  5361.             DoEscape($val, $_[1]);
  5362.         }
  5363.     }
  5364. }
  5365.  
  5366. #------------------------------------------------------------------------------
  5367. # Set the FileType and MIMEType tags
  5368. # Inputs: 0) ExifTool object reference
  5369. #         1) Optional file type (uses FILE_TYPE if not specified)
  5370. #         2) Optional MIME type (uses our lookup if not specified)
  5371. # Notes:  Will NOT set file type twice (subsequent calls ignored)
  5372. sub SetFileType($;$$)
  5373. {
  5374.     my ($self, $fileType, $mimeType) = @_;
  5375.     unless ($self->{VALUE}{FileType}) {
  5376.         my $baseType = $self->{FILE_TYPE};
  5377.         $fileType or $fileType = $baseType;
  5378.         $mimeType or $mimeType = $mimeType{$fileType};
  5379.         # use base file type if necessary (except if 'TIFF', which is a special case)
  5380.         $mimeType = $mimeType{$baseType} unless $mimeType or $baseType eq 'TIFF';
  5381.         $self->FoundTag('FileType', $fileType);
  5382.         $self->FoundTag('MIMEType', $mimeType || 'application/unknown');
  5383.     }
  5384. }
  5385.  
  5386. #------------------------------------------------------------------------------
  5387. # Override the FileType and MIMEType tags
  5388. # Inputs: 0) ExifTool object ref, 1) file type
  5389. # Notes:  does nothing if FileType was not previously defined (ie. when writing)
  5390. sub OverrideFileType($$)
  5391. {
  5392.     my ($self, $fileType) = @_;
  5393.     if (defined $$self{VALUE}{FileType} and $fileType ne $$self{VALUE}{FileType}) {
  5394.         $$self{VALUE}{FileType} = $fileType;
  5395.         $$self{VALUE}{MIMEType} = $mimeType{$fileType} || 'application/unknown';
  5396.         if ($$self{OPTIONS}{Verbose}) {
  5397.             $self->VPrint(0,"$$self{INDENT}FileType [override] = $fileType\n");
  5398.             $self->VPrint(0,"$$self{INDENT}MIMEType [override] = $$self{VALUE}{MIMEType}\n");
  5399.         }
  5400.     }
  5401. }
  5402.  
  5403. #------------------------------------------------------------------------------
  5404. # Modify the value of the MIMEType tag
  5405. # Inputs: 0) ExifTool object reference, 1) file or MIME type
  5406. # Notes: combines existing type with new type: ie) a/b + c/d => c/b-d
  5407. sub ModifyMimeType($;$)
  5408. {
  5409.     my ($self, $mime) = @_;
  5410.     $mime =~ m{/} or $mime = $mimeType{$mime} or return;
  5411.     my $old = $self->{VALUE}{MIMEType};
  5412.     if (defined $old) {
  5413.         my ($a, $b) = split '/', $old;
  5414.         my ($c, $d) = split '/', $mime;
  5415.         $d =~ s/^x-//;
  5416.         $self->{VALUE}{MIMEType} = "$c/$b-$d";
  5417.         $self->VPrint(0, "  Modified MIMEType = $c/$b-$d\n");
  5418.     } else {
  5419.         $self->FoundTag('MIMEType', $mime);
  5420.     }
  5421. }
  5422.  
  5423. #------------------------------------------------------------------------------
  5424. # Print verbose output
  5425. # Inputs: 0) ExifTool ref, 1) verbose level (prints if level > this), 2-N) print args
  5426. sub VPrint($$@)
  5427. {
  5428.     my $self = shift;
  5429.     my $level = shift;
  5430.     if ($self->{OPTIONS}{Verbose} and $self->{OPTIONS}{Verbose} > $level) {
  5431.         my $out = $self->{OPTIONS}{TextOut};
  5432.         print $out @_;
  5433.     }
  5434. }
  5435.  
  5436. #------------------------------------------------------------------------------
  5437. # Verbose dump
  5438. # Inputs: 0) ExifTool ref, 1) data ref, 2-N) HexDump options
  5439. sub VerboseDump($$;%)
  5440. {
  5441.     my $self = shift;
  5442.     my $dataPt = shift;
  5443.     if ($self->{OPTIONS}{Verbose} and $self->{OPTIONS}{Verbose} > 2) {
  5444.         my %parms = (
  5445.             Prefix => $self->{INDENT},
  5446.             Out    => $self->{OPTIONS}{TextOut},
  5447.             MaxLen => $self->{OPTIONS}{Verbose} < 4 ? 96 : undef,
  5448.         );
  5449.         HexDump($dataPt, undef, %parms, @_);
  5450.     }
  5451. }
  5452.  
  5453. #------------------------------------------------------------------------------
  5454. # Extract binary data from file
  5455. # 0) ExifTool object reference, 1) offset, 2) length, 3) tag name if conditional
  5456. # Returns: binary data, or undef on error
  5457. # Notes: Returns "Binary data #### bytes" instead of data unless tag is
  5458. #        specifically requested or the Binary option is set
  5459. sub ExtractBinary($$$;$)
  5460. {
  5461.     my ($self, $offset, $length, $tag) = @_;
  5462.     my $isPreview;
  5463.  
  5464.     if ($tag and $tag eq 'PreviewImage') {
  5465.         # save PreviewImage start/length in case we want to dump trailer
  5466.         $$self{PreviewImageStart} = $offset;
  5467.         $$self{PreviewImageLength} = $length;
  5468.         $isPreview = 1;
  5469.     }
  5470.     if ($tag and not $self->{OPTIONS}{Binary} and not $self->{OPTIONS}{Verbose} and
  5471.         not $self->{REQ_TAG_LOOKUP}{lc($tag)})
  5472.     {
  5473.         return "Binary data $length bytes";
  5474.     }
  5475.     my $buff;
  5476.     unless ($self->{RAF}->Seek($offset,0)
  5477.         and $self->{RAF}->Read($buff, $length) == $length)
  5478.     {
  5479.         $tag or $tag = 'binary data';
  5480.         if ($isPreview and not $$self{BuildingComposite}) {
  5481.             $$self{PreviewError} = 1;
  5482.         } else {
  5483.             $self->Warn("Error reading $tag from file", $isPreview);
  5484.         }
  5485.         return undef;
  5486.     }
  5487.     return $buff;
  5488. }
  5489.  
  5490. #------------------------------------------------------------------------------
  5491. # Process binary data
  5492. # Inputs: 0) ExifTool object ref, 1) directory information ref, 2) tag table ref
  5493. # Returns: 1 on success
  5494. sub ProcessBinaryData($$$)
  5495. {
  5496.     my ($self, $dirInfo, $tagTablePtr) = @_;
  5497.     my $dataPt = $$dirInfo{DataPt};
  5498.     my $offset = $$dirInfo{DirStart} || 0;
  5499.     my $size = $$dirInfo{DirLen} || (length($$dataPt) - $offset);
  5500.     my $base = $$dirInfo{Base} || 0;
  5501.     my $verbose = $self->{OPTIONS}{Verbose};
  5502.     my $unknown = $self->{OPTIONS}{Unknown};
  5503.     my $dataPos = $$dirInfo{DataPos} || 0;
  5504.  
  5505.     # get default format ('int8u' unless specified)
  5506.     my $defaultFormat = $$tagTablePtr{FORMAT} || 'int8u';
  5507.     my $increment = $formatSize{$defaultFormat};
  5508.     unless ($increment) {
  5509.         warn "Unknown format $defaultFormat\n";
  5510.         $defaultFormat = 'int8u';
  5511.         $increment = $formatSize{$defaultFormat};
  5512.     }
  5513.     # prepare list of tag numbers to extract
  5514.     my @tags;
  5515.     if ($unknown > 1 and defined $$tagTablePtr{FIRST_ENTRY}) {
  5516.         # scan through entire binary table
  5517.         @tags = ($$tagTablePtr{FIRST_ENTRY}..(int($size/$increment) - 1));
  5518.         # add in floating point tag ID's if they exist
  5519.         my @ftags = grep /\./, TagTableKeys($tagTablePtr);
  5520.         @tags = sort { $a <=> $b } @tags, @ftags if @ftags;
  5521.     } elsif ($$dirInfo{DataMember}) {
  5522.         @tags = @{$$dirInfo{DataMember}};
  5523.         $verbose = 0;   # no verbose output of extracted values when writing
  5524.     } else {
  5525.         # extract known tags in numerical order
  5526.         @tags = sort { $a <=> $b } TagTableKeys($tagTablePtr);
  5527.     }
  5528.     $self->VerboseDir('BinaryData', undef, $size) if $verbose;
  5529.     # avoid creating unknown tags for tags that fail condition if Unknown is 1
  5530.     $$self{NO_UNKNOWN} = 1 if $unknown < 2;
  5531.     my ($index, %val);
  5532.     my $nextIndex = 0;
  5533.     my $varSize = 0;
  5534.     foreach $index (@tags) {
  5535.         my ($tagInfo, $val, $saveNextIndex, $len, $mask);
  5536.         if ($$tagTablePtr{$index}) {
  5537.             $tagInfo = $self->GetTagInfo($tagTablePtr, $index) or next;
  5538.             next if $$tagInfo{Unknown} and
  5539.                    ($$tagInfo{Unknown} > $unknown or $index < $nextIndex);
  5540.         } else {
  5541.             # don't generate unknown tags in binary tables unless Unknown > 1
  5542.             next unless $unknown > 1;
  5543.             next if $index < $nextIndex;    # skip if data already used
  5544.             $tagInfo = $self->GetTagInfo($tagTablePtr, $index) or next;
  5545.             $$tagInfo{Unknown} = 2;    # set unknown to 2 for binary unknowns
  5546.         }
  5547.         # get relative offset of this entry
  5548.         my $entry = int($index) * $increment + $varSize;
  5549.         my $more = $size - $entry;
  5550.         last if $more <= 0;     # all done if we have reached the end of data
  5551.         my $count = 1;
  5552.         my $format = $$tagInfo{Format};
  5553.         if (not $format) {
  5554.             $format = $defaultFormat;
  5555.         } elsif ($format eq 'string') {
  5556.             # string with no specified count runs to end of block
  5557.             $count = $more;
  5558.         } elsif ($format eq 'pstring') {
  5559.             $format = 'string';
  5560.             $count = Get8u($dataPt, ($entry++)+$offset);
  5561.             --$more;
  5562.         } elsif (not $formatSize{$format}) {
  5563.             if ($format =~ /(.*)\[(.*)\]/) {
  5564.                 # handle format count field
  5565.                 $format = $1;
  5566.                 $count = $2;
  5567.                 # evaluate count to allow count to be based on previous values
  5568.                 #### eval Format size (%val, $size, $self)
  5569.                 $count = eval $count;
  5570.                 $@ and warn("Format $$tagInfo{Name}: $@"), next;
  5571.                 next if $count < 0;
  5572.             } elsif ($format =~ /^var_/) {
  5573.                 # handle variable-length string formats
  5574.                 $format = substr($format, 4);
  5575.                 pos($$dataPt) = $entry + $offset;
  5576.                 undef $count;
  5577.                 if ($format eq 'ustring') {
  5578.                     $count = pos($$dataPt) - ($entry+$offset) if $$dataPt =~ /\G(..)*?\0\0/sg;
  5579.                     $varSize -= 2;  # ($count includes base size of 2 bytes)
  5580.                 } elsif ($format eq 'pstring') {
  5581.                     $count = Get8u($dataPt, ($entry++)+$offset);
  5582.                     --$more;
  5583.                 } elsif ($$dataPt =~ /\0/g) {
  5584.                     $count = pos($$dataPt) - ($entry+$offset);
  5585.                     --$varSize;     # ($count includes base size of 1 byte)
  5586.                 }
  5587.                 $count = $more if not defined $count or $count > $more;
  5588.                 $varSize += $count; # shift subsequent indices
  5589.                 $val = substr($$dataPt, $entry+$offset, $count);
  5590.                 $val = $self->Decode($val, 'UCS2') if $format eq 'ustring';
  5591.                 $val =~ s/\0.*//s;  # truncate at null
  5592.             }
  5593.         }
  5594.         if ($unknown > 1) {
  5595.             # calculate next valid index for unknown tag
  5596.             my $ni = int($index) + ($formatSize{$format} * $count) / $increment;
  5597.             $saveNextIndex = $nextIndex;
  5598.             $nextIndex = $ni unless $nextIndex > $ni;
  5599.         }
  5600.         # read value now if necessary
  5601.         unless (defined $val and not $$tagInfo{SubDirectory}) {
  5602.             $val = ReadValue($dataPt, $entry+$offset, $format, $count, $more);
  5603.             $mask = $$tagInfo{Mask};
  5604.             $val &= $mask if $mask;
  5605.         }
  5606.         if ($verbose and not $$tagInfo{Hidden}) {
  5607.             if (not $$tagInfo{SubDirectory} or $$tagInfo{Format}) {
  5608.                 $len = $count * ($formatSize{$format} || 1);
  5609.                 $len = $more if $len > $more;
  5610.             } else {
  5611.                 $len = $more;
  5612.             }
  5613.             $self->VerboseInfo($index, $tagInfo,
  5614.                 Table  => $tagTablePtr,
  5615.                 Value  => $val,
  5616.                 DataPt => $dataPt,
  5617.                 Size   => $len,
  5618.                 Start  => $entry+$offset,
  5619.                 Addr   => $entry+$offset+$base+$dataPos,
  5620.                 Format => $format,
  5621.                 Count  => $count,
  5622.                 Extra  => $mask ? sprintf(', mask 0x%.2x',$mask) : undef,
  5623.             );
  5624.         }
  5625.         # parse nested BinaryData directories
  5626.         if ($$tagInfo{SubDirectory}) {
  5627.             # use specified subdirectory length if given
  5628.             if ($$tagInfo{Format} and $formatSize{$format}) {
  5629.                 $len = $count * $formatSize{$format};
  5630.                 $len = $more if $len > $more;
  5631.             } else {
  5632.                 $len = $more;
  5633.             }
  5634.             my %subdirInfo = (
  5635.                 DataPt   => $dataPt,
  5636.                 DirStart => $entry + $offset,
  5637.                 DirLen   => $len,
  5638.                 Base     => $base,
  5639.             );
  5640.             my $subTablePtr = GetTagTable($tagInfo->{SubDirectory}{TagTable});
  5641.             $self->ProcessDirectory(\%subdirInfo, $subTablePtr);
  5642.             next;
  5643.         }
  5644.         if ($$tagInfo{IsOffset}) {
  5645.             my $exifTool = $self;
  5646.             #### eval IsOffset ($val, $exifTool)
  5647.             $val += $base + $$self{BASE} if eval $$tagInfo{IsOffset};
  5648.         }
  5649.         $val{$index} = $val;
  5650.         unless ($self->FoundTag($tagInfo,$val)) {
  5651.             # don't increment nextIndex if we didn't extract a tag
  5652.             $nextIndex = $saveNextIndex if defined $saveNextIndex;
  5653.         }
  5654.     }
  5655.     delete $$self{NO_UNKNOWN};
  5656.     return 1;
  5657. }
  5658.  
  5659. #..............................................................................
  5660. # Load .ExifTool_config file from user's home directory
  5661. # (use of noConfig is now deprecated, use configFile = '' instead)
  5662. until ($Image::ExifTool::noConfig) {
  5663.     my $file = $Image::ExifTool::configFile;
  5664.     if (not defined $file) {
  5665.         my $config = '.ExifTool_config';
  5666.         # get our home directory (HOMEDRIVE and HOMEPATH are used in Windows cmd shell)
  5667.         my $home = $ENV{EXIFTOOL_HOME} || $ENV{HOME} ||
  5668.                    ($ENV{HOMEDRIVE} || '') . ($ENV{HOMEPATH} || '') || '.';
  5669.         # look for the config file in 1) the home directory, 2) the program dir
  5670.         $file = "$home/$config";
  5671.         -r $file or $file = ($0 =~ /(.*[\\\/])/ ? $1 : './') . $config;
  5672.         -r $file or last;
  5673.     } else {
  5674.         length $file or last;   # filename of "" disables configuration
  5675.         -r $file or warn("Config file not found\n"), last;
  5676.     }
  5677.     eval "require '$file'"; # load the config file
  5678.     # print warning (minus "Compilation failed" part)
  5679.     $@ and $_=$@, s/Compilation failed.*//s, warn $_;
  5680.     if (@Image::ExifTool::UserDefined::Lenses) {
  5681.         foreach (@Image::ExifTool::UserDefined::Lenses) {
  5682.             $Image::ExifTool::userLens{$_} = 1;
  5683.         }
  5684.     }
  5685.     last;
  5686. }
  5687.  
  5688. #------------------------------------------------------------------------------
  5689. 1;  # end
  5690.